OSDN Git Service

initial import
authortoshinagata1964 <toshinagata1964@a2be9bc6-48de-4e38-9406-05402d4bc13c>
Thu, 21 Jan 2010 16:56:59 +0000 (16:56 +0000)
committertoshinagata1964 <toshinagata1964@a2be9bc6-48de-4e38-9406-05402d4bc13c>
Thu, 21 Jan 2010 16:56:59 +0000 (16:56 +0000)
git-svn-id: svn+ssh://svn.sourceforge.jp/svnroot/molby/trunk@1 a2be9bc6-48de-4e38-9406-05402d4bc13c

176 files changed:
COPYING [new file with mode: 0644]
COPYING.ja [new file with mode: 0644]
Documents/etc/gpl.ja.txt [new file with mode: 0644]
Documents/etc/gpl.txt [new file with mode: 0644]
Documents/etc/indane.png [new file with mode: 0644]
Documents/etc/style.css [new file with mode: 0644]
Documents/etc/tutorial_01.png [new file with mode: 0644]
Documents/etc/tutorial_02.png [new file with mode: 0644]
Documents/etc/tutorial_03.png [new file with mode: 0644]
Documents/etc/tutorial_04.png [new file with mode: 0644]
Documents/etc/tutorial_05.png [new file with mode: 0644]
Documents/etc/tutorial_06.png [new file with mode: 0644]
Documents/etc/tutorial_07.png [new file with mode: 0644]
Documents/etc/tutorial_08.png [new file with mode: 0644]
Documents/etc/tutorial_09.png [new file with mode: 0644]
Documents/etc/tutorial_10.png [new file with mode: 0644]
Documents/etc/tutorial_11.png [new file with mode: 0644]
Documents/etc/tutorial_12.png [new file with mode: 0644]
Documents/etc/tutorial_13.png [new file with mode: 0644]
Documents/etc/tutorial_14.png [new file with mode: 0644]
Documents/etc/tutorial_15.png [new file with mode: 0644]
Documents/etc/tutorial_16.png [new file with mode: 0644]
Documents/etc/tutorial_17.png [new file with mode: 0644]
Documents/etc/tutorial_18.png [new file with mode: 0644]
Documents/etc/tutorial_19.png [new file with mode: 0644]
Documents/etc/tutorial_20.png [new file with mode: 0644]
Documents/etc/tutorial_21.png [new file with mode: 0644]
Documents/etc/tutorial_22.png [new file with mode: 0644]
Documents/etc/tutorial_23.png [new file with mode: 0644]
Documents/etc/tutorial_24.png [new file with mode: 0644]
Documents/etc/tutorial_25.png [new file with mode: 0644]
Documents/etc/tutorial_26.png [new file with mode: 0644]
Documents/etc/tutorial_27.png [new file with mode: 0644]
Documents/etc/tutorial_28.png [new file with mode: 0644]
Documents/etc/tutorial_29.png [new file with mode: 0644]
Documents/etc/tutorial_30.png [new file with mode: 0644]
Documents/etc/tutorial_31.png [new file with mode: 0644]
Documents/etc/tutorial_32.png [new file with mode: 0644]
Documents/etc/tutorial_33.png [new file with mode: 0644]
Documents/etc/tutorial_34.png [new file with mode: 0644]
Documents/etc/tutorial_35.png [new file with mode: 0644]
Documents/etc/tutorial_36.png [new file with mode: 0644]
Documents/etc/tutorial_37.png [new file with mode: 0644]
Documents/etc/tutorial_38.png [new file with mode: 0644]
Documents/etc/tutorial_39.png [new file with mode: 0644]
Documents/makedoc.rb [new file with mode: 0644]
Documents/src/doc_source.html [new file with mode: 0644]
Makefile [new file with mode: 0755]
MolLib/Dcd.c [new file with mode: 0644]
MolLib/Dcd.h [new file with mode: 0644]
MolLib/IntGroup.c [new file with mode: 0755]
MolLib/IntGroup.h [new file with mode: 0755]
MolLib/MD/MDCore.c [new file with mode: 0644]
MolLib/MD/MDCore.h [new file with mode: 0644]
MolLib/MD/MDForce.c [new file with mode: 0644]
MolLib/MD/MDForce.h [new file with mode: 0644]
MolLib/MD/MDGraphite.c [new file with mode: 0644]
MolLib/MD/MDGraphite.h [new file with mode: 0644]
MolLib/MD/MDPressure.c [new file with mode: 0644]
MolLib/MD/MDPressure.h [new file with mode: 0644]
MolLib/MD/MDSurface.c [new file with mode: 0644]
MolLib/MD/MDSurface.h [new file with mode: 0644]
MolLib/MainView.c [new file with mode: 0755]
MolLib/MainView.h [new file with mode: 0755]
MolLib/Makefile [new file with mode: 0755]
MolLib/Missing.c [new file with mode: 0755]
MolLib/Missing.h [new file with mode: 0755]
MolLib/MolAction.c [new file with mode: 0644]
MolLib/MolAction.h [new file with mode: 0644]
MolLib/MolLib.h [new file with mode: 0755]
MolLib/Molecule.c [new file with mode: 0755]
MolLib/Molecule.h [new file with mode: 0755]
MolLib/Object.c [new file with mode: 0755]
MolLib/Object.h [new file with mode: 0755]
MolLib/Parameter.c [new file with mode: 0755]
MolLib/Parameter.h [new file with mode: 0755]
MolLib/Ruby_bind/Makefile [new file with mode: 0755]
MolLib/Ruby_bind/Molby.h [new file with mode: 0755]
MolLib/Ruby_bind/Molby_extern.h [new file with mode: 0755]
MolLib/Ruby_bind/ruby_bind.c [new file with mode: 0644]
MolLib/Ruby_bind/ruby_dialog.c [new file with mode: 0644]
MolLib/Ruby_bind/ruby_dialog.h [new file with mode: 0644]
MolLib/Ruby_bind/ruby_md.c [new file with mode: 0644]
MolLib/Ruby_bind/ruby_types.c [new file with mode: 0644]
MolLib/Trackball.c [new file with mode: 0644]
MolLib/Trackball.h [new file with mode: 0644]
MolLib/Types.c [new file with mode: 0755]
MolLib/Types.h [new file with mode: 0755]
README [new file with mode: 0644]
Scripts/6311Gdp.txt [new file with mode: 0644]
Scripts/631Gd.txt [new file with mode: 0644]
Scripts/631Gdp.txt [new file with mode: 0644]
Scripts/LanL2DZ.txt [new file with mode: 0644]
Scripts/commands.rb [new file with mode: 0755]
Scripts/default.par [new file with mode: 0644]
Scripts/dialog.rb [new file with mode: 0755]
Scripts/dispatom.par [new file with mode: 0644]
Scripts/formula.rb [new file with mode: 0755]
Scripts/gaff.par [new file with mode: 0644]
Scripts/gamess.rb [new file with mode: 0755]
Scripts/lib/scanf.rb [new file with mode: 0644]
Scripts/loadsave.rb [new file with mode: 0755]
Scripts/md.rb [new file with mode: 0755]
Scripts/molecule.rb [new file with mode: 0755]
Scripts/parm99.par [new file with mode: 0644]
Scripts/startup.rb [new file with mode: 0755]
Scripts/transform.rb [new file with mode: 0755]
Version [new file with mode: 0644]
amberparm2molby.pl [new file with mode: 0644]
bitmaps/chart.xpm [new file with mode: 0755]
bitmaps/copy.xpm [new file with mode: 0755]
bitmaps/cut.xpm [new file with mode: 0755]
bitmaps/help.xpm [new file with mode: 0755]
bitmaps/jump_to_end.xpm [new file with mode: 0755]
bitmaps/jump_to_start.xpm [new file with mode: 0755]
bitmaps/molby_icon.icns [new file with mode: 0644]
bitmaps/molby_icon.ico [new file with mode: 0644]
bitmaps/molby_icon.tif [new file with mode: 0644]
bitmaps/new.xpm [new file with mode: 0755]
bitmaps/open.xpm [new file with mode: 0755]
bitmaps/paste.xpm [new file with mode: 0755]
bitmaps/play_backward.xpm [new file with mode: 0755]
bitmaps/play_forward.xpm [new file with mode: 0755]
bitmaps/preview.xpm [new file with mode: 0755]
bitmaps/print.xpm [new file with mode: 0755]
bitmaps/rotate_bond.xpm [new file with mode: 0755]
bitmaps/rotate_x.xpm [new file with mode: 0755]
bitmaps/rotate_y.xpm [new file with mode: 0755]
bitmaps/sample.xpm [new file with mode: 0755]
bitmaps/save.xpm [new file with mode: 0755]
clapack-3.1.1.1-mingw.patch [new file with mode: 0755]
memo.txt [new file with mode: 0755]
msw-build/Makefile [new file with mode: 0755]
msw-build/molby.iss [new file with mode: 0755]
msw-build/molby.rc [new file with mode: 0644]
osx-build/Info.plist [new file with mode: 0644]
osx-build/Makefile [new file with mode: 0755]
ruby-1.8.7-p160-tn.patch [new file with mode: 0644]
update_version.rb [new file with mode: 0644]
wxSources/ConsoleFrame.cpp [new file with mode: 0755]
wxSources/ConsoleFrame.h [new file with mode: 0755]
wxSources/GlobalParameterFilesFrame.cpp [new file with mode: 0644]
wxSources/GlobalParameterFilesFrame.h [new file with mode: 0644]
wxSources/GlobalParameterFrame.cpp [new file with mode: 0755]
wxSources/GlobalParameterFrame.h [new file with mode: 0755]
wxSources/MoleculeView.cpp [new file with mode: 0755]
wxSources/MoleculeView.h [new file with mode: 0755]
wxSources/MyApp.cpp [new file with mode: 0755]
wxSources/MyApp.h [new file with mode: 0755]
wxSources/MyClipboardData.cpp [new file with mode: 0644]
wxSources/MyClipboardData.h [new file with mode: 0644]
wxSources/MyCommand.cpp [new file with mode: 0755]
wxSources/MyCommand.h [new file with mode: 0755]
wxSources/MyDocManager.cpp [new file with mode: 0644]
wxSources/MyDocManager.h [new file with mode: 0644]
wxSources/MyDocument.cpp [new file with mode: 0755]
wxSources/MyDocument.h [new file with mode: 0755]
wxSources/MyGLCanvas.cpp [new file with mode: 0755]
wxSources/MyGLCanvas.h [new file with mode: 0755]
wxSources/MyListCtrl.cpp [new file with mode: 0644]
wxSources/MyListCtrl.h [new file with mode: 0644]
wxSources/MySlider.cpp [new file with mode: 0755]
wxSources/MySlider.h [new file with mode: 0755]
wxSources/MyThread.cpp [new file with mode: 0644]
wxSources/MyThread.h [new file with mode: 0644]
wxSources/MyVersion.c [new file with mode: 0644]
wxSources/ProgressFrame.cpp [new file with mode: 0755]
wxSources/ProgressFrame.h [new file with mode: 0755]
wxSources/RubyDialogFrame.cpp [new file with mode: 0644]
wxSources/RubyDialogFrame.h [new file with mode: 0644]
wxSources/listctrl.cpp [new file with mode: 0644]
xcode-build/English.lproj/InfoPlist.strings [new file with mode: 0755]
xcode-build/Info.plist [new file with mode: 0755]
xcode-build/Molby.xcodeproj/project.pbxproj [new file with mode: 0755]
xcode-build/Molby.xcodeproj/toshi_n.mode1v3 [new file with mode: 0644]
xcode-build/Molby.xcodeproj/toshi_n.pbxuser [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..1bf1526
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          59 Temple Place, Suite 330, Boston, MA 02111 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.ja b/COPYING.ja
new file mode 100644 (file)
index 0000000..cb6d419
--- /dev/null
@@ -0,0 +1,416 @@
+                    GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+                       ¥Ð¡¼¥¸¥ç¥ó2¡¢1991ǯ6·î
+                       ÆüËܸìÌõ¡¢2002ǯ5·î20Æü
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ ¤³¤ÎÍøÍѵöÂú·ÀÌó½ñ¤ò¡¢°ì»ú°ì¶ç¤½¤Î¤Þ¤Þ¤ËÊ£À½¤·ÈÒÉÛ¤¹¤ë¤³¤È¤Ïµö²Ä¤¹¤ë¡£
+ ¤·¤«¤·Êѹ¹¤Ïǧ¤á¤Ê¤¤¡£
+
+ This is an unofficial translation of the GNU General Public License
+ into Japanese.  It was not published by the Free Software Foundation,
+ and does not legally state the distribution terms for software that
+ uses the GNU GPL--only the original English text of the GNU GPL does
+ that. However, we hope that this translation will help Japanese
+ speakers understand the GNU GPL better.
+
+ (Ìõ: °Ê²¼¤ÏGNU General Public License¤ÎÈó¸ø¼°¤ÊÆüËܸìÌõ¤Ç¤¹¡£¤³¤ì¤Ï¥Õ
+ ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃÄ(the Free Software Foundataion)¤Ë¤è¤Ã¤Æȯɽ¤µ¤ì¤¿
+ ¤â¤Î¤Ç¤Ï¤Ê¤¯¡¢GNU GPL¤òŬÍѤ·¤¿¥½¥Õ¥È¥¦¥§¥¢¤ÎÈÒÉÛ¾ò·ï¤òˡŪ¤ËÍ­¸ú¤Ê·Á
+ ¤Ç½Ò¤Ù¤¿¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£ÈÒÉÛ¾ò·ï¤È¤·¤Æ¤ÏGNU GPL¤Î±Ñ¸ìÈǥƥ­¥¹¥È¤Ç
+ »ØÄꤵ¤ì¤Æ¤¤¤ë¤â¤Î¤Î¤ß¤¬Í­¸ú¤Ç¤¹¡£¤·¤«¤·¤Ê¤¬¤é¡¢»ä¤¿¤Á¤Ï¤³¤ÎËÝÌõ¤¬¡¢
+ ÆüËܸì¤ò»ÈÍѤ¹¤ë¿Í¡¹¤Ë¤È¤Ã¤ÆGNU GPL¤ò¤è¤êÎɤ¯Íý²ò¤¹¤ë½õ¤±¤È¤Ê¤ë¤³¤È¤ò
+ Ë¾¤ó¤Ç¤¤¤Þ¤¹¡£)
+
+ ËÝÌõ¤Ï È¬ÅÄ¿¿¹Ô<mhatta@gnu.org>¤¬¹Ô¤Ã¤¿¡£¸¶Ê¸¤Ï
+ http://www.gnu.org/licenses/gpl.txt¤Ç¤¢¤ë¡£¸íÌõ¤Î»ØŦ¤ä²þÁ±°Æ¤ò´¿·Þ¤¹
+ ¤ë¡£
+                            ¤Ï¤¸¤á¤Ë
+
+¥½¥Õ¥È¥¦¥§¥¢¸þ¤±¥é¥¤¥»¥ó¥¹¤ÎÂçȾ¤Ï¡¢¤¢¤Ê¤¿¤¬¤½¤Î¥½¥Õ¥È¥¦¥§¥¢¤ò¶¦Í­¤·¤¿
+¤êÊѹ¹¤·¤¿¤ê¤¹¤ë¼«Í³¤òÃ¥¤¦¤è¤¦¤ËÀ߷פµ¤ì¤Æ¤¤¤Þ¤¹¡£ÂоÈŪ¤Ë¡¢GNU °ìÈ̸ø
+½°ÍøÍѵöÂú·ÀÌó½ñ¤Ï¡¢¤¢¤Ê¤¿¤¬¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤ò¶¦Í­¤·¤¿¤êÊѹ¹¤·¤¿¤ê¤¹
+¤ë¼«Í³¤òÊݾڤ¹¤ë--¤¹¤Ê¤ï¤Á¡¢¥½¥Õ¥È¥¦¥§¥¢¤¬¤½¤Î¥æ¡¼¥¶¤¹¤Ù¤Æ¤Ë¤È¤Ã¤Æ¥Õ¥ê¡¼
+¤Ç¤¢¤ë¤³¤È¤òÊݾڤ¹¤ë¤³¤È¤òÌÜŪ¤È¤·¤Æ¤¤¤Þ¤¹¡£¤³¤Î°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+¤Ï¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤΥ½¥Õ¥È¥¦¥§¥¢¤Î¤Û¤È¤ó¤É¤ËŬÍѤµ¤ì¤Æ¤ª¤ê¡¢¤Þ¤¿
+GNU GPL¤òŬÍѤ¹¤ë¤È·è¤á¤¿¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃİʳ°¤Îºî¼Ô¤Ë¤è¤ë¥×¥í¥°
+¥é¥à¤Ë¤âŬÍѤµ¤ì¤Æ¤¤¤Þ¤¹(¤¤¤¯¤Ä¤«¤Î¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤΥ½¥Õ¥È¥¦¥§
+¥¢¤Ë¤Ï¡¢GNU GPL¤Ç¤Ï¤Ê¤¯GNU ¥é¥¤¥Ö¥é¥ê°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤¬Å¬ÍѤµ¤ì
+¤Æ¤¤¤ë¤³¤È¤â¤¢¤ê¤Þ¤¹)¡£¤¢¤Ê¤¿¤â¤Þ¤¿¡¢¤´¼«Ê¬¤Î¥×¥í¥°¥é¥à¤ËGNU GPL¤òŬÍÑ
+¤¹¤ë¤³¤È¤¬²Äǽ¤Ç¤¹¡£
+
+»ä¤¿¤Á¤¬¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤È¸À¤¦¤È¤­¡¢¤½¤ì¤ÏÍøÍѤμ«Í³¤Ë¤Ä¤¤¤Æ¸ÀµÚ¤·¤Æ
+¤¤¤ë¤Î¤Ç¤¢¤Ã¤Æ¡¢²Á³Ê¤ÏÌäÂê¤Ë¤·¤Æ¤¤¤Þ¤»¤ó¡£»ä¤¿¤Á¤Î°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó
+½ñ¤Ï¡¢¤¢¤Ê¤¿¤¬¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤ÎÊ£À½Êª¤òÈÒÉÛ¤¹¤ë¼«Í³¤òÊݾڤ¹¤ë¤è¤¦Àß
+·×¤µ¤ì¤Æ¤¤¤Þ¤¹(´õ˾¤Ë±þ¤¸¤Æ¤½¤Î¼ï¤Î¥µ¡¼¥Ó¥¹¤Ë¼ê¿ôÎÁ¤ò²Ý¤¹¼«Í³¤âÊݾڤµ
+¤ì¤Þ¤¹)¡£¤Þ¤¿¡¢¤¢¤Ê¤¿¤¬¥½¡¼¥¹¥³¡¼¥É¤ò¼õ¤±¼è¤ë¤«¡¢¤¢¤ë¤¤¤Ï˾¤á¤Ð¤½¤ì¤ò
+Æþ¼ê¤¹¤ë¤³¤È¤¬²Äǽ¤Ç¤¢¤ë¤È¤¤¤¦¤³¤È¡¢¤¢¤Ê¤¿¤¬¥½¥Õ¥È¥¦¥§¥¢¤òÊѹ¹¤·¡¢¤½¤Î
+°ìÉô¤ò¿·¤¿¤Ê¥Õ¥ê¡¼¤Î¥×¥í¥°¥é¥à¤ÇÍøÍѤǤ­¤ë¤È¤¤¤¦¤³¤È¡¢¤½¤·¤Æ¡¢°Ê¾å¤Ç½Ò
+¤Ù¤¿¤è¤¦¤Ê¤³¤È¤¬¤Ç¤­¤ë¤È¤¤¤¦¤³¤È¤¬¤¢¤Ê¤¿¤ËÃΤ餵¤ì¤ë¤È¤¤¤¦¤³¤È¤âÊݾڤµ
+¤ì¤Þ¤¹¡£
+
+¤¢¤Ê¤¿¤Î¸¢Íø¤ò¼é¤ë¤¿¤á¡¢»ä¤¿¤Á¤Ï狼¤¬¤¢¤Ê¤¿¤ÎÍ­¤¹¤ë¤³¤ì¤é¤Î¸¢Íø¤òÈÝÄê
+¤¹¤ë¤³¤È¤ä¡¢¤³¤ì¤é¤Î¸¢Íø¤òÊü´þ¤¹¤ë¤è¤¦Í׵᤹¤ë¤³¤È¤ò¶Ø»ß¤¹¤ë¤È¤¤¤¦À©¸Â
+¤ò²Ã¤¨¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£¤è¤Ã¤Æ¡¢¤¢¤Ê¤¿¤¬¥½¥Õ¥È¥¦¥§¥¢¤ÎÊ£À½Êª¤òÈÒÉÛ¤·¤¿
+¤ê¤½¤ì¤òÊѹ¹¤·¤¿¤ê¤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¤³¤ì¤é¤ÎÀ©¸Â¤Î¤¿¤á¤Ë¤¢¤Ê¤¿¤Ë¤¢¤ë¼ï¤ÎÀÕ
+Ǥ¤¬È¯À¸¤¹¤ë¤³¤È¤Ë¤Ê¤ê¤Þ¤¹¡£
+
+Î㤨¤Ð¡¢¤¢¤Ê¤¿¤¬¥Õ¥ê¡¼¤Ê¥×¥í¥°¥é¥à¤ÎÊ£À½Êª¤òÈÒÉÛ¤¹¤ë¾ì¹ç¡¢Í­ÎÁ¤«ÌµÎÁ¤Ë
+´Ø¤ï¤é¤º¡¢¤¢¤Ê¤¿¤Ï¼«Ê¬¤¬Í­¤¹¤ë¸¢Íø¤òÁ´¤Æ¼õÎμԤËÍ¿¤¨¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+¤Þ¤¿¡¢¤¢¤Ê¤¿¤ÏÈà¤é¤â¥½¡¼¥¹¥³¡¼¥É¤ò¼õ¤±¼è¤ë¤«¼ê¤ËÆþ¤ì¤ë¤³¤È¤¬¤Ç¤­¤ë¤è¤¦
+Êݾڤ·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£¤½¤·¤Æ¡¢¤¢¤Ê¤¿¤ÏÈà¤é¤ËÂФ·¤Æ°Ê²¼¤Ç½Ò¤Ù¤ë¾ò·ï
+¤ò¼¨¤·¡¢Èà¤é¤Ë¼«¤é¤Î»ý¤Ä¸¢Íø¤Ë¤Ä¤¤¤ÆÃΤ餷¤á¤ë¤è¤¦¤Ë¤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»
+¤ó¡£
+
+»ä¤¿¤Á¤Ï¤¢¤Ê¤¿¤Î¸¢Íø¤òÆóÃʳ¬¤Î¼ê½ç¤òƧ¤ó¤ÇÊݸ¤Þ¤¹¡£(1) ¤Þ¤º¥½¥Õ¥È¥¦¥§
+¥¢¤ËÂФ·¤ÆÃøºî¸¢¤ò¼çÄ¥¤·¡¢¤½¤·¤Æ (2) ¤¢¤Ê¤¿¤ËÂФ·¤Æ¡¢¥½¥Õ¥È¥¦¥§¥¢¤ÎÊ£
+À½¤äÈÒÉÛ¤Þ¤¿¤Ï²þÊѤˤĤ¤¤Æ¤ÎˡŪ¤Êµö²Ä¤òÍ¿¤¨¤ë¤³¤Î·ÀÌó½ñ¤òÄ󼨤·¤Þ¤¹¡£
+
+¤Þ¤¿¡¢³Æºî¼Ô¤ä»ä¤¿¤Á¤òÊݸ¤ë¤¿¤á¡¢»ä¤¿¤Á¤Ï¤³¤Î¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ë¤Ï
+²¿¤ÎÊݾڤâ̵¤¤¤È¤¤¤¦¤³¤È¤òï¤â¤¬³Î¼Â¤ËÍý²ò¤¹¤ë¤è¤¦¤Ë¤·¡¢¤Þ¤¿¥½¥Õ¥È¥¦¥§
+¥¢¤¬Ã¯¤«Â¾¿Í¤Ë¤è¤Ã¤Æ²þÊѤµ¤ì¡¢¤½¤ì¤¬¼¡¡¹¤ÈÈÒÉÛ¤µ¤ì¤Æ¤¤¤Ã¤¿¤È¤·¤Æ¤â¡¢¤½
+¤Î¼õÎμԤÏÈà¤é¤¬¼ê¤ËÆþ¤ì¤¿¥½¥Õ¥È¥¦¥§¥¢¤¬¥ª¥ê¥¸¥Ê¥ë¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç¤Ï̵¤¤
+¤³¤È¡¢¤½¤·¤Æ¸¶ºî¼Ô¤Î̾À¼¤Ï¾¿Í¤Ë¤è¤Ã¤Æ»ý¤Á¹þ¤Þ¤ì¤¿²ÄǽÀ­¤Î¤¢¤ëÌäÂê¤Ë¤è¤Ã
+¤Æ±Æ¶Á¤µ¤ì¤ë¤³¤È¤¬¤Ê¤¤¤È¤¤¤¦¤³¤È¤ò¼þÃΤµ¤»¤¿¤¤¤È»×¤¤¤Þ¤¹¡£
+
+ºÇ¸å¤Ë¡¢¥½¥Õ¥È¥¦¥§¥¢Æõö¤¬¤¤¤«¤Ê¤ë¥Õ¥ê¡¼¤Î¥×¥í¥°¥é¥à¤Î¸ºß¤Ë¤âÉÔÃǤζ¼
+°Ò¤òÅꤲ¤«¤±¤Æ¤¤¤Þ¤¹¤¬¡¢»ä¤¿¤Á¤Ï¡¢¥Õ¥ê¡¼¤Ê¥×¥í¥°¥é¥à¤ÎºÆÈÒÉÛ¼Ô¤¬¸Ä¡¹¤Ë
+Æõö¥é¥¤¥»¥ó¥¹¤ò¼èÆÀ¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¡¢»ö¼Â¾å¥×¥í¥°¥é¥à¤òÆÈÀêŪ¤Ë¤·¤Æ¤·
+¤Þ¤¦¤È¤¤¤¦´í¸±¤òÈò¤±¤¿¤¤¤È»×¤¤¤Þ¤¹¡£¤³¤¦¤¤¤Ã¤¿»öÂÖ¤òͽËɤ¹¤ë¤¿¤á¡¢»ä¤¿
+¤Á¤Ï¤¤¤«¤Ê¤ëÆõö¤âï¤â¤¬¼«Í³¤ËÍøÍѤǤ­¤ë¤è¤¦¥é¥¤¥»¥ó¥¹¤µ¤ì¤ë¤«¡¢Á´¤¯¥é
+¥¤¥»¥ó¥¹¤µ¤ì¤Ê¤¤¤«¤Î¤É¤Á¤é¤«¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤³¤È¤òÌÀ³Î¤Ë¤·¤Þ¤·¤¿¡£
+
+(ÌõÃí: ËÜ·ÀÌó½ñ¤Ç¡ÖÆÈÀêŪ(proprietary)¡×¤È¤Ï¡¢¥½¥Õ¥È¥¦¥§¥¢¤ÎÍøÍѤäºÆÈÒ
+ÉÛ¡¢²þÊѤ¬¶Ø»ß¤µ¤ì¤Æ¤¤¤ë¤«¡¢µö²Ä¤òÆÀ¤ë¤³¤È¤¬É¬ÍפȤµ¤ì¤Æ¤¤¤ë¤«¡¢¤¢¤ë¤¤
+¤Ï¸·¤·¤¤À©¸Â¤¬²Ý¤»¤é¤ì¤Æ¤¤¤Æ¼«Í³¤Ë¤½¤¦¤¹¤ë¤³¤È¤¬»ö¼Â¾å¤Ç¤­¤Ê¤¯¤Ê¤Ã¤Æ¤¤
+¤ë¾õÂ֤Τ³¤È¤ò»Ø¤¹¡£¾Ü¤·¤¯¤Ï
+http://www.gnu.org/philosophy/categories.ja.html#ProprietarySoftware¤ò
+»²¾È¤»¤è¡£)
+
+Ê£À½¤äÈÒÉÛ¡¢²þÊѤˤĤ¤¤Æ¤ÎÀµ³Î¤Ê¾ò·ï¤ÈÀ©Ìó¤ò°Ê²¼¤Ç½Ò¤Ù¤Æ¤¤¤­¤Þ¤¹¡£
+\f
+                    GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+                 Ê£À½¡¢ÈÒÉÛ¡¢²þÊѤ˴ؤ¹¤ë¾ò·ï¤ÈÀ©Ìó
+
+0. ¤³¤ÎÍøÍѵöÂú·ÀÌó½ñ¤Ï¡¢¤½¤Î¥×¥í¥°¥é¥à(¤Þ¤¿¤Ï¤½¤Î¾¤ÎÃøºîʪ)¤ò¤³¤Î°ì
+È̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤ÎÄê¤á¤ë¾ò·ï¤Î²¼¤ÇÈÒÉۤǤ­¤ë¤È¤¤¤¦¹ðÃΤ¬Ãøºî¸¢¼Ô¤Ë
+¤è¤Ã¤Æµ­ºÜ¤µ¤ì¤¿¥×¥í¥°¥é¥à¤Þ¤¿¤Ï¤½¤Î¾¤ÎÃøºîʪÁ´È̤ËŬÍѤµ¤ì¤ë¡£°Ê²¼¤Ç
+¤Ï¡¢¡Ö¡Ø¥×¥í¥°¥é¥à¡Ù¡×¤È¤Ï¤½¤Î¤è¤¦¤Ë¤·¤Æ¤³¤Î·ÀÌó½ñ¤¬Å¬ÍѤµ¤ì¤¿¥×¥í¥°¥é
+¥à¤äÃøºîʪÁ´È̤ò°ÕÌ£¤·¡¢¤Þ¤¿¡Ö¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ¡×¤È¤Ï¡Ø¥×
+¥í¥°¥é¥à¡Ù¤ä¤½¤Î¾Ãøºî¸¢Ë¡¤Î²¼¤ÇÇÉÀ¸Êª¤È¸«¤Ê¤µ¤ì¤ë¤â¤ÎÁ´È̤ò»Ø¤¹¡£¤¹¤Ê
+¤ï¤Á¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤«¤½¤Î°ìÉô¤ò¡¢Á´¤¯Æ±°ì¤Î¤Þ¤Þ¤«¡¢²þÊѤò²Ã¤¨¤¿¤«¡¢¤¢
+¤ë¤¤¤Ï¾¤Î¸À¸ì¤ËËÝÌõ¤µ¤ì¤¿·Á¤Ç´Þ¤àÃøºîʪ¤Î¤³¤È¤Ç¤¢¤ë(¡Ö²þÊѡפȤ¤¤¦¸ì
+¤ÎËÜÍè¤Î°ÕÌ£¤«¤é¤Ï¤º¤ì¤ë¤¬¡¢°Ê²¼¤Ç¤ÏËÝÌõ¤â²þÊѤΰì¼ï¤È¸«¤Ê¤¹)¡£¤½¤ì¤¾
+¤ì¤Î·ÀÌó¼Ô¤Ï¡Ö¤¢¤Ê¤¿¡×¤Èɽ¸½¤µ¤ì¤ë¡£
+
+Ê£À½¤äÈÒÉÛ¡¢²þÊѰʳ°¤Î³èÆ°¤Ï¤³¤Î·ÀÌó½ñ¤Ç¤Ï¥«¥Ð¡¼¤µ¤ì¤Ê¤¤¡£¤½¤ì¤é¤Ï¤³¤Î
+·ÀÌó½ñ¤ÎÂоݳ°¤Ç¤¢¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤ò¼Â¹Ô¤¹¤ë¹Ô°Ù¼«ÂΤËÀ©¸Â¤Ï¤Ê¤¤¡£¤Þ
+¤¿¡¢¤½¤Î¤è¤¦¤Ê¡Ø¥×¥í¥°¥é¥à¡Ù¤Î½ÐÎÏ·ë²Ì¤Ï¡¢¤½¤ÎÆâÍƤ¬¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð
+¤Ë¤·¤¿Ãøºîʪ¤ò¹½À®¤¹¤ë¾ì¹ç¤Î¤ß¤³¤Î·ÀÌó½ñ¤Ë¤è¤Ã¤ÆÊݸ¤ì¤ë(¡Ø¥×¥í¥°¥é
+¥à¡Ù¤ò¼Â¹Ô¤·¤¿¤³¤È¤Ë¤è¤Ã¤ÆºîÀ®¤µ¤ì¤¿¤È¤¤¤¦¤³¤È¤È¤Ï̵´Ø·¸¤Ç¤¢¤ë)¡£¤³¤Î
+¤è¤¦¤ÊÀþ°ú¤­¤ÎÂÅÅöÀ­¤Ï¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤¬²¿¤ò¤¹¤ë¤Î¤«¤Ë°Í¸¤¹¤ë¡£
+
+1. ¤½¤ì¤¾¤ì¤ÎÊ£À½Êª¤Ë¤ª¤¤¤ÆŬÀÚ¤ÊÃøºî¸¢É½¼¨¤ÈÊݾڤÎÈÝǧÀ¼ÌÀ(disclaimer
+of warranty)¤òÌÜΩ¤Ä¤è¤¦Å¬Àڤ˷Ǻܤ·¡¢¤Þ¤¿¤³¤Î·ÀÌó½ñ¤ª¤è¤Ó°ìÀÚ¤ÎÊݾڤÎ
+ÉԺߤ˿¨¤ì¤¿¹ðÃΤ¹¤Ù¤Æ¤ò¤½¤Î¤Þ¤Þ»Ä¤·¡¢¤½¤·¤Æ¤³¤Î·ÀÌó½ñ¤ÎÊ£À½Êª¤ò¡Ø¥×¥í
+¥°¥é¥à¡Ù¤Î¤¤¤«¤Ê¤ë¼õÎμԤˤâ¡Ø¥×¥í¥°¥é¥à¡Ù¤È¶¦¤ËÈÒÉÛ¤¹¤ë¸Â¤ê¡¢¤¢¤Ê¤¿¤Ï
+¡Ø¥×¥í¥°¥é¥à¡Ù¤Î¥½¡¼¥¹¥³¡¼¥É¤ÎÊ£À½Êª¤ò¡¢¤¢¤Ê¤¿¤¬¼õ¤±¼è¤Ã¤¿Ä̤ê¤Î·Á¤ÇÊ£
+À½¤Þ¤¿¤ÏÈÒÉÛ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ÇÞÂΤÏÌä¤ï¤Ê¤¤¡£
+
+¤¢¤Ê¤¿¤Ï¡¢ÊªÍýŪ¤ËÊ£À½Êª¤ò¾ùÅϤ¹¤ë¤È¤¤¤¦¹Ô°Ù¤Ë´Ø¤·¤Æ¼ê¿ôÎÁ¤ò²Ý¤·¤Æ¤âÎÉ
+¤¤¤·¡¢´õ˾¤Ë¤è¤Ã¤Æ¤Ï¼ê¿ôÎÁ¤ò¼è¤Ã¤Æ¸ò´¹¤Ë¤ª¤±¤ëÊݸî¤ÎÊݾڤòÄ󶡤·¤Æ¤âÎÉ
+¤¤¡£
+
+2. ¤¢¤Ê¤¿¤Ï¼«Ê¬¤Î¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÊ£À½Êª¤«¤½¤Î°ìÉô¤ò²þÊѤ·¤Æ¡Ø¥×¥í¥°¥é
+¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ¤ò·ÁÀ®¤·¡¢¤½¤Î¤è¤¦¤Ê²þÊÑÅÀ¤äÃøºîʪ¤ò¾åµ­Âè1Àá¤ÎÄê
+¤á¤ë¾ò·ï¤Î²¼¤ÇÊ£À½¤Þ¤¿¤ÏÈÒÉÛ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤¿¤À¤·¡¢¤½¤Î¤¿¤á¤Ë¤Ï°Ê²¼
+¤Î¾ò·ï¤¹¤Ù¤Æ¤òËþ¤¿¤·¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤:
+
+    a) ¤¢¤Ê¤¿¤¬¤½¤ì¤é¤Î¥Õ¥¡¥¤¥ë¤òÊѹ¹¤·¤¿¤È¤¤¤¦¤³¤È¤ÈÊѹ¹¤·¤¿Æü»þ¤¬ÎÉ
+    ¤¯Ê¬¤«¤ë¤è¤¦¡¢²þÊѤµ¤ì¤¿¥Õ¥¡¥¤¥ë¤Ë¹ð¼¨¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
+
+    b) ¡Ø¥×¥í¥°¥é¥à¡Ù¤Þ¤¿¤Ï¤½¤Î°ìÉô¤ò´Þ¤àÃøºîʪ¡¢¤¢¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù
+    ¤«¤½¤Î°ìÉô¤«¤éÇÉÀ¸¤·¤¿Ãøºîʪ¤òÈÒÉÛ¤¢¤ë¤¤¤Ïȯɽ¤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¤½¤ÎÁ´
+    ÂΤò¤³¤Î·ÀÌó½ñ¤Î¾ò·ï¤Ë½¾¤Ã¤ÆÂè»°¼Ô¤Ø̵½þ¤ÇÍøÍѵöÂú¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê
+    ¤¤¡£
+
+    c) ²þÊѤµ¤ì¤¿¥×¥í¥°¥é¥à¤¬¡¢Ä̾ï¼Â¹Ô¤¹¤ëºÝ¤ËÂÐÏÃŪ¤Ë¥³¥Þ¥ó¥É¤òÆɤà
+    ¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤Ê¤é¤Ð¡¢¤½¤Î¥×¥í¥°¥é¥à¤òºÇ¤â°ìÈÌŪ¤ÊÊýË¡¤ÇÂÐÏÃŪ¤Ë
+    ¼Â¹Ô¤¹¤ëºÝ¡¢Å¬ÀÚ¤ÊÃøºî¸¢É½¼¨¡¢ÌµÊݾڤǤ¢¤ë¤³¤È(¤¢¤ë¤¤¤Ï¤¢¤Ê¤¿¤¬ÊÝ
+    ¾Ú¤òÄ󶡤¹¤ë¤È¤¤¤¦¤³¤È)¡¢¥æ¡¼¥¶¤¬¥×¥í¥°¥é¥à¤ò¤³¤Î·ÀÌó½ñ¤Ç½Ò¤Ù¤¿¾ò
+    ·ï¤Î²¼¤ÇÈÒÉÛ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¤È¤¤¤¦¤³¤È¡¢¤½¤·¤Æ¤³¤Î·ÀÌó½ñ¤ÎÊ£À½Êª¤ò
+    ±ÜÍ÷¤¹¤ë¤Ë¤Ï¤É¤¦¤·¤¿¤é¤è¤¤¤«¤È¤¤¤¦¥æ¡¼¥¶¤Ø¤ÎÀâÌÀ¤ò´Þ¤à¹ðÃΤ¬°õºþ¤µ
+    ¤ì¤ë¤«¡¢¤¢¤ë¤¤¤Ï²èÌ̤Ëɽ¼¨¤µ¤ì¤ë¤è¤¦¤Ë¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤(Îã³°¤È¤·
+    ¤Æ¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤½¤Î¤â¤Î¤ÏÂÐÏÃŪ¤Ç¤¢¤Ã¤Æ¤âÄ̾綠¤Î¤è¤¦¤Ê¹ðÃΤò°õ
+    ºþ¤·¤Ê¤¤¾ì¹ç¤Ë¤Ï¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿¤¢¤Ê¤¿¤ÎÃøºîʪ¤Ë¤½¤Î¤è¤¦
+    ¤Ê¹ðÃΤò°õºþ¤µ¤»¤ëɬÍפϤʤ¤)¡£
+\f
+°Ê¾å¤ÎɬÍ×¾ò·ï¤ÏÁ´ÂΤȤ·¤Æ¤Î²þÊѤµ¤ì¤¿Ãøºîʪ¤ËŬÍѤµ¤ì¤ë¡£Ãøºîʪ¤Î°ìÉô
+¤¬¡Ø¥×¥í¥°¥é¥à¡Ù¤«¤éÇÉÀ¸¤·¤¿¤â¤Î¤Ç¤Ï¤Ê¤¤¤È³Îǧ¤Ç¤­¡¢¤½¤ì¤é¼«¿ÈÊ̤ÎÆÈΩ
+¤·¤¿Ãøºîʪ¤Ç¤¢¤ë¤È¹çÍýŪ¤Ë¹Í¤¨¤é¤ì¤ë¤Ê¤é¤Ð¡¢¤¢¤Ê¤¿¤¬¤½¤ì¤é¤òÊ̤ÎÃøºîʪ
+¤È¤·¤Æʬ¤±¤ÆÈÒÉÛ¤¹¤ë¾ì¹ç¡¢¤½¤¦¤¤¤Ã¤¿Éôʬ¤Ë¤Ï¤³¤Î·ÀÌó½ñ¤È¤½¤Î¾ò·ï¤Ï
+ŬÍѤµ¤ì¤Ê¤¤¡£¤·¤«¤·¡¢¤¢¤Ê¤¿¤¬Æ±¤¸Éôʬ¤ò¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ
+Á´ÂΤΰìÉô¤È¤·¤ÆÈÒÉÛ¤¹¤ë¤Ê¤é¤Ð¡¢Á´ÂΤȤ·¤Æ¤ÎÈÒÉÛʪ¤Ï¡¢¤³¤Î·ÀÌó½ñ¤¬
+²Ý¤¹¾ò·ï¤Ë½¾¤ï¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤È¤¤¤¦¤Î¤Ï¡¢¤³¤Î·ÀÌó½ñ¤¬Â¾¤Î·ÀÌó¼Ô
+¤ËÍ¿¤¨¤ëµö²Ä¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù´Ý¤´¤ÈÁ´ÂΤ˵ڤӡ¢Ã¯¤¬½ñ¤¤¤¿¤«¤Ï´Ø·¸¤Ê¤¯³Æ
+Éôʬ¤Î¤¹¤Ù¤Æ¤òÊݸ¤ë¤«¤é¤Ç¤¢¤ë¡£
+
+¤è¤Ã¤Æ¡¢¤¹¤Ù¤Æ¤¢¤Ê¤¿¤Ë¤è¤Ã¤Æ½ñ¤«¤ì¤¿Ãøºîʪ¤ËÂФ·¡¢¸¢Íø¤ò¼çÄ¥¤·¤¿¤ê¤¢¤Ê
+¤¿¤Î¸¢Íø¤Ë°ÛµÄ¤ò¿½¤·Î©¤Æ¤ë¤³¤È¤Ï¤³¤ÎÀá¤Î°Õ¿Þ¤¹¤ë¤È¤³¤í¤Ç¤Ï¤Ê¤¤¡£¤à¤·¤í¡¢
+¤½¤Î¼ñ»Ý¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿ÇÉÀ¸Êª¤Ê¤¤¤·½¸¹çÃøºîʪ¤ÎÈÒÉÛ¤ò´ÉÍý¤¹
+¤ë¸¢Íø¤ò¹Ô»È¤¹¤ë¤È¤¤¤¦¤³¤È¤Ë¤¢¤ë¡£
+
+¤Þ¤¿¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤Æ¤¤¤Ê¤¤¤½¤Î¾¤ÎÃøºîʪ¤ò¡Ø¥×¥í¥°¥é¥à¡Ù(¤¢
+¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ)¤È°ì½ï¤Ë½¸¤á¤¿¤À¤±¤Î¤â¤Î¤ò°ì´¬¤Î
+ÊÝ´ÉÁõÃ֤ʤ¤¤·ÈÒÉÛÇÞÂΤ˼ý¤á¤Æ¤â¡¢¤½¤Î¾¤ÎÃøºîʪ¤Þ¤Ç¤³¤Î·ÀÌó½ñ¤¬ÊÝ
+¸î¤¹¤ëÂоݤˤʤë¤È¤¤¤¦¤³¤È¤Ë¤Ï¤Ê¤é¤Ê¤¤¡£
+
+3. ¤¢¤Ê¤¿¤Ï¾åµ­Âè1Àᤪ¤è¤Ó2Àá¤Î¾ò·ï¤Ë½¾¤¤¡¢¡Ø¥×¥í¥°¥é¥à¡Ù(¤¢¤ë¤¤¤ÏÂè2
+Àá¤Ë¤ª¤±¤ëÇÉÀ¸Êª)¤ò¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼¥É¤Ê¤¤¤·¼Â¹Ô·Á¼°¤ÇÊ£À½¤Þ¤¿¤ÏÈÒÉÛ¤¹
+¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤¿¤À¤·¡¢¤½¤Î¾ì¹ç¤¢¤Ê¤¿¤Ï°Ê²¼¤Î¤¦¤Á¤É¤ì¤«°ì¤Ä¤ò¼Â»Ü¤·¤Ê
+¤±¤ì¤Ð¤Ê¤é¤Ê¤¤:
+
+    a) Ãøºîʪ¤Ë¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ËÂбþ¤·¤¿´°Á´¤«¤Äµ¡³£¤ÇÆɤ߼è¤ê²Äǽ¤Ê
+    ¥½¡¼¥¹¥³¡¼¥É¤òźÉÕ¤¹¤ë¡£¤¿¤À¤·¡¢¥½¡¼¥¹¥³¡¼¥É¤Ï¾åµ­Âè1Àᤪ¤è¤Ó2Àá¤Î
+    ¾ò·ï¤Ë½¾¤¤¥½¥Õ¥È¥¦¥§¥¢¤Î¸ò´¹¤Ç½¬´·Åª¤Ë»È¤ï¤ì¤ëÇÞÂΤÇÈÒÉÛ¤·¤Ê¤±¤ì¤Ð
+    ¤Ê¤é¤Ê¤¤¡£¤¢¤ë¤¤¤Ï¡¢
+
+    b) Ãøºîʪ¤Ë¡¢¤¤¤«¤Ê¤ëÂè»°¼Ô¤ËÂФ·¤Æ¤â¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ËÂбþ¤·¤¿´°
+    Á´¤«¤Äµ¡³£¤ÇÆɤ߼è¤ê²Äǽ¤Ê¥½¡¼¥¹¥³¡¼¥É¤ò¡¢ÈÒÉÛ¤ËÍפ¹¤ëʪÍýŪ¥³¥¹¥È
+    ¤ò¾å²ó¤é¤Ê¤¤ÄøÅ٤μê¿ôÎÁ¤È°ú¤­´¹¤¨¤ËÄ󶡤¹¤ë»Ý½Ò¤Ù¤¿¾¯¤Ê¤¯¤È¤â3ǯ
+    ´Ö¤ÏÍ­¸ú¤Ê½ñÌ̤ˤʤä¿¿½¤·½Ð¤òź¤¨¤ë¡£¤¿¤À¤·¡¢¥½¡¼¥¹¥³¡¼¥É¤Ï¾åµ­Âè
+    1Àᤪ¤è¤Ó2Àá¤Î¾ò·ï¤Ë½¾¤¤¥½¥Õ¥È¥¦¥§¥¢¤Î¸ò´¹¤Ç½¬´·Åª¤Ë»È¤ï¤ì¤ëÇÞÂΤÇ
+    ÈÒÉÛ¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤¢¤ë¤¤¤Ï¡¢
+
+    c) Âбþ¤¹¤ë¥½¡¼¥¹¥³¡¼¥ÉÈÒÉۤ、·½Ð¤ËºÝ¤·¤Æ¡¢¤¢¤Ê¤¿¤¬ÆÀ¤¿¾ðÊó¤ò°ì
+    ½ï¤Ë°ú¤­ÅϤ¹(¤³¤ÎÁªÂò»è¤Ï¡¢±ÄÍø¤òÌÜŪ¤È¤·¤Ê¤¤ÈÒÉۤǤ¢¤Ã¤Æ¡¢¤«¤Ä¤¢
+    ¤Ê¤¿¤¬¾åµ­¾®Àáb¤Ç»ØÄꤵ¤ì¤Æ¤¤¤ë¤è¤¦¤Ê¿½¤·½Ð¤È¶¦¤Ë¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼
+    ¥É¤¢¤ë¤¤¤Ï¼Â¹Ô·Á¼°¤Î¥×¥í¥°¥é¥à¤·¤«Æþ¼ê¤·¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¸Â¤êµö²Ä¤µ¤ì
+    ¤ë)¡£
+
+Ãøºîʪ¤Î¥½¡¼¥¹¥³¡¼¥É¤È¤Ï¡¢¤½¤ì¤ËÂФ·¤Æ²þÊѤò²Ã¤¨¤ë¾å¤Ç¹¥¤Þ¤·¤¤¤È¤µ¤ì¤ë
+Ãøºîʪ¤Î·Á¼°¤ò°ÕÌ£¤¹¤ë¡£¤¢¤ë¼Â¹Ô·Á¼°¤ÎÃøºîʪ¤Ë¤È¤Ã¤Æ´°Á´¤Ê¥½¡¼¥¹¥³¡¼¥É
+¤È¤Ï¡¢¤½¤ì¤¬´Þ¤à¥â¥¸¥å¡¼¥ë¤¹¤Ù¤Æ¤Î¥½¡¼¥¹¥³¡¼¥ÉÁ´Éô¤Ë²Ã¤¨¡¢´ØÏ¢¤¹¤ë¥¤¥ó
+¥¿¡¼¥Õ¥§¡¼¥¹ÄêµÁ¥Õ¥¡¥¤¥ë¤Î¤¹¤Ù¤Æ¤È¥é¥¤¥Ö¥é¥ê¤Î¥³¥ó¥Ñ¥¤¥ë¤ä¥¤¥ó¥¹¥È¡¼¥ë
+¤òÀ©¸æ¤¹¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¥¹¥¯¥ê¥×¥È¤ò¤â²Ã¤¨¤¿¤â¤Î¤ò°ÕÌ£¤¹¤ë¡£¤·¤«¤·ÆÃÊÌ
+¤ÊÎã³°¤È¤·¤Æ¡¢¤½¤Î¥³¥ó¥Ý¡¼¥Í¥ó¥È¼«ÂΤ¬¼Â¹Ô·Á¼°¤ËÉտ魯¤ë¤Î¤Ç¤Ï̵¤¤¸Â¤ê¡¢
+ÈÒÉÛ¤µ¤ì¤ë¤â¤Î¤ÎÃæ¤Ë¡¢¼Â¹Ô·Á¼°¤¬¼Â¹Ô¤µ¤ì¤ë¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Î¼ç
+Íפʥ³¥ó¥Ý¡¼¥Í¥ó¥È(¥³¥ó¥Ñ¥¤¥é¤ä¥«¡¼¥Í¥ëÅù)¤ÈÄ̾ï°ì½ï¤Ë(¥½¡¼¥¹¤«¥Ð¥¤¥Ê
+¥ê·Á¼°¤Î¤É¤Á¤é¤«¤Ç)ÈÒÉÛ¤µ¤ì¤ë¤â¤Î¤ò´Þ¤ó¤Ç¤¤¤ëɬÍפϤʤ¤¤È¤¹¤ë¡£
+
+¼Â¹Ô·Á¼°¤Þ¤¿¤Ï¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼¥É¤ÎÈÒÉÛ¤¬¡¢»ØÄꤵ¤ì¤¿¾ì½ê¤«¤é¥³¥Ô¡¼¤¹¤ë
+¤¿¤á¤Î¥¢¥¯¥»¥¹¼êÃʤòÄ󶡤¹¤ë¤³¤È¤Ç°Ù¤µ¤ì¤ë¤È¤·¤Æ¡¢¤½¤Î¾å¤Ç¥½¡¼¥¹¥³¡¼¥É
+¤âƱÅù¤Î¥¢¥¯¥»¥¹¼êÃʤˤè¤Ã¤ÆƱ¤¸¾ì½ê¤«¤é¥³¥Ô¡¼¤Ç¤­¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤Ê
+¤é¤Ð¡¢Âè»°¼Ô¤¬¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼¥É¤È°ì½ï¤Ë¥½¡¼¥¹¤â¶¯À©Åª¤Ë¥³¥Ô¡¼¤µ¤»¤é¤ì
+¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤¯¤Æ¤â¥½¡¼¥¹¥³¡¼¥ÉÈÒÉۤξò·ï¤òËþ¤¿¤·¤Æ¤¤¤ë¤â¤Î¤È¤¹¤ë¡£
+\f
+4. ¤¢¤Ê¤¿¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò¡¢¤³¤Î·ÀÌó½ñ¤Ë¤ª¤¤¤ÆÌÀ³Î¤ËÄ󼨤µ¤ì¤¿¹Ô
+°Ù¤ò½ü¤­Ê£À½¤ä²þÊÑ¡¢¥µ¥Ö¥é¥¤¥»¥ó¥¹¡¢¤¢¤ë¤¤¤ÏÈÒÉÛ¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£Â¾¤Ë
+¡Ø¥×¥í¥°¥é¥à¡Ù¤òÊ£À½¤ä²þÊÑ¡¢¥µ¥Ö¥é¥¤¥»¥ó¥¹¡¢¤¢¤ë¤¤¤ÏÈÒÉÛ¤¹¤ë´ë¤Æ¤Ï¤¹¤Ù
+¤Æ̵¸ú¤Ç¤¢¤ê¡¢¤³¤Î·ÀÌó½ñ¤Î²¼¤Ç¤Î¤¢¤Ê¤¿¤Î¸¢Íø¤ò¼«Æ°Åª¤Ë½ª·ë¤µ¤»¤ë¤³
+¤È¤Ë¤Ê¤í¤¦¡£¤·¤«¤·¡¢Ê£À½Êª¤ä¸¢Íø¤ò¤³¤Î·ÀÌó½ñ¤Ë½¾¤Ã¤Æ¤¢¤Ê¤¿¤«¤éÆÀ¤¿
+¿Í¡¹¤Ë´Ø¤·¤Æ¤Ï¡¢¤½¤Î¤è¤¦¤Ê¿Í¡¹¤¬¤³¤Î·ÀÌó½ñ¤Ë´°Á´¤Ë½¾¤Ã¤Æ¤¤¤ë¸Â¤êÈà
+¤é¤Î¥é¥¤¥»¥ó¥¹¤Þ¤Ç½ª·ë¤¹¤ë¤³¤È¤Ï¤Ê¤¤¡£
+
+5. ¤¢¤Ê¤¿¤Ï¤³¤Î·ÀÌó½ñ¤ò¼õÂú¤¹¤ëɬÍפÏ̵¤¤¡£¤È¤¤¤¦¤Î¤Ï¡¢¤¢¤Ê¤¿¤Ï¤³
+¤ì¤Ë½ð̾¤·¤Æ¤¤¤Ê¤¤¤«¤é¤Ç¤¢¤ë¡£¤·¤«¤·¡¢¤³¤Î·ÀÌó½ñ°Ê³°¤Ë¤¢¤Ê¤¿¤ËÂФ·
+¤Æ¡Ø¥×¥í¥°¥é¥à¡Ù¤ä¤½¤ÎÇÉÀ¸Êª¤òÊѹ¹¡¢ÈÒÉÛ¤¹¤ëµö²Ä¤òÍ¿¤¨¤ë¤â¤Î¤Ï¸ºß¤·¤Ê
+¤¤¡£¤³¤ì¤é¤Î¹Ô°Ù¤Ï¡¢¤¢¤Ê¤¿¤¬¤³¤Î·ÀÌó½ñ¤ò¼õ¤±Æþ¤ì¤Ê¤¤¸Â¤êË¡¤Ë¤è¤Ã¤Æ
+¶Ø¤¸¤é¤ì¤Æ¤¤¤ë¡£¤½¤³¤Ç¡¢¡Ø¥×¥í¥°¥é¥à¡Ù(¤¢¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·
+¤¿Ãøºîʪ¤Î¤¹¤Ù¤Æ)¤ò²þÊѤʤ¤¤·ÈÒÉÛ¤¹¤ë¤³¤È¤Ë¤è¤ê¡¢¤¢¤Ê¤¿¤Ï¼«Ê¬¤¬¤½¤Î¤è
+¤¦¤Ê¹Ô°Ù¤ò¹Ô¤¦¤¿¤á¤Ë¤³¤Î·ÀÌó½ñ¤ò¼õÂú¤·¤¿¤È¤¤¤¦¤³¤È¡¢¤½¤·¤Æ¡Ø¥×¥í¥°
+¥é¥à¡Ù¤È¤½¤ì¤Ë´ð¤Å¤¯Ãøºîʪ¤ÎÊ£À½¤äÈÒÉÛ¡¢²þÊѤˤĤ¤¤Æ¤³¤Î·ÀÌó½ñ¤¬²Ý
+¤¹À©Ìó¤È¾ò·ï¤ò¤¹¤Ù¤Æ¼õ¤±Æþ¤ì¤¿¤È¤¤¤¦¤³¤È¤ò¼¨¤·¤¿¤â¤Î¤È¸«¤Ê¤¹¡£
+
+6. ¤¢¤Ê¤¿¤¬¡Ø¥×¥í¥°¥é¥à¡Ù(¤Þ¤¿¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿ÃøºîʪÁ´ÈÌ)¤ò
+ºÆÈÒÉÛ¤¹¤ë¤¿¤Ó¤Ë¡¢¤½¤Î¼õÎμԤϸµ¡¹¤Î¥é¥¤¥»¥ó¥¹µö²Ä¼Ô¤«¤é¡¢¤³¤Î·ÀÌó½ñ¤Ç
+»ØÄꤵ¤ì¤¿¾ò·ï¤ÈÀ©Ìó¤Î²¼¤Ç¡Ø¥×¥í¥°¥é¥à¡Ù¤òÊ£À½¤äÈÒÉÛ¡¢¤¢¤ë¤¤¤Ï²þÊѤ¹¤ë
+µö²Ä¤ò¼«Æ°Åª¤ËÆÀ¤ë¤â¤Î¤È¤¹¤ë¡£¤¢¤Ê¤¿¤Ï¡¢¼õÎμԤ¬¤³¤³¤Çǧ¤á¤é¤ì¤¿¸¢Íø¤ò
+¹Ô»È¤¹¤ë¤³¤È¤Ë´Ø¤·¤Æ¤³¤ì°Ê¾å¾¤Î¤¤¤«¤Ê¤ëÀ©¸Â¤â²Ý¤¹¤³¤È¤¬¤Ç¤­¤Ê¤¤¡£¤¢¤Ê
+¤¿¤Ë¤Ï¡¢Âè»°¼Ô¤¬¤³¤Î·ÀÌó½ñ¤Ë½¾¤¦¤³¤È¤ò¶¯À©¤¹¤ëÀÕǤ¤Ï¤Ê¤¤¡£
+
+7. Æõö¿¯³²¤¢¤ë¤¤¤Ï¤½¤Î¾¤ÎÍýͳ(Æõö´Ø·¸¤Ë¸Â¤é¤Ê¤¤)¤«¤é¡¢ºÛȽ½ê¤ÎȽ·è
+¤¢¤ë¤¤¤Ï¿½¤·Î©¤Æ¤Î·ë²Ì¤È¤·¤Æ¤¢¤Ê¤¿¤Ë(ºÛȽ½êÌ¿Îá¤ä·ÀÌó¤Ê¤É¤Ë¤è¤ê)¤³¤Î·À
+Ìó½ñ¤Î¾ò·ï¤ÈÌ·½â¤¹¤ëÀ©Ì󤬲ݤµ¤ì¤¿¾ì¹ç¤Ç¤â¡¢¤¢¤Ê¤¿¤¬¤³¤Î·ÀÌó½ñ¤Î¾ò·ï¤ò
+ÌȽü¤µ¤ì¤ë¤ï¤±¤Ç¤Ï¤Ê¤¤¡£¤â¤·¤³¤Î·ÀÌó½ñ¤Î²¼¤Ç¤¢¤Ê¤¿¤Ë²Ý¤»¤é¤ì¤¿ÀÕǤ¤È¾
+¤Î´ØÏ¢¤¹¤ëÀÕǤ¤òƱ»þ¤ËËþ¤¿¤¹¤è¤¦¤Ê·Á¤ÇÈÒÉۤǤ­¤Ê¤¤¤Ê¤é¤Ð¡¢·ë²Ì¤È¤·¤Æ¤¢
+¤Ê¤¿¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤òÈÒÉÛ¤¹¤ë¤³¤È¤¬Á´¤¯¤Ç¤­¤Ê¤¤¤È¤¤¤¦¤³¤È¤Ç¤¢¤ë¡£Î㤨
+¤ÐÆõö¥é¥¤¥»¥ó¥¹¤¬¡¢¤¢¤Ê¤¿¤«¤éľÀÜ´ÖÀܤòÌä¤ï¤º¥³¥Ô¡¼¤ò¼õ¤±¼è¤Ã¤¿¿Í¤¬Ã¯
+¤Ç¤â¡Ø¥×¥í¥°¥é¥à¡Ù¤ò»ÈÍÑÎÁ̵ÎÁ¤ÇºÆÈÒÉÛ¤¹¤ë¤³¤È¤òǧ¤á¤Æ¤¤¤Ê¤¤¾ì¹ç¡¢¤¢¤Ê
+¤¿¤¬¤½¤ÎÀ©Ìó¤È¤³¤Î·ÀÌó½ñ¤òξÊý¤È¤âËþ¤¿¤¹¤Ë¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÈÒÉÛ¤ò´°Á´
+¤ËÃæ»ß¤¹¤ë¤·¤«¤Ê¤¤¤À¤í¤¦¡£
+
+¤³¤ÎÀá¤Î°ìÉôʬ¤¬ÆÃÄê¤Î¾õ¶·¤Î²¼¤Ç̵¸ú¤Ê¤¤¤·¼Â»ÜÉÔ²Äǽ¤Ê¾ì¹ç¤Ç¤â¡¢Àá¤Î»Ä
+¤ê¤ÎÉôʬ¤ÏŬÍѤµ¤ì¤ë¤è¤¦°Õ¿Þ¤µ¤ì¤Æ¤¤¤ë¡£¤½¤Î¾¤Î¾õ¶·¤Ç¤ÏÀ᤬Á´ÂΤȤ·¤Æ
+ŬÍѤµ¤ì¤ë¤è¤¦°Õ¿Þ¤µ¤ì¤Æ¤¤¤ë¡£
+
+Æõö¤ä¤½¤Î¾¤Îºâ»º¸¢¤ò¿¯³²¤·¤¿¤ê¡¢¤½¤Î¤è¤¦¤Ê¸¢Íø¤Î¼çÄ¥¤Î¸úÎϤ˰۵Ĥò¾§
+¤¨¤¿¤ê¤¹¤ë¤è¤¦¤¢¤Ê¤¿¤òͶÏǤ¹¤ë¤³¤È¤¬¤³¤ÎÀá¤ÎÌÜŪ¤Ç¤Ï¤Ê¤¤¡£¤³¤ÎÀá¤Ë¤Ï¡¢
+¿Í¡¹¤Ë¤è¤Ã¤Æ¥é¥¤¥»¥ó¥¹´·¹Ô¤È¤·¤Æ¼Â¸½¤µ¤ì¤Æ¤­¤¿¡¢¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ÈÒÉÛ
+¤Î¥·¥¹¥Æ¥à¤Î´°Á´À­¤ò¸î¤ë¤È¤¤¤¦ÌÜŪ¤·¤«¤Ê¤¤¡£Â¿¤¯¤Î¿Í¡¹¤¬¡¢¥Õ¥ê¡¼¥½¥Õ¥È
+¥¦¥§¥¢¤ÎÈÒÉÛ¥·¥¹¥Æ¥à¤¬¼óÈø°ì´Ó¤·¤ÆŬÍѤµ¤ì¤Æ¤¤¤ë¤È¤¤¤¦¿®Íê¤Ë´ð¤Å¤­¡¢¤³
+¤Î¥·¥¹¥Æ¥à¤òÄ̤¸¤ÆÈÒÉÛ¤µ¤ì¤ë¿Íͤʥ½¥Õ¥È¥¦¥§¥¢¤Ë´²Âç¤Ê¹×¸¥¤ò¤·¤Æ¤­¤¿¤Î
+¤Ï»ö¼Â¤Ç¤¢¤ë¤¬¡¢¿Í¤¬¤É¤Î¤è¤¦¤Ê¥·¥¹¥Æ¥à¤òÄ̤¸¤Æ¥½¥Õ¥È¥¦¥§¥¢¤òÈÒÉÛ¤·¤¿¤¤
+¤È»×¤¦¤«¤Ï¤¢¤¯¤Þ¤Ç¤âºî¼Ô/´óÍ¿¼Ô¼¡Âè¤Ç¤¢¤ê¡¢¤¢¤Ê¤¿¤¬ÁªÂò¤ò²¡¤·¤Ä¤±¤ë¤³
+¤È¤Ï¤Ç¤­¤Ê¤¤¡£
+
+¤³¤ÎÀá¤Ï¡¢¤³¤Î·ÀÌó½ñ¤Î¤³¤ÎÀá°Ê³°¤ÎÉôʬ¤Î°ìµ¢·ë¤Ë¤Ê¤ë¤È¹Í¤¨¤é¤ì¤ë¥±¡¼
+¥¹¤òÅ°ÄìŪ¤ËÌÀ¤é¤«¤Ë¤¹¤ë¤³¤È¤òÌÜŪ¤È¤·¤Æ¤¤¤ë¡£
+\f
+8. ¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÈÒÉÛ¤äÍøÍѤ¬¡¢¤¢¤ë¹ñ¤Ë¤ª¤¤¤Æ¤ÏÆõö¤Þ¤¿¤ÏÃøºî¸¢¤¬¼ç
+Ä¥¤µ¤ì¤¿¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤Î¤¤¤º¤ì¤«¤Ë¤è¤Ã¤ÆÀ©¸Â¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¡Ø¥×¥í¥°
+¥é¥à¡Ù¤Ë¤³¤Î·ÀÌó½ñ¤òŬÍѤ·¤¿¸µ¤ÎÃøºî¸¢¼Ô¤Ï¡¢¤½¤¦¤¤¤Ã¤¿¹ñ¡¹¤òÇÓ½ü¤·
+¤¿ÌÀ³Î¤ÊÃÏÍýŪÈÒÉÛÀ©¸Â¤ò²Ã¤¨¡¢¤½¤³¤ÇÇÓ½ü¤µ¤ì¤Æ¤¤¤Ê¤¤¹ñ¤ÎÃæ¤ä¤½¤ì¤é¤Î¹ñ¡¹
+¤Î´Ö¤Ç¤Î¤ßÈÒÉÛ¤¬µö²Ä¤µ¤ì¤ë¤è¤¦¤Ë¤·¤Æ¤â¹½¤ï¤Ê¤¤¡£¤½¤Î¾ì¹ç¡¢¤½¤Î¤è¤¦¤ÊÀ©
+¸Â¤Ï¤³¤Î·ÀÌó½ñËÜʸ¤Ç½ñ¤«¤ì¤Æ¤¤¤ë¤Î¤ÈƱÍͤ˸«¤Ê¤µ¤ì¤ë¡£
+
+9. ¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤϡ¢»þ¤Ë¤è¤Ã¤Æ²þÄû¤Þ¤¿¤Ï¿·ÈǤΰìÈ̸ø½°ÍøÍѵö
+Âú½ñ¤òȯɽ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î¤è¤¦¤Ê¿·ÈǤϸ½ºß¤Î¥Ð¡¼¥¸¥ç¥ó¤È¤½¤ÎÀº¿À
+¤Ë¤ª¤¤¤Æ¤Ï»÷¤¿¤â¤Î¤Ë¤Ê¤ë¤À¤í¤¦¤¬¡¢¿·¤¿¤ÊÌäÂê¤ä·üÇ°¤ò²ò·è¤¹¤ë¤¿¤áºÙÉô¤Ç
+¤Ï°Û¤Ê¤ë²ÄǽÀ­¤¬¤¢¤ë¡£
+
+¤½¤ì¤¾¤ì¤Î¥Ð¡¼¥¸¥ç¥ó¤Ë¤Ï¡¢¸«Ê¬¤±¤¬ÉÕ¤¯¤è¤¦¤Ë¥Ð¡¼¥¸¥ç¥óÈֹ椬¿¶¤é¤ì¤Æ¤¤
+¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Ë¤ª¤¤¤Æ¤½¤ì¤ËŬÍѤµ¤ì¤ë¤³¤Î·ÀÌó½ñ¤Î¥Ð¡¼¥¸¥ç¥óÈֹ椬
+»ØÄꤵ¤ì¤Æ¤¤¤Æ¡¢¹¹¤Ë¡Ö¤½¤ì°Ê¹ß¤Î¤¤¤«¤Ê¤ë¥Ð¡¼¥¸¥ç¥ó¡×¤âŬÍѤ·¤ÆÎɤ¤¤È¤Ê¤Ã
+¤Æ¤¤¤¿¾ì¹ç¡¢¤¢¤Ê¤¿¤Ï½¾¤¦¾ò·ï¤ÈÀ©Ìó¤È¤·¤Æ¡¢»ØÄê¤Î¥Ð¡¼¥¸¥ç¥ó¤«¡¢¥Õ¥ê¡¼¥½
+¥Õ¥È¥¦¥§¥¢ºâÃĤˤè¤Ã¤Æȯ¹Ô¤µ¤ì¤¿»ØÄê¤Î¥Ð¡¼¥¸¥ç¥ó°Ê¹ß¤ÎÈǤΤɤ줫°ì¤Ä¤Î
+¤É¤Á¤é¤«¤òÁª¤Ö¤³¤È¤¬½ÐÍè¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Ç¥é¥¤¥»¥ó¥¹¤Î¥Ð¡¼¥¸¥ç¥óÈÖ¹æ
+¤¬»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤¤Ê¤é¤Ð¡¢¤¢¤Ê¤¿¤Ïº£¤Þ¤Ç¤Ë¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤ«¤éȯ
+¹Ô¤µ¤ì¤¿¥Ð¡¼¥¸¥ç¥ó¤ÎÃ椫¤é¹¥¤­¤ËÁª¤ó¤Ç¹½¤ï¤Ê¤¤¡£
+
+10. ¤â¤·¤¢¤Ê¤¿¤¬¡Ø¥×¥í¥°¥é¥à¡Ù¤Î°ìÉô¤ò¡¢¤½¤ÎÈÒÉÛ¾ò·ï¤¬¤³¤Î·ÀÌó½ñ¤È
+°Û¤Ê¤ë¾¤Î¥Õ¥ê¡¼¤Ê¥×¥í¥°¥é¥à¤ÈÅý¹ç¤·¤¿¤¤¤Ê¤é¤Ð¡¢ºî¼Ô¤ËÏ¢Íí¤·¤Æµö²Ä¤òµá
+¤á¤è¡£¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤ¬Ãøºî¸¢¤òÊÝÍ­¤¹¤ë¥½¥Õ¥È¥¦¥§¥¢¤Ë¤Ä¤¤¤Æ¤Ï¡¢
+¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤËÏ¢Íí¤»¤è¡£»ä¤¿¤Á¤Ï¡¢¤³¤Î¤è¤¦¤Ê¾ì¹ç¤Î¤¿¤á¤ËÆÃÊÌ
+¤ÊÎã³°¤òÀߤ±¤ë¤³¤È¤â¤¢¤ë¡£»ä¤¿¤Á¤¬·èÄê¤ò²¼¤¹¤Ë¤¢¤¿¤Ã¤Æ¤Ï¡¢»ä¤¿¤Á¤Î¥Õ¥ê¡¼
+¥½¥Õ¥È¥¦¥§¥¢¤ÎÇÉÀ¸Êª¤¹¤Ù¤Æ¤¬¥Õ¥ê¡¼¤Ê¾õÂÖ¤ËÊݤ¿¤ì¤ë¤È¤¤¤¦¤³¤È¤È¡¢°ìÈÌŪ
+¤Ë¥½¥Õ¥È¥¦¥§¥¢¤Î¶¦Í­¤ÈºÆÍøÍѤòÂ¥¿Ê¤¹¤ë¤È¤¤¤¦Æó¤Ä¤ÎÌÜɸ¤òµ¬½à¤Ë¸¡Æ¤¤µ¤ì
+¤ë¤Ç¤¢¤í¤¦¡£
+                            ÌµÊݾڤˤĤ¤¤Æ
+
+11. ¡Ø¥×¥í¥°¥é¥à¡Ù¤ÏÂå²Á̵¤·¤ËÍøÍѤ¬µö²Ä¤µ¤ì¤ë¤Î¤Ç¡¢Å¬ÀÚ¤ÊË¡¤¬Ç§¤á¤ë¸Â
+¤ê¤Ë¤ª¤¤¤Æ¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤Ë´Ø¤¹¤ë¤¤¤«¤Ê¤ëÊݾڤ⸺ߤ·¤Ê¤¤¡£½ñÌ̤ÇÊ̤Ë
+½Ò¤Ù¤ë¾ì¹ç¤ò½ü¤¤¤Æ¡¢Ãøºî¸¢¼Ô¡¢¤Þ¤¿¤Ï¤½¤Î¾¤ÎÃÄÂΤϡ¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ò¡¢
+ɽÌÀ¤µ¤ì¤¿¤«¸À³°¤Ë¤«¤ÏÌä¤ï¤º¡¢¾¦¶ÈŪŬÀ­¤òÊݾڤ¹¤ë¤Û¤Î¤á¤«¤·¤ä¤¢¤ëÆÃÄê
+¤ÎÌÜŪ¤Ø¤ÎŬ¹çÀ­(¤Ë¸Â¤é¤ì¤Ê¤¤)¤ò´Þ¤à°ìÀÚ¤ÎÊݾÚ̵¤·¤Ë¡Ö¤¢¤ë¤¬¤Þ¤Þ¡×¤ÇÄó
+¶¡¤¹¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Î¼Á¤ÈÀ­Ç½¤Ë´Ø¤¹¤ë¥ê¥¹¥¯¤Î¤¹¤Ù¤Æ¤Ï¤¢¤Ê¤¿¤Ëµ¢Â°¤¹
+¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Ë·ç´Ù¤¬¤¢¤ë¤ÈȽÌÀ¤·¤¿¾ì¹ç¡¢¤¢¤Ê¤¿¤ÏɬÍפÊÊݼéÅÀ¸¡¤ä
+Ê佤¡¢½¤Àµ¤ËÍפ¹¤ë¥³¥¹¥È¤Î¤¹¤Ù¤Æ¤ò°ú¤­¼õ¤±¤ë¤³¤È¤Ë¤Ê¤ë¡£
+
+12. Å¬ÀÚ¤ÊË¡¤«½ñÌ̤ǤÎƱ°Õ¤Ë¤è¤Ã¤ÆÌ¿¤¼¤é¤ì¤Ê¤¤¸Â¤ê¡¢Ãøºî¸¢¼Ô¡¢¤Þ¤¿¤Ï¾å
+µ­¤Çµö²Ä¤µ¤ì¤Æ¤¤¤ëÄ̤ê¤Ë¡Ø¥×¥í¥°¥é¥à¡Ù¤ò²þÊѤޤ¿¤ÏºÆÈÒÉÛ¤·¤¿¤½¤Î¾¤ÎÃÄ
+ÂΤϡ¢¤¢¤Ê¤¿¤ËÂФ·¤Æ¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÍøÍѤʤ¤¤·ÍøÍÑÉÔǽ¤ÇÀ¸¤¸¤¿°ìÈÌŪ¡¢
+ÆÃÊÌŪ¡¢¶öÁ³Åª¡¢É¬Á³Åª¤Ê»³²(¥Ç¡¼¥¿¤Î¾Ã¼º¤äÉÔÀµ³Î¤Ê½èÍý¡¢¤¢¤Ê¤¿¤«Âè»°
+¼Ô¤¬Èï¤Ã¤¿Â»¼º¡¢¤¢¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤¬Â¾¤Î¥½¥Õ¥È¥¦¥§¥¢¤È°ì½ï¤ËÆ°ºî¤·
+¤Ê¤¤¤È¤¤¤¦ÉÔ¶ñ¹ç¤Ê¤É¤ò´Þ¤à¤¬¤½¤ì¤é¤Ë¸Â¤é¤Ê¤¤)¤Ë°ìÀÚ¤ÎÀÕǤ¤òÉé¤ï¤Ê¤¤¡£
+¤½¤Î¤è¤¦¤Ê»³²¤¬À¸¤º¤ë²ÄǽÀ­¤Ë¤Ä¤¤¤ÆÈà¤é¤¬Ãé¹ð¤µ¤ì¤Æ¤¤¤¿¤È¤·¤Æ¤âƱÍͤÇ
+¤¢¤ë¡£
+
+                          ¾ò·ï¤ÈÀ©Ì󽪤ï¤ê
+\f
+            °Ê¾å¤Î¾ò¹à¤ò¤¢¤Ê¤¿¤Î¿·¤·¤¤¥×¥í¥°¥é¥à¤ËŬÍѤ¹¤ëÊýË¡
+
+¤¢¤Ê¤¿¤¬¿·¤·¤¤¥×¥í¥°¥é¥à¤ò³«È¯¤·¤¿¤È¤·¤Æ¡¢¸ø½°¤Ë¤è¤Ã¤Æ¤½¤ì¤¬ÍøÍѤµ¤ì¤ë
+²ÄǽÀ­¤òºÇÂç¤Ë¤·¤¿¤¤¤Ê¤é¡¢¤½¤Î¥×¥í¥°¥é¥à¤ò¤³¤Î·ÀÌó½ñ¤Î¾ò¹à¤Ë½¾¤Ã¤Æ
+ï¤Ç¤âºÆÈÒÉÛ¤¢¤ë¤¤¤ÏÊѹ¹¤Ç¤­¤ë¤è¤¦¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ë¤¹¤ë¤Î¤¬ºÇÁ±¤Ç¤¹¡£
+
+¤½¤Î¤¿¤á¤Ë¤Ï¡¢¥×¥í¥°¥é¥à¤Ë°Ê²¼¤Î¤è¤¦¤Êɽ¼¨¤òźÉÕ¤·¤Æ¤¯¤À¤µ¤¤¡£¤½¤Î¾ì¹ç¡¢
+Êݾڤ¬ÇÓ½ü¤µ¤ì¤Æ¤¤¤ë¤È¤¤¤¦¤³¤È¤òºÇ¤â¸ú²ÌŪ¤ËÅÁ¤¨¤ë¤¿¤á¤Ë¡¢¤½¤ì¤¾¤ì¤Î¥½¡¼
+¥¹¥Õ¥¡¥¤¥ë¤ÎËÁƬ¤Ëɽ¼¨¤òźÉÕ¤¹¤ì¤ÐºÇ¤â°ÂÁ´¤Ç¤¹¡£¾¯¤Ê¤¯¤È¤â¡¢¡ÖÃøºî¸¢É½
+¼¨¡×¤È¤¤¤¦¹Ô¤ÈÁ´Ê¸¤¬¤¢¤ë¾ì½ê¤Ø¤Î¥Ý¥¤¥ó¥¿¤À¤±¤Ï³Æ¥Õ¥¡¥¤¥ë¤Ë´Þ¤á¤ÆÃÖ¤¤¤Æ
+¤¯¤À¤µ¤¤¡£
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    (Ìõ:
+
+    <¥×¥í¥°¥é¥à¤Î̾Á°¤È¡¢¤½¤ì¤¬²¿¤ò¤¹¤ë¤«¤Ë¤Ä¤¤¤Æ¤Î´Êñ¤ÊÀâÌÀ¡£>
+    Copyright (C) <À¾Îñǯ>  <ºî¼Ô¤Î̾Á°>
+
+    ¤³¤Î¥×¥í¥°¥é¥à¤Ï¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ç¤¹¡£¤¢¤Ê¤¿¤Ï¤³¤ì¤ò¡¢¥Õ¥ê¡¼¥½¥Õ
+    ¥È¥¦¥§¥¢ºâÃĤˤè¤Ã¤Æȯ¹Ô¤µ¤ì¤¿ GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ(¥Ð¡¼¥¸¥ç
+    ¥ó2¤«¡¢´õ˾¤Ë¤è¤Ã¤Æ¤Ï¤½¤ì°Ê¹ß¤Î¥Ð¡¼¥¸¥ç¥ó¤Î¤¦¤Á¤É¤ì¤«)¤ÎÄê¤á¤ë¾ò·ï
+    ¤Î²¼¤ÇºÆÈÒÉÛ¤Þ¤¿¤Ï²þÊѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+
+    ¤³¤Î¥×¥í¥°¥é¥à¤ÏÍ­ÍѤǤ¢¤ë¤³¤È¤ò´ê¤Ã¤ÆÈÒÉÛ¤µ¤ì¤Þ¤¹¤¬¡¢*Á´¤¯¤Î̵ÊÝ
+    ¾Ú* ¤Ç¤¹¡£¾¦¶È²ÄǽÀ­¤ÎÊݾڤäÆÃÄê¤ÎÌÜŪ¤Ø¤ÎŬ¹çÀ­¤Ï¡¢¸À³°¤Ë¼¨¤µ¤ì¤¿
+    ¤â¤Î¤â´Þ¤áÁ´¤¯Â¸ºß¤·¤Þ¤»¤ó¡£¾Ü¤·¤¯¤ÏGNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤ò¤´
+    Í÷¤¯¤À¤µ¤¤¡£
+
+    ¤¢¤Ê¤¿¤Ï¤³¤Î¥×¥í¥°¥é¥à¤È¶¦¤Ë¡¢GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤ÎÊ£À½Êª¤ò
+    °ìÉô¼õ¤±¼è¤Ã¤¿¤Ï¤º¤Ç¤¹¡£¤â¤·¼õ¤±¼è¤Ã¤Æ¤¤¤Ê¤±¤ì¤Ð¡¢¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§
+    ¥¢ºâÃĤޤÇÀÁµá¤·¤Æ¤¯¤À¤µ¤¤(°¸Àè¤Ï the Free Software Foundation,
+    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA)¡£
+
+    )
+
+ÅŻҤʤ¤¤·»æ¤Î¥á¡¼¥ë¤Ç¤¢¤Ê¤¿¤ËÌ䤤¹ç¤ï¤»¤ëÊýË¡¤Ë¤Ä¤¤¤Æ¤Î¾ðÊó¤â½ñ¤­²Ã¤¨
+¤Þ¤·¤ç¤¦¡£
+
+¥×¥í¥°¥é¥à¤¬ÂÐÏÃŪ¤Ê¤â¤Î¤Ê¤é¤Ð¡¢ÂÐÏå⡼¥É¤Çµ¯Æ°¤·¤¿ºÝ¤Ë½ÐÎϤȤ·¤Æ°Ê²¼
+¤Î¤è¤¦¤Êû¤¤¹ðÃΤ¬É½¼¨¤µ¤ì¤ë¤è¤¦¤Ë¤·¤Æ¤¯¤À¤µ¤¤:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+    (Ìõ:
+
+    Gnomovision ¥Ð¡¼¥¸¥ç¥ó 69, Copyright (C) Ç¯ ºî¼Ô¤Î̾Á°
+    Gnomovision ¤Ï*Á´¤¯¤Î̵ÊݾÚ*¤ÇÄ󶡤µ¤ì¤Þ¤¹¡£¾Ü¤·¤¯¤Ï¡Öshow w¡×
+    ¤È¥¿¥¤¥×¤·¤Æ²¼¤µ¤¤¡£¤³¤ì¤Ï¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ç¤¢¤ê¡¢¤¢¤ë¾ò·ï¤Î²¼¤Ç
+    ºÆÈÒÉÛ¤¹¤ë¤³¤È¤¬¾©Î夵¤ì¤Æ¤¤¤Þ¤¹¡£¾Ü¤·¤¯¤Ï¡Öshow c¡×¤È¥¿¥¤¥×¤·¤Æ²¼
+    ¤µ¤¤¡£
+
+    )
+
+¤³¤³¤Ç¡¢²¾ÁÛŪ¤Ê¥³¥Þ¥ó¥É¡Öshow w¡×¤È¡Öshow c¡×¤Ï°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+¤ÎŬÀÚ¤ÊÉôʬ¤òɽ¼¨¤¹¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£¤â¤Á¤í¤ó¡¢¤¢¤Ê
+¤¿¤¬»È¤¦¥³¥Þ¥ó¥É¤ò¡Öshow w¡×¤ä¡Öshow c¡×¤È¸Æ¤ÖɬÁ³À­¤Ï¤¢¤ê¤Þ¤»¤ó¤Î¤Ç¡¢
+¤¢¤Ê¤¿¤Î¥×¥í¥°¥é¥à¤Ë¹ç¤ï¤»¤Æ¥Þ¥¦¥¹¤Î¥¯¥ê¥Ã¥¯¤ä¥á¥Ë¥å¡¼¤Î¥¢¥¤¥Æ¥à¤Ë¤·¤Æ
+¤â·ë¹½¤Ç¤¹¡£
+
+¤Þ¤¿¤¢¤Ê¤¿¤Ï¡¢É¬Íפʤé¤Ð(¥×¥í¥°¥é¥Þ¡¼¤È¤·¤ÆƯ¤¤¤Æ¤¤¤¿¤é)¤¢¤Ê¤¿¤Î¸ÛÍѼ硢
+¤¢¤ë¤¤¤Ï¾ì¹ç¤Ë¤è¤Ã¤Æ¤Ï³Ø¹»¤«¤é¡¢¤½¤Î¥×¥í¥°¥é¥à¤Ë´Ø¤¹¤ë¡ÖÃøºî¸¢Êü´þÀ¼ÌÀ
+(copyright disclaimer)¡×¤Ë½ð̾¤·¤Æ¤â¤é¤¦¤Ù¤­¤Ç¤¹¡£°Ê²¼¤ÏÎã¤Ç¤¹¤Î¤Ç¡¢Ì¾
+Á°¤òÊѤ¨¤Æ¤¯¤À¤µ¤¤:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+  (Ìõ:
+
+  Yoyodyne¼Ò¤Ï¤³¤³¤Ë¡¢James Hacker¤Ë¤è¤Ã¤Æ½ñ¤«¤ì¤¿¥×¥í¥°¥é¥à
+  ¡ÖGnomovision¡×(¥³¥ó¥Ñ¥¤¥é¤ØÄ̤¹¥×¥í¥°¥é¥à)¤Ë´Ø¤¹¤ë°ìÀÚ¤ÎÃøºî¸¢¤ÎÍø
+  ±×¤òÊü´þ¤·¤Þ¤¹¡£
+
+   <Ty Coon»á¤Î½ð̾>¡¢1989ǯ4·î1Æü
+   Ty Coon¡¢Éû¼ÒĹ
+
+  )
+
+¤³¤Î°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤Ç¤Ï¡¢¤¢¤Ê¤¿¤Î¥×¥í¥°¥é¥à¤òÆÈÀêŪ¤Ê¥×¥í¥°¥é¥à
+¤ËÅý¹ç¤¹¤ë¤³¤È¤òǧ¤á¤Æ¤¤¤Þ¤»¤ó¡£¤¢¤Ê¤¿¤Î¥×¥í¥°¥é¥à¤¬¥µ¥Ö¥ë¡¼¥Á¥ó¥é¥¤¥Ö
+¥é¥ê¤Ê¤é¤Ð¡¢ÆÈÀêŪ¤Ê¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤È¤¢¤Ê¤¿¤Î¥é¥¤¥Ö¥é¥ê¤ò¥ê¥ó¥¯¤¹¤ë¤³
+¤È¤òµö²Ä¤·¤¿¤Û¤¦¤¬¤è¤êÊØÍø¤Ç¤¢¤ë¤È¹Í¤¨¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£¤â¤·¤³¤ì¤¬¤¢¤Ê
+¤¿¤Î˾¤à¤³¤È¤Ê¤é¤Ð¡¢¤³¤Î·ÀÌó½ñ¤ÎÂå¤ï¤ê¤ËGNU ¥é¥¤¥Ö¥é¥ê°ìÈ̸ø½°ÍøÍѵöÂú
+·ÀÌó½ñ¤òŬÍѤ·¤Æ¤¯¤À¤µ¤¤¡£
diff --git a/Documents/etc/gpl.ja.txt b/Documents/etc/gpl.ja.txt
new file mode 100644 (file)
index 0000000..cb6d419
--- /dev/null
@@ -0,0 +1,416 @@
+                    GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+                       ¥Ð¡¼¥¸¥ç¥ó2¡¢1991ǯ6·î
+                       ÆüËܸìÌõ¡¢2002ǯ5·î20Æü
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ ¤³¤ÎÍøÍѵöÂú·ÀÌó½ñ¤ò¡¢°ì»ú°ì¶ç¤½¤Î¤Þ¤Þ¤ËÊ£À½¤·ÈÒÉÛ¤¹¤ë¤³¤È¤Ïµö²Ä¤¹¤ë¡£
+ ¤·¤«¤·Êѹ¹¤Ïǧ¤á¤Ê¤¤¡£
+
+ This is an unofficial translation of the GNU General Public License
+ into Japanese.  It was not published by the Free Software Foundation,
+ and does not legally state the distribution terms for software that
+ uses the GNU GPL--only the original English text of the GNU GPL does
+ that. However, we hope that this translation will help Japanese
+ speakers understand the GNU GPL better.
+
+ (Ìõ: °Ê²¼¤ÏGNU General Public License¤ÎÈó¸ø¼°¤ÊÆüËܸìÌõ¤Ç¤¹¡£¤³¤ì¤Ï¥Õ
+ ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃÄ(the Free Software Foundataion)¤Ë¤è¤Ã¤Æȯɽ¤µ¤ì¤¿
+ ¤â¤Î¤Ç¤Ï¤Ê¤¯¡¢GNU GPL¤òŬÍѤ·¤¿¥½¥Õ¥È¥¦¥§¥¢¤ÎÈÒÉÛ¾ò·ï¤òˡŪ¤ËÍ­¸ú¤Ê·Á
+ ¤Ç½Ò¤Ù¤¿¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£ÈÒÉÛ¾ò·ï¤È¤·¤Æ¤ÏGNU GPL¤Î±Ñ¸ìÈǥƥ­¥¹¥È¤Ç
+ »ØÄꤵ¤ì¤Æ¤¤¤ë¤â¤Î¤Î¤ß¤¬Í­¸ú¤Ç¤¹¡£¤·¤«¤·¤Ê¤¬¤é¡¢»ä¤¿¤Á¤Ï¤³¤ÎËÝÌõ¤¬¡¢
+ ÆüËܸì¤ò»ÈÍѤ¹¤ë¿Í¡¹¤Ë¤È¤Ã¤ÆGNU GPL¤ò¤è¤êÎɤ¯Íý²ò¤¹¤ë½õ¤±¤È¤Ê¤ë¤³¤È¤ò
+ Ë¾¤ó¤Ç¤¤¤Þ¤¹¡£)
+
+ ËÝÌõ¤Ï È¬ÅÄ¿¿¹Ô<mhatta@gnu.org>¤¬¹Ô¤Ã¤¿¡£¸¶Ê¸¤Ï
+ http://www.gnu.org/licenses/gpl.txt¤Ç¤¢¤ë¡£¸íÌõ¤Î»ØŦ¤ä²þÁ±°Æ¤ò´¿·Þ¤¹
+ ¤ë¡£
+                            ¤Ï¤¸¤á¤Ë
+
+¥½¥Õ¥È¥¦¥§¥¢¸þ¤±¥é¥¤¥»¥ó¥¹¤ÎÂçȾ¤Ï¡¢¤¢¤Ê¤¿¤¬¤½¤Î¥½¥Õ¥È¥¦¥§¥¢¤ò¶¦Í­¤·¤¿
+¤êÊѹ¹¤·¤¿¤ê¤¹¤ë¼«Í³¤òÃ¥¤¦¤è¤¦¤ËÀ߷פµ¤ì¤Æ¤¤¤Þ¤¹¡£ÂоÈŪ¤Ë¡¢GNU °ìÈ̸ø
+½°ÍøÍѵöÂú·ÀÌó½ñ¤Ï¡¢¤¢¤Ê¤¿¤¬¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤ò¶¦Í­¤·¤¿¤êÊѹ¹¤·¤¿¤ê¤¹
+¤ë¼«Í³¤òÊݾڤ¹¤ë--¤¹¤Ê¤ï¤Á¡¢¥½¥Õ¥È¥¦¥§¥¢¤¬¤½¤Î¥æ¡¼¥¶¤¹¤Ù¤Æ¤Ë¤È¤Ã¤Æ¥Õ¥ê¡¼
+¤Ç¤¢¤ë¤³¤È¤òÊݾڤ¹¤ë¤³¤È¤òÌÜŪ¤È¤·¤Æ¤¤¤Þ¤¹¡£¤³¤Î°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+¤Ï¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤΥ½¥Õ¥È¥¦¥§¥¢¤Î¤Û¤È¤ó¤É¤ËŬÍѤµ¤ì¤Æ¤ª¤ê¡¢¤Þ¤¿
+GNU GPL¤òŬÍѤ¹¤ë¤È·è¤á¤¿¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃİʳ°¤Îºî¼Ô¤Ë¤è¤ë¥×¥í¥°
+¥é¥à¤Ë¤âŬÍѤµ¤ì¤Æ¤¤¤Þ¤¹(¤¤¤¯¤Ä¤«¤Î¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤΥ½¥Õ¥È¥¦¥§
+¥¢¤Ë¤Ï¡¢GNU GPL¤Ç¤Ï¤Ê¤¯GNU ¥é¥¤¥Ö¥é¥ê°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤¬Å¬ÍѤµ¤ì
+¤Æ¤¤¤ë¤³¤È¤â¤¢¤ê¤Þ¤¹)¡£¤¢¤Ê¤¿¤â¤Þ¤¿¡¢¤´¼«Ê¬¤Î¥×¥í¥°¥é¥à¤ËGNU GPL¤òŬÍÑ
+¤¹¤ë¤³¤È¤¬²Äǽ¤Ç¤¹¡£
+
+»ä¤¿¤Á¤¬¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤È¸À¤¦¤È¤­¡¢¤½¤ì¤ÏÍøÍѤμ«Í³¤Ë¤Ä¤¤¤Æ¸ÀµÚ¤·¤Æ
+¤¤¤ë¤Î¤Ç¤¢¤Ã¤Æ¡¢²Á³Ê¤ÏÌäÂê¤Ë¤·¤Æ¤¤¤Þ¤»¤ó¡£»ä¤¿¤Á¤Î°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó
+½ñ¤Ï¡¢¤¢¤Ê¤¿¤¬¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤ÎÊ£À½Êª¤òÈÒÉÛ¤¹¤ë¼«Í³¤òÊݾڤ¹¤ë¤è¤¦Àß
+·×¤µ¤ì¤Æ¤¤¤Þ¤¹(´õ˾¤Ë±þ¤¸¤Æ¤½¤Î¼ï¤Î¥µ¡¼¥Ó¥¹¤Ë¼ê¿ôÎÁ¤ò²Ý¤¹¼«Í³¤âÊݾڤµ
+¤ì¤Þ¤¹)¡£¤Þ¤¿¡¢¤¢¤Ê¤¿¤¬¥½¡¼¥¹¥³¡¼¥É¤ò¼õ¤±¼è¤ë¤«¡¢¤¢¤ë¤¤¤Ï˾¤á¤Ð¤½¤ì¤ò
+Æþ¼ê¤¹¤ë¤³¤È¤¬²Äǽ¤Ç¤¢¤ë¤È¤¤¤¦¤³¤È¡¢¤¢¤Ê¤¿¤¬¥½¥Õ¥È¥¦¥§¥¢¤òÊѹ¹¤·¡¢¤½¤Î
+°ìÉô¤ò¿·¤¿¤Ê¥Õ¥ê¡¼¤Î¥×¥í¥°¥é¥à¤ÇÍøÍѤǤ­¤ë¤È¤¤¤¦¤³¤È¡¢¤½¤·¤Æ¡¢°Ê¾å¤Ç½Ò
+¤Ù¤¿¤è¤¦¤Ê¤³¤È¤¬¤Ç¤­¤ë¤È¤¤¤¦¤³¤È¤¬¤¢¤Ê¤¿¤ËÃΤ餵¤ì¤ë¤È¤¤¤¦¤³¤È¤âÊݾڤµ
+¤ì¤Þ¤¹¡£
+
+¤¢¤Ê¤¿¤Î¸¢Íø¤ò¼é¤ë¤¿¤á¡¢»ä¤¿¤Á¤Ï狼¤¬¤¢¤Ê¤¿¤ÎÍ­¤¹¤ë¤³¤ì¤é¤Î¸¢Íø¤òÈÝÄê
+¤¹¤ë¤³¤È¤ä¡¢¤³¤ì¤é¤Î¸¢Íø¤òÊü´þ¤¹¤ë¤è¤¦Í׵᤹¤ë¤³¤È¤ò¶Ø»ß¤¹¤ë¤È¤¤¤¦À©¸Â
+¤ò²Ã¤¨¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£¤è¤Ã¤Æ¡¢¤¢¤Ê¤¿¤¬¥½¥Õ¥È¥¦¥§¥¢¤ÎÊ£À½Êª¤òÈÒÉÛ¤·¤¿
+¤ê¤½¤ì¤òÊѹ¹¤·¤¿¤ê¤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¤³¤ì¤é¤ÎÀ©¸Â¤Î¤¿¤á¤Ë¤¢¤Ê¤¿¤Ë¤¢¤ë¼ï¤ÎÀÕ
+Ǥ¤¬È¯À¸¤¹¤ë¤³¤È¤Ë¤Ê¤ê¤Þ¤¹¡£
+
+Î㤨¤Ð¡¢¤¢¤Ê¤¿¤¬¥Õ¥ê¡¼¤Ê¥×¥í¥°¥é¥à¤ÎÊ£À½Êª¤òÈÒÉÛ¤¹¤ë¾ì¹ç¡¢Í­ÎÁ¤«ÌµÎÁ¤Ë
+´Ø¤ï¤é¤º¡¢¤¢¤Ê¤¿¤Ï¼«Ê¬¤¬Í­¤¹¤ë¸¢Íø¤òÁ´¤Æ¼õÎμԤËÍ¿¤¨¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+¤Þ¤¿¡¢¤¢¤Ê¤¿¤ÏÈà¤é¤â¥½¡¼¥¹¥³¡¼¥É¤ò¼õ¤±¼è¤ë¤«¼ê¤ËÆþ¤ì¤ë¤³¤È¤¬¤Ç¤­¤ë¤è¤¦
+Êݾڤ·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£¤½¤·¤Æ¡¢¤¢¤Ê¤¿¤ÏÈà¤é¤ËÂФ·¤Æ°Ê²¼¤Ç½Ò¤Ù¤ë¾ò·ï
+¤ò¼¨¤·¡¢Èà¤é¤Ë¼«¤é¤Î»ý¤Ä¸¢Íø¤Ë¤Ä¤¤¤ÆÃΤ餷¤á¤ë¤è¤¦¤Ë¤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»
+¤ó¡£
+
+»ä¤¿¤Á¤Ï¤¢¤Ê¤¿¤Î¸¢Íø¤òÆóÃʳ¬¤Î¼ê½ç¤òƧ¤ó¤ÇÊݸ¤Þ¤¹¡£(1) ¤Þ¤º¥½¥Õ¥È¥¦¥§
+¥¢¤ËÂФ·¤ÆÃøºî¸¢¤ò¼çÄ¥¤·¡¢¤½¤·¤Æ (2) ¤¢¤Ê¤¿¤ËÂФ·¤Æ¡¢¥½¥Õ¥È¥¦¥§¥¢¤ÎÊ£
+À½¤äÈÒÉÛ¤Þ¤¿¤Ï²þÊѤˤĤ¤¤Æ¤ÎˡŪ¤Êµö²Ä¤òÍ¿¤¨¤ë¤³¤Î·ÀÌó½ñ¤òÄ󼨤·¤Þ¤¹¡£
+
+¤Þ¤¿¡¢³Æºî¼Ô¤ä»ä¤¿¤Á¤òÊݸ¤ë¤¿¤á¡¢»ä¤¿¤Á¤Ï¤³¤Î¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ë¤Ï
+²¿¤ÎÊݾڤâ̵¤¤¤È¤¤¤¦¤³¤È¤òï¤â¤¬³Î¼Â¤ËÍý²ò¤¹¤ë¤è¤¦¤Ë¤·¡¢¤Þ¤¿¥½¥Õ¥È¥¦¥§
+¥¢¤¬Ã¯¤«Â¾¿Í¤Ë¤è¤Ã¤Æ²þÊѤµ¤ì¡¢¤½¤ì¤¬¼¡¡¹¤ÈÈÒÉÛ¤µ¤ì¤Æ¤¤¤Ã¤¿¤È¤·¤Æ¤â¡¢¤½
+¤Î¼õÎμԤÏÈà¤é¤¬¼ê¤ËÆþ¤ì¤¿¥½¥Õ¥È¥¦¥§¥¢¤¬¥ª¥ê¥¸¥Ê¥ë¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç¤Ï̵¤¤
+¤³¤È¡¢¤½¤·¤Æ¸¶ºî¼Ô¤Î̾À¼¤Ï¾¿Í¤Ë¤è¤Ã¤Æ»ý¤Á¹þ¤Þ¤ì¤¿²ÄǽÀ­¤Î¤¢¤ëÌäÂê¤Ë¤è¤Ã
+¤Æ±Æ¶Á¤µ¤ì¤ë¤³¤È¤¬¤Ê¤¤¤È¤¤¤¦¤³¤È¤ò¼þÃΤµ¤»¤¿¤¤¤È»×¤¤¤Þ¤¹¡£
+
+ºÇ¸å¤Ë¡¢¥½¥Õ¥È¥¦¥§¥¢Æõö¤¬¤¤¤«¤Ê¤ë¥Õ¥ê¡¼¤Î¥×¥í¥°¥é¥à¤Î¸ºß¤Ë¤âÉÔÃǤζ¼
+°Ò¤òÅꤲ¤«¤±¤Æ¤¤¤Þ¤¹¤¬¡¢»ä¤¿¤Á¤Ï¡¢¥Õ¥ê¡¼¤Ê¥×¥í¥°¥é¥à¤ÎºÆÈÒÉÛ¼Ô¤¬¸Ä¡¹¤Ë
+Æõö¥é¥¤¥»¥ó¥¹¤ò¼èÆÀ¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¡¢»ö¼Â¾å¥×¥í¥°¥é¥à¤òÆÈÀêŪ¤Ë¤·¤Æ¤·
+¤Þ¤¦¤È¤¤¤¦´í¸±¤òÈò¤±¤¿¤¤¤È»×¤¤¤Þ¤¹¡£¤³¤¦¤¤¤Ã¤¿»öÂÖ¤òͽËɤ¹¤ë¤¿¤á¡¢»ä¤¿
+¤Á¤Ï¤¤¤«¤Ê¤ëÆõö¤âï¤â¤¬¼«Í³¤ËÍøÍѤǤ­¤ë¤è¤¦¥é¥¤¥»¥ó¥¹¤µ¤ì¤ë¤«¡¢Á´¤¯¥é
+¥¤¥»¥ó¥¹¤µ¤ì¤Ê¤¤¤«¤Î¤É¤Á¤é¤«¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤³¤È¤òÌÀ³Î¤Ë¤·¤Þ¤·¤¿¡£
+
+(ÌõÃí: ËÜ·ÀÌó½ñ¤Ç¡ÖÆÈÀêŪ(proprietary)¡×¤È¤Ï¡¢¥½¥Õ¥È¥¦¥§¥¢¤ÎÍøÍѤäºÆÈÒ
+ÉÛ¡¢²þÊѤ¬¶Ø»ß¤µ¤ì¤Æ¤¤¤ë¤«¡¢µö²Ä¤òÆÀ¤ë¤³¤È¤¬É¬ÍפȤµ¤ì¤Æ¤¤¤ë¤«¡¢¤¢¤ë¤¤
+¤Ï¸·¤·¤¤À©¸Â¤¬²Ý¤»¤é¤ì¤Æ¤¤¤Æ¼«Í³¤Ë¤½¤¦¤¹¤ë¤³¤È¤¬»ö¼Â¾å¤Ç¤­¤Ê¤¯¤Ê¤Ã¤Æ¤¤
+¤ë¾õÂ֤Τ³¤È¤ò»Ø¤¹¡£¾Ü¤·¤¯¤Ï
+http://www.gnu.org/philosophy/categories.ja.html#ProprietarySoftware¤ò
+»²¾È¤»¤è¡£)
+
+Ê£À½¤äÈÒÉÛ¡¢²þÊѤˤĤ¤¤Æ¤ÎÀµ³Î¤Ê¾ò·ï¤ÈÀ©Ìó¤ò°Ê²¼¤Ç½Ò¤Ù¤Æ¤¤¤­¤Þ¤¹¡£
+\f
+                    GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+                 Ê£À½¡¢ÈÒÉÛ¡¢²þÊѤ˴ؤ¹¤ë¾ò·ï¤ÈÀ©Ìó
+
+0. ¤³¤ÎÍøÍѵöÂú·ÀÌó½ñ¤Ï¡¢¤½¤Î¥×¥í¥°¥é¥à(¤Þ¤¿¤Ï¤½¤Î¾¤ÎÃøºîʪ)¤ò¤³¤Î°ì
+È̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤ÎÄê¤á¤ë¾ò·ï¤Î²¼¤ÇÈÒÉۤǤ­¤ë¤È¤¤¤¦¹ðÃΤ¬Ãøºî¸¢¼Ô¤Ë
+¤è¤Ã¤Æµ­ºÜ¤µ¤ì¤¿¥×¥í¥°¥é¥à¤Þ¤¿¤Ï¤½¤Î¾¤ÎÃøºîʪÁ´È̤ËŬÍѤµ¤ì¤ë¡£°Ê²¼¤Ç
+¤Ï¡¢¡Ö¡Ø¥×¥í¥°¥é¥à¡Ù¡×¤È¤Ï¤½¤Î¤è¤¦¤Ë¤·¤Æ¤³¤Î·ÀÌó½ñ¤¬Å¬ÍѤµ¤ì¤¿¥×¥í¥°¥é
+¥à¤äÃøºîʪÁ´È̤ò°ÕÌ£¤·¡¢¤Þ¤¿¡Ö¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ¡×¤È¤Ï¡Ø¥×
+¥í¥°¥é¥à¡Ù¤ä¤½¤Î¾Ãøºî¸¢Ë¡¤Î²¼¤ÇÇÉÀ¸Êª¤È¸«¤Ê¤µ¤ì¤ë¤â¤ÎÁ´È̤ò»Ø¤¹¡£¤¹¤Ê
+¤ï¤Á¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤«¤½¤Î°ìÉô¤ò¡¢Á´¤¯Æ±°ì¤Î¤Þ¤Þ¤«¡¢²þÊѤò²Ã¤¨¤¿¤«¡¢¤¢
+¤ë¤¤¤Ï¾¤Î¸À¸ì¤ËËÝÌõ¤µ¤ì¤¿·Á¤Ç´Þ¤àÃøºîʪ¤Î¤³¤È¤Ç¤¢¤ë(¡Ö²þÊѡפȤ¤¤¦¸ì
+¤ÎËÜÍè¤Î°ÕÌ£¤«¤é¤Ï¤º¤ì¤ë¤¬¡¢°Ê²¼¤Ç¤ÏËÝÌõ¤â²þÊѤΰì¼ï¤È¸«¤Ê¤¹)¡£¤½¤ì¤¾
+¤ì¤Î·ÀÌó¼Ô¤Ï¡Ö¤¢¤Ê¤¿¡×¤Èɽ¸½¤µ¤ì¤ë¡£
+
+Ê£À½¤äÈÒÉÛ¡¢²þÊѰʳ°¤Î³èÆ°¤Ï¤³¤Î·ÀÌó½ñ¤Ç¤Ï¥«¥Ð¡¼¤µ¤ì¤Ê¤¤¡£¤½¤ì¤é¤Ï¤³¤Î
+·ÀÌó½ñ¤ÎÂоݳ°¤Ç¤¢¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤ò¼Â¹Ô¤¹¤ë¹Ô°Ù¼«ÂΤËÀ©¸Â¤Ï¤Ê¤¤¡£¤Þ
+¤¿¡¢¤½¤Î¤è¤¦¤Ê¡Ø¥×¥í¥°¥é¥à¡Ù¤Î½ÐÎÏ·ë²Ì¤Ï¡¢¤½¤ÎÆâÍƤ¬¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð
+¤Ë¤·¤¿Ãøºîʪ¤ò¹½À®¤¹¤ë¾ì¹ç¤Î¤ß¤³¤Î·ÀÌó½ñ¤Ë¤è¤Ã¤ÆÊݸ¤ì¤ë(¡Ø¥×¥í¥°¥é
+¥à¡Ù¤ò¼Â¹Ô¤·¤¿¤³¤È¤Ë¤è¤Ã¤ÆºîÀ®¤µ¤ì¤¿¤È¤¤¤¦¤³¤È¤È¤Ï̵´Ø·¸¤Ç¤¢¤ë)¡£¤³¤Î
+¤è¤¦¤ÊÀþ°ú¤­¤ÎÂÅÅöÀ­¤Ï¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤¬²¿¤ò¤¹¤ë¤Î¤«¤Ë°Í¸¤¹¤ë¡£
+
+1. ¤½¤ì¤¾¤ì¤ÎÊ£À½Êª¤Ë¤ª¤¤¤ÆŬÀÚ¤ÊÃøºî¸¢É½¼¨¤ÈÊݾڤÎÈÝǧÀ¼ÌÀ(disclaimer
+of warranty)¤òÌÜΩ¤Ä¤è¤¦Å¬Àڤ˷Ǻܤ·¡¢¤Þ¤¿¤³¤Î·ÀÌó½ñ¤ª¤è¤Ó°ìÀÚ¤ÎÊݾڤÎ
+ÉԺߤ˿¨¤ì¤¿¹ðÃΤ¹¤Ù¤Æ¤ò¤½¤Î¤Þ¤Þ»Ä¤·¡¢¤½¤·¤Æ¤³¤Î·ÀÌó½ñ¤ÎÊ£À½Êª¤ò¡Ø¥×¥í
+¥°¥é¥à¡Ù¤Î¤¤¤«¤Ê¤ë¼õÎμԤˤâ¡Ø¥×¥í¥°¥é¥à¡Ù¤È¶¦¤ËÈÒÉÛ¤¹¤ë¸Â¤ê¡¢¤¢¤Ê¤¿¤Ï
+¡Ø¥×¥í¥°¥é¥à¡Ù¤Î¥½¡¼¥¹¥³¡¼¥É¤ÎÊ£À½Êª¤ò¡¢¤¢¤Ê¤¿¤¬¼õ¤±¼è¤Ã¤¿Ä̤ê¤Î·Á¤ÇÊ£
+À½¤Þ¤¿¤ÏÈÒÉÛ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ÇÞÂΤÏÌä¤ï¤Ê¤¤¡£
+
+¤¢¤Ê¤¿¤Ï¡¢ÊªÍýŪ¤ËÊ£À½Êª¤ò¾ùÅϤ¹¤ë¤È¤¤¤¦¹Ô°Ù¤Ë´Ø¤·¤Æ¼ê¿ôÎÁ¤ò²Ý¤·¤Æ¤âÎÉ
+¤¤¤·¡¢´õ˾¤Ë¤è¤Ã¤Æ¤Ï¼ê¿ôÎÁ¤ò¼è¤Ã¤Æ¸ò´¹¤Ë¤ª¤±¤ëÊݸî¤ÎÊݾڤòÄ󶡤·¤Æ¤âÎÉ
+¤¤¡£
+
+2. ¤¢¤Ê¤¿¤Ï¼«Ê¬¤Î¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÊ£À½Êª¤«¤½¤Î°ìÉô¤ò²þÊѤ·¤Æ¡Ø¥×¥í¥°¥é
+¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ¤ò·ÁÀ®¤·¡¢¤½¤Î¤è¤¦¤Ê²þÊÑÅÀ¤äÃøºîʪ¤ò¾åµ­Âè1Àá¤ÎÄê
+¤á¤ë¾ò·ï¤Î²¼¤ÇÊ£À½¤Þ¤¿¤ÏÈÒÉÛ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤¿¤À¤·¡¢¤½¤Î¤¿¤á¤Ë¤Ï°Ê²¼
+¤Î¾ò·ï¤¹¤Ù¤Æ¤òËþ¤¿¤·¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤:
+
+    a) ¤¢¤Ê¤¿¤¬¤½¤ì¤é¤Î¥Õ¥¡¥¤¥ë¤òÊѹ¹¤·¤¿¤È¤¤¤¦¤³¤È¤ÈÊѹ¹¤·¤¿Æü»þ¤¬ÎÉ
+    ¤¯Ê¬¤«¤ë¤è¤¦¡¢²þÊѤµ¤ì¤¿¥Õ¥¡¥¤¥ë¤Ë¹ð¼¨¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
+
+    b) ¡Ø¥×¥í¥°¥é¥à¡Ù¤Þ¤¿¤Ï¤½¤Î°ìÉô¤ò´Þ¤àÃøºîʪ¡¢¤¢¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù
+    ¤«¤½¤Î°ìÉô¤«¤éÇÉÀ¸¤·¤¿Ãøºîʪ¤òÈÒÉÛ¤¢¤ë¤¤¤Ïȯɽ¤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¤½¤ÎÁ´
+    ÂΤò¤³¤Î·ÀÌó½ñ¤Î¾ò·ï¤Ë½¾¤Ã¤ÆÂè»°¼Ô¤Ø̵½þ¤ÇÍøÍѵöÂú¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê
+    ¤¤¡£
+
+    c) ²þÊѤµ¤ì¤¿¥×¥í¥°¥é¥à¤¬¡¢Ä̾ï¼Â¹Ô¤¹¤ëºÝ¤ËÂÐÏÃŪ¤Ë¥³¥Þ¥ó¥É¤òÆɤà
+    ¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤Ê¤é¤Ð¡¢¤½¤Î¥×¥í¥°¥é¥à¤òºÇ¤â°ìÈÌŪ¤ÊÊýË¡¤ÇÂÐÏÃŪ¤Ë
+    ¼Â¹Ô¤¹¤ëºÝ¡¢Å¬ÀÚ¤ÊÃøºî¸¢É½¼¨¡¢ÌµÊݾڤǤ¢¤ë¤³¤È(¤¢¤ë¤¤¤Ï¤¢¤Ê¤¿¤¬ÊÝ
+    ¾Ú¤òÄ󶡤¹¤ë¤È¤¤¤¦¤³¤È)¡¢¥æ¡¼¥¶¤¬¥×¥í¥°¥é¥à¤ò¤³¤Î·ÀÌó½ñ¤Ç½Ò¤Ù¤¿¾ò
+    ·ï¤Î²¼¤ÇÈÒÉÛ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¤È¤¤¤¦¤³¤È¡¢¤½¤·¤Æ¤³¤Î·ÀÌó½ñ¤ÎÊ£À½Êª¤ò
+    ±ÜÍ÷¤¹¤ë¤Ë¤Ï¤É¤¦¤·¤¿¤é¤è¤¤¤«¤È¤¤¤¦¥æ¡¼¥¶¤Ø¤ÎÀâÌÀ¤ò´Þ¤à¹ðÃΤ¬°õºþ¤µ
+    ¤ì¤ë¤«¡¢¤¢¤ë¤¤¤Ï²èÌ̤Ëɽ¼¨¤µ¤ì¤ë¤è¤¦¤Ë¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤(Îã³°¤È¤·
+    ¤Æ¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤½¤Î¤â¤Î¤ÏÂÐÏÃŪ¤Ç¤¢¤Ã¤Æ¤âÄ̾綠¤Î¤è¤¦¤Ê¹ðÃΤò°õ
+    ºþ¤·¤Ê¤¤¾ì¹ç¤Ë¤Ï¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿¤¢¤Ê¤¿¤ÎÃøºîʪ¤Ë¤½¤Î¤è¤¦
+    ¤Ê¹ðÃΤò°õºþ¤µ¤»¤ëɬÍפϤʤ¤)¡£
+\f
+°Ê¾å¤ÎɬÍ×¾ò·ï¤ÏÁ´ÂΤȤ·¤Æ¤Î²þÊѤµ¤ì¤¿Ãøºîʪ¤ËŬÍѤµ¤ì¤ë¡£Ãøºîʪ¤Î°ìÉô
+¤¬¡Ø¥×¥í¥°¥é¥à¡Ù¤«¤éÇÉÀ¸¤·¤¿¤â¤Î¤Ç¤Ï¤Ê¤¤¤È³Îǧ¤Ç¤­¡¢¤½¤ì¤é¼«¿ÈÊ̤ÎÆÈΩ
+¤·¤¿Ãøºîʪ¤Ç¤¢¤ë¤È¹çÍýŪ¤Ë¹Í¤¨¤é¤ì¤ë¤Ê¤é¤Ð¡¢¤¢¤Ê¤¿¤¬¤½¤ì¤é¤òÊ̤ÎÃøºîʪ
+¤È¤·¤Æʬ¤±¤ÆÈÒÉÛ¤¹¤ë¾ì¹ç¡¢¤½¤¦¤¤¤Ã¤¿Éôʬ¤Ë¤Ï¤³¤Î·ÀÌó½ñ¤È¤½¤Î¾ò·ï¤Ï
+ŬÍѤµ¤ì¤Ê¤¤¡£¤·¤«¤·¡¢¤¢¤Ê¤¿¤¬Æ±¤¸Éôʬ¤ò¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ
+Á´ÂΤΰìÉô¤È¤·¤ÆÈÒÉÛ¤¹¤ë¤Ê¤é¤Ð¡¢Á´ÂΤȤ·¤Æ¤ÎÈÒÉÛʪ¤Ï¡¢¤³¤Î·ÀÌó½ñ¤¬
+²Ý¤¹¾ò·ï¤Ë½¾¤ï¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤È¤¤¤¦¤Î¤Ï¡¢¤³¤Î·ÀÌó½ñ¤¬Â¾¤Î·ÀÌó¼Ô
+¤ËÍ¿¤¨¤ëµö²Ä¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù´Ý¤´¤ÈÁ´ÂΤ˵ڤӡ¢Ã¯¤¬½ñ¤¤¤¿¤«¤Ï´Ø·¸¤Ê¤¯³Æ
+Éôʬ¤Î¤¹¤Ù¤Æ¤òÊݸ¤ë¤«¤é¤Ç¤¢¤ë¡£
+
+¤è¤Ã¤Æ¡¢¤¹¤Ù¤Æ¤¢¤Ê¤¿¤Ë¤è¤Ã¤Æ½ñ¤«¤ì¤¿Ãøºîʪ¤ËÂФ·¡¢¸¢Íø¤ò¼çÄ¥¤·¤¿¤ê¤¢¤Ê
+¤¿¤Î¸¢Íø¤Ë°ÛµÄ¤ò¿½¤·Î©¤Æ¤ë¤³¤È¤Ï¤³¤ÎÀá¤Î°Õ¿Þ¤¹¤ë¤È¤³¤í¤Ç¤Ï¤Ê¤¤¡£¤à¤·¤í¡¢
+¤½¤Î¼ñ»Ý¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿ÇÉÀ¸Êª¤Ê¤¤¤·½¸¹çÃøºîʪ¤ÎÈÒÉÛ¤ò´ÉÍý¤¹
+¤ë¸¢Íø¤ò¹Ô»È¤¹¤ë¤È¤¤¤¦¤³¤È¤Ë¤¢¤ë¡£
+
+¤Þ¤¿¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤Æ¤¤¤Ê¤¤¤½¤Î¾¤ÎÃøºîʪ¤ò¡Ø¥×¥í¥°¥é¥à¡Ù(¤¢
+¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿Ãøºîʪ)¤È°ì½ï¤Ë½¸¤á¤¿¤À¤±¤Î¤â¤Î¤ò°ì´¬¤Î
+ÊÝ´ÉÁõÃ֤ʤ¤¤·ÈÒÉÛÇÞÂΤ˼ý¤á¤Æ¤â¡¢¤½¤Î¾¤ÎÃøºîʪ¤Þ¤Ç¤³¤Î·ÀÌó½ñ¤¬ÊÝ
+¸î¤¹¤ëÂоݤˤʤë¤È¤¤¤¦¤³¤È¤Ë¤Ï¤Ê¤é¤Ê¤¤¡£
+
+3. ¤¢¤Ê¤¿¤Ï¾åµ­Âè1Àᤪ¤è¤Ó2Àá¤Î¾ò·ï¤Ë½¾¤¤¡¢¡Ø¥×¥í¥°¥é¥à¡Ù(¤¢¤ë¤¤¤ÏÂè2
+Àá¤Ë¤ª¤±¤ëÇÉÀ¸Êª)¤ò¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼¥É¤Ê¤¤¤·¼Â¹Ô·Á¼°¤ÇÊ£À½¤Þ¤¿¤ÏÈÒÉÛ¤¹
+¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤¿¤À¤·¡¢¤½¤Î¾ì¹ç¤¢¤Ê¤¿¤Ï°Ê²¼¤Î¤¦¤Á¤É¤ì¤«°ì¤Ä¤ò¼Â»Ü¤·¤Ê
+¤±¤ì¤Ð¤Ê¤é¤Ê¤¤:
+
+    a) Ãøºîʪ¤Ë¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ËÂбþ¤·¤¿´°Á´¤«¤Äµ¡³£¤ÇÆɤ߼è¤ê²Äǽ¤Ê
+    ¥½¡¼¥¹¥³¡¼¥É¤òźÉÕ¤¹¤ë¡£¤¿¤À¤·¡¢¥½¡¼¥¹¥³¡¼¥É¤Ï¾åµ­Âè1Àᤪ¤è¤Ó2Àá¤Î
+    ¾ò·ï¤Ë½¾¤¤¥½¥Õ¥È¥¦¥§¥¢¤Î¸ò´¹¤Ç½¬´·Åª¤Ë»È¤ï¤ì¤ëÇÞÂΤÇÈÒÉÛ¤·¤Ê¤±¤ì¤Ð
+    ¤Ê¤é¤Ê¤¤¡£¤¢¤ë¤¤¤Ï¡¢
+
+    b) Ãøºîʪ¤Ë¡¢¤¤¤«¤Ê¤ëÂè»°¼Ô¤ËÂФ·¤Æ¤â¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ËÂбþ¤·¤¿´°
+    Á´¤«¤Äµ¡³£¤ÇÆɤ߼è¤ê²Äǽ¤Ê¥½¡¼¥¹¥³¡¼¥É¤ò¡¢ÈÒÉÛ¤ËÍפ¹¤ëʪÍýŪ¥³¥¹¥È
+    ¤ò¾å²ó¤é¤Ê¤¤ÄøÅ٤μê¿ôÎÁ¤È°ú¤­´¹¤¨¤ËÄ󶡤¹¤ë»Ý½Ò¤Ù¤¿¾¯¤Ê¤¯¤È¤â3ǯ
+    ´Ö¤ÏÍ­¸ú¤Ê½ñÌ̤ˤʤä¿¿½¤·½Ð¤òź¤¨¤ë¡£¤¿¤À¤·¡¢¥½¡¼¥¹¥³¡¼¥É¤Ï¾åµ­Âè
+    1Àᤪ¤è¤Ó2Àá¤Î¾ò·ï¤Ë½¾¤¤¥½¥Õ¥È¥¦¥§¥¢¤Î¸ò´¹¤Ç½¬´·Åª¤Ë»È¤ï¤ì¤ëÇÞÂΤÇ
+    ÈÒÉÛ¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤¢¤ë¤¤¤Ï¡¢
+
+    c) Âбþ¤¹¤ë¥½¡¼¥¹¥³¡¼¥ÉÈÒÉۤ、·½Ð¤ËºÝ¤·¤Æ¡¢¤¢¤Ê¤¿¤¬ÆÀ¤¿¾ðÊó¤ò°ì
+    ½ï¤Ë°ú¤­ÅϤ¹(¤³¤ÎÁªÂò»è¤Ï¡¢±ÄÍø¤òÌÜŪ¤È¤·¤Ê¤¤ÈÒÉۤǤ¢¤Ã¤Æ¡¢¤«¤Ä¤¢
+    ¤Ê¤¿¤¬¾åµ­¾®Àáb¤Ç»ØÄꤵ¤ì¤Æ¤¤¤ë¤è¤¦¤Ê¿½¤·½Ð¤È¶¦¤Ë¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼
+    ¥É¤¢¤ë¤¤¤Ï¼Â¹Ô·Á¼°¤Î¥×¥í¥°¥é¥à¤·¤«Æþ¼ê¤·¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¸Â¤êµö²Ä¤µ¤ì
+    ¤ë)¡£
+
+Ãøºîʪ¤Î¥½¡¼¥¹¥³¡¼¥É¤È¤Ï¡¢¤½¤ì¤ËÂФ·¤Æ²þÊѤò²Ã¤¨¤ë¾å¤Ç¹¥¤Þ¤·¤¤¤È¤µ¤ì¤ë
+Ãøºîʪ¤Î·Á¼°¤ò°ÕÌ£¤¹¤ë¡£¤¢¤ë¼Â¹Ô·Á¼°¤ÎÃøºîʪ¤Ë¤È¤Ã¤Æ´°Á´¤Ê¥½¡¼¥¹¥³¡¼¥É
+¤È¤Ï¡¢¤½¤ì¤¬´Þ¤à¥â¥¸¥å¡¼¥ë¤¹¤Ù¤Æ¤Î¥½¡¼¥¹¥³¡¼¥ÉÁ´Éô¤Ë²Ã¤¨¡¢´ØÏ¢¤¹¤ë¥¤¥ó
+¥¿¡¼¥Õ¥§¡¼¥¹ÄêµÁ¥Õ¥¡¥¤¥ë¤Î¤¹¤Ù¤Æ¤È¥é¥¤¥Ö¥é¥ê¤Î¥³¥ó¥Ñ¥¤¥ë¤ä¥¤¥ó¥¹¥È¡¼¥ë
+¤òÀ©¸æ¤¹¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¥¹¥¯¥ê¥×¥È¤ò¤â²Ã¤¨¤¿¤â¤Î¤ò°ÕÌ£¤¹¤ë¡£¤·¤«¤·ÆÃÊÌ
+¤ÊÎã³°¤È¤·¤Æ¡¢¤½¤Î¥³¥ó¥Ý¡¼¥Í¥ó¥È¼«ÂΤ¬¼Â¹Ô·Á¼°¤ËÉտ魯¤ë¤Î¤Ç¤Ï̵¤¤¸Â¤ê¡¢
+ÈÒÉÛ¤µ¤ì¤ë¤â¤Î¤ÎÃæ¤Ë¡¢¼Â¹Ô·Á¼°¤¬¼Â¹Ô¤µ¤ì¤ë¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Î¼ç
+Íפʥ³¥ó¥Ý¡¼¥Í¥ó¥È(¥³¥ó¥Ñ¥¤¥é¤ä¥«¡¼¥Í¥ëÅù)¤ÈÄ̾ï°ì½ï¤Ë(¥½¡¼¥¹¤«¥Ð¥¤¥Ê
+¥ê·Á¼°¤Î¤É¤Á¤é¤«¤Ç)ÈÒÉÛ¤µ¤ì¤ë¤â¤Î¤ò´Þ¤ó¤Ç¤¤¤ëɬÍפϤʤ¤¤È¤¹¤ë¡£
+
+¼Â¹Ô·Á¼°¤Þ¤¿¤Ï¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼¥É¤ÎÈÒÉÛ¤¬¡¢»ØÄꤵ¤ì¤¿¾ì½ê¤«¤é¥³¥Ô¡¼¤¹¤ë
+¤¿¤á¤Î¥¢¥¯¥»¥¹¼êÃʤòÄ󶡤¹¤ë¤³¤È¤Ç°Ù¤µ¤ì¤ë¤È¤·¤Æ¡¢¤½¤Î¾å¤Ç¥½¡¼¥¹¥³¡¼¥É
+¤âƱÅù¤Î¥¢¥¯¥»¥¹¼êÃʤˤè¤Ã¤ÆƱ¤¸¾ì½ê¤«¤é¥³¥Ô¡¼¤Ç¤­¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤Ê
+¤é¤Ð¡¢Âè»°¼Ô¤¬¥ª¥Ö¥¸¥§¥¯¥È¥³¡¼¥É¤È°ì½ï¤Ë¥½¡¼¥¹¤â¶¯À©Åª¤Ë¥³¥Ô¡¼¤µ¤»¤é¤ì
+¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤¯¤Æ¤â¥½¡¼¥¹¥³¡¼¥ÉÈÒÉۤξò·ï¤òËþ¤¿¤·¤Æ¤¤¤ë¤â¤Î¤È¤¹¤ë¡£
+\f
+4. ¤¢¤Ê¤¿¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò¡¢¤³¤Î·ÀÌó½ñ¤Ë¤ª¤¤¤ÆÌÀ³Î¤ËÄ󼨤µ¤ì¤¿¹Ô
+°Ù¤ò½ü¤­Ê£À½¤ä²þÊÑ¡¢¥µ¥Ö¥é¥¤¥»¥ó¥¹¡¢¤¢¤ë¤¤¤ÏÈÒÉÛ¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£Â¾¤Ë
+¡Ø¥×¥í¥°¥é¥à¡Ù¤òÊ£À½¤ä²þÊÑ¡¢¥µ¥Ö¥é¥¤¥»¥ó¥¹¡¢¤¢¤ë¤¤¤ÏÈÒÉÛ¤¹¤ë´ë¤Æ¤Ï¤¹¤Ù
+¤Æ̵¸ú¤Ç¤¢¤ê¡¢¤³¤Î·ÀÌó½ñ¤Î²¼¤Ç¤Î¤¢¤Ê¤¿¤Î¸¢Íø¤ò¼«Æ°Åª¤Ë½ª·ë¤µ¤»¤ë¤³
+¤È¤Ë¤Ê¤í¤¦¡£¤·¤«¤·¡¢Ê£À½Êª¤ä¸¢Íø¤ò¤³¤Î·ÀÌó½ñ¤Ë½¾¤Ã¤Æ¤¢¤Ê¤¿¤«¤éÆÀ¤¿
+¿Í¡¹¤Ë´Ø¤·¤Æ¤Ï¡¢¤½¤Î¤è¤¦¤Ê¿Í¡¹¤¬¤³¤Î·ÀÌó½ñ¤Ë´°Á´¤Ë½¾¤Ã¤Æ¤¤¤ë¸Â¤êÈà
+¤é¤Î¥é¥¤¥»¥ó¥¹¤Þ¤Ç½ª·ë¤¹¤ë¤³¤È¤Ï¤Ê¤¤¡£
+
+5. ¤¢¤Ê¤¿¤Ï¤³¤Î·ÀÌó½ñ¤ò¼õÂú¤¹¤ëɬÍפÏ̵¤¤¡£¤È¤¤¤¦¤Î¤Ï¡¢¤¢¤Ê¤¿¤Ï¤³
+¤ì¤Ë½ð̾¤·¤Æ¤¤¤Ê¤¤¤«¤é¤Ç¤¢¤ë¡£¤·¤«¤·¡¢¤³¤Î·ÀÌó½ñ°Ê³°¤Ë¤¢¤Ê¤¿¤ËÂФ·
+¤Æ¡Ø¥×¥í¥°¥é¥à¡Ù¤ä¤½¤ÎÇÉÀ¸Êª¤òÊѹ¹¡¢ÈÒÉÛ¤¹¤ëµö²Ä¤òÍ¿¤¨¤ë¤â¤Î¤Ï¸ºß¤·¤Ê
+¤¤¡£¤³¤ì¤é¤Î¹Ô°Ù¤Ï¡¢¤¢¤Ê¤¿¤¬¤³¤Î·ÀÌó½ñ¤ò¼õ¤±Æþ¤ì¤Ê¤¤¸Â¤êË¡¤Ë¤è¤Ã¤Æ
+¶Ø¤¸¤é¤ì¤Æ¤¤¤ë¡£¤½¤³¤Ç¡¢¡Ø¥×¥í¥°¥é¥à¡Ù(¤¢¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·
+¤¿Ãøºîʪ¤Î¤¹¤Ù¤Æ)¤ò²þÊѤʤ¤¤·ÈÒÉÛ¤¹¤ë¤³¤È¤Ë¤è¤ê¡¢¤¢¤Ê¤¿¤Ï¼«Ê¬¤¬¤½¤Î¤è
+¤¦¤Ê¹Ô°Ù¤ò¹Ô¤¦¤¿¤á¤Ë¤³¤Î·ÀÌó½ñ¤ò¼õÂú¤·¤¿¤È¤¤¤¦¤³¤È¡¢¤½¤·¤Æ¡Ø¥×¥í¥°
+¥é¥à¡Ù¤È¤½¤ì¤Ë´ð¤Å¤¯Ãøºîʪ¤ÎÊ£À½¤äÈÒÉÛ¡¢²þÊѤˤĤ¤¤Æ¤³¤Î·ÀÌó½ñ¤¬²Ý
+¤¹À©Ìó¤È¾ò·ï¤ò¤¹¤Ù¤Æ¼õ¤±Æþ¤ì¤¿¤È¤¤¤¦¤³¤È¤ò¼¨¤·¤¿¤â¤Î¤È¸«¤Ê¤¹¡£
+
+6. ¤¢¤Ê¤¿¤¬¡Ø¥×¥í¥°¥é¥à¡Ù(¤Þ¤¿¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ò´ð¤Ë¤·¤¿ÃøºîʪÁ´ÈÌ)¤ò
+ºÆÈÒÉÛ¤¹¤ë¤¿¤Ó¤Ë¡¢¤½¤Î¼õÎμԤϸµ¡¹¤Î¥é¥¤¥»¥ó¥¹µö²Ä¼Ô¤«¤é¡¢¤³¤Î·ÀÌó½ñ¤Ç
+»ØÄꤵ¤ì¤¿¾ò·ï¤ÈÀ©Ìó¤Î²¼¤Ç¡Ø¥×¥í¥°¥é¥à¡Ù¤òÊ£À½¤äÈÒÉÛ¡¢¤¢¤ë¤¤¤Ï²þÊѤ¹¤ë
+µö²Ä¤ò¼«Æ°Åª¤ËÆÀ¤ë¤â¤Î¤È¤¹¤ë¡£¤¢¤Ê¤¿¤Ï¡¢¼õÎμԤ¬¤³¤³¤Çǧ¤á¤é¤ì¤¿¸¢Íø¤ò
+¹Ô»È¤¹¤ë¤³¤È¤Ë´Ø¤·¤Æ¤³¤ì°Ê¾å¾¤Î¤¤¤«¤Ê¤ëÀ©¸Â¤â²Ý¤¹¤³¤È¤¬¤Ç¤­¤Ê¤¤¡£¤¢¤Ê
+¤¿¤Ë¤Ï¡¢Âè»°¼Ô¤¬¤³¤Î·ÀÌó½ñ¤Ë½¾¤¦¤³¤È¤ò¶¯À©¤¹¤ëÀÕǤ¤Ï¤Ê¤¤¡£
+
+7. Æõö¿¯³²¤¢¤ë¤¤¤Ï¤½¤Î¾¤ÎÍýͳ(Æõö´Ø·¸¤Ë¸Â¤é¤Ê¤¤)¤«¤é¡¢ºÛȽ½ê¤ÎȽ·è
+¤¢¤ë¤¤¤Ï¿½¤·Î©¤Æ¤Î·ë²Ì¤È¤·¤Æ¤¢¤Ê¤¿¤Ë(ºÛȽ½êÌ¿Îá¤ä·ÀÌó¤Ê¤É¤Ë¤è¤ê)¤³¤Î·À
+Ìó½ñ¤Î¾ò·ï¤ÈÌ·½â¤¹¤ëÀ©Ì󤬲ݤµ¤ì¤¿¾ì¹ç¤Ç¤â¡¢¤¢¤Ê¤¿¤¬¤³¤Î·ÀÌó½ñ¤Î¾ò·ï¤ò
+ÌȽü¤µ¤ì¤ë¤ï¤±¤Ç¤Ï¤Ê¤¤¡£¤â¤·¤³¤Î·ÀÌó½ñ¤Î²¼¤Ç¤¢¤Ê¤¿¤Ë²Ý¤»¤é¤ì¤¿ÀÕǤ¤È¾
+¤Î´ØÏ¢¤¹¤ëÀÕǤ¤òƱ»þ¤ËËþ¤¿¤¹¤è¤¦¤Ê·Á¤ÇÈÒÉۤǤ­¤Ê¤¤¤Ê¤é¤Ð¡¢·ë²Ì¤È¤·¤Æ¤¢
+¤Ê¤¿¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤òÈÒÉÛ¤¹¤ë¤³¤È¤¬Á´¤¯¤Ç¤­¤Ê¤¤¤È¤¤¤¦¤³¤È¤Ç¤¢¤ë¡£Î㤨
+¤ÐÆõö¥é¥¤¥»¥ó¥¹¤¬¡¢¤¢¤Ê¤¿¤«¤éľÀÜ´ÖÀܤòÌä¤ï¤º¥³¥Ô¡¼¤ò¼õ¤±¼è¤Ã¤¿¿Í¤¬Ã¯
+¤Ç¤â¡Ø¥×¥í¥°¥é¥à¡Ù¤ò»ÈÍÑÎÁ̵ÎÁ¤ÇºÆÈÒÉÛ¤¹¤ë¤³¤È¤òǧ¤á¤Æ¤¤¤Ê¤¤¾ì¹ç¡¢¤¢¤Ê
+¤¿¤¬¤½¤ÎÀ©Ìó¤È¤³¤Î·ÀÌó½ñ¤òξÊý¤È¤âËþ¤¿¤¹¤Ë¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÈÒÉÛ¤ò´°Á´
+¤ËÃæ»ß¤¹¤ë¤·¤«¤Ê¤¤¤À¤í¤¦¡£
+
+¤³¤ÎÀá¤Î°ìÉôʬ¤¬ÆÃÄê¤Î¾õ¶·¤Î²¼¤Ç̵¸ú¤Ê¤¤¤·¼Â»ÜÉÔ²Äǽ¤Ê¾ì¹ç¤Ç¤â¡¢Àá¤Î»Ä
+¤ê¤ÎÉôʬ¤ÏŬÍѤµ¤ì¤ë¤è¤¦°Õ¿Þ¤µ¤ì¤Æ¤¤¤ë¡£¤½¤Î¾¤Î¾õ¶·¤Ç¤ÏÀ᤬Á´ÂΤȤ·¤Æ
+ŬÍѤµ¤ì¤ë¤è¤¦°Õ¿Þ¤µ¤ì¤Æ¤¤¤ë¡£
+
+Æõö¤ä¤½¤Î¾¤Îºâ»º¸¢¤ò¿¯³²¤·¤¿¤ê¡¢¤½¤Î¤è¤¦¤Ê¸¢Íø¤Î¼çÄ¥¤Î¸úÎϤ˰۵Ĥò¾§
+¤¨¤¿¤ê¤¹¤ë¤è¤¦¤¢¤Ê¤¿¤òͶÏǤ¹¤ë¤³¤È¤¬¤³¤ÎÀá¤ÎÌÜŪ¤Ç¤Ï¤Ê¤¤¡£¤³¤ÎÀá¤Ë¤Ï¡¢
+¿Í¡¹¤Ë¤è¤Ã¤Æ¥é¥¤¥»¥ó¥¹´·¹Ô¤È¤·¤Æ¼Â¸½¤µ¤ì¤Æ¤­¤¿¡¢¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ÈÒÉÛ
+¤Î¥·¥¹¥Æ¥à¤Î´°Á´À­¤ò¸î¤ë¤È¤¤¤¦ÌÜŪ¤·¤«¤Ê¤¤¡£Â¿¤¯¤Î¿Í¡¹¤¬¡¢¥Õ¥ê¡¼¥½¥Õ¥È
+¥¦¥§¥¢¤ÎÈÒÉÛ¥·¥¹¥Æ¥à¤¬¼óÈø°ì´Ó¤·¤ÆŬÍѤµ¤ì¤Æ¤¤¤ë¤È¤¤¤¦¿®Íê¤Ë´ð¤Å¤­¡¢¤³
+¤Î¥·¥¹¥Æ¥à¤òÄ̤¸¤ÆÈÒÉÛ¤µ¤ì¤ë¿Íͤʥ½¥Õ¥È¥¦¥§¥¢¤Ë´²Âç¤Ê¹×¸¥¤ò¤·¤Æ¤­¤¿¤Î
+¤Ï»ö¼Â¤Ç¤¢¤ë¤¬¡¢¿Í¤¬¤É¤Î¤è¤¦¤Ê¥·¥¹¥Æ¥à¤òÄ̤¸¤Æ¥½¥Õ¥È¥¦¥§¥¢¤òÈÒÉÛ¤·¤¿¤¤
+¤È»×¤¦¤«¤Ï¤¢¤¯¤Þ¤Ç¤âºî¼Ô/´óÍ¿¼Ô¼¡Âè¤Ç¤¢¤ê¡¢¤¢¤Ê¤¿¤¬ÁªÂò¤ò²¡¤·¤Ä¤±¤ë¤³
+¤È¤Ï¤Ç¤­¤Ê¤¤¡£
+
+¤³¤ÎÀá¤Ï¡¢¤³¤Î·ÀÌó½ñ¤Î¤³¤ÎÀá°Ê³°¤ÎÉôʬ¤Î°ìµ¢·ë¤Ë¤Ê¤ë¤È¹Í¤¨¤é¤ì¤ë¥±¡¼
+¥¹¤òÅ°ÄìŪ¤ËÌÀ¤é¤«¤Ë¤¹¤ë¤³¤È¤òÌÜŪ¤È¤·¤Æ¤¤¤ë¡£
+\f
+8. ¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÈÒÉÛ¤äÍøÍѤ¬¡¢¤¢¤ë¹ñ¤Ë¤ª¤¤¤Æ¤ÏÆõö¤Þ¤¿¤ÏÃøºî¸¢¤¬¼ç
+Ä¥¤µ¤ì¤¿¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤Î¤¤¤º¤ì¤«¤Ë¤è¤Ã¤ÆÀ©¸Â¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¡Ø¥×¥í¥°
+¥é¥à¡Ù¤Ë¤³¤Î·ÀÌó½ñ¤òŬÍѤ·¤¿¸µ¤ÎÃøºî¸¢¼Ô¤Ï¡¢¤½¤¦¤¤¤Ã¤¿¹ñ¡¹¤òÇÓ½ü¤·
+¤¿ÌÀ³Î¤ÊÃÏÍýŪÈÒÉÛÀ©¸Â¤ò²Ã¤¨¡¢¤½¤³¤ÇÇÓ½ü¤µ¤ì¤Æ¤¤¤Ê¤¤¹ñ¤ÎÃæ¤ä¤½¤ì¤é¤Î¹ñ¡¹
+¤Î´Ö¤Ç¤Î¤ßÈÒÉÛ¤¬µö²Ä¤µ¤ì¤ë¤è¤¦¤Ë¤·¤Æ¤â¹½¤ï¤Ê¤¤¡£¤½¤Î¾ì¹ç¡¢¤½¤Î¤è¤¦¤ÊÀ©
+¸Â¤Ï¤³¤Î·ÀÌó½ñËÜʸ¤Ç½ñ¤«¤ì¤Æ¤¤¤ë¤Î¤ÈƱÍͤ˸«¤Ê¤µ¤ì¤ë¡£
+
+9. ¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤϡ¢»þ¤Ë¤è¤Ã¤Æ²þÄû¤Þ¤¿¤Ï¿·ÈǤΰìÈ̸ø½°ÍøÍѵö
+Âú½ñ¤òȯɽ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î¤è¤¦¤Ê¿·ÈǤϸ½ºß¤Î¥Ð¡¼¥¸¥ç¥ó¤È¤½¤ÎÀº¿À
+¤Ë¤ª¤¤¤Æ¤Ï»÷¤¿¤â¤Î¤Ë¤Ê¤ë¤À¤í¤¦¤¬¡¢¿·¤¿¤ÊÌäÂê¤ä·üÇ°¤ò²ò·è¤¹¤ë¤¿¤áºÙÉô¤Ç
+¤Ï°Û¤Ê¤ë²ÄǽÀ­¤¬¤¢¤ë¡£
+
+¤½¤ì¤¾¤ì¤Î¥Ð¡¼¥¸¥ç¥ó¤Ë¤Ï¡¢¸«Ê¬¤±¤¬ÉÕ¤¯¤è¤¦¤Ë¥Ð¡¼¥¸¥ç¥óÈֹ椬¿¶¤é¤ì¤Æ¤¤
+¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Ë¤ª¤¤¤Æ¤½¤ì¤ËŬÍѤµ¤ì¤ë¤³¤Î·ÀÌó½ñ¤Î¥Ð¡¼¥¸¥ç¥óÈֹ椬
+»ØÄꤵ¤ì¤Æ¤¤¤Æ¡¢¹¹¤Ë¡Ö¤½¤ì°Ê¹ß¤Î¤¤¤«¤Ê¤ë¥Ð¡¼¥¸¥ç¥ó¡×¤âŬÍѤ·¤ÆÎɤ¤¤È¤Ê¤Ã
+¤Æ¤¤¤¿¾ì¹ç¡¢¤¢¤Ê¤¿¤Ï½¾¤¦¾ò·ï¤ÈÀ©Ìó¤È¤·¤Æ¡¢»ØÄê¤Î¥Ð¡¼¥¸¥ç¥ó¤«¡¢¥Õ¥ê¡¼¥½
+¥Õ¥È¥¦¥§¥¢ºâÃĤˤè¤Ã¤Æȯ¹Ô¤µ¤ì¤¿»ØÄê¤Î¥Ð¡¼¥¸¥ç¥ó°Ê¹ß¤ÎÈǤΤɤ줫°ì¤Ä¤Î
+¤É¤Á¤é¤«¤òÁª¤Ö¤³¤È¤¬½ÐÍè¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Ç¥é¥¤¥»¥ó¥¹¤Î¥Ð¡¼¥¸¥ç¥óÈÖ¹æ
+¤¬»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤¤Ê¤é¤Ð¡¢¤¢¤Ê¤¿¤Ïº£¤Þ¤Ç¤Ë¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤ«¤éȯ
+¹Ô¤µ¤ì¤¿¥Ð¡¼¥¸¥ç¥ó¤ÎÃ椫¤é¹¥¤­¤ËÁª¤ó¤Ç¹½¤ï¤Ê¤¤¡£
+
+10. ¤â¤·¤¢¤Ê¤¿¤¬¡Ø¥×¥í¥°¥é¥à¡Ù¤Î°ìÉô¤ò¡¢¤½¤ÎÈÒÉÛ¾ò·ï¤¬¤³¤Î·ÀÌó½ñ¤È
+°Û¤Ê¤ë¾¤Î¥Õ¥ê¡¼¤Ê¥×¥í¥°¥é¥à¤ÈÅý¹ç¤·¤¿¤¤¤Ê¤é¤Ð¡¢ºî¼Ô¤ËÏ¢Íí¤·¤Æµö²Ä¤òµá
+¤á¤è¡£¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤ¬Ãøºî¸¢¤òÊÝÍ­¤¹¤ë¥½¥Õ¥È¥¦¥§¥¢¤Ë¤Ä¤¤¤Æ¤Ï¡¢
+¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢ºâÃĤËÏ¢Íí¤»¤è¡£»ä¤¿¤Á¤Ï¡¢¤³¤Î¤è¤¦¤Ê¾ì¹ç¤Î¤¿¤á¤ËÆÃÊÌ
+¤ÊÎã³°¤òÀߤ±¤ë¤³¤È¤â¤¢¤ë¡£»ä¤¿¤Á¤¬·èÄê¤ò²¼¤¹¤Ë¤¢¤¿¤Ã¤Æ¤Ï¡¢»ä¤¿¤Á¤Î¥Õ¥ê¡¼
+¥½¥Õ¥È¥¦¥§¥¢¤ÎÇÉÀ¸Êª¤¹¤Ù¤Æ¤¬¥Õ¥ê¡¼¤Ê¾õÂÖ¤ËÊݤ¿¤ì¤ë¤È¤¤¤¦¤³¤È¤È¡¢°ìÈÌŪ
+¤Ë¥½¥Õ¥È¥¦¥§¥¢¤Î¶¦Í­¤ÈºÆÍøÍѤòÂ¥¿Ê¤¹¤ë¤È¤¤¤¦Æó¤Ä¤ÎÌÜɸ¤òµ¬½à¤Ë¸¡Æ¤¤µ¤ì
+¤ë¤Ç¤¢¤í¤¦¡£
+                            ÌµÊݾڤˤĤ¤¤Æ
+
+11. ¡Ø¥×¥í¥°¥é¥à¡Ù¤ÏÂå²Á̵¤·¤ËÍøÍѤ¬µö²Ä¤µ¤ì¤ë¤Î¤Ç¡¢Å¬ÀÚ¤ÊË¡¤¬Ç§¤á¤ë¸Â
+¤ê¤Ë¤ª¤¤¤Æ¡¢¡Ø¥×¥í¥°¥é¥à¡Ù¤Ë´Ø¤¹¤ë¤¤¤«¤Ê¤ëÊݾڤ⸺ߤ·¤Ê¤¤¡£½ñÌ̤ÇÊ̤Ë
+½Ò¤Ù¤ë¾ì¹ç¤ò½ü¤¤¤Æ¡¢Ãøºî¸¢¼Ô¡¢¤Þ¤¿¤Ï¤½¤Î¾¤ÎÃÄÂΤϡ¢¡Ø¥×¥í¥°¥é¥à¡Ù¤ò¡¢
+ɽÌÀ¤µ¤ì¤¿¤«¸À³°¤Ë¤«¤ÏÌä¤ï¤º¡¢¾¦¶ÈŪŬÀ­¤òÊݾڤ¹¤ë¤Û¤Î¤á¤«¤·¤ä¤¢¤ëÆÃÄê
+¤ÎÌÜŪ¤Ø¤ÎŬ¹çÀ­(¤Ë¸Â¤é¤ì¤Ê¤¤)¤ò´Þ¤à°ìÀÚ¤ÎÊݾÚ̵¤·¤Ë¡Ö¤¢¤ë¤¬¤Þ¤Þ¡×¤ÇÄó
+¶¡¤¹¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Î¼Á¤ÈÀ­Ç½¤Ë´Ø¤¹¤ë¥ê¥¹¥¯¤Î¤¹¤Ù¤Æ¤Ï¤¢¤Ê¤¿¤Ëµ¢Â°¤¹
+¤ë¡£¡Ø¥×¥í¥°¥é¥à¡Ù¤Ë·ç´Ù¤¬¤¢¤ë¤ÈȽÌÀ¤·¤¿¾ì¹ç¡¢¤¢¤Ê¤¿¤ÏɬÍפÊÊݼéÅÀ¸¡¤ä
+Ê佤¡¢½¤Àµ¤ËÍפ¹¤ë¥³¥¹¥È¤Î¤¹¤Ù¤Æ¤ò°ú¤­¼õ¤±¤ë¤³¤È¤Ë¤Ê¤ë¡£
+
+12. Å¬ÀÚ¤ÊË¡¤«½ñÌ̤ǤÎƱ°Õ¤Ë¤è¤Ã¤ÆÌ¿¤¼¤é¤ì¤Ê¤¤¸Â¤ê¡¢Ãøºî¸¢¼Ô¡¢¤Þ¤¿¤Ï¾å
+µ­¤Çµö²Ä¤µ¤ì¤Æ¤¤¤ëÄ̤ê¤Ë¡Ø¥×¥í¥°¥é¥à¡Ù¤ò²þÊѤޤ¿¤ÏºÆÈÒÉÛ¤·¤¿¤½¤Î¾¤ÎÃÄ
+ÂΤϡ¢¤¢¤Ê¤¿¤ËÂФ·¤Æ¡Ø¥×¥í¥°¥é¥à¡Ù¤ÎÍøÍѤʤ¤¤·ÍøÍÑÉÔǽ¤ÇÀ¸¤¸¤¿°ìÈÌŪ¡¢
+ÆÃÊÌŪ¡¢¶öÁ³Åª¡¢É¬Á³Åª¤Ê»³²(¥Ç¡¼¥¿¤Î¾Ã¼º¤äÉÔÀµ³Î¤Ê½èÍý¡¢¤¢¤Ê¤¿¤«Âè»°
+¼Ô¤¬Èï¤Ã¤¿Â»¼º¡¢¤¢¤ë¤¤¤Ï¡Ø¥×¥í¥°¥é¥à¡Ù¤¬Â¾¤Î¥½¥Õ¥È¥¦¥§¥¢¤È°ì½ï¤ËÆ°ºî¤·
+¤Ê¤¤¤È¤¤¤¦ÉÔ¶ñ¹ç¤Ê¤É¤ò´Þ¤à¤¬¤½¤ì¤é¤Ë¸Â¤é¤Ê¤¤)¤Ë°ìÀÚ¤ÎÀÕǤ¤òÉé¤ï¤Ê¤¤¡£
+¤½¤Î¤è¤¦¤Ê»³²¤¬À¸¤º¤ë²ÄǽÀ­¤Ë¤Ä¤¤¤ÆÈà¤é¤¬Ãé¹ð¤µ¤ì¤Æ¤¤¤¿¤È¤·¤Æ¤âƱÍͤÇ
+¤¢¤ë¡£
+
+                          ¾ò·ï¤ÈÀ©Ì󽪤ï¤ê
+\f
+            °Ê¾å¤Î¾ò¹à¤ò¤¢¤Ê¤¿¤Î¿·¤·¤¤¥×¥í¥°¥é¥à¤ËŬÍѤ¹¤ëÊýË¡
+
+¤¢¤Ê¤¿¤¬¿·¤·¤¤¥×¥í¥°¥é¥à¤ò³«È¯¤·¤¿¤È¤·¤Æ¡¢¸ø½°¤Ë¤è¤Ã¤Æ¤½¤ì¤¬ÍøÍѤµ¤ì¤ë
+²ÄǽÀ­¤òºÇÂç¤Ë¤·¤¿¤¤¤Ê¤é¡¢¤½¤Î¥×¥í¥°¥é¥à¤ò¤³¤Î·ÀÌó½ñ¤Î¾ò¹à¤Ë½¾¤Ã¤Æ
+ï¤Ç¤âºÆÈÒÉÛ¤¢¤ë¤¤¤ÏÊѹ¹¤Ç¤­¤ë¤è¤¦¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ë¤¹¤ë¤Î¤¬ºÇÁ±¤Ç¤¹¡£
+
+¤½¤Î¤¿¤á¤Ë¤Ï¡¢¥×¥í¥°¥é¥à¤Ë°Ê²¼¤Î¤è¤¦¤Êɽ¼¨¤òźÉÕ¤·¤Æ¤¯¤À¤µ¤¤¡£¤½¤Î¾ì¹ç¡¢
+Êݾڤ¬ÇÓ½ü¤µ¤ì¤Æ¤¤¤ë¤È¤¤¤¦¤³¤È¤òºÇ¤â¸ú²ÌŪ¤ËÅÁ¤¨¤ë¤¿¤á¤Ë¡¢¤½¤ì¤¾¤ì¤Î¥½¡¼
+¥¹¥Õ¥¡¥¤¥ë¤ÎËÁƬ¤Ëɽ¼¨¤òźÉÕ¤¹¤ì¤ÐºÇ¤â°ÂÁ´¤Ç¤¹¡£¾¯¤Ê¤¯¤È¤â¡¢¡ÖÃøºî¸¢É½
+¼¨¡×¤È¤¤¤¦¹Ô¤ÈÁ´Ê¸¤¬¤¢¤ë¾ì½ê¤Ø¤Î¥Ý¥¤¥ó¥¿¤À¤±¤Ï³Æ¥Õ¥¡¥¤¥ë¤Ë´Þ¤á¤ÆÃÖ¤¤¤Æ
+¤¯¤À¤µ¤¤¡£
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    (Ìõ:
+
+    <¥×¥í¥°¥é¥à¤Î̾Á°¤È¡¢¤½¤ì¤¬²¿¤ò¤¹¤ë¤«¤Ë¤Ä¤¤¤Æ¤Î´Êñ¤ÊÀâÌÀ¡£>
+    Copyright (C) <À¾Îñǯ>  <ºî¼Ô¤Î̾Á°>
+
+    ¤³¤Î¥×¥í¥°¥é¥à¤Ï¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ç¤¹¡£¤¢¤Ê¤¿¤Ï¤³¤ì¤ò¡¢¥Õ¥ê¡¼¥½¥Õ
+    ¥È¥¦¥§¥¢ºâÃĤˤè¤Ã¤Æȯ¹Ô¤µ¤ì¤¿ GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ(¥Ð¡¼¥¸¥ç
+    ¥ó2¤«¡¢´õ˾¤Ë¤è¤Ã¤Æ¤Ï¤½¤ì°Ê¹ß¤Î¥Ð¡¼¥¸¥ç¥ó¤Î¤¦¤Á¤É¤ì¤«)¤ÎÄê¤á¤ë¾ò·ï
+    ¤Î²¼¤ÇºÆÈÒÉÛ¤Þ¤¿¤Ï²þÊѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+
+    ¤³¤Î¥×¥í¥°¥é¥à¤ÏÍ­ÍѤǤ¢¤ë¤³¤È¤ò´ê¤Ã¤ÆÈÒÉÛ¤µ¤ì¤Þ¤¹¤¬¡¢*Á´¤¯¤Î̵ÊÝ
+    ¾Ú* ¤Ç¤¹¡£¾¦¶È²ÄǽÀ­¤ÎÊݾڤäÆÃÄê¤ÎÌÜŪ¤Ø¤ÎŬ¹çÀ­¤Ï¡¢¸À³°¤Ë¼¨¤µ¤ì¤¿
+    ¤â¤Î¤â´Þ¤áÁ´¤¯Â¸ºß¤·¤Þ¤»¤ó¡£¾Ü¤·¤¯¤ÏGNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤ò¤´
+    Í÷¤¯¤À¤µ¤¤¡£
+
+    ¤¢¤Ê¤¿¤Ï¤³¤Î¥×¥í¥°¥é¥à¤È¶¦¤Ë¡¢GNU °ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤ÎÊ£À½Êª¤ò
+    °ìÉô¼õ¤±¼è¤Ã¤¿¤Ï¤º¤Ç¤¹¡£¤â¤·¼õ¤±¼è¤Ã¤Æ¤¤¤Ê¤±¤ì¤Ð¡¢¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§
+    ¥¢ºâÃĤޤÇÀÁµá¤·¤Æ¤¯¤À¤µ¤¤(°¸Àè¤Ï the Free Software Foundation,
+    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA)¡£
+
+    )
+
+ÅŻҤʤ¤¤·»æ¤Î¥á¡¼¥ë¤Ç¤¢¤Ê¤¿¤ËÌ䤤¹ç¤ï¤»¤ëÊýË¡¤Ë¤Ä¤¤¤Æ¤Î¾ðÊó¤â½ñ¤­²Ã¤¨
+¤Þ¤·¤ç¤¦¡£
+
+¥×¥í¥°¥é¥à¤¬ÂÐÏÃŪ¤Ê¤â¤Î¤Ê¤é¤Ð¡¢ÂÐÏå⡼¥É¤Çµ¯Æ°¤·¤¿ºÝ¤Ë½ÐÎϤȤ·¤Æ°Ê²¼
+¤Î¤è¤¦¤Êû¤¤¹ðÃΤ¬É½¼¨¤µ¤ì¤ë¤è¤¦¤Ë¤·¤Æ¤¯¤À¤µ¤¤:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+    (Ìõ:
+
+    Gnomovision ¥Ð¡¼¥¸¥ç¥ó 69, Copyright (C) Ç¯ ºî¼Ô¤Î̾Á°
+    Gnomovision ¤Ï*Á´¤¯¤Î̵ÊݾÚ*¤ÇÄ󶡤µ¤ì¤Þ¤¹¡£¾Ü¤·¤¯¤Ï¡Öshow w¡×
+    ¤È¥¿¥¤¥×¤·¤Æ²¼¤µ¤¤¡£¤³¤ì¤Ï¥Õ¥ê¡¼¥½¥Õ¥È¥¦¥§¥¢¤Ç¤¢¤ê¡¢¤¢¤ë¾ò·ï¤Î²¼¤Ç
+    ºÆÈÒÉÛ¤¹¤ë¤³¤È¤¬¾©Î夵¤ì¤Æ¤¤¤Þ¤¹¡£¾Ü¤·¤¯¤Ï¡Öshow c¡×¤È¥¿¥¤¥×¤·¤Æ²¼
+    ¤µ¤¤¡£
+
+    )
+
+¤³¤³¤Ç¡¢²¾ÁÛŪ¤Ê¥³¥Þ¥ó¥É¡Öshow w¡×¤È¡Öshow c¡×¤Ï°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ
+¤ÎŬÀÚ¤ÊÉôʬ¤òɽ¼¨¤¹¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£¤â¤Á¤í¤ó¡¢¤¢¤Ê
+¤¿¤¬»È¤¦¥³¥Þ¥ó¥É¤ò¡Öshow w¡×¤ä¡Öshow c¡×¤È¸Æ¤ÖɬÁ³À­¤Ï¤¢¤ê¤Þ¤»¤ó¤Î¤Ç¡¢
+¤¢¤Ê¤¿¤Î¥×¥í¥°¥é¥à¤Ë¹ç¤ï¤»¤Æ¥Þ¥¦¥¹¤Î¥¯¥ê¥Ã¥¯¤ä¥á¥Ë¥å¡¼¤Î¥¢¥¤¥Æ¥à¤Ë¤·¤Æ
+¤â·ë¹½¤Ç¤¹¡£
+
+¤Þ¤¿¤¢¤Ê¤¿¤Ï¡¢É¬Íפʤé¤Ð(¥×¥í¥°¥é¥Þ¡¼¤È¤·¤ÆƯ¤¤¤Æ¤¤¤¿¤é)¤¢¤Ê¤¿¤Î¸ÛÍѼ硢
+¤¢¤ë¤¤¤Ï¾ì¹ç¤Ë¤è¤Ã¤Æ¤Ï³Ø¹»¤«¤é¡¢¤½¤Î¥×¥í¥°¥é¥à¤Ë´Ø¤¹¤ë¡ÖÃøºî¸¢Êü´þÀ¼ÌÀ
+(copyright disclaimer)¡×¤Ë½ð̾¤·¤Æ¤â¤é¤¦¤Ù¤­¤Ç¤¹¡£°Ê²¼¤ÏÎã¤Ç¤¹¤Î¤Ç¡¢Ì¾
+Á°¤òÊѤ¨¤Æ¤¯¤À¤µ¤¤:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+  (Ìõ:
+
+  Yoyodyne¼Ò¤Ï¤³¤³¤Ë¡¢James Hacker¤Ë¤è¤Ã¤Æ½ñ¤«¤ì¤¿¥×¥í¥°¥é¥à
+  ¡ÖGnomovision¡×(¥³¥ó¥Ñ¥¤¥é¤ØÄ̤¹¥×¥í¥°¥é¥à)¤Ë´Ø¤¹¤ë°ìÀÚ¤ÎÃøºî¸¢¤ÎÍø
+  ±×¤òÊü´þ¤·¤Þ¤¹¡£
+
+   <Ty Coon»á¤Î½ð̾>¡¢1989ǯ4·î1Æü
+   Ty Coon¡¢Éû¼ÒĹ
+
+  )
+
+¤³¤Î°ìÈ̸ø½°ÍøÍѵöÂú·ÀÌó½ñ¤Ç¤Ï¡¢¤¢¤Ê¤¿¤Î¥×¥í¥°¥é¥à¤òÆÈÀêŪ¤Ê¥×¥í¥°¥é¥à
+¤ËÅý¹ç¤¹¤ë¤³¤È¤òǧ¤á¤Æ¤¤¤Þ¤»¤ó¡£¤¢¤Ê¤¿¤Î¥×¥í¥°¥é¥à¤¬¥µ¥Ö¥ë¡¼¥Á¥ó¥é¥¤¥Ö
+¥é¥ê¤Ê¤é¤Ð¡¢ÆÈÀêŪ¤Ê¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤È¤¢¤Ê¤¿¤Î¥é¥¤¥Ö¥é¥ê¤ò¥ê¥ó¥¯¤¹¤ë¤³
+¤È¤òµö²Ä¤·¤¿¤Û¤¦¤¬¤è¤êÊØÍø¤Ç¤¢¤ë¤È¹Í¤¨¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£¤â¤·¤³¤ì¤¬¤¢¤Ê
+¤¿¤Î˾¤à¤³¤È¤Ê¤é¤Ð¡¢¤³¤Î·ÀÌó½ñ¤ÎÂå¤ï¤ê¤ËGNU ¥é¥¤¥Ö¥é¥ê°ìÈ̸ø½°ÍøÍѵöÂú
+·ÀÌó½ñ¤òŬÍѤ·¤Æ¤¯¤À¤µ¤¤¡£
diff --git a/Documents/etc/gpl.txt b/Documents/etc/gpl.txt
new file mode 100644 (file)
index 0000000..1bf1526
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          59 Temple Place, Suite 330, Boston, MA 02111 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Documents/etc/indane.png b/Documents/etc/indane.png
new file mode 100644 (file)
index 0000000..0eb02fb
Binary files /dev/null and b/Documents/etc/indane.png differ
diff --git a/Documents/etc/style.css b/Documents/etc/style.css
new file mode 100644 (file)
index 0000000..9c52ac9
--- /dev/null
@@ -0,0 +1,114 @@
+body {
+    color: black;
+    margin: 0px 3%;
+    background-color: #e0f0ff;
+}
+
+div.topic_path {
+       background-color: #f2f2f2;
+       padding: 3px 1em 3px 1em;
+    margin: 6px 1em 6px 1em;
+       border: 1px solid #bfbfbf;
+    font-size: small;
+}
+div.navigation {
+       background-color: #f2f2f2;
+       padding: 3px 1em 3px 1em;
+    margin: 6px 1em 6px 1em;
+       border: 1px solid #bfbfbf;
+       font-size: small;
+}
+div.float_right {
+       float: right;
+}
+div.toc {
+       font-size: normal;
+}
+div.contents {
+       font-size: 12pt;
+       font-family: "Times New Roman", serif;
+}
+
+div.header {
+    font-size: normal;
+}
+
+div.centered {
+    text-align: center;
+}
+
+h1 {
+       font-size: 16pt;
+       font-family: "Lucida Grande", Arial, sans-serif;
+       font-weight: bold;
+       margin-top: 12px;
+       margin-bottom: 12px;
+    margin-left: 1em;
+    margin-right: 1em;
+}
+
+h2 {
+       font-size: 14pt;
+       font-family: "Lucida Grande", Arial, sans-serif;
+       font-weight: bold;
+       margin-top: 8px;
+       margin-bottom: 8px;
+    margin-left: 1em;
+    margin-right: 1em;
+}
+
+h3 {
+       font-size: 12pt;
+       font-family: "Lucida Grande", Arial, sans-serif;
+       font-weight: bold;
+       margin-top: 6px;
+       margin-bottom: 6px;
+    margin-left: 1em;
+    margin-right: 1em;
+}
+
+p {
+    margin-top: 8px;
+       text-indent: 0.5em;
+    line-height: 1.3;
+    margin-left: 1.5em;
+    margin-right: 1.5em;
+}
+
+p.note {
+       margin-left: 2.0em;
+       margin-right: 2.0em;
+       font-size: 10pt;
+       line-height: 1.2;
+       color: #000080;
+}
+
+span.note {
+       font-size: 10pt;
+       line-height: 1.2;
+       color: #000080;
+}
+
+ul {
+    margin-top: 8px;
+    line-height: 1.3;
+}
+
+code {
+    font-family: monospace;
+    font-size: 10pt;
+}
+
+blockquote {
+    margin: 0px 1.5em;
+       border: thin dashed;
+       background-color: #ccffcc;
+}
+
+span.keyword {
+       font-weight: bold;
+}
+
+span.italic {
+    font-style: italic;
+}
\ No newline at end of file
diff --git a/Documents/etc/tutorial_01.png b/Documents/etc/tutorial_01.png
new file mode 100644 (file)
index 0000000..5db3463
Binary files /dev/null and b/Documents/etc/tutorial_01.png differ
diff --git a/Documents/etc/tutorial_02.png b/Documents/etc/tutorial_02.png
new file mode 100644 (file)
index 0000000..3a7952d
Binary files /dev/null and b/Documents/etc/tutorial_02.png differ
diff --git a/Documents/etc/tutorial_03.png b/Documents/etc/tutorial_03.png
new file mode 100644 (file)
index 0000000..2f03d41
Binary files /dev/null and b/Documents/etc/tutorial_03.png differ
diff --git a/Documents/etc/tutorial_04.png b/Documents/etc/tutorial_04.png
new file mode 100644 (file)
index 0000000..66dcaf7
Binary files /dev/null and b/Documents/etc/tutorial_04.png differ
diff --git a/Documents/etc/tutorial_05.png b/Documents/etc/tutorial_05.png
new file mode 100644 (file)
index 0000000..b3ece01
Binary files /dev/null and b/Documents/etc/tutorial_05.png differ
diff --git a/Documents/etc/tutorial_06.png b/Documents/etc/tutorial_06.png
new file mode 100644 (file)
index 0000000..cece4c0
Binary files /dev/null and b/Documents/etc/tutorial_06.png differ
diff --git a/Documents/etc/tutorial_07.png b/Documents/etc/tutorial_07.png
new file mode 100644 (file)
index 0000000..c9d1278
Binary files /dev/null and b/Documents/etc/tutorial_07.png differ
diff --git a/Documents/etc/tutorial_08.png b/Documents/etc/tutorial_08.png
new file mode 100644 (file)
index 0000000..64a2538
Binary files /dev/null and b/Documents/etc/tutorial_08.png differ
diff --git a/Documents/etc/tutorial_09.png b/Documents/etc/tutorial_09.png
new file mode 100644 (file)
index 0000000..55ee252
Binary files /dev/null and b/Documents/etc/tutorial_09.png differ
diff --git a/Documents/etc/tutorial_10.png b/Documents/etc/tutorial_10.png
new file mode 100644 (file)
index 0000000..2c71dc3
Binary files /dev/null and b/Documents/etc/tutorial_10.png differ
diff --git a/Documents/etc/tutorial_11.png b/Documents/etc/tutorial_11.png
new file mode 100644 (file)
index 0000000..005ab7c
Binary files /dev/null and b/Documents/etc/tutorial_11.png differ
diff --git a/Documents/etc/tutorial_12.png b/Documents/etc/tutorial_12.png
new file mode 100644 (file)
index 0000000..ccc044a
Binary files /dev/null and b/Documents/etc/tutorial_12.png differ
diff --git a/Documents/etc/tutorial_13.png b/Documents/etc/tutorial_13.png
new file mode 100644 (file)
index 0000000..cd30c3d
Binary files /dev/null and b/Documents/etc/tutorial_13.png differ
diff --git a/Documents/etc/tutorial_14.png b/Documents/etc/tutorial_14.png
new file mode 100644 (file)
index 0000000..0279c66
Binary files /dev/null and b/Documents/etc/tutorial_14.png differ
diff --git a/Documents/etc/tutorial_15.png b/Documents/etc/tutorial_15.png
new file mode 100644 (file)
index 0000000..bd2c3c1
Binary files /dev/null and b/Documents/etc/tutorial_15.png differ
diff --git a/Documents/etc/tutorial_16.png b/Documents/etc/tutorial_16.png
new file mode 100644 (file)
index 0000000..62ab73a
Binary files /dev/null and b/Documents/etc/tutorial_16.png differ
diff --git a/Documents/etc/tutorial_17.png b/Documents/etc/tutorial_17.png
new file mode 100644 (file)
index 0000000..28dd65a
Binary files /dev/null and b/Documents/etc/tutorial_17.png differ
diff --git a/Documents/etc/tutorial_18.png b/Documents/etc/tutorial_18.png
new file mode 100644 (file)
index 0000000..3e39ce8
Binary files /dev/null and b/Documents/etc/tutorial_18.png differ
diff --git a/Documents/etc/tutorial_19.png b/Documents/etc/tutorial_19.png
new file mode 100644 (file)
index 0000000..ef3b085
Binary files /dev/null and b/Documents/etc/tutorial_19.png differ
diff --git a/Documents/etc/tutorial_20.png b/Documents/etc/tutorial_20.png
new file mode 100644 (file)
index 0000000..e2a3723
Binary files /dev/null and b/Documents/etc/tutorial_20.png differ
diff --git a/Documents/etc/tutorial_21.png b/Documents/etc/tutorial_21.png
new file mode 100644 (file)
index 0000000..fdc7b8b
Binary files /dev/null and b/Documents/etc/tutorial_21.png differ
diff --git a/Documents/etc/tutorial_22.png b/Documents/etc/tutorial_22.png
new file mode 100644 (file)
index 0000000..c5713c3
Binary files /dev/null and b/Documents/etc/tutorial_22.png differ
diff --git a/Documents/etc/tutorial_23.png b/Documents/etc/tutorial_23.png
new file mode 100644 (file)
index 0000000..c47bf7a
Binary files /dev/null and b/Documents/etc/tutorial_23.png differ
diff --git a/Documents/etc/tutorial_24.png b/Documents/etc/tutorial_24.png
new file mode 100644 (file)
index 0000000..2aa7a4f
Binary files /dev/null and b/Documents/etc/tutorial_24.png differ
diff --git a/Documents/etc/tutorial_25.png b/Documents/etc/tutorial_25.png
new file mode 100644 (file)
index 0000000..e2334ac
Binary files /dev/null and b/Documents/etc/tutorial_25.png differ
diff --git a/Documents/etc/tutorial_26.png b/Documents/etc/tutorial_26.png
new file mode 100644 (file)
index 0000000..b73440c
Binary files /dev/null and b/Documents/etc/tutorial_26.png differ
diff --git a/Documents/etc/tutorial_27.png b/Documents/etc/tutorial_27.png
new file mode 100644 (file)
index 0000000..bb94774
Binary files /dev/null and b/Documents/etc/tutorial_27.png differ
diff --git a/Documents/etc/tutorial_28.png b/Documents/etc/tutorial_28.png
new file mode 100644 (file)
index 0000000..5ff5402
Binary files /dev/null and b/Documents/etc/tutorial_28.png differ
diff --git a/Documents/etc/tutorial_29.png b/Documents/etc/tutorial_29.png
new file mode 100644 (file)
index 0000000..ce63b4d
Binary files /dev/null and b/Documents/etc/tutorial_29.png differ
diff --git a/Documents/etc/tutorial_30.png b/Documents/etc/tutorial_30.png
new file mode 100644 (file)
index 0000000..212c1cc
Binary files /dev/null and b/Documents/etc/tutorial_30.png differ
diff --git a/Documents/etc/tutorial_31.png b/Documents/etc/tutorial_31.png
new file mode 100644 (file)
index 0000000..6490421
Binary files /dev/null and b/Documents/etc/tutorial_31.png differ
diff --git a/Documents/etc/tutorial_32.png b/Documents/etc/tutorial_32.png
new file mode 100644 (file)
index 0000000..020d24c
Binary files /dev/null and b/Documents/etc/tutorial_32.png differ
diff --git a/Documents/etc/tutorial_33.png b/Documents/etc/tutorial_33.png
new file mode 100644 (file)
index 0000000..ec74c4c
Binary files /dev/null and b/Documents/etc/tutorial_33.png differ
diff --git a/Documents/etc/tutorial_34.png b/Documents/etc/tutorial_34.png
new file mode 100644 (file)
index 0000000..6539db8
Binary files /dev/null and b/Documents/etc/tutorial_34.png differ
diff --git a/Documents/etc/tutorial_35.png b/Documents/etc/tutorial_35.png
new file mode 100644 (file)
index 0000000..cc63534
Binary files /dev/null and b/Documents/etc/tutorial_35.png differ
diff --git a/Documents/etc/tutorial_36.png b/Documents/etc/tutorial_36.png
new file mode 100644 (file)
index 0000000..e4a76af
Binary files /dev/null and b/Documents/etc/tutorial_36.png differ
diff --git a/Documents/etc/tutorial_37.png b/Documents/etc/tutorial_37.png
new file mode 100644 (file)
index 0000000..4bcf436
Binary files /dev/null and b/Documents/etc/tutorial_37.png differ
diff --git a/Documents/etc/tutorial_38.png b/Documents/etc/tutorial_38.png
new file mode 100644 (file)
index 0000000..da4a061
Binary files /dev/null and b/Documents/etc/tutorial_38.png differ
diff --git a/Documents/etc/tutorial_39.png b/Documents/etc/tutorial_39.png
new file mode 100644 (file)
index 0000000..687d9de
Binary files /dev/null and b/Documents/etc/tutorial_39.png differ
diff --git a/Documents/makedoc.rb b/Documents/makedoc.rb
new file mode 100644 (file)
index 0000000..952df8a
--- /dev/null
@@ -0,0 +1,284 @@
+#!/usr/bin/ruby
+require "rexml/document"
+
+class REXML::Element
+  attr_accessor :up, :down, :prev, :next, :title_en, :title_ja
+  def each_element_recursive(&block)
+    e = block.call(self)
+    return e if e != nil
+    i = 1
+    while (e = elements[i]) != nil
+      ee = e.each_element_recursive(&block)
+      if ee != nil && ee != e
+        elements[i] = ee
+      end
+      i += 1
+    end
+    return nil
+  end
+end
+
+#  Parse the source document
+file = open("| cat src/doc_source*.html", "r")
+xml = file.read
+file.close
+doc = REXML::Document.new(xml)
+
+xmlns = doc.elements["html"].attributes["xmlns"]
+head = doc.elements["html/head"]
+body = doc.elements["html/body"]
+
+#  Get all 'file' nodes
+@file_hash = Hash.new
+body.each_element_with_attribute('file') { |e|
+  file = e.attributes['file']
+  @file_hash[file] = e
+  j = 1
+  title_en = title_ja = lang = nil
+  e.each_element_recursive { |ee|
+    if (ln = ee.attributes["lang"]) != nil
+      lang = ln
+    end
+    if ee.name == "h1"
+      text = ee.text
+      if lang == "ja"
+        title_ja = text
+      else
+        title_en = text
+      end
+    end
+    nil
+  }
+  e.title_en = title_en
+  e.title_ja = title_ja
+  while (ee = e.elements[j, "link"]) != nil
+    if (rel = ee.attributes["rel"]) != nil
+      href = ee.attributes["href"]
+      if href == nil || href == ""
+        raise "No href attribute at node #{ee.xpath} in #{e.inspect}"
+      elsif (el = @file_hash[href]) == nil
+        raise "Unknown href '#{href}' at node #{ee.xpath} in #{e.inspect}"
+      end
+      if rel == "Up"
+        e.up = href
+        (el.down = (el.down || Array.new)) << file
+      elsif rel == "Prev"
+        e.prev = href
+        el.next = file
+      end
+    end
+    j += 1
+  end
+}
+
+def replace_special_nodes(e)
+  i = 1
+  while (ee = elements[i]) != nil
+    if ee.name == "link" && (id = ee.attributes["id"]) != nil
+      enew = nil
+      if id == "\#header"
+      elsif id == "\#navigation"
+      elsif id == "\#toc"
+      end
+      if enew != nil
+        elements[i] = enew
+      end
+    end
+    i += 1
+  end
+  if e.name == "link" && (id = e.attributes["id"]) != nil
+    if id == "#header"
+    end
+  end 
+end
+
+def make_a_element(href, text)
+  a = REXML::Element.new("a")
+  a.add_attribute("href", href)
+  a.add_text(text)
+  a
+end
+
+def toc_all(e, level, lang)
+  ul = REXML::Element.new("ul")
+  e.down.each { |href|
+    li = ul.add_element("li")
+    ep = @file_hash[href]
+    a = make_a_element(href, (lang == "ja" ? ep.title_ja : ep.title_en))
+    if level == 0
+      li.add_element("h3").add_element(a)
+    else
+      li.add_element(a)
+    end
+    if ep.down != nil && ep.down.length > 0
+      li.add_element(toc_all(ep, level + 1, lang))
+    end
+  }
+  ul
+end
+
+def prev_link(ef)
+  return ef.prev if ef.prev
+  ef_up = (ef.up && @file_hash[ef.up])
+  if ef_up != nil
+       ef_up_prev = prev_link(ef_up)
+       if ef_up_prev && (down = @file_hash[ef_up_prev].down) && down.length > 0
+         return down[-1]
+       end
+  end
+  return nil
+end
+
+def next_link(ef)
+  return ef.next if ef.next
+  ef_up = (ef.up && @file_hash[ef.up])
+  if ef_up != nil
+       ef_up_next = next_link(ef_up)
+       if ef_up_next && (down = @file_hash[ef_up_next].down) && down.length > 0
+         return down[0]
+       end
+  end
+  return nil
+end
+         
+def special_node(e, ef, lang)
+  return nil if e.name != "link" || (id = e.attributes["id"]) == nil
+  if id == "\#header"
+    en = REXML::Element.new("div")
+    en.add_attribute("class", "topic_path")
+    ep = ef
+    c = [(lang == "ja" ? ef.title_ja : ef.title_en)]
+    while ep.up != nil
+      c.unshift(REXML::Text.new(" &gt; ", false, nil, true))
+      href = ep.up
+      ep = @file_hash[href]
+      c.unshift(make_a_element(href, (lang == "ja" ? ep.title_ja : ep.title_en)))
+    end
+    c.each { |n|
+      if n.is_a?(String)
+        en.add_text(n)
+      else
+        en.add(n)
+      end
+    }
+  elsif id == "\#navigation"
+    en = REXML::Element.new("div")
+    en.add_attribute("class", "navigation")
+    if (href = ef.up) != nil
+      if href != "index.html"
+        en.add(make_a_element("index.html", (lang == "ja" ? "[トップ]" : "[Top]")))
+        en.add_text(" ")
+      end
+      ep = @file_hash[href]
+      en.add(make_a_element(href, (lang == "ja" ? "[上: #{ep.title_ja}]" : "[Up: #{ep.title_en}]")))
+      en.add_text(" ")
+    else
+      en.add_text(lang == "ja" ? "[トップ] " : "[Top] ")
+    end
+    if (href = prev_link(ef)) != nil
+      ep = @file_hash[href]
+      en.add(make_a_element(href, (lang == "ja" ? "[前: #{ep.title_ja}]" : "[Prev: #{ep.title_en}]")))
+      en.add_text(" ")
+    end
+    if (href = next_link(ef)) != nil
+      ep = @file_hash[href]
+      en.add(make_a_element(href, (lang == "ja" ? "[次: #{ep.title_ja}]" : "[Next: #{ep.title_en}]")))
+      en.add_text(" ")
+    end
+  elsif id == "\#toc"
+    en = REXML::Element.new("div")
+    en.add_attribute("class", "contents")
+       if ef.down
+      ul = en.add_element("ul")
+      ef.down.each { |href|
+        li = ul.add_element("li")
+        ep = @file_hash[href]
+        li.add_element(make_a_element(href, (lang == "ja" ? ep.title_ja : ep.title_en)))
+      }
+       end
+  elsif id == "\#toc_all"
+    en = REXML::Element.new("div")
+    en.add_attribute("class", "contents")
+    en.add_element(toc_all(ef, 0, lang))
+  else
+    en = nil
+  end
+  en
+end
+
+base_dir = "doc"
+system("mkdir -p #{base_dir}; rm -rf #{base_dir}/*")
+
+#  Output to files (en and jp)
+preamble = doc.xml_decl.to_s + "\n" + doc.doctype.to_s + "\n"
+body.each_element_with_attribute('file') { |ef|
+  file = ef.attributes['file']
+  for lang in ["ja", "en"]
+    system("mkdir -p #{base_dir}/#{lang}")
+    ndoc = REXML::Document.new(preamble)
+    #  Root element (html)
+    html = REXML::Element.new("html")
+       html.attributes["lang"] = lang
+       html.attributes["xml:lang"] = lang
+       if xmlns
+         html.attributes["xmlns"] = xmlns
+       end
+    ndoc.add(html)
+    #  head section
+    nhead = html.add_element("head")
+    head.each_element { |e|
+      e = e.deep_clone
+      if e.name == "title"
+        e = REXML::Element.new("title")
+        e.add_text(lang == "ja" ? ef.title_ja : ef.title_ja)
+      elsif e.name == "meta"
+        if e.attributes["http-equiv"] == "Content-Type"
+          e.attributes["lang"] = lang
+                 e.attributes["xml:lang"] = lang
+        end
+      end
+      nhead.add_element(e)
+    }
+    nhead.add_element("link", "rel"=>"Start", "href"=>"index.html")
+    if ef.up != nil && ef.up != ""
+      nhead.add_element("link", "rel"=>"Index", "href"=>ef.up)
+    end
+    if ef.prev != nil && ef.prev != ""
+      nhead.add_element("link", "rel"=>"Prev", "href"=>ef.prev)
+    end
+    if ef.next != nil && ef.next != ""
+      nhead.add_element("link", "rel"=>"Next", "href"=>ef.next)
+    end
+    #  body section
+    bd = html.add_element("body")
+    ef.each_element { |e|
+      next if (la = e.attributes["lang"]) != nil && la != lang
+      next if e.name == "link" && e.attributes["id"] == nil
+         if la
+           e.attributes["xml:lang"] = la
+         end
+      bd.add_element(e.deep_clone)
+    }
+    #  Convert special nodes
+    bd.each_element_recursive { |e|
+      if e.name == "link" && e.attributes["id"] != nil
+        val = special_node(e, ef, lang)
+      else
+           if e.name == "div" && e.attributes["lang"] != nil
+                 e.attributes["xml:lang"] = e.attributes["lang"]
+               end
+        nil
+      end
+    }
+    open("#{base_dir}/#{lang}/_#{file}", "w") { |fp|
+      ndoc.write(fp)
+    }
+       system("cd #{base_dir}/#{lang}; sed 's!/>! />!g' _#{file} >#{file}; rm _#{file}")
+  end
+}
+target_dir = "../doc"
+system("cp -r etc #{base_dir}; rm -rf #{base_dir}/etc/CVS") 
+system("rm -rf #{target_dir}/*")
+system("mv #{base_dir}/* #{target_dir}")
+system("rmdir #{base_dir}")
+print "Documents were successfully created in #{target_dir}/{en,ja}.\n"
diff --git a/Documents/src/doc_source.html b/Documents/src/doc_source.html
new file mode 100644 (file)
index 0000000..7024151
--- /dev/null
@@ -0,0 +1,785 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<title>Molby</title>
+<link rel="stylesheet" href="../etc/style.css" type="text/css" />
+</head>
+
+<body>
+
+<div file="index.html">
+<div class="centered" lang="en">
+<h1>Molby</h1>
+<h2>An Interactive Molecular Modeling Software<br />with Integrated Ruby Interpreter</h2>
+<h3>Version 0.5.0 build 20100121</h3> <!-- version -->
+<h3>Toshi Nagata</h3>
+</div>
+<div class="centered" lang="ja">
+<h1>Molby</h1>
+<h2>対話型分子モデリングソフトウェア<br />(Ruby インタプリタ内蔵)</h2>
+<h3>Version 0.5.0 build 20100121</h3> <!-- version -->
+<h3>永田 央</h3>
+</div>
+<hr />
+<div class="contents" lang="en">
+<h2>Table of Contents</h2>
+<link id="#toc_all" />
+</div>
+<div class="contents" lang="ja">
+<h2>目次</h2>
+<link id="#toc_all" />
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="introduction.html">
+<link rel="Up" href="index.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Introduction</h1>
+<link id="#toc" />
+</div>
+<div class="contents" lang="ja">
+<h1>はじめに</h1>
+<link id="#toc" />
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="whatis.html">
+<link rel="Up" href="introduction.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>What is Molby?</h1>
+<p>
+Molby is an application to visualize and build molecular models interactively through 3D display. It provides the following capability:
+</p>
+<ul>
+<li>Viewing molecular models in either ball-and-stick or "line" display.</li>
+<li>Interactive modification of molecular models by mouse operation.</li>
+<li>Viewing and editing atomic coordinates and other parameters in numeric tables.</li>
+<li>Importing and exporting structure/coordinate data for other program packages such as Gaussian, GAMESS, NAMD and SHELX.</li>
+<li>Embedded Ruby interpreter for automated processing of molecular models.</li>
+</ul>
+</div>
+<div class="contents" lang="ja">
+<h1>Molbyとは?</h1>
+<p>
+Molby は、分子を 3D 表示して、画面上で分子モデルを構築するためのアプリケーションです。以下のような機能があります。
+</p>
+<ul>
+<li>分子を ball-and-stick または「線」で表示できます。</li>
+<li>分子モデルをマウス操作で編集できます。</li>
+<li>原子座標などのパラメータを、数値テーブルで編集できます。</li>
+<li>Gaussian, GAMESS, NAMD, SHELX などの他のプログラムパッケージのファイルを読み込み/書き出しできます。</li>
+<li>分子モデルの操作を自動化できるように Ruby インタプリタを内蔵しています。</li>
+</ul>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="installation.html">
+<link rel="Up" href="introduction.html" />
+<link rel="Prev" href="whatis.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Installation</h1>
+<h2>1. Supported Platforms</h2>
+<p>
+Molby runs on the following platforms.
+</p>
+<ul>
+<li>Microsoft Windows (XP or later).</li>
+<li>Mac OS X (10.4 or later, PowerPC or Intel).</li>
+</ul>
+
+<p>
+Installation procedures are described below for each of these platforms.
+</p>
+
+<h2>2. Get the Software</h2>
+<p>
+Download from the official distribution web site, <a href="http://sourceforge.jp/projects/molby/">http://sourceforge.jp/projects/molby/</a>.
+</p>
+
+<h2>3. Installation</h2>
+
+<h3>3-1. Microsoft Windows</h3>
+
+<p>
+The Windows version is provided as a standard setup package (<code>SetupMolby.exe</code>). Double-click the setup package, and follow the instructions. If you are not sure, just select "OK" to go proceed.
+</p>
+<p>
+After installation is finished, you will find the Molby application registered in the "Start" menu under the item "All Programs."
+</p>
+
+<h3>3-2. Mac OS X</h3>
+<p>
+The Mac version is provided as a disk image (<code>Molby.dmg</code>). Double-click the disk image file, and you will find a virtual disk drive mounted on the desktop. Find the Molby application inside it, and drag it to the "Applications" folder in your hard drive.
+</p>
+<p>
+<span class="italic">Note:</span> The Mac version is provided as a universal binary, which runs natively on both PowerPC and Intel platforms.
+</p>
+
+<h2>4. Uninstallation</h2>
+<h3>4-1. Microsoft Windows</h3>
+<p>
+Use the uninstaller in the Molby folder. You can access the Molby folder from the "Start" menu (Start -&gt; All Programs -&gt; Molby).
+</p>
+<h3>4-2. Mac OS X</h3>
+<p>
+Trash the Molby application in the "Applications" folder.
+</p>
+
+</div>
+<div class="contents" lang="ja">
+<h1>インストール</h1>
+<h2>1. サポートするプラットフォーム</h2>
+<p>
+Molbyは以下のプラットフォームで動作します。
+</p>
+<ul>
+<li>Microsoft Windows (XPまたはそれ以降).</li>
+<li>Mac OS X (10.4またはそれ以降, PowerPCまたはIntel).</li>
+</ul>
+
+<p>
+それぞれのプラットフォームでのインストール方法を、以下に説明します。
+</p>
+
+<h2>2. ソフトウェアの入手</h2>
+<p>
+公式配布サイト <a href="http://sourceforge.jp/projects/molby/">http://sourceforge.jp/projects/molby/</a> から最新版をダウンロードしてください。
+</p>
+
+<h2>3. インストール</h2>
+
+<h3>3-1. Microsoft Windows</h3>
+
+<p>
+Windows版は、標準のセットアップパッケージ (<code>SetupMolby.exe</code>) で配布されています。セットアップパッケージをダブルクリックして、指示に従ってください。どうしたらよいかわからない時は、"OK"を押して進んで構いません。
+</p>
+<p>
+インストールが終了したら、「スタート」メニューの「すべてのプログラム」の中に Molby が入っているはずです。
+</p>
+
+<h3>3-2. Mac OS X</h3>
+<p>
+Mac版は、ディスクイメージ (<code>Molby.dmg</code>) で配布されています。ディスクイメージをダブルクリックすると、仮想ディスクがデスクトップにマウントされます。Molby アプリケーションがその中にありますので、ハードディスクの「アプリケーション」フォルダにドラッグコピーしてください。
+</p>
+<p>
+<span class="italic">注:</span> Mac 版は universal binary です。PowerPC, Intel の両方のマシンで動作します。
+</p>
+
+<h2>4. アンインストール</h2>
+<h3>4-1. Microsoft Windows</h3>
+<p>
+Molbyフォルダの中にアンインストーラがあります。「スタート」メニューで「すべてのプログラム」-&gt; Molby とたどってください。
+</p>
+<h3>4-2. Mac OS X</h3>
+<p>
+「アプリケーション」フォルダの中の Molby アプリケーションをゴミ箱に捨てます。
+</p>
+
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="copyright.html">
+<link rel="Up" href="introduction.html" />
+<link rel="Prev" href="installation.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Copyright and License</h1>
+<h2>1. Copyright Notice</h2>
+<p>
+Molby is a copyrighted product of Toshi Nagata.
+</p>
+<p>
+Copyright (C) 2009 Toshi Nagata <!-- copyright -->
+</p>
+<p>
+Molby includes (more technically: is statically linked to) the following softwares, which are copyrighted products as described below:
+</p>
+<ul>
+<li>
+<a href="http://www.wxwidgets.org/">wxWidgets 2.8.9</a>: Copyright (C) 1992-2008 Julian Smart, Robert Roebling, Vadim Zeitlin and other members of the wxWidgets team. Portions (c) 1996 Artificial Intelligence Applications Institute
+</li>
+<li>
+<a href="http://www.ruby-lang.org/">Ruby 1.8.7</a>: Copyright (C) 1993-2009 Yukihiro Matsumoto
+</li>
+<li>
+<a href="http://www.netlib.org/clapack/">CLAPACK</a>: Copyright (c) 1992-2008 The University of Tennessee.  All rights reserved.
+</li>
+</ul>
+
+<h2>2. License</h2>
+<p>
+Molby is distributed under the GNU General Public License (version 2).
+</p>
+<blockquote>
+<p>
+Molby: An Interactive Molecular Modeling Software with Integrated Ruby Interpreter
+</p>
+<p>
+Copyright (C) 2009 Toshi Nagata <!-- copyright -->
+</p>
+<p>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</p>
+<p>
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+<a href="../etc/gpl.txt">GNU General Public License</a> for more details.
+</p>
+</blockquote>
+</div>
+<div class="contents" lang="ja">
+<h1>著作権とライセンス</h1>
+<h2>1. 著作権表示</h2>
+<p>
+Molby の著作権は永田 央が保持しています。
+</p>
+<p>
+Copyright (C) 2009 Toshi Nagata <!-- copyright -->
+</p>
+<p>
+Molby は以下のソフトウェアを含んでいます(静的にリンクしています)。それぞれの著作権表示は下の通りです。
+</p>
+<ul>
+<li>
+<a href="http://www.wxwidgets.org/">wxWidgets 2.8.9</a>: Copyright (C) 1992-2008 Julian Smart, Robert Roebling, Vadim Zeitlin and other members of the wxWidgets team. Portions (c) 1996 Artificial Intelligence Applications Institute
+</li>
+<li>
+<a href="http://www.ruby-lang.org/">Ruby 1.8.7</a>: Copyright (C) 1993-2009 Yukihiro Matsumoto
+</li>
+</ul>
+
+<h2>2. ライセンス</h2>
+<p>
+Molby は <a href="../etc/gpl.txt">GNU General Public License (GNU 一般公衆利用許諾契約書, version 2)</a> に従って配布します。
+</p>
+<blockquote>
+<p>
+Molby: 対話型分子モデルソフトウェア(Ruby インタプリタ内蔵)
+</p>
+<p>
+Copyright (C) 2009 Toshi Nagata <!-- copyright -->
+</p>
+<p>
+このプログラムはフリーソフトウェアです。あなたはこれを、フリーソフトウェア財団によって発行された GNU 一般公衆利用許諾契約書(バージョン2か、希望によってはそれ以降のバージョンのうちどれか)の定める条件の下で再頒布または改変することができます。
+</p>
+<p>
+このプログラムは有用であることを願って頒布されますが、*全くの無保証* です。商業可能性の保証や特定の目的への適合性は、言外に示されたものも含め全く存在しません。詳しくはGNU 一般公衆利用許諾契約書をご覧ください。
+</p>
+</blockquote>
+<p>
+参考のため、GNU 一般公衆利用許諾契約書の<a href="../etc/gpl.ja.txt">非公式日本語訳</a>を添付します。ただし、正式な文書は英語版の方です。
+</p>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="tutorials.html">
+<link rel="Up" href="index.html" />
+<link rel="Prev" href="introduction.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Tutorials</h1>
+<link id="#toc" />
+</div>
+<div class="contents" lang="ja">
+<h1>チュートリアル</h1>
+<link id="#toc" />
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="drawing.html">
+<link rel="Up" href="tutorials.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Step One: Draw a New Molecule</h1>
+<h2>1. Create a Molecule</h2>
+<p>
+Start up Molby. You will see a blank window like below. This is the main window to manipulate your molecule.
+</p>
+<p><img src="../etc/tutorial_01.png" /></p>
+<p>
+Suppose you want a benzene molecule. Double-click on the black area, and you will be asked to "enter formula". Type "C6H6" and hit "OK".
+</p>
+<p><img src="../etc/tutorial_02.png" /></p>
+<p>
+Then you see a benzene molecule in the black area. The atoms and bonds are drawn in red, which means these atoms and bonds are "selected". On the left to this area, there is a table, which shows in numbers the positions of all atoms, and other useful information. We will get into this part in more details later.
+</p>
+<p><img src="../etc/tutorial_03.png" /></p>
+<h2>2. Rotate the Molecule</h2>
+<p>
+You can rotate the molecule around by use of the three bars on the both sides and the bottom of the black area. Press the left button of the mouse on one of these bars, and try dragging.
+</p>
+<p><img src="../etc/tutorial_04.png" /></p>
+<ul>
+<li>Bar 1: rotate the whole molecule around the horizontal axis.</li>
+<li>Bar 2: rotate the whole molecule around the vertical axis.</li>
+<li>Bar 3: rotate the selected part of the molecule. (In the present example, the whole molecule is selected, so that the bar 3 works in the same manner as the bar 2.)</li>
+</ul>
+<p>
+Next, look at the buttons above the black area. The left-most button "Rot" is now selected. This means the window is in the "Rotation" mode. In this case, you can drag in the black area to rotate freely the whole molecule.
+</p>
+<p class="note">
+<span class="italic">Note:</span> Be careful not to start drag from a very near point to the molecule. If you start drag on a selected atom or bond, the selected part will move along the mouse, instead of the whole molecule to rotate.
+</p>
+
+<h2>3. Translate and Scale</h2>
+<p>
+Press the button "Trans" above the black area. The window is now in "Translate" mode. When you drag in the black area, the whole molecule moves along with the mouse movement.
+</p>
+<p class="note">
+<span class="italic">Note:</span> the functions of the rotation bars do not change.
+</p>
+<p><img src="../etc/tutorial_05.png" /></p>
+<p>
+Next, press the button "Scale". The window is now in "Scale" mode, and you can expand and shrink the molecule by dragging in the black area.
+</p>
+<p><img src="../etc/tutorial_06.png" /></p>
+<p>
+The "Fit to Screen" menu command, avaiable under the "Show" menu, is a convenient way to fit the whole molecule to the window.
+</p>
+<p><img src="../etc/tutorial_07.png" /></p>
+<p>
+Now you have learned how to move the whole molecule around. In the next chapter, you will learn how to edit the molecule.
+</p>
+</div>
+
+<div class="contents" lang="ja">
+<h1>第一段階:新しい分子を描く</h1>
+<h2>1. 分子を作る</h2>
+<p>
+Molbyを立ち上げてください。下のような空のウィンドウが現れます。このウィンドウが、分子を取り扱うためのメインウィンドウです。
+</p>
+<p><img src="../etc/tutorial_01.png" /></p>
+<p>
+ベンゼン分子を作ってみたいとします。画面の黒いところ(編集エリア)をダブルクリックすると、"enter formula" と指示が出ます。"C6H6" とタイプして、OK を押します。
+</p>
+<p><img src="../etc/tutorial_02.png" /></p>
+<p>
+編集エリアにベンゼン分子が現れます。原子と結合は赤色で描かれていますが、これは「選択されている」という表示です。左側には表があり、原子の位置などの情報を数値で表示しています。この部分については、あとで詳しい使い方が出てきます。
+</p>
+<p><img src="../etc/tutorial_03.png" /></p>
+<h2>2. 分子を回転させる</h2>
+<p>
+編集エリアの両側と下には、分子を回転させるバーがあります。このバーでマウスボタンを押し、そのままドラッグしてみてください。
+</p>
+<p><img src="../etc/tutorial_04.png" /></p>
+<ul>
+<li>バー1:水平軸の周りに分子を回転させる。</li>
+<li>バー2:垂直軸の周りに分子を回転させる。</li>
+<li>バー3:分子の選択部分を回転させる。(今は分子全体が選択されているため、バー2と同じ動作をします。)</li>
+</ul>
+<p>
+次に、編集エリアの上にあるボタンを見てください。一番左の "Rot" が選択されています。これは、このウィンドウが現在「回転 (Rotation)」モードにあることを示しています。この場合、編集エリアでマウスをドラッグすると、分子全体を自由に回転させることができます。
+</p>
+<p class="note">
+<span class="italic">注:</span> 分子に近すぎるところからドラッグを始めないように注意してください。選択している原子・結合の上でドラッグを始めると、選択部分がマウスについて動いてしまい、分子全体を動かすことができません。
+</p>
+<h2>3. Translate and scale</h2>
+<p>
+"Trans" と書かれたボタンを押してください。ウィンドウは「並進 (Translate)」モードになります。編集エリアでマウスをドラッグすると、分子全体を平行移動させることができます。
+</p>
+<p class="note">
+<span class="italic">注:</span> 回転バーの機能は変わりません。
+</p>
+<p><img src="../etc/tutorial_05.png" /></p>
+<p>
+次に、"Scale" と書かれたボタンを押してください。ウィンドウは「拡大縮小 (Scale)」モードになります。編集エリアでマウスをドラッグすると、分子全体を拡大・縮小することができます。
+</p>
+<p><img src="../etc/tutorial_06.png" /></p>
+<p>
+"Show" メニューの中に "Fit to Screen" メニューコマンドがあります。このコマンドは、分子をウィンドウサイズに合わせて表示したいときに便利です。
+</p>
+<p><img src="../etc/tutorial_07.png" /></p>
+<p>
+分子を画面上で動かす方法がわかりました。次は、編集のやり方を説明します。
+</p>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="editing.html">
+<link rel="Up" href="tutorials.html" />
+<link rel="Prev" href="drawing.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Step Two: Edit a Molecule</h1>
+<h2>1. Append a Substituent</h2>
+<p>
+Suppose you have a benzene molecule now, and want to append a methyl substituent to give a toluene.
+</p>
+<p>
+Press the button "Select", and click on the black area where no atom nor bond is present. The selection becomes canceled.
+</p>
+<p><img src="../etc/tutorial_08.png" /></p>
+<p>
+Select one of the hydrogen atoms. There are two ways to do this. You can click on the target atom, or you can drag to select atoms within a rectangular region.
+</p>
+<p><img src="../etc/tutorial_09.png" /> <img src="../etc/tutorial_10.png" /></p>
+<p>
+Double-click on the hydrogen atom. This time, be sure to hit the selection! Then you will see the dialog "enter formula" as shown below. Type "CH3" in the text box and hit "OK".
+</p>
+<p><img src="../etc/tutorial_11.png" /></p>
+<p>
+Now you get toluene.
+</p>
+<p><img src="../etc/tutorial_12.png" /></p>
+<p>
+You want to save the result to a file. Select "Save as..." from the File menu, choose the file format, enter the filename, and hit "Save".
+</p>
+<p>
+The "native" file format in Molby is "mbsf", i.e. <u>M</u>ol<u>b</u>y <u>S</u>tructure <u>F</u>ile. The mbsf format is a private format with no compatibility with any existing file format for chemistry. Nevertheless, it is a plain text file that is reasonably compact and easy to read. Unless you have a particular reason to choose other formats, it is recommended that you save the structues as a "mbsf" file.
+</p>
+<h2>2. Manipulate Molecular Fragments</h2>
+<p>
+After building a molecule, you may want to move some part of a molecule. Here are examples how you can do it.
+</p>
+<p>
+Select the methyl group of the toluene, if you have not done so yet <span class="note">(Tip: you can add to the current selection by shift-clicking)</span>. Press the left rotation bar (marked "3" in the previous page), and drag up and down. You will see the methyl group rotates to the right and left.
+</p>
+<p><img src="../etc/tutorial_13.png" /></p>
+<p>
+The same manipulation can be achieved by selecting one bond and dragging the left rotation bar. In this case, you can also rotate the benzene ring (with the methyl group fixed) by pressing Option (Mac) or Alt (Win) key when dragging.
+</p>
+<p><img src="../etc/tutorial_14.png" /></p>
+<p>
+The selected atoms can be dragged to give a translational move. Use this feature with care, because it may result in a chemically unnatural structure.
+</p>
+<p><img src="../etc/tutorial_15.png" /></p>
+<h2>3. Adding and Deleting Atoms</h2>
+<p>
+We have already learned one way to add atoms: double-clicking on the selected part of the molecule (or on the black area where nothing is present), and type-in the formula. Actually, this is the most convenient way to add atoms in Molby. However, there are also other ways to add atoms.
+</p>
+<p>
+Look at our familiar toluene molecule. Suppose we want to convert it to indane.
+</p>
+<p><img src="../etc/indane.png" /> indane</p>
+<p>
+Rotate the molecule so that its orientation matches the chemical structure. We will start from C3, add two carbons, and then close the ring.
+</p>
+<p><img src="../etc/tutorial_16.png" /></p>
+<p>
+Press the "Erase" button above the black area. Click on the "H3" hydrogen. The hydrogen atom and the bond between C3 and H3 disappear.
+</p>
+<p><img src="../etc/tutorial_17.png" /></p>
+<p>
+Press the "Bond" button. Drag from the C3 atom to the right-bottom, and release the mouse button. A new atom and a bond to C3 are created. Drag from the new atom to the right-up, and another atom and bond are created.
+</p>
+<p><img src="../etc/tutorial_18.png" /> <img src="../etc/tutorial_19.png" /></p>
+<p>
+Press the "Erase" button again, and erase one hydrogen from the methyl group. Press the "Select" button, select the benzene-methylene bond, and rotate the methylene group as appropriate for the five-membered ring, by use of the left rotation bar.
+</p>
+<p><img src="../etc/tutorial_20.png" /> <img src="../etc/tutorial_21.png" /></p>
+<p>
+Now you can close the ring. Press the "Bond" button, and drag from the methylene carbon. When the mouse cursor comes close enough to the target carbon, the new atom will snap to the target and make a new bond. Then release the mouse button.
+</p>
+<p><img src="../etc/tutorial_22.png" /></p>
+<p>
+Finally, you want to add hydrogens to the newly created carbons. Press the "Select" button, and select these two carbons. Go to the "Edit" menu, and select the "Add Hydrogen" -&gt; "Tetrahedral sp3" menu command.
+</p>
+<p><img src="../etc/tutorial_23.png" /></p>
+<p>
+Here is the result.
+</p>
+<p><img src="../etc/tutorial_24.png" /></p>
+<p class="note">
+A similar result can be achieved by selecting the hydrogen ortho to the methyl group, double-click it, type "CH2CH3" in, erase one hydrogen atom from each of the methyl groups, and make a bond. This is better because the newly created methylenes have reasonable bond lengths and angles as methylene groups. The above example is just for explanation of the editing features.
+</p>
+</div>
+<div class="contents" lang="ja">
+<h1>第二段階:分子を編集する</h1>
+<h2>1. 置換基をつける</h2>
+<p>
+ベンゼン分子があり、これにメチル基をつけてトルエンを作りたいとします。
+</p>
+<p>
+"Select"ボタンを押して、編集エリアの原子や結合がないところをクリックします。選択が解除され、分子の表示が赤から原子ごとの色に変わります。
+</p>
+<p><img src="../etc/tutorial_08.png" /></p>
+<p>
+水素原子の一つを選択します。やり方は二つあります。選択したい原子をクリックするか、またはドラッグで現れる四角い領域の中の原子を選択します。
+</p>
+<p><img src="../etc/tutorial_09.png" /> <img src="../etc/tutorial_10.png" /></p>
+<p>
+水素原子の上でダブルクリックします。このとき、選択部分の上でダブルクリックするように注意してください。"Enter formula"というダイアログが出てきます。"CH3"と入力し、"OK"を押します。
+</p>
+<p><img src="../etc/tutorial_11.png" /></p>
+<p>
+トルエンができました。
+</p>
+<p><img src="../etc/tutorial_12.png" /></p>
+<p>
+ファイルに保存しておきたい時は、通常通り File メニューから "Save as..." を選び、ファイルフォーマットを選び、ファイル名をタイプして "Save" を押します。
+</p>
+<p>
+Molby の標準ファイルフォーマットは "mbsf" (<u>M</u>ol<u>b</u>y <u>S</u>tructure <u>F</u>ile) です。Mbsf フォーマットは独自のもので、既存の化学ファイルフォーマットとは互換性がありません。しかし、mbsfはテキストファイルであり、比較的コンパクトで、読みやすいものです。他のフォーマットで保存する積極的な理由がない場合は、"mbsf"フォーマットで保存しておくことをおすすめします。
+</p>
+
+<h2>2. 分子の一部を操作する</h2>
+<p>
+分子を組み立てたら、その一部を動かしたくなることがあります。ここでは、そのやり方の例を示します。
+</p>
+<p>
+トルエンのメチル基を選択します<span class="note">(コツ:シフトキーを押しながらクリックすると、現在の選択範囲に原子を付け加えることができます)</span>。左の回転バー(前ページで③と書かれていたもの)でマウスボタンを押し、上下にドラッグしてください。メチル基が右左に回転するのがわかります。
+</p>
+<p><img src="../etc/tutorial_13.png" /></p>
+<p>
+同じ操作は、結合を1つ選択して左の回転バーをドラッグしてもできます。この場合、メチル基を固定してベンゼン環の方を回すこともできます。ドラッグの時にOptionキー(Mac)、Altキー(Win)を押してください。
+</p>
+<p><img src="../etc/tutorial_14.png" /></p>
+<p>
+選択した原子をドラッグすると平行移動できます。化学的に不自然な構造になりますので、注意が必要です。
+</p>
+<p><img src="../etc/tutorial_15.png" /></p>
+<h2>3. 原子を追加する・削除する</h2>
+<p>
+原子を追加する方法はすでに一つ説明しました。選択部分または編集エリアの何もないところをダブルクリックして、構造式をタイプする方法です。実際、Molbyで原子を追加するにはこれが最も便利です。しかし、他の方法もありますので、それを紹介します。
+</p>
+<p>
+先ほどのトルエン分子を使います。これをインダンに変えたいとします。
+</p>
+<p><img src="../etc/indane.png" /> インダン</p>
+<p>
+分子を回転させて、化学構造式と同じ向きになるようにします。C3から始めて、炭素原子を2つ追加し、環を閉じることにします。
+</p>
+<p><img src="../etc/tutorial_16.png" /></p>
+<p>
+編集エリアの上の"Erase"ボタンを押します。"H3"水素をクリックします。この水素原子と、C3-H3の結合が消えます。
+</p>
+<p><img src="../etc/tutorial_17.png" /></p>
+<p>
+"Bond"ボタンを押します。C3原子から右下にドラッグし、マウスボタンを離します。新しい原子と、その原子とC3の間の結合が新しく作られます。新しい原子から右上にドラッグすると、もう一つの原子と結合が作られます。
+</p>
+<p><img src="../etc/tutorial_18.png" /> <img src="../etc/tutorial_19.png" /></p>
+<p>
+"Erase"ボタンをもう一度押して、メチル基の原子を一つ消します。"Select"ボタンを押して、ベンゼン環とメチレン炭素(今水素原子を一つ消したところ)の間の結合を選択します。左の回転バーを使って、五員環に適切な向きになるようにメチレン基を回転させます。
+</p>
+<p><img src="../etc/tutorial_20.png" /> <img src="../etc/tutorial_21.png" /></p>
+<p>
+環を閉じます。"Bond"ボタンを押し、メチレン炭素原子から右下の炭素原子へドラッグします。マウスカーソルが目標の炭素原子に近づくと、ドラッグしてできた結合が炭素原子にくっつきます。ここでマウスボタンを離します。
+</p>
+<p><img src="../etc/tutorial_22.png" /></p>
+<p>
+最後に、新しく作った二つの炭素原子に水素原子を付加します。"Select"ボタンを押して、マウスを使って二つの炭素原子を選択します。"Edit"メニューから、"Add Hydrogen"-&gt;"Tetrahedral sp3"コマンドを実行します。
+</p>
+<p><img src="../etc/tutorial_23.png" /></p>
+<p>
+結果はこのようになります。
+</p>
+<p><img src="../etc/tutorial_24.png" /></p>
+<p class="note">
+トルエンからインダンは次のような操作でも作ることができます。メチル基のオルト位の水素原子を選択、ダブルクリック、ダイアログが出たら"CH2CH3"と入力、二つのメチル基からそれぞれ水素原子を一つ削除、結合回転で向きを調整、結合を作成。この方法の方が、新しく作ったメチレン基の結合距離・結合角が正しくなるため、よりよい結果になります。上の例は、編集操作の説明のためのものと考えてください。
+</p>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="cut_copy_paste.html">
+<link rel="Up" href="tutorials.html" />
+<link rel="Prev" href="editing.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Step Three: Edit a Molecule: Cut/Copy/Paste</h1>
+<p>
+Like any other decent "editing" applications, Molby has a capability of cut/copy/paste by use of a clipboard. These functions (especially copy and paste) are also quite useful for building complex molecules.
+</p>
+<p>
+We start from the toluene molecule again. Select the methyl group and do copy.
+</p>
+<p><img src="../etc/tutorial_25.png" /></p>
+<p>
+Unselect the methyl group, and do paste. Another methyl group appears, with no connection to the existing atoms. This is how "paste" works when nothing is selected beforehand.
+</p>
+<p><img src="../etc/tutorial_26.png" /></p>
+<p>
+Now we will see what happens when something is selected before pasting. As described above, start with the toluene molecule, select the methyl group, and copy it. Unselect the methyl group, and this time select the hydrogen atom that is "ortho" to the methyl group.
+</p>
+<p><img src="../etc/tutorial_27.png" /></p>
+<p>
+Do paste. The selected hydrogen atom is replaced with the pasted methyl group. Note that a new bond is created between the pasted methyl carbon and the ortho carbon, with an acceptable bond length and angles. 
+</p>
+<p><img src="../etc/tutorial_28.png" /></p>
+<p class="note">
+The dihedral angle may not be acceptable. In that case, you can rotate the pasted fragment by use of the left rotation bar until the dihedral angle looks good.
+</p>
+<p>
+This "select and paste" technique is very useful for building large molecules. Suppose we want to build an oligobenzamide.
+</p>
+<p><img src="../etc/tutorial_39.png" /></p>
+<p>
+Create a "monomer", i.e. N-methyl-4-acetamidobenzamide. This is done by (1) create a benzene, (2) select H1, double-click, and enter "CONHCH3", (3) select H4, double-click, and enter "NHCOCH3". After entering each formula, you need to rotate the fragment to make the dihedral angle appropriate.
+</p>
+<p><img src="../etc/tutorial_29.png" /></p>
+<p>
+Select the whole molecule except for the COCH3 group at the left. Do copy.
+</p>
+<p><img src="../etc/tutorial_30.png" /></p>
+<p>
+Select the NHCH3 group at the right. Do paste.
+</p>
+<p><img src="../etc/tutorial_31.png" /></p>
+<p>
+The newly created amide bond has a bad dihedral angle. Rotate the pasted fragment by the left rotation bar, so that the amide bond becomes properly trans.
+</p>
+<p><img src="../etc/tutorial_32.png" /><img src="../etc/tutorial_33.png" /></p>
+<p>
+Select "Fit to Screen" from the "Show" menu, to make the whole molecule visible.
+</p>
+<p><img src="../etc/tutorial_34.png" /></p>
+<p><img src="../etc/tutorial_35.png" /></p>
+<p>
+Repeat these procedures to make the tetramer.
+</p>
+<p><img src="../etc/tutorial_36.png" /></p>
+<p><img src="../etc/tutorial_37.png" /></p>
+<p><img src="../etc/tutorial_38.png" /></p>
+<p>
+Make one more iteration to make the octamer.
+</p>
+<p><img src="../etc/tutorial_39.png" /></p>
+</div>
+<div class="contents" lang="ja">
+<h1>第三段階:分子を編集する:カット・コピー・ペースト</h1>
+<p>
+他のアプリケーションと同様に、Molby もクリップボードを使ってカット・コピー・ペーストを行う機能があります。これらの機能、特にコピー・ペーストは複雑な分子を作るときにたいへん有用です。
+</p>
+<p>
+ふたたびトルエン分子から始めます。メチル基を選択して、コピーします。
+</p>
+<p><img src="../etc/tutorial_25.png" /></p>
+<p>
+メチル基の選択を解除して、ペーストを実行します。他の原子と結合していないメチル基がもう一つ現れます。原子が選択されていない状態で「ペースト」すると、このような動作になります。
+</p>
+<p><img src="../etc/tutorial_26.png" /></p>
+<p>
+今度は、ペーストする前に原子が選択されているときにどうなるかを見てみます。上と同じように、トルエンから始めて、メチル基を選択して、コピーします。メチル基の選択を解除して、今度はメチル基のオルト位の水素原子を選択します。
+</p>
+<p><img src="../etc/tutorial_27.png" /></p>
+<p>
+ペーストを実行します。選択された水素原子がメチル基に置き換わります。オルト位の炭素とメチル基の間に新しい結合ができ、その長さと結合角が適切なものになっていることがわかります。
+</p>
+<p><img src="../etc/tutorial_28.png" /></p>
+<p class="note">
+二面角は適切とはいえないかも知れません。その場合は、ペーストされたグループを左の回転バーを使って回転させて、適切な二面角にします。
+</p>
+<p>
+この「選択してペースト」のテクニックは、大きな分子を作るときにたいへん役に立ちます。たとえば、オリゴベンズアミドを作りたいとします。
+</p>
+<p><img src="../etc/tutorial_39.png" /></p>
+<p>
+まず、「モノマー」となる N-メチル-4-アセトアミドベンズアミドを作ります。次のようにします:(1) ベンゼンを作る、(2) H1 を選択してダブルクリックし、「CONHCH3」とタイプする、(3) H4 を選択してダブルクリックし、「NHCOCH3」とタイプする。化学式をタイプしたあとは、作成したフラグメントを回転させて二面角を調整しておきます。
+</p>
+<p><img src="../etc/tutorial_29.png" /></p>
+<p>
+左端の COCH3 以外全部を選択し、コピーします。
+</p>
+<p><img src="../etc/tutorial_30.png" /></p>
+<p>
+右端の NHCH3 を選択し、「ペースト」を実行します。
+</p>
+<p><img src="../etc/tutorial_31.png" /></p>
+<p>
+新しく作られたアミド結合は二面角が不適切です。左の回転バーを使ってペーストしたフラグメントを回転し、アミド結合が正しく trans になるようにします。
+</p>
+<p><img src="../etc/tutorial_32.png" /><img src="../etc/tutorial_33.png" /></p>
+<p>
+"Show" メニューから "Fit to Screen" を選び、分子全体が見えるようにします。
+</p>
+<p><img src="../etc/tutorial_34.png" /></p>
+<p><img src="../etc/tutorial_35.png" /></p>
+<p>
+この操作を繰り返して、四量体を作ります。
+</p>
+<p><img src="../etc/tutorial_36.png" /></p>
+<p><img src="../etc/tutorial_37.png" /></p>
+<p><img src="../etc/tutorial_38.png" /></p>
+<p>
+もう一度繰り返して、八量体を作ります。
+</p>
+<p><img src="../etc/tutorial_39.png" /></p>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="proptable.html">
+<link rel="Up" href="tutorials.html" />
+<link rel="Prev" href="cut_copy_paste.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Step Four: Edit a Molecule: Using a Property Table</h1>
+</div>
+<div class="contents" lang="ja">
+<h1>第四段階:分子を編集する:属性テーブルを使う</h1>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="mm_minimize.html">
+<link rel="Up" href="tutorials.html" />
+<link rel="Prev" href="proptable.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Step Five: Energy Minimization by Molecular Mechanics</h1>
+</div>
+<div class="contents" lang="ja">
+<h1>第五段階:分子力学計算によるエネルギー最小化</h1>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="qchem.html">
+<link rel="Up" href="tutorials.html" />
+<link rel="Prev" href="mm_minimize.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Step Six: Collaboration with Other Quantum Chemistry Softwares</h1>
+</div>
+<div class="contents" lang="ja">
+<h1>第六段階:他の量子化学ソフトウェアとの連携</h1>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="ruby.html">
+<link rel="Up" href="tutorials.html" />
+<link rel="Prev" href="qchem.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Step Seven: Using Embedded Ruby Interpreter</h1>
+</div>
+<div class="contents" lang="ja">
+<h1>第七段階:組み込み Ruby インタプリタを使う</h1>
+</div>
+<link id="#navigation" />
+</div>
+
+<div file="reference.html">
+<link rel="Up" href="index.html" />
+<link rel="Prev" href="tutorials.html" />
+<link id="#header" />
+<div class="contents" lang="en">
+<h1>Reference</h1>
+<link id="#toc" />
+</div>
+<div class="contents" lang="ja">
+<h1>リファレンス</h1>
+<link id="#toc" />
+</div>
+<link id="#navigation" />
+</div>
+
+</body>
+</html>
diff --git a/Makefile b/Makefile
new file mode 100755 (executable)
index 0000000..bc6356f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,113 @@
+ifeq ($(TARGET_PLATFORM),MAC)
+ WXCONFIG_PREFIX = $(HOME)/Development/wxMac/osx-build/
+ CPP_EXTRA_FLAGS = -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -arch ppc -arch i386 -DUSE_RUBY=1 -g
+ LD_EXTRA_FLAGS = -framework Accelerate -framework GLUT
+ RUBY_DIR = $(HOME)/Development/ruby-1.8.7-static
+ RUBY_CFLAGS = -I$(RUBY_DIR)
+ RUBY_LDFLAGS = -L$(RUBY_DIR) -lruby-static
+ EXECUTABLE = Molby
+endif
+
+ifeq ($(TARGET_PLATFORM),MSW)
+ WXCONFIG_PREFIX = $(HOME)/wxMSW-2.8.9/msw-build/
+ CPP_EXTRA_FLAGS = -O2 -I/lib/clapack -g
+ LD_EXTRA_FLAGS = -L/lib/clapack -llapack -lblas -lf2c_nomain
+ RUBY_DIR = $(HOME)/ruby-1.8.7-static
+ RUBY_CFLAGS = -I$(RUBY_DIR)
+# RUBY_LDFLAGS = -L$(RUBY_DIR) -lruby-static /c/Ruby/bin/msvcrt-ruby18.dll
+ RUBY_LDFLAGS = -L$(RUBY_DIR) -lmsvcrt-ruby18-static -lws2_32
+ EXECUTABLE = _Molby.exe_
+ FINAL_EXECUTABLE = Molby.exe
+endif
+
+WXLIB_LIST = core,base,gl,adv
+OBJECTS = ConsoleFrame.o GlobalParameterFrame.o GlobalParameterFilesFrame.o MoleculeView.o MyApp.o MyCommand.o MyDocument.o MyGLCanvas.o MySlider.o MyClipboardData.o ProgressFrame.o MyListCtrl.o MyDocManager.o RubyDialogFrame.o MyVersion.o MyThread.o MolLib.a Ruby_bind.a
+RUBY_EXTLIB = scanf.rb
+
+ifeq ($(TARGET_PLATFORM),MAC)
+PRODUCT = Molby.app
+else
+PRODUCT_DIR = Molby
+PRODUCT = $(PRODUCT_DIR)/$(EXECUTABLE)
+endif
+
+CC = g++
+CFLAGS = $(CPPFLAGS) $(CPP_EXTRA_FLAGS) $(RUBY_CFLAGS) $(shell $(WXCONFIG_PREFIX)wx-config --cppflags)
+LDFLAGS = $(shell $(WXCONFIG_PREFIX)wx-config --libs $(WXLIB_LIST)) $(LD_EXTRA_FLAGS) $(RUBY_LDFLAGS)
+DESTPREFIX = build
+DESTDIR = $(PWD)/$(DESTPREFIX)
+export CFLAGS
+export LDFLAGS
+export DESTDIR
+
+all: $(DESTPREFIX) $(DESTPREFIX)/$(PRODUCT)
+
+$(DESTPREFIX) :
+       mkdir -p $(DESTPREFIX)
+
+ifeq ($(TARGET_PLATFORM),MSW)
+EXTRA_OBJECTS = listctrl.o
+RESOURCE = molby_rc.o
+#  The following HOMETEMP kludges are to work around a bug where '#include "..."' 
+#  does not work when the include path is on the C: drive whereas the source is 
+#  on the Z: drive. 2009.7.24. Toshi Nagata
+HOMETEMP = $(HOME)/__molby_temp_build__
+$(DESTPREFIX)/$(RESOURCE) : molby.rc
+       mkdir -p $(HOMETEMP)/msw_build $(HOMETEMP)/bitmaps
+       cp molby.rc $(HOMETEMP)/msw_build
+       cp ../bitmaps/*.ico $(HOMETEMP)/bitmaps
+       (cd $(HOMETEMP)/msw_build; windres -i molby.rc -o molby_rc.o -I$(HOME)/wxMSW-2.8.9/include)
+       cp $(HOMETEMP)/msw_build/molby_rc.o $@
+       rm -rf $(HOMETEMP)
+endif
+
+$(DESTPREFIX)/%.o : ../wxSources/%.cpp
+       $(CC) -c $< -o $@ $(CFLAGS)
+
+$(DESTPREFIX)/%.o : ../wxSources/%.c
+       $(CC) -c $< -o $@ $(CFLAGS)
+
+$(DESTPREFIX)/MolLib.a : ../MolLib/*.[ch] ../MolLib/MD/*.[ch]
+       mkdir -p $(DESTPREFIX)/MolLib/MD; cd ../MolLib; $(MAKE)
+
+$(DESTPREFIX)/Ruby_bind.a : ../MolLib/Ruby_bind/*.[ch]
+       mkdir -p $(DESTPREFIX)/MolLib/Ruby_bind; cd ../MolLib/Ruby_bind; $(MAKE)
+
+ALL_OBJECTS = $(OBJECTS) $(EXTRA_OBJECTS) $(RESOURCE)
+DESTOBJECTS = $(addprefix $(DESTPREFIX)/,$(ALL_OBJECTS))
+$(DESTPREFIX)/$(EXECUTABLE) : $(DESTOBJECTS)
+       $(CC) -o $@ $(DESTOBJECTS) $(CFLAGS) $(LDFLAGS)
+
+$(DESTPREFIX)/$(PRODUCT) : $(DESTPREFIX)/$(EXECUTABLE) ../Scripts/*.rb
+ifeq ($(TARGET_PLATFORM),MAC)
+       rm -rf $(DESTPREFIX)/$(PRODUCT)
+       mkdir -p $(DESTPREFIX)/$(PRODUCT)/Contents/MacOS
+       mkdir -p $(DESTPREFIX)/$(PRODUCT)/Contents/Resources
+       cp -f Info.plist $(DESTPREFIX)/$(PRODUCT)/Contents
+       echo -n "APPL????" > $(DESTPREFIX)/$(PRODUCT)/Contents/PkgInfo
+       cp -r ../Scripts $(DESTPREFIX)/$(PRODUCT)/Contents/Resources
+       mkdir -p $(DESTPREFIX)/$(PRODUCT)/Contents/Resources/Scripts/lib
+       for i in $(RUBY_EXTLIB); do cp $(RUBY_DIR)/lib/$$i $(DESTPREFIX)/$(PRODUCT)/Contents/Resources/Scripts/lib; done
+       cp $(DESTPREFIX)/$(EXECUTABLE) $(DESTPREFIX)/$(PRODUCT)/Contents/MacOS
+endif
+ifeq ($(TARGET_PLATFORM),MSW)
+       rm -rf $(DESTPREFIX)/$(PRODUCT_DIR)
+       mkdir -p $(DESTPREFIX)/$(PRODUCT_DIR)
+       cp $(DESTPREFIX)/$(EXECUTABLE) $(DESTPREFIX)/$(PRODUCT_DIR)/$(FINAL_EXECUTABLE)
+       cp `which mingwm10.dll` $(DESTPREFIX)/$(PRODUCT_DIR)
+       cp -r ../Scripts $(DESTPREFIX)/$(PRODUCT_DIR)
+       mkdir -p $(DESTPREFIX)/$(PRODUCT_DIR)/Scripts/lib
+       for i in $(RUBY_EXTLIB); do cp $(RUBY_DIR)/lib/$$i $(DESTPREFIX)/$(PRODUCT_DIR)/Scripts/lib; done
+endif
+
+ifeq ($(TARGET_PLATFORM),MSW)
+setup: $(DESTPREFIX)/$(PRODUCT_DIR)/$(FINAL_EXECUTABLE)
+       /c/Program\ Files/Inno\ Setup\ 5/iscc molby.iss
+endif
+
+clean:
+       rm -rf $(DESTPREFIX)/*
+#      rm -f $(EXECUTABLE) $(OBJECTS)
+#      rm -rf $(PRODUCT)
+#      cd ../MolLib; $(MAKE) clean
+#      cd ../MolLib/Ruby_bind; $(MAKE) clean
diff --git a/MolLib/Dcd.c b/MolLib/Dcd.c
new file mode 100644 (file)
index 0000000..a1fc3dc
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ *  Dcd.c
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/01/20.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "Dcd.h"
+
+static void
+s_Swap4(char *cp)
+{
+       char w[4];
+       w[0] = cp[0]; w[1] = cp[1]; w[2] = cp[2]; w[3] = cp[3];
+       cp[0] = w[3]; cp[1] = w[2]; cp[2] = w[1]; cp[3] = w[0];
+}
+
+#define s_SwapInt32(ip)  s_Swap4((char *)(ip))
+#define s_SwapSFloat32(fp) s_Swap4((char *)(fp))
+
+#define s_ReadInt32(dcd, ip) \
+       (read(dcd->fd, ip, 4) == 4 ? \
+               ((dcd->reverse_endian ? s_SwapInt32(ip) : 0), 1) : \
+               0)
+#define s_ReadSFloat32(dcd, fp) \
+       (read(dcd->fd, fp, 4) == 4 ? \
+               ((dcd->reverse_endian ? s_SwapSFloat32(fp) : 0), 1) : \
+               0)
+
+static int
+s_Write4(int fd, const char *cp, int swap)
+{
+       if (swap) {
+               char w[4];
+               w[3] = cp[0]; w[2] = cp[1]; w[1] = cp[2]; w[0] = cp[3];
+               return write(fd, w, 4);
+       } else return write(fd, cp, 4);
+}
+
+static int
+s_WriteInt32(DcdRecord *dcd, Int32 i)
+{
+       return (s_Write4(dcd->fd, (const char *)(&i), dcd->reverse_endian) == 4);
+}
+
+static int
+s_WriteSFloat32(DcdRecord *dcd, SFloat32 f)
+{
+       return (s_Write4(dcd->fd, (const char *)(&f), dcd->reverse_endian) == 4);
+}
+
+static int
+s_WriteZeros(int fd, int count)
+{
+       char buf[128];
+       memset(buf, 0, sizeof(buf));
+       while (count > 0) {
+               int n = (count > 128 ? 128 : count);
+               if (write(fd, buf, n) != n)
+                       return 0;
+               count -= n;
+       }
+       return 1;
+}
+
+int
+DcdOpen(const char *name, DcdRecord *dr)
+{
+    Int32 nn, nnn;
+    char buf[5];
+       
+       if (dr == NULL)
+               return -1;  /*  Internal error  */
+       memset(dr, 0, sizeof(DcdRecord));
+       dr->fd = open(name, O_RDONLY, 0);
+       if (dr->fd < 0)
+               return -2;  /*  Cannot open file  */
+       
+    /*  Section 1: 'CORD', NFILE, NPRIV, NSAVC, NSTEP, 5*ZERO, DELTA,  */
+    /* WITH_UNITCELL, 4*ZERO, 24  */
+       if (!s_ReadInt32(dr, &nn))
+               return 1;   /*  Bad format: premature EOF  */
+       if (nn != 84) {
+               s_SwapInt32(&nn);
+               if (nn == 84)
+                       dr->reverse_endian = 1;
+               else return 2;  /*  Bad format: bad block length of the first section  */
+       }
+    if (read(dr->fd, buf, 4) != 4 || strncmp(buf, "CORD", 4) != 0)
+        return 3;   /*  Bad format: missing "CORD" signature  */
+    s_ReadInt32(dr, &(dr->nframes));
+       s_ReadInt32(dr, &(dr->nstart));
+       s_ReadInt32(dr, &(dr->ninterval));
+       s_ReadInt32(dr, &(dr->nend));
+    lseek(dr->fd, 20, SEEK_CUR);  /*  Skip 5 zeros  */
+    s_ReadSFloat32(dr, &(dr->delta));
+       s_ReadInt32(dr, &(dr->with_unitcell));
+       lseek(dr->fd, 32, SEEK_CUR);  /*  Skip 8 zeros  */
+       s_ReadInt32(dr, &nn);  /*  This should be 24  */
+       if (nn != 24)
+               return 4;   /*  Bad format: bad end of first section  */
+       s_ReadInt32(dr, &nn);  /*  This should be 84  */
+       if (nn != 84)
+               return 4;
+       
+    /*  Section 2: Title lines  */
+    if (!s_ReadInt32(dr, &nn) || lseek(dr->fd, nn, SEEK_CUR) < 0 || !s_ReadInt32(dr, &nnn) || nn != nnn)
+               return 5;   /*  Bad format: the second section looks strange  */
+    
+    /*  Section 3: Number of atoms  */
+       if (!s_ReadInt32(dr, &nn) || nn != 4)
+               return 6;   /*  Bad format: the third section is not correct  */
+       s_ReadInt32(dr, &(dr->natoms));
+       if (!s_ReadInt32(dr, &nn) || nn != 4)
+               return 6;
+
+       dr->header_size = lseek(dr->fd, 0, SEEK_CUR);
+       return 0;
+}
+
+int
+DcdCreate(const char *name, DcdRecord *dr)
+{
+       char buf[80];
+       
+       if (dr == NULL)
+               return -1;  /*  Internal error  */
+       dr->fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+       if (dr->fd < 0)
+               return -2;  /*  Cannot create file  */
+       memset(buf, ' ', 80);
+       
+    /*  Section 1: 'CORD', NFILE, NPRIV, NSAVC, NSTEP, 5*ZERO, DELTA,  */
+    /* WITH_UNITCELL, 8*ZERO, 24 (92 bytes) */
+       if (!s_WriteInt32(dr, 84) ||
+               write(dr->fd, "CORD", 4) != 4 ||
+               !s_WriteInt32(dr, dr->nframes) ||
+               !s_WriteInt32(dr, dr->nstart) ||
+               !s_WriteInt32(dr, dr->ninterval) || 
+               !s_WriteInt32(dr, dr->nend) ||
+               !s_WriteZeros(dr->fd, 20) ||
+               !s_WriteSFloat32(dr, dr->delta) ||
+               !s_WriteInt32(dr, dr->with_unitcell) ||
+               !s_WriteZeros(dr->fd, 32) ||
+               !s_WriteInt32(dr, 24) ||
+               !s_WriteInt32(dr, 84))
+               return 1;   /*  Cannot write  */
+       
+    /*  Section 2: Title lines (92 bytes)  */
+       if (!s_WriteInt32(dr, 84) ||
+               !s_WriteInt32(dr, 1) ||
+               write(dr->fd, buf, 80) != 80 ||
+               !s_WriteInt32(dr, 84))
+               return 1;   /*  Cannot write  */
+    
+    /*  Section 3: Number of atoms (12 bytes)  */
+       if (!s_WriteInt32(dr, 4) ||
+               !s_WriteInt32(dr, dr->natoms) ||
+               !s_WriteInt32(dr, 4))
+               return 1;
+       
+       /*  The header size is 196 bytes  */
+       dr->header_size = lseek(dr->fd, 0, SEEK_CUR);
+    return 0;
+}
+
+int
+DcdClose(DcdRecord *dr)
+{
+       if (dr->fd >= 0)
+               close(dr->fd);
+       dr->fd = -1;
+       return 0;
+}
+
+/*  Read one frame. Xp, yp, zp should be larger enough for sizeof(SFloat32)*dr->natoms.
+    Cellp may be NULL, but if non-NULL then it should be large enough for sizeof(SFloat32)*6.  */
+int
+DcdReadFrame(DcdRecord *dr, int index, SFloat32 *xp, SFloat32 *yp, SFloat32 *zp, SFloat32 *cellp)
+{
+    Int32 nn, nnn, i;
+    off_t block_size = 24 + (off_t)(dr->natoms * 12) + (dr->with_unitcell ? 56 : 0);
+    lseek(dr->fd, dr->header_size + block_size * index, SEEK_SET);
+    if (dr->with_unitcell) {
+        if (!s_ReadInt32(dr, &nn) || nn != 48)
+            goto error;
+        if (cellp != NULL) {
+                       read(dr->fd, cellp, 48);
+                       for (i = 0; i < 6; i++)
+                               s_SwapSFloat32(cellp + i);
+               }
+        if (!s_ReadInt32(dr, &nn) || nn != 48)
+            goto error;
+    }
+    if (!s_ReadInt32(dr, &nn) || nn != dr->natoms * 4 ||
+               read(dr->fd, xp, nn) < nn ||
+               !s_ReadInt32(dr, &nnn) || nn != nnn)
+        goto error;
+       if (dr->reverse_endian) {
+               for (i = 0; i < dr->natoms; i++)
+                       s_SwapSFloat32(xp + i);
+       }
+    if (!s_ReadInt32(dr, &nn) || nn != dr->natoms * 4 || 
+               read(dr->fd, yp, nn) < nn || 
+               !s_ReadInt32(dr, &nnn) || nn != nnn)
+        goto error;
+       if (dr->reverse_endian) {
+               for (i = 0; i < dr->natoms; i++)
+                       s_SwapSFloat32(yp + i);
+       }
+    if (!s_ReadInt32(dr, &nn) || nn != dr->natoms * 4 || 
+               read(dr->fd, zp, nn) < nn || 
+               !s_ReadInt32(dr, &nnn) || nn != nnn)
+        goto error;
+       if (dr->reverse_endian) {
+               for (i = 0; i < dr->natoms; i++)
+                       s_SwapSFloat32(zp + i);
+       }
+    return 0;
+error:
+    return 1;
+}
+
+/*  Write one frame. Xp, yp, zp should be an array of SFloat32 * dr->natoms. Cellp may be
+    NULL, but if non-NULL it should be an array of SFloat32 * 6.
+    This function does _not_ do lseek, so it should be called sequentially from index 0.  */
+int
+DcdWriteFrame(DcdRecord *dr, int index, const SFloat32 *xp, const SFloat32 *yp, const SFloat32 *zp, const SFloat32 *cellp)
+{
+       Int32 i;
+    if (dr->with_unitcell) {
+        if (!s_WriteInt32(dr, 48))
+            goto error;
+               for (i = 0; i < 6; i++)
+                       s_WriteSFloat32(dr, (cellp != NULL ? cellp[i] : 0.0));
+               if (!s_WriteInt32(dr, 48))
+            goto error;
+    }
+       
+       /*  X coordinates  */
+       if (!s_WriteInt32(dr, dr->natoms * 4))
+               goto error;
+       for (i = 0; i < dr->natoms; i++) {
+               if (!s_WriteSFloat32(dr, xp[i]))
+                       goto error;
+       }
+       if (!s_WriteInt32(dr, dr->natoms * 4))
+               goto error;
+
+       /*  Y coordinates  */
+       if (!s_WriteInt32(dr, dr->natoms * 4))
+               goto error;
+       for (i = 0; i < dr->natoms; i++) {
+               if (!s_WriteSFloat32(dr, yp[i]))
+                       goto error;
+       }
+       if (!s_WriteInt32(dr, dr->natoms * 4))
+               goto error;
+       
+       /*  Z coordinates  */
+       if (!s_WriteInt32(dr, dr->natoms * 4))
+               goto error;
+       for (i = 0; i < dr->natoms; i++) {
+               if (!s_WriteSFloat32(dr, zp[i]))
+                       goto error;
+       }
+       if (!s_WriteInt32(dr, dr->natoms * 4))
+               goto error;
+
+       return 0;
+
+error:
+       return 1;
+}
diff --git a/MolLib/Dcd.h b/MolLib/Dcd.h
new file mode 100644 (file)
index 0000000..495a9ce
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  Dcd.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/01/20.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __DCD_H__
+#define __DCD_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+               
+typedef int Int32;
+typedef float SFloat32;
+
+typedef struct DcdRecord {
+    int fd;                 /*  File descripter  */    
+       int reverse_endian;     /*  Need to reverse endian?  */
+    Int32 natoms;              /*  Number of atoms  */
+       Int32 nframes;          /*  Number of frames  */
+       Int32 nstart;           /*  Start timestep  */
+       Int32 ninterval;        /*  Number of timesteps between frames  */
+       Int32 nend;             /*  Last timestep  */
+       Int32 with_unitcell;    /*  Has a unit cell information?  */
+    SFloat32 delta;         /*  Step time  */
+    SFloat32 globalcell[6]; /*  cell size and origin; used when with_unitcell == 0 */
+    off_t header_size;      /*  Header size  */
+} DcdRecord;
+
+int DcdOpen(const char *name, DcdRecord *dr);
+int DcdCreate(const char *name, DcdRecord *dr);
+int DcdClose(DcdRecord *dr);
+int DcdReadFrame(DcdRecord *dr, int index, SFloat32 *xp, SFloat32 *yp, SFloat32 *zp, SFloat32 *cellp);
+int DcdWriteFrame(DcdRecord *dr, int index, const SFloat32 *xp, const SFloat32 *yp, const SFloat32 *zp, const SFloat32 *cellp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DCD_H__ */
diff --git a/MolLib/IntGroup.c b/MolLib/IntGroup.c
new file mode 100755 (executable)
index 0000000..7530020
--- /dev/null
@@ -0,0 +1,1048 @@
+/*
+   IntGroup.c
+   Created by Toshi Nagata, 2000.12.3.
+
+   Copyright (c) 2000-2008 Toshi Nagata.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "IntGroup.h"
+
+#include <stdio.h>             /*  for fprintf() in IntGroupDump()  */
+#include <stdlib.h>            /*  for malloc(), realloc(), and free()  */
+#include <string.h>            /*  for memmove()  */
+#include <limits.h>            /*  for INT_MAX  */
+#include <stdarg.h>
+
+#ifdef __MWERKS__
+#pragma mark ====== Private definitions ======
+#endif
+
+struct IntGroup {
+       int                     refCount;       /*  the reference count  */
+       int                     num;            /*  the number of entries  */
+       int *           entries;        /*  entries[2*n]: begin point, entries[2*n+1]: end point */
+};
+
+typedef short IntGroupOperation;
+enum {
+       kIntGroupIntersect,
+       kIntGroupConvolute,
+       kIntGroupDeconvolute
+};
+
+#ifdef __MWERKS__
+#pragma mark ====== Private (static) functions ======
+#endif
+
+/* --------------------------------------
+       ・ IntGroupCalcRequiredStorage
+   -------------------------------------- */
+static int
+IntGroupCalcRequiredStorage(int inLength)
+{
+       return ((inLength * 2 + 3) / 4) * 4 * sizeof(int);
+}
+
+/* --------------------------------------
+       ・ IntGroupAdjustStorage
+   -------------------------------------- */
+static IntGroupStatus
+IntGroupAdjustStorage(IntGroup *psRef, int inLength)
+{
+       int theOldSize, theNewSize;
+       
+       theOldSize = IntGroupCalcRequiredStorage(psRef->num);
+       theNewSize = IntGroupCalcRequiredStorage(inLength);
+       if (theOldSize == theNewSize)
+               return 0;
+       
+       if (theOldSize == 0 && theNewSize != 0) {
+               psRef->entries = (int *)malloc(theNewSize);
+               if (psRef->entries == NULL)
+                       return kIntGroupStatusOutOfMemory;
+       } else if (theOldSize != 0 && theNewSize == 0) {
+               free(psRef->entries);
+               psRef->entries = NULL;
+       } else {
+               int *ptr = (int *)realloc(psRef->entries, theNewSize);
+               if (ptr == NULL)
+                       return kIntGroupStatusOutOfMemory;
+               psRef->entries = ptr;
+       }
+       return kIntGroupStatusNoError;
+}
+
+/* --------------------------------------
+       ・ IntGroupInsertAnEntry
+   -------------------------------------- */
+static IntGroupStatus
+IntGroupInsertAnEntry(IntGroup *psRef, int inIndex, int inBeginPt, int inEndPt)
+{
+       IntGroupStatus result;
+       int moveAmount;
+       result = IntGroupAdjustStorage(psRef, psRef->num + 1);
+       if (result != kIntGroupStatusNoError)
+               return result;  /* out of memory */
+       moveAmount = (psRef->num - inIndex) * 2 * sizeof(int);
+       if (moveAmount > 0)
+               memmove(&(psRef->entries[inIndex * 2 + 2]), &(psRef->entries[inIndex * 2]), moveAmount);
+       psRef->entries[inIndex * 2] = inBeginPt;
+       psRef->entries[inIndex * 2 + 1] = inEndPt;
+       psRef->num++;
+       return kIntGroupStatusNoError;
+}
+
+/* --------------------------------------
+       ・ IntGroupDeleteEntries
+   -------------------------------------- */
+static IntGroupStatus
+IntGroupDeleteEntries(IntGroup *psRef, int inStartIndex, int inEndIndex)
+{
+       IntGroupStatus result;
+       int moveAmount;
+       if (inStartIndex > inEndIndex)
+               return 0;       /*  do nothing  */
+       moveAmount = sizeof(int) * 2 * (psRef->num - inEndIndex - 1);
+       if (moveAmount > 0)
+               memmove(&(psRef->entries[inStartIndex * 2]), &(psRef->entries[inEndIndex * 2 + 2]), moveAmount);
+       result = IntGroupAdjustStorage(psRef, psRef->num - (inEndIndex - inStartIndex + 1));
+       if (result == kIntGroupStatusNoError)
+               psRef->num -= inEndIndex - inStartIndex + 1;
+       return result;
+}
+
+#ifdef __MWERKS__
+#pragma mark ====== New/Retain/Release ======
+#endif
+
+/* --------------------------------------
+       ・ IntGroupNew
+   -------------------------------------- */
+IntGroup *
+IntGroupNew(void)
+{
+       IntGroup *psRef = (IntGroup *)malloc(sizeof(*psRef));
+       if (psRef == NULL)
+               return NULL;    /* out of memory */
+       psRef->entries = NULL;
+       psRef->num = 0;
+       psRef->refCount = 1;
+       return psRef;
+}
+
+/* --------------------------------------
+       ・ IntGroupNewFromIntGroup
+   -------------------------------------- */
+IntGroup *
+IntGroupNewFromIntGroup(const IntGroup *src)
+{
+       IntGroup *dst = IntGroupNew();
+       if (dst == NULL)
+               return NULL;
+       if (IntGroupCopy(dst, src) != kIntGroupStatusNoError) {
+               IntGroupRelease(dst);
+               return NULL;
+       }
+       return dst;
+}
+
+/* --------------------------------------
+       ・ IntGroupNewWithPoints
+   -------------------------------------- */
+IntGroup *
+IntGroupNewWithPoints(int start, ...)
+{
+       va_list ap;
+       int length;
+       IntGroup *psRef = IntGroupNew();
+       if (psRef == NULL)
+               return NULL;
+       va_start(ap, start);
+       while (start >= 0) {
+               length = va_arg(ap, int);
+               if (IntGroupAdd(psRef, start, length) != 0) {
+                       IntGroupRelease(psRef);
+                       return NULL;
+               }
+               start = va_arg(ap, int);
+       }
+       va_end(ap);
+       return psRef;
+}
+
+/* --------------------------------------
+       ・ IntGroupRetain
+   -------------------------------------- */
+void
+IntGroupRetain(IntGroup *psRef)
+{
+       if (psRef == NULL)
+               return;
+       psRef->refCount++;
+}
+
+/* --------------------------------------
+       ・ IntGroupRelease
+   -------------------------------------- */
+void
+IntGroupRelease(IntGroup *psRef)
+{
+       if (psRef == NULL)
+               return;
+       if (--psRef->refCount == 0) {
+               IntGroupClear(psRef);
+               free(psRef);
+       }
+}
+
+#ifdef __MWERKS__
+#pragma mark ====== Clear/Copy ======
+#endif
+/* --------------------------------------
+       ・ IntGroupClear
+   -------------------------------------- */
+void
+IntGroupClear(IntGroup *psRef)
+{
+       if (psRef == NULL)
+               return;
+       if (psRef->entries != NULL) {
+               free(psRef->entries);
+               psRef->entries = NULL;
+       }
+       psRef->num = 0;
+}
+
+/* --------------------------------------
+       ・ IntGroupCopy
+   -------------------------------------- */
+IntGroupStatus
+IntGroupCopy(IntGroup *psRef1, const IntGroup *psRef2)
+{
+       IntGroupStatus sts;
+       if (psRef1 == NULL || psRef2 == NULL)
+               return kIntGroupStatusNoError;
+       sts = IntGroupAdjustStorage(psRef1, psRef2->num);
+       if (sts == kIntGroupStatusNoError) {
+               memmove(psRef1->entries, psRef2->entries, psRef2->num * 2 * sizeof(int));
+        psRef1->num = psRef2->num;
+    }
+       return sts;
+}
+
+#ifdef __MWERKS__
+#pragma mark ====== Point Manipulations ======
+#endif
+
+/* --------------------------------------
+       ・ IntGroupLookup
+   -------------------------------------- */
+int
+IntGroupLookup(const IntGroup *psRef, int inPoint, int *outIndex)
+{
+       int i;
+       if (psRef == NULL)
+               return 0;
+       for (i = 0; i < psRef->num; i++) {
+               if (inPoint < psRef->entries[i*2]) {
+                        if (outIndex != NULL)
+                            *outIndex = i;
+                        return 0;
+               } else if (inPoint < psRef->entries[i*2+1]) {
+                        if (outIndex != NULL)
+                            *outIndex = i;
+                        return 1;
+               }
+       }
+       if (outIndex != NULL)
+               *outIndex = psRef->num;
+       return 0;
+}
+
+/* --------------------------------------
+       ・ IntGroupIsEqual
+   -------------------------------------- */
+int
+IntGroupIsEqual(const IntGroup *psRef1, const IntGroup *psRef2)
+{
+       int i;
+       if (psRef1 == NULL || psRef2 == NULL)
+               return (psRef1 == psRef2);
+       if (psRef1->num != psRef2->num)
+               return 0;
+       for (i = 0; i < psRef1->num * 2; i++) {
+               if (psRef1->entries[i] != psRef2->entries[i])
+                       return 0;
+       }
+       return 1;
+}
+
+/* --------------------------------------
+       ・ IntGroupGetCount
+   -------------------------------------- */
+int
+IntGroupGetCount(const IntGroup *psRef)
+{
+       int i, n;
+       if (psRef == NULL)
+               return 0;
+       n = 0;
+       for (i = 0; i < psRef->num; i++)
+               n += psRef->entries[i*2+1] - psRef->entries[i*2];
+       return n;
+}
+
+/* --------------------------------------
+       ・ IntGroupGetIntervalCount
+   -------------------------------------- */
+int
+IntGroupGetIntervalCount(const IntGroup *psRef)
+{
+       if (psRef == NULL)
+               return 0;
+       return psRef->num;
+}
+
+/* --------------------------------------
+       ・ IntGroupAdd
+   -------------------------------------- */
+IntGroupStatus
+IntGroupAdd(IntGroup *psRef, int inStart, int inCount)
+{
+       int theBeginIndex, theEndIndex;
+       int theBeginFlag, theEndFlag;
+
+       if (psRef == NULL)
+               return kIntGroupStatusNoError;
+       
+       /*  inStart, inStart+inCount が位置指定の中でどこにあるか探す  */
+       theBeginFlag = IntGroupLookup(psRef, inStart, &theBeginIndex);
+       theEndFlag = IntGroupLookup(psRef, inStart + inCount, &theEndIndex);
+       
+       if (theBeginFlag) {
+               /*  psRef->entries[theBeginIndex*2] <= inStart < psRef->entries[theBeginIndex*2+1]  */
+               if (theEndFlag) {
+                       /*  psRef->entries[theEndIndex*2] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2+1]  */
+                       if (theBeginIndex < theEndIndex) {
+                               psRef->entries[theBeginIndex*2+1] = psRef->entries[theEndIndex*2+1];
+                               return IntGroupDeleteEntries(psRef, theBeginIndex + 1, theEndIndex);
+                       } else return 0;
+               } else {
+                       /*  psRef->entries[(theEndIndex-1)*2+1] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2]  */
+                       psRef->entries[theBeginIndex*2+1] = inStart + inCount;
+                       return IntGroupDeleteEntries(psRef, theBeginIndex + 1, theEndIndex - 1);
+               }
+       } else {
+               /*  psRef->entries[(theBeginIndex-1)*2+1] <= inStart < psRef->entries[theBeginIndex*2]  */
+               int thePoint = 0;
+               if (theBeginIndex > 0 && psRef->entries[(theBeginIndex-1)*2+1] == inStart) {
+                       /*  1つ前のブロックとくっついてしまう  */
+                       theBeginIndex--;
+               } else if (theBeginIndex < psRef->num) {
+                       thePoint = psRef->entries[theBeginIndex*2];     /*  あとで必要かもしれない  */
+                       psRef->entries[theBeginIndex*2] = inStart;
+               }
+               if (theEndFlag) {
+                       /*  psRef->entries[theEndIndex*2] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2+1]  */
+                       psRef->entries[theBeginIndex*2+1] = psRef->entries[theEndIndex*2+1];
+                       return IntGroupDeleteEntries(psRef, theBeginIndex + 1, theEndIndex);
+               } else {
+                       /*  psRef->entries[(theEndIndex-1)*2+1] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2]  */
+                       if (theBeginIndex == theEndIndex) {
+                               if (theBeginIndex < psRef->num)
+                                       psRef->entries[theBeginIndex*2] = thePoint;     /*  元に戻す  */
+                               return IntGroupInsertAnEntry(psRef, theBeginIndex, inStart, inStart + inCount);
+                       } else {
+                               psRef->entries[theBeginIndex*2+1] = inStart + inCount;
+                               return IntGroupDeleteEntries(psRef, theBeginIndex + 1, theEndIndex - 1);
+                       }
+               }
+       }
+       
+}
+
+/* --------------------------------------
+       ・ IntGroupRemove
+   -------------------------------------- */
+IntGroupStatus
+IntGroupRemove(IntGroup *psRef, int inStart, int inCount)
+{
+       int theBeginIndex, theEndIndex;
+       int theBeginFlag, theEndFlag;
+       
+       if (psRef == NULL)
+               return kIntGroupStatusNoError;
+
+       /*  inStart, inStart+inCount が位置指定の中でどこにあるか探す  */
+       theBeginFlag = IntGroupLookup(psRef, inStart, &theBeginIndex);
+       theEndFlag = IntGroupLookup(psRef, inStart + inCount, &theEndIndex);
+       
+       if (theBeginFlag) {
+               /*  psRef->entries[theBeginIndex*2] <= inStart < psRef->entries[theBeginIndex*2+1]  */
+               int thePoint = psRef->entries[theBeginIndex*2];
+               if (theEndFlag) {
+                       /*  psRef->entries[theEndIndex*2] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2+1]  */
+                       psRef->entries[theEndIndex*2] = inStart + inCount;
+                       if (theBeginIndex == theEndIndex) {
+                               if (thePoint == inStart)
+                                       return 0;
+                               else
+                                       return IntGroupInsertAnEntry(psRef, theBeginIndex, thePoint, inStart);
+                       } else {
+                               if (thePoint == inStart)
+                                       theBeginIndex--;
+                               else
+                                       psRef->entries[theBeginIndex*2+1] = inStart;
+                               return IntGroupDeleteEntries(psRef, theBeginIndex + 1, theEndIndex - 1);
+                       }
+               } else {
+                       /*  psRef->entries[(theEndIndex-1)*2+1] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2]  */
+                       if (thePoint == inStart)
+                               theBeginIndex--;
+                       else
+                               psRef->entries[theBeginIndex*2+1] = inStart;
+                       return IntGroupDeleteEntries(psRef, theBeginIndex + 1, theEndIndex - 1);
+               }
+       } else {
+               /*  psRef->entries[(theBeginIndex-1)*2+1] <= inStart < psRef->entries[theBeginIndex*2]  */
+               if (theEndFlag) {
+                       /*  psRef->entries[theEndIndex*2] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2+1]  */
+                       psRef->entries[theEndIndex*2] = inStart + inCount;
+                       return IntGroupDeleteEntries(psRef, theBeginIndex, theEndIndex - 1);
+               } else {
+                       /*  psRef->entries[(theEndIndex-1)*2+1] <= inStart + inCount
+                           < psRef->entries[theEndIndex*2]  */
+                       return IntGroupDeleteEntries(psRef, theBeginIndex, theEndIndex - 1);
+               }
+       }
+}
+
+/* --------------------------------------
+       ・ IntGroupReverse
+   -------------------------------------- */
+IntGroupStatus
+IntGroupReverse(IntGroup *psRef, int inStart, int inCount)
+{
+       int theBeginIndex, theEndIndex, theIndex;
+       int theBeginFlag, theEndFlag;
+       IntGroupStatus result;
+       
+       if (psRef == NULL)
+               return kIntGroupStatusNoError;
+
+       /*  inStart, inStart+inCount が位置指定の中でどこにあるか探す  */
+       theBeginFlag = IntGroupLookup(psRef, inStart, &theBeginIndex);
+       theEndFlag = IntGroupLookup(psRef, inStart + inCount, &theEndIndex);
+
+       if (theBeginFlag) {
+               /*  psRef->entries[theBeginIndex*2] <= inStart < psRef->entries[theBeginIndex*2+1]  */
+               if (psRef->entries[theBeginIndex*2] < inStart) {
+                       result = IntGroupInsertAnEntry(psRef, theBeginIndex, psRef->entries[theBeginIndex*2], inStart);
+                       if (result != 0)
+                               return result;
+                       theBeginIndex++;
+                       theEndIndex++;
+               }
+       } else {
+               /*  psRef->entries[(theBeginIndex-1)*2+1] <= inStart < psRef->entries[theBeginIndex*2]  */
+               int thePoint;
+               if (theBeginIndex == theEndIndex && !theEndFlag)
+                       thePoint = inStart + inCount;   /* theBeginIndex == mNumberOfEntries の場合を含む  */
+               else
+                       thePoint = psRef->entries[theBeginIndex*2];
+               result = IntGroupInsertAnEntry(psRef, theBeginIndex, inStart, thePoint);
+               if (result != 0)
+                       return result;
+               theBeginIndex++;
+               theEndIndex++;
+       }
+       
+       if (theEndFlag) {
+               /*  psRef->entries[theEndIndex*2] <= inStart + inCount
+                   < psRef->entries[theEndIndex*2+1] */
+               for (theIndex = theBeginIndex; theIndex < theEndIndex; theIndex++) {
+                       psRef->entries[theIndex*2] = psRef->entries[theIndex*2+1];
+                       psRef->entries[theIndex*2+1] = psRef->entries[(theIndex+1)*2];
+               }
+               psRef->entries[theEndIndex*2] = inStart + inCount;
+               if (theEndIndex > 0 && psRef->entries[(theEndIndex-1)*2+1] == inStart + inCount) {
+                       /*  1つ前のブロックとくっついてしまう  */
+                       psRef->entries[(theEndIndex-1)*2+1] = psRef->entries[theEndIndex*2+1];
+                       return IntGroupDeleteEntries(psRef, theEndIndex, theEndIndex);
+               }
+       } else {
+               /*  psRef->entries[(theEndIndex-1)*2+1] <= inStart + inCount
+                   < psRef->entries[theEndIndex*2]  */
+               for (theIndex = theBeginIndex; theIndex < theEndIndex - 1; theIndex++) {
+                       psRef->entries[theIndex*2] = psRef->entries[theIndex*2+1];
+                       psRef->entries[theIndex*2+1] = psRef->entries[(theIndex+1)*2];
+               }
+               if (theIndex == theEndIndex - 1) {
+                       if (psRef->entries[theIndex*2+1] == inStart + inCount)
+                               return IntGroupDeleteEntries(psRef, theIndex, theIndex);
+                       else {
+                               psRef->entries[theIndex*2] = psRef->entries[theIndex*2+1];
+                               psRef->entries[theIndex*2+1] = inStart + inCount;
+                       }
+               }
+       }
+       return 0;
+}
+
+/* --------------------------------------
+ ・ IntGroupAddIntGroup
+ -------------------------------------- */
+IntGroupStatus
+IntGroupAddIntGroup(IntGroup *psRef1, const IntGroup *psRef2)
+{
+       int i, n1, n2, result;
+       if (psRef1 == NULL || psRef2 == NULL)
+               return 0;
+       for (i = 0; (n1 = IntGroupGetStartPoint(psRef2, i)) >= 0; i++) {
+               n2 = IntGroupGetInterval(psRef2, i);
+               if ((result = IntGroupAdd(psRef1, n1, n2)) != 0)
+                       return result;
+       }
+       return 0;
+}
+
+/* --------------------------------------
+ ・ IntGroupRemoveIntGroup
+ -------------------------------------- */
+IntGroupStatus
+IntGroupRemoveIntGroup(IntGroup *psRef1, const IntGroup *psRef2)
+{
+       int i, n1, n2, result;
+       if (psRef1 == NULL || psRef2 == NULL)
+               return 0;
+       for (i = 0; (n1 = IntGroupGetStartPoint(psRef2, i)) >= 0; i++) {
+               n2 = IntGroupGetInterval(psRef2, i);
+               if ((result = IntGroupRemove(psRef1, n1, n2)) != 0)
+                       return result;
+       }
+       return 0;
+}
+
+/* --------------------------------------
+ ・ IntGroupReverseIntGroup
+ -------------------------------------- */
+IntGroupStatus
+IntGroupReverseIntGroup(IntGroup *psRef1, const IntGroup *psRef2)
+{
+       int i, n1, n2, result;
+       if (psRef1 == NULL || psRef2 == NULL)
+               return 0;
+       for (i = 0; (n1 = IntGroupGetStartPoint(psRef2, i)) >= 0; i++) {
+               n2 = IntGroupGetInterval(psRef2, i);
+               if ((result = IntGroupReverse(psRef1, n1, n2)) != 0)
+                       return result;
+       }
+       return 0;
+}
+
+/* --------------------------------------
+       ・ IntGroupGetStartPoint
+   -------------------------------------- */
+int
+IntGroupGetStartPoint(const IntGroup *psRef, int inIndex)
+{
+       if (psRef == NULL || inIndex < 0 || inIndex >= psRef->num)
+               return -1;
+       else return psRef->entries[inIndex*2];
+}
+
+/* --------------------------------------
+       ・ IntGroupGetEndPoint
+   -------------------------------------- */
+int
+IntGroupGetEndPoint(const IntGroup *psRef, int inIndex)
+{
+       if (psRef == NULL || inIndex < 0 || inIndex >= psRef->num)
+               return -1;
+       else return psRef->entries[inIndex*2+1];
+}
+
+/* --------------------------------------
+       ・ IntGroupGetInterval
+   -------------------------------------- */
+int
+IntGroupGetInterval(const IntGroup *psRef, int inIndex)
+{
+       if (psRef == NULL || inIndex < 0 || inIndex >= psRef->num)
+               return -1;
+       else return psRef->entries[inIndex*2+1] - psRef->entries[inIndex*2];
+}
+
+/* --------------------------------------
+       ・ IntGroupGetNthPoint
+   -------------------------------------- */
+int
+IntGroupGetNthPoint(const IntGroup *psRef, int inCount)
+{
+       int i, n, dn;
+       if (psRef == NULL || inCount < 0)
+               return -1;
+       n = 0;
+       for (i = 0; i < psRef->num; i++) {
+               dn = psRef->entries[i*2+1] - psRef->entries[i*2];
+               if (inCount < n + dn) {
+                       /*  The inCount-th point is in this interval  */
+                       return psRef->entries[i*2] + inCount - n;
+               }
+               n += dn;
+       }
+       /*  No such point  */
+       return -1;
+}
+
+/* --------------------------------------
+       ・ IntGroupLookupPoint
+   -------------------------------------- */
+int
+IntGroupLookupPoint(const IntGroup *psRef, int inPoint)
+{
+       int i, n;
+       if (psRef == NULL || inPoint < 0)
+               return -1;
+       n = 0;
+       for (i = 0; i < psRef->num; i++) {
+               if (inPoint >= psRef->entries[i*2] && inPoint < psRef->entries[i*2+1]) {
+                       return n + inPoint - psRef->entries[i*2];
+               }
+               n += psRef->entries[i*2+1] - psRef->entries[i*2];
+       }
+       /*  No such point  */
+       return -1;
+}
+
+#ifdef __MWERKS__
+#pragma mark ====== Binary Operations ======
+#endif
+
+/* --------------------------------------
+       ・ IntGroupMyIntersect
+   -------------------------------------- */
+static IntGroupStatus
+IntGroupMyIntersect(
+       IntGroupOperation inCode,
+       const IntGroup *psRef1,
+       const IntGroup *psRef2,
+       IntGroup *psRef3)
+{
+       int base = 0;
+       int i, j, offset1, offset2, where;
+       int theBeginIndex, theEndIndex;
+       int theBeginFlag, theEndFlag;
+       const int *ptr;
+       IntGroupStatus result = kIntGroupStatusNoError;
+
+       IntGroupClear(psRef3);
+       offset1 = offset2 = 0;
+       where = 0;
+
+       for (i = 0; result == 0 && i < psRef2->num; i++) {
+
+               int beginPt, endPt;
+               int newBeginPt, newEndPt;
+
+               ptr = &(psRef2->entries[i * 2]);
+               switch (inCode) {
+                       case kIntGroupIntersect:
+                               break;  /* offset1 = offset2 = 0  */
+                       case kIntGroupConvolute:
+                               offset1 = base - ptr[0];
+                               offset2 = -offset1;
+                               break;
+                       case kIntGroupDeconvolute:
+                               offset2 = base - ptr[0];
+                               break;
+               }
+               beginPt = ptr[0] + offset1;
+               endPt = ptr[1] + offset1;
+               theBeginFlag = IntGroupLookup(psRef1, beginPt, &theBeginIndex);
+               theEndFlag = IntGroupLookup(psRef1, endPt, &theEndIndex);
+
+               if (theBeginIndex == psRef1->num)
+                       break;  /*  もう加えるべき区間はない  */
+
+               if (theBeginFlag) {
+                       newBeginPt = beginPt + offset2;
+               } else {
+                       newBeginPt = psRef1->entries[theBeginIndex * 2] + offset2;
+               }
+               if (theEndFlag && theBeginIndex == theEndIndex) {
+                       newEndPt = endPt + offset2;
+               } else if (!theEndFlag && theBeginIndex == theEndIndex) {
+                       newEndPt = newBeginPt;  /* null interval */
+               } else {
+                       newEndPt = psRef1->entries[theBeginIndex * 2 + 1] + offset2;
+               }
+               /*  直前の区間と連続していないかどうかチェック  */
+                if (where > 0 && newBeginPt == psRef3->entries[where * 2 - 1]) {
+                       psRef3->entries[where * 2 - 1] = newEndPt;
+               } else if (newBeginPt < newEndPt) {
+                        result = IntGroupInsertAnEntry(psRef3, where++, newBeginPt, newEndPt);
+               }
+               if (result == kIntGroupStatusNoError) {
+                       for (j = theBeginIndex + 1; j < theEndIndex; j++) {
+                               result = IntGroupInsertAnEntry(psRef3,
+                                                                       where++,
+                                                                       psRef1->entries[j * 2] + offset2,
+                                                                       psRef1->entries[j * 2 + 1] + offset2);
+                               if (result != kIntGroupStatusNoError)
+                                       break;
+                       }
+               }
+               if (result == kIntGroupStatusNoError) {
+                       if (theEndFlag && theBeginIndex < theEndIndex
+                       && psRef1->entries[theEndIndex * 2] < endPt)
+                               result = IntGroupInsertAnEntry(psRef3,
+                                                                       where++,
+                                                                       psRef1->entries[theEndIndex * 2] + offset2,
+                                                                       endPt + offset2);
+               }
+
+               base += ptr[1] - ptr[0];
+       }
+
+       /*  *****   debug   *****  */
+/*
+       FILE *fp;
+       fp = ::fopen("intersect.out", "at");
+       switch (inCode) {
+               case code_Intersect: ::fprintf(fp, "Intersect:\n"); break;
+               case code_Convolute: ::fprintf(fp, "Convolute:\n"); break;
+               case code_Deconvolute: ::fprintf(fp, "Deconvolute:\n"); break;
+       }
+       for (i = 0; i < psRef1.mNumberOfEntries; i++) {
+               ::fprintf(fp, "%c%ld %ld", (i == 0 ? '(' : ' '),
+                       (int)psRef1.mEntries[i].beginPt,
+                       (int)psRef1.mEntries[i].endPt);
+       }
+       ::fprintf(fp, ")\n");
+       for (i = 0; i < psRef2.mNumberOfEntries; i++) {
+               ::fprintf(fp, "%c%ld %ld", (i == 0 ? '(' : ' '),
+                       (int)psRef2.mEntries[i].beginPt,
+                       (int)psRef2.mEntries[i].endPt);
+       }
+       ::fprintf(fp, ")\n");
+       for (i = 0; i < psRef3.mNumberOfEntries; i++) {
+               ::fprintf(fp, "%c%ld %ld", (i == 0 ? '(' : ' '),
+                       (int)psRef3.mEntries[i].beginPt,
+                       (int)psRef3.mEntries[i].endPt);
+       }
+       ::fprintf(fp, ")\n");
+       ::fclose(fp);
+*/
+       /*  *********************  */
+       
+       return result;
+}
+
+/* --------------------------------------
+       ・ IntGroupUnion
+   -------------------------------------- */
+IntGroupStatus
+IntGroupUnion(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3)
+{
+       int i, startPt;
+       IntGroupStatus result;
+       result = IntGroupCopy(psRef3, psRef2);
+       if (result != kIntGroupStatusNoError)
+               return result;
+       for (i = 0; i < psRef1->num; i++) {
+               startPt = psRef1->entries[i*2];
+               result = IntGroupAdd(psRef3, startPt, psRef1->entries[i*2+1] - startPt);
+               if (result != kIntGroupStatusNoError)
+                       return result;
+       }
+       return kIntGroupStatusNoError;
+}
+
+/* --------------------------------------
+       ・ IntGroupIntersect
+   -------------------------------------- */
+IntGroupStatus
+IntGroupIntersect(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3)
+{
+       return IntGroupMyIntersect(kIntGroupIntersect, psRef1, psRef2, psRef3);
+}
+
+/* --------------------------------------
+       ・ IntGroupXor
+   -------------------------------------- */
+IntGroupStatus
+IntGroupXor(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3)
+{
+    IntGroupStatus result;
+    int i, startPt;
+    result = IntGroupCopy(psRef3, psRef1);
+    if (result != kIntGroupStatusNoError)
+        return result;
+    for (i = 0; i < psRef2->num; i++) {
+        startPt = psRef2->entries[i*2];
+        result = IntGroupReverse(psRef3, startPt, psRef2->entries[i*2+1] - startPt);
+        if (result != kIntGroupStatusNoError)
+            return result;
+    }
+    return kIntGroupStatusNoError;
+}
+
+/* --------------------------------------
+       ・ IntGroupConvolute
+   -------------------------------------- */
+IntGroupStatus
+IntGroupConvolute(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3)
+{
+       return IntGroupMyIntersect(kIntGroupConvolute, psRef1, psRef2, psRef3);
+}
+
+/* --------------------------------------
+       ・ IntGroupDeconvolute
+   -------------------------------------- */
+IntGroupStatus
+IntGroupDeconvolute(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3)
+{
+       return IntGroupMyIntersect(kIntGroupDeconvolute, psRef1, psRef2, psRef3);
+}
+
+/* --------------------------------------
+       ・ IntGroupDifference
+   -------------------------------------- */
+IntGroupStatus
+IntGroupDifference(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3)
+{
+    IntGroupStatus result;
+    int i, startPt;
+    result = IntGroupCopy(psRef3, psRef1);
+    if (result != kIntGroupStatusNoError)
+        return result;
+    for (i = 0; i < psRef2->num; i++) {
+        startPt = psRef2->entries[i*2];
+        result = IntGroupRemove(psRef3, startPt, psRef2->entries[i*2+1] - startPt);
+        if (result != kIntGroupStatusNoError)
+            return result;
+    }
+    return kIntGroupStatusNoError;
+}
+
+/* --------------------------------------
+       ・ IntGroupNegate
+   -------------------------------------- */
+IntGroupStatus
+IntGroupNegate(const IntGroup *psRef1, IntGroup *psRef2)
+{
+       int i;
+
+       IntGroupCopy(psRef2, psRef1);
+       if (psRef1->num == 0) {
+               //  空集合
+               return IntGroupInsertAnEntry(psRef2, 0, 0, INT_MAX);
+       }
+       
+       if (psRef1->entries[0] == 0) {
+               for (i = 0; i < psRef1->num - 1; i++) {
+                       psRef2->entries[i*2] = psRef1->entries[i*2+1];
+                       psRef2->entries[i*2+1] = psRef1->entries[(i+1)*2];
+               }
+               if (psRef1->entries[i*2+1] != INT_MAX) {
+                       psRef2->entries[i*2] = psRef1->entries[i*2+1];
+                       psRef2->entries[i*2+1] = INT_MAX;
+               } else return IntGroupDeleteEntries(psRef2, i, i);
+       } else {
+               psRef2->entries[0] = 0;
+               psRef2->entries[1] = psRef1->entries[0];
+               for (i = 1; i < psRef1->num; i++) {
+                       psRef2->entries[i*2] = psRef1->entries[(i-1)*2+1];
+                       psRef2->entries[i*2+1] = psRef1->entries[i*2];
+               }
+               if (psRef1->entries[(i-1)*2+1] != INT_MAX) {
+                       return IntGroupInsertAnEntry(psRef2, i, psRef1->entries[(i-1)*2+1], INT_MAX);
+               }
+       }
+       return kIntGroupStatusNoError;
+}
+
+/* --------------------------------------
+       ・ IntGroupOffset
+   -------------------------------------- */
+IntGroupStatus
+IntGroupOffset(IntGroup *psRef, int offset)
+{
+       int i;
+       if (psRef == NULL || psRef->num == 0)
+               return kIntGroupStatusNoError;
+       if (psRef->entries[0] + offset < 0)
+               return kIntGroupStatusOutOfRange;  /*  Negative number is not allowed  */
+       for (i = 0; i < psRef->num; i++) {
+               psRef->entries[i*2] += offset;
+               psRef->entries[i*2+1] += offset;
+       }
+       return kIntGroupStatusNoError;
+}
+
+#ifdef __MWERKS__
+#pragma mark ====== Debugging ======
+#endif
+
+/* --------------------------------------
+       ・ IntGroupDump
+   -------------------------------------- */
+void
+IntGroupDump(const IntGroup *pset)
+{
+    int i, n, m;
+    fprintf(stderr, "IntGroup[%p]: ", pset);
+    for (i = 0; i < pset->num; i++) {
+        n = pset->entries[i*2];
+        m = pset->entries[i*2+1];
+        fprintf(stderr, "%d", n);
+        if (m > n + 1)
+            fprintf(stderr, "-%d", m-1);
+        if (i < pset->num - 1)
+            fprintf(stderr, ",");
+    }
+    fprintf(stderr, "\n");
+}
+
+#pragma mark ====== Iterators ======
+
+/* --------------------------------------
+       ・ IntGroupIteratorNew
+   -------------------------------------- */
+IntGroupIterator *
+IntGroupIteratorNew(IntGroup *psRef)
+{
+       IntGroupIterator *piRef = (IntGroupIterator *)malloc(sizeof(*piRef));
+       if (piRef == NULL)
+               return NULL;    /* out of memory */
+       IntGroupIteratorInit(psRef, piRef);
+       piRef->refCount = 1;
+       return piRef;
+}
+
+/* --------------------------------------
+       ・ IntGroupIteratorInit
+   -------------------------------------- */
+IntGroupIterator *
+IntGroupIteratorInit(IntGroup *psRef, IntGroupIterator *piRef)
+{
+       piRef->intGroup = psRef;
+       IntGroupRetain(psRef);
+       piRef->index = -1;
+       piRef->position = -1;
+       piRef->refCount = -1;
+       return piRef;
+}
+
+/* --------------------------------------
+       ・ IntGroupIteratorRetain
+   -------------------------------------- */
+void
+IntGroupIteratorRetain(IntGroupIterator *piRef)
+{
+       if (piRef == NULL)
+               return;
+       else if (piRef->refCount < 0)
+               piRef->refCount--;
+       else
+               piRef->refCount++;
+}
+
+/* --------------------------------------
+       ・ IntGroupIteratorRelease
+   -------------------------------------- */
+void
+IntGroupIteratorRelease(IntGroupIterator *piRef)
+{
+       if (piRef == NULL)
+               return;
+       else if (piRef->refCount < 0) {
+               if (++piRef->refCount == 0)
+                       IntGroupRelease(piRef->intGroup);
+       } else {
+               if (--piRef->refCount == 0) {
+                       IntGroupRelease(piRef->intGroup);
+                       free(piRef);
+               }
+       }
+}
+
+void
+IntGroupIteratorReset(IntGroupIterator *piRef)
+{
+    if (piRef == NULL)
+        return;
+    piRef->index = -1;
+    piRef->position = -1;
+}
+
+void
+IntGroupIteratorResetAtLast(IntGroupIterator *piRef)
+{
+    if (piRef == NULL || piRef->intGroup == NULL)
+        return;
+    piRef->index = piRef->intGroup->num - 1;
+    if (piRef->index >= 0)
+        piRef->position = piRef->intGroup->entries[piRef->intGroup->num * 2 - 1];
+    else piRef->position = -1;
+}
+
+int
+IntGroupIteratorNext(IntGroupIterator *piRef)
+{
+    if (piRef == NULL || piRef->intGroup == NULL || piRef->intGroup->num == 0 || piRef->index >= piRef->intGroup->num)
+        return -1;
+    if (piRef->position < 0) {
+        piRef->index = 0;
+        piRef->position = piRef->intGroup->entries[0];
+        return piRef->position;
+    } else {
+        piRef->position++;
+        if (piRef->intGroup->entries[piRef->index * 2 + 1] > piRef->position)
+            return piRef->position;
+        if (piRef->index == piRef->intGroup->num - 1) {
+            piRef->index = piRef->intGroup->num;
+            return -1;
+        } else {
+            piRef->index++;
+            piRef->position = piRef->intGroup->entries[piRef->index * 2];
+            return piRef->position;
+        }
+    }
+}
+
+int
+IntGroupIteratorLast(IntGroupIterator *piRef)
+{
+    if (piRef == NULL || piRef->intGroup == NULL || piRef->intGroup->num == 0 || piRef->index < 0)
+        return -1;
+    piRef->position--;
+    if (piRef->intGroup->entries[piRef->index * 2] <= piRef->position)
+        return piRef->position;
+    if (piRef->index == 0) {
+        piRef->index = -1;
+        piRef->position = -1;
+        return -1;
+    } else {
+        piRef->index--;
+        piRef->position = piRef->intGroup->entries[piRef->index * 2 + 1] - 1;
+        return piRef->position;
+    }
+}
diff --git a/MolLib/IntGroup.h b/MolLib/IntGroup.h
new file mode 100755 (executable)
index 0000000..a91960e
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ *  IntGroup.h
+ *
+ *  Created by Toshi Nagata on Sun Jun 17 2001.
+
+   Copyright (c) 2000-2008 Toshi Nagata. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __IntGroup_h__
+#define __IntGroup_h__
+
+typedef struct IntGroup                        IntGroup;
+typedef struct IntGroupIterator        IntGroupIterator;
+
+struct IntGroupIterator {
+    int                        refCount;
+    IntGroup * intGroup;
+    int                        index;
+    int                        position;
+};
+
+typedef int IntGroupStatus;
+enum {
+    kIntGroupStatusNoError = 0,
+    kIntGroupStatusOutOfMemory,
+       kIntGroupStatusOutOfRange
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -------------------------------------------------------------------
+    IntGroup functions
+   -------------------------------------------------------------------  */
+
+/*  新しい IntGroupRecord をアロケートする。メモリ不足の場合は NULL を返す。 */
+IntGroup *     IntGroupNew(void);
+
+/*  Initialize a new IntGroupRecord that has been statically allocated  */
+IntGroupIterator *IntGroupIteratorInit(IntGroup *psRef, IntGroupIterator *piRef);
+
+/*  Allocate a new IntGroup, with points specified by arguments
+    (start, length, start, length, etc...). Arguments end when start < 0.  */
+IntGroup *  IntGroupNewWithPoints(int start, ...);
+
+/*  Duplicate an existing IntGroup  */
+IntGroup *IntGroupNewFromIntGroup(const IntGroup *src);
+
+/*  IntGroup の retain/release。 */
+void   IntGroupRetain(IntGroup *psRef);
+void   IntGroupRelease(IntGroup *psRef);
+
+/*  すべての点を取り除いて空集合にする  */
+void   IntGroupClear(IntGroup *psRef);
+
+IntGroupStatus IntGroupCopy(IntGroup *psRef1, const IntGroup *psRef2);
+
+/*  inStart から始まる inCount 個の点を集合に加える・取り除く・反転する */
+IntGroupStatus IntGroupAdd(IntGroup *psRef, int inStart, int inCount);
+IntGroupStatus IntGroupRemove(IntGroup *psRef, int inStart, int inCount);
+IntGroupStatus IntGroupReverse(IntGroup *psRef, int inStart, int inCount);
+
+IntGroupStatus IntGroupAddIntGroup(IntGroup *psRef1, const IntGroup *psRef2);
+IntGroupStatus IntGroupRemoveIntGroup(IntGroup *psRef1, const IntGroup *psRef2);
+IntGroupStatus IntGroupReverseIntGroup(IntGroup *psRef1, const IntGroup *psRef2);
+       
+/*  inPoint なる点が集合に含まれていれば non-zero, 含まれていなければ zero を返す。
+    outIndex が NULL でなければ、*outIndex に「何番目の区間」に含まれているかを返す。 */
+int                    IntGroupLookup(const IntGroup *psRef, int inPoint, int *outIndex);
+
+int     IntGroupIsEqual(const IntGroup *psRef1, const IntGroup *psRef2);
+
+/*  含まれる点の数を返す  */
+int            IntGroupGetCount(const IntGroup *psRef);
+
+/*  含まれる区間の数を返す  */
+int            IntGroupGetIntervalCount(const IntGroup *psRef);
+
+/*  inIndex 番目(0からスタート)の区間の開始点を返す。inIndex 番目の区間が
+    存在しなければ -1 を返す。 */
+int            IntGroupGetStartPoint(const IntGroup *psRef, int inIndex);
+
+/*  inIndex 番目(0からスタート)の区間の終了点を返す(この点自身は区間には含まれない)。
+    inIndex 番目の区間が存在しなければ -1 を返す。 */
+int            IntGroupGetEndPoint(const IntGroup *psRef, int inIndex);
+
+/*  inIndex 番目(0からスタート)の区間の長さを返す。inIndex 番目の区間が
+    存在しなければ -1 を返す。 */
+int            IntGroupGetInterval(const IntGroup *psRef, int inIndex);
+
+/*  inCount 番目の点を返す。そのような点が存在しなければ -1 を返す。 */
+int    IntGroupGetNthPoint(const IntGroup *psRef, int inCount);
+
+/*  inPoint なる点が存在するなら、先頭から何番目になるかを返す。存在しなければ-1を返す。 */
+int IntGroupLookupPoint(const IntGroup *psRef, int inPoint);
+
+/*  2項演算  */
+IntGroupStatus IntGroupUnion(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3);
+IntGroupStatus IntGroupIntersect(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3);
+IntGroupStatus IntGroupXor(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3);
+IntGroupStatus IntGroupConvolute(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3);
+IntGroupStatus IntGroupDeconvolute(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3);
+IntGroupStatus IntGroupDifference(const IntGroup *psRef1, const IntGroup *psRef2, IntGroup *psRef3);
+
+/*  反転  */
+IntGroupStatus IntGroupNegate(const IntGroup *psRef1, IntGroup *psRef2);
+
+/*  Add offset to all points  */
+IntGroupStatus  IntGroupOffset(IntGroup *psRef, int offset);
+
+/*  Debug  */
+void           IntGroupDump(const IntGroup *pset);
+
+/*  Iterator support  */
+IntGroupIterator *IntGroupIteratorNew(IntGroup *psRef);
+IntGroupIterator *IntGroupIteratorInit(IntGroup *psRef, IntGroupIterator *piRef);
+void IntGroupIteratorRetain(IntGroupIterator *piRef);
+void IntGroupIteratorRelease(IntGroupIterator *piRef);
+void IntGroupIteratorReset(IntGroupIterator *piRef);
+void IntGroupIteratorResetAtLast(IntGroupIterator *piRef);
+int IntGroupIteratorNext(IntGroupIterator *piRef);
+int IntGroupIteratorLast(IntGroupIterator *piRef);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /*  __IntGroup_h__  */
diff --git a/MolLib/MD/MDCore.c b/MolLib/MD/MDCore.c
new file mode 100644 (file)
index 0000000..9e5337b
--- /dev/null
@@ -0,0 +1,3035 @@
+/*
+ *  MDCore.c
+ *
+ *  Created by Toshi Nagata on 2005/06/06.
+ *  Copyright 2005 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MDCore.h"
+#include "MDGraphite.h"
+#include "MDPressure.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#if __WXMSW__
+#define ftello(x) ftell(x)
+#endif
+
+static char sErrBuf[256];
+
+void
+md_panic(MDArena *arena, const char *fmt,...)
+{
+       va_list ap;
+       jmp_buf *envp;
+
+       /*  Clean up the running simulation  */
+       md_finish(arena);
+       arena->is_running = 0;
+       arena->mol->needsMDRebuild = 1;
+
+       va_start(ap, fmt);
+       vsnprintf(arena->errmsg, sizeof(arena->errmsg), fmt, ap);
+       va_end(ap);
+
+       envp = arena->setjmp_buf;
+       arena->setjmp_buf = NULL;
+       if (arena->md_panic_func != NULL)
+               (*(arena->md_panic_func))(arena, arena->errmsg);
+       if (envp != NULL)
+               longjmp(*envp, 1);
+       else {
+               fprintf(stderr, "%s\n", sErrBuf);
+               exit(1);
+       }
+}
+
+/*  Message output  */
+extern int MyAppCallback_showScriptMessage(const char *fmt, ...);
+       
+int
+md_warning(MDArena *arena, const char *fmt, ...)
+{
+       va_list ap;
+       char buf[1024];
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof buf, fmt, ap);
+       va_end(ap);
+       MyAppCallback_showScriptMessage("%s", buf);
+       if (arena->log_result != NULL)
+               fputs(buf, arena->log_result);
+       return strlen(buf);
+}
+
+int
+md_log(MDArena *arena, const char *fmt, ...)
+{
+       va_list ap;
+       char buf[1024];
+       if (arena->log_result == NULL)
+               return 0;
+       if (fmt == NULL) {
+               fflush(arena->log_result);
+               return 0;
+       }
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof buf, fmt, ap);
+       va_end(ap);
+       fputs(buf, arena->log_result);
+       return strlen(buf);
+}
+
+void
+md_debug(MDArena *arena, const char *fmt,...)
+{
+       va_list ap;
+       if (arena->debug_result == NULL || arena->debug_output_level == 0)
+               return;
+       va_start(ap, fmt);
+       vfprintf(arena->debug_result, fmt, ap);
+       va_end(ap);
+       fflush(arena->debug_result);
+}
+
+#if DEBUG
+#define DEBUG_FIT_COORDINATES 0  /*  set to 1 while debugging md_fit_coordinates */
+#endif
+
+/*  Calculate the transform that moves the current coordinates to the reference
+  coordinates with least displacements. The reference coordinates are given
+  as one of the snapshots (refno = zero-based)  */
+int
+md_fit_coordinates(MDArena *arena, Int refno, Double *weights, Transform trans)
+{
+       Atom *ap;
+       Vector *rvf;
+       Int natoms, nn;
+       Vector org1, org2;
+       Int i, j, k;
+       Double w, w1;
+       Mat33 r, q, u;
+       Double eigen_val[3];
+       Vector eigen_vec[3];
+       Vector s[3];
+       if (arena == NULL || arena->mol == NULL || arena->mol->natoms == 0)
+               return 1;  /*  Molecule is empty  */
+       if (refno < 0 || refno >= arena->nsnapshots)
+               return 2;  /*  No such snapshot  */
+       natoms = arena->mol->natoms;
+       ap = arena->mol->atoms;
+       rvf = arena->snapshots[refno]->rvf;
+       
+       /*  Calculate the weighted center  */
+       for (j = 0; j < 2; j++) {
+               Vector *op = (j == 0 ? &org1 : &org2);
+               VecZero(*op);
+               w = 0.0;
+               for (i = 0; i < natoms; i++) {
+                       w1 = (weights != NULL ? weights[i] : ap[i].weight);
+                       if (w1 == 0.0)
+                               continue;
+                       if (j == 0)
+                               VecScaleInc(*op, ap[i].r, w1);
+                       else
+                               VecScaleInc(*op, rvf[i * 3], w1);
+                       w += w1;
+               }
+               w = 1.0 / w;
+               VecScaleSelf(*op, w);
+       }
+       
+    /*  R = sum(weight[n] * x[n] * t(y[n]));  */
+    /*  Matrix to diagonalize = R * tR    */
+       memset(r, 0, sizeof(Mat33));
+       memset(q, 0, sizeof(Mat33));
+       memset(u, 0, sizeof(Mat33));
+       nn = 0;
+       for (i = 0; i < natoms; i++) {
+               Vector v1, v2;
+               w1 = (weights != NULL ? weights[i] : ap[i].weight);
+               if (w1 == 0.0)
+                       continue;
+               VecSub(v1, ap[i].r, org1);
+               VecSub(v2, rvf[3 * i], org2);
+               r[0] += w1 * v2.x * v1.x;
+               r[1] += w1 * v2.x * v1.y;
+               r[2] += w1 * v2.x * v1.z;
+               r[3] += w1 * v2.y * v1.x;
+               r[4] += w1 * v2.y * v1.y;
+               r[5] += w1 * v2.y * v1.z;
+               r[6] += w1 * v2.z * v1.x;
+               r[7] += w1 * v2.z * v1.y;
+               r[8] += w1 * v2.z * v1.z;
+/*             for (j = 0; j < 3; j++) {
+                       for (k = 0; k < 3; k++) {
+                               r[3*j+k] += w1 * VecIndex(&v2, j) * VecIndex(&v1, k);
+#if DEBUG_FIT_COORDINATES
+                               printf("r(%d,%d) += %.6g * %.6g * %.6g (%.6g)\n", j, k, w1, VecIndex(&v2, j), VecIndex(&v1, k), r[3*j+k]);
+#endif
+                       }
+               }
+*/
+               nn++;
+       }
+       for (i = 0; i < 9; i++)
+               r[i] /= (nn * nn);
+       for (i = 0; i < 3; i++) {
+               for (j = 0; j < 3; j++) {
+                       for (k = 0; k < 3; k++) {
+                               q[i*3+j] += r[i*3+k] * r[j*3+k];
+                       }
+               }
+       }
+
+#if DEBUG_FIT_COORDINATES
+       printf("Matrix to diagonalize:\n");
+       for (i = 0; i < 3; i++) {
+               printf("%10.6g %10.6g %10.6g\n", q[i*3], q[i*3+1], q[i*3+2]);
+       }
+#endif
+
+       if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0)
+               return 3;  /*  Cannot determine the eigenvector  */
+
+#if DEBUG_FIT_COORDINATES
+       for (i = 0; i < 3; i++) {
+               printf("Eigenvalue %d = %.6g\n", i+1, eigen_val[i]);
+               printf("Eigenvector %d: %.6g %.6g %.6g\n", i+1, eigen_vec[i].x, eigen_vec[i].y, eigen_vec[i].z);
+       }
+#endif
+
+    /*  s[i] = normalize(tR * v[i])  */
+    /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
+       MatrixTranspose(r, r);
+       for (i = 0; i < 3; i++) {
+               MatrixVec(&s[i], r, &eigen_vec[i]);
+               w1 = 1.0 / VecLength(s[i]);
+               VecScaleSelf(s[i], w1);
+#if DEBUG_FIT_COORDINATES
+               printf("s[%d] = %.6g %.6g %.6g\n", i+1, s[i].x, s[i].y, s[i].z);
+#endif
+       }
+/*     for (i = 0; i < 3; i++) {
+               for (j = 0; j < 3; j++) {
+                       for (k = 0; k < 3; k++) {
+                               u[3*i+j] += VecIndex(&s[k], j) * VecIndex(&eigen_vec[k], i);
+                       }
+               }
+       }
+*/
+       for (k = 0; k < 3; k++) {
+               u[0] += s[k].x * eigen_vec[k].x;
+               u[1] += s[k].y * eigen_vec[k].x;
+               u[2] += s[k].z * eigen_vec[k].x;
+               u[3] += s[k].x * eigen_vec[k].y;
+               u[4] += s[k].y * eigen_vec[k].y;
+               u[5] += s[k].z * eigen_vec[k].y;
+               u[6] += s[k].x * eigen_vec[k].z;
+               u[7] += s[k].y * eigen_vec[k].z;
+               u[8] += s[k].z * eigen_vec[k].z;
+       }
+       
+       /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
+       MatrixVec(&org1, u, &org1);
+       VecDec(org2, org1);
+       for (i = 0; i < 9; i++)
+               trans[i] = u[i];
+       trans[9] = org2.x;
+       trans[10] = org2.y;
+       trans[11] = org2.z;
+       
+       return 0;
+}
+
+static void
+s_register_missing_parameters(Int **missing, Int *nmissing, Int type, Int t1, Int t2, Int t3, Int t4)
+{
+       Int *mp;
+       Int i;
+       for (i = 0, mp = *missing; i < *nmissing; i++, mp += 5) {
+               if (mp[0] == type && mp[1] == t1 && mp[2] == t2 && mp[3] == t3 && mp[4] == t4)
+                       return; /* Already registered */
+       }
+       mp = (Int *)AssignArray(missing, nmissing, sizeof(Int)*5, *nmissing, NULL);
+       if (mp != NULL) {
+               mp[0] = type;
+               mp[1] = t1;
+               mp[2] = t2;
+               mp[3] = t3;
+               mp[4] = t4;
+       }
+}
+
+/*  Check the bonded atoms and append to results if not already present */
+/*  results[] is terminated by -1, hence must be at least (natom+1) size  */
+static int
+s_check_bonded(Atom *ap, Int *results)
+{
+       int i, n, *ip;
+       for (i = 0; i < ap->nconnects; i++) {
+               n = ap->connects[i];
+               for (ip = results; *ip >= 0; ip++) {
+                       if (n == *ip)
+                               break;
+               }
+               if (*ip < 0) {
+                       *ip++ = n;
+                       *ip = -1;
+               }
+       }
+       for (ip = results; *ip >= 0; ip++)
+               ;
+       return ip - results;
+}
+
+static void
+s_make_exclusion_list(MDArena *arena)
+{
+       Int *results;
+       Int natoms = arena->mol->natoms;
+       Atom *atoms = arena->mol->atoms;
+       MDExclusion *exinfo;
+       int next_index, i, j;
+
+       results = (Int *)calloc(sizeof(Int), natoms + 1);
+       if (results == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+
+       if (arena->exlist != NULL) {
+               free(arena->exlist);
+               arena->exlist = NULL;
+       }
+       arena->nexlist = 0;
+
+       if (arena->exinfo != NULL)
+               free(arena->exinfo);
+       arena->exinfo = (MDExclusion *)calloc(sizeof(MDExclusion), natoms + 1);
+       if (arena->exinfo == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       exinfo = arena->exinfo;
+
+       next_index = 0;
+       for (i = 0; i < natoms; i++) {
+               int n;
+               exinfo[i].index0 = 0;
+               /* special exclusion: only self */
+               results[0] = i;
+               results[1] = -1;
+               exinfo[i].index1 = 1;
+               /*  1-2 exclusion (directly bonded)  */
+               exinfo[i].index2 = s_check_bonded(&atoms[i], results);
+               n = exinfo[i].index2;
+               /*  1-3 exclusion: atoms bonded to 1-2 exclusions  */
+               for (j = exinfo[i].index1; j < exinfo[i].index2; j++)
+                       n = s_check_bonded(&atoms[results[j]], results);
+               exinfo[i].index3 = n;
+               /*  1-4 exclusion: atoms bonded to 1-3 exclusions  */
+               for (j = exinfo[i].index2; j < exinfo[i].index3; j++)
+                       n = s_check_bonded(&atoms[results[j]], results);
+               AssignArray(&arena->exlist, &arena->nexlist, sizeof(Int), next_index + n, NULL);
+               memcpy(arena->exlist + next_index, results, n * sizeof(Int));
+               exinfo[i].index0 += next_index;
+               exinfo[i].index1 += next_index;
+               exinfo[i].index2 += next_index;
+               exinfo[i].index3 += next_index;
+               next_index += n;
+       }
+       exinfo[natoms].index0 = next_index;  /*  End of exlist  */
+       
+       free(results);
+}
+
+static int
+s_lookup_improper_pars(Parameter *par, Int type1, Int type2, Int type3, Int type4)
+{
+       Int idx;
+       Int t1, t2, t3, t4;
+       for (idx = par->nimproperPars - 1; idx >= 0; idx--) {
+               t1 = par->improperPars[idx].type1;
+               t2 = par->improperPars[idx].type2;
+               t3 = par->improperPars[idx].type3;
+               t4 = par->improperPars[idx].type4;
+               if (t1 == -3)
+                       continue;  /*  Custom parameter  */
+               if (t3 >= 0 && t3 != type3)
+                       continue;
+               if ((t1 < 0 || t1 == type1) &&
+                       (t2 < 0 || t2 == type2) &&
+                       (t4 < 0 || t4 == type4))
+                       break;
+               if ((t1 < 0 || t1 == type1) &&
+                       (t2 < 0 || t2 == type4) &&
+                       (t4 < 0 || t4 == type2))
+                       break;
+               if ((t1 < 0 || t1 == type2) &&
+                       (t2 < 0 || t2 == type1) &&
+                       (t4 < 0 || t4 == type4))
+                       break;
+               if ((t1 < 0 || t1 == type2) &&
+                       (t2 < 0 || t2 == type4) &&
+                       (t4 < 0 || t4 == type1))
+                       break;
+               if ((t1 < 0 || t1 == type4) &&
+                       (t2 < 0 || t2 == type1) &&
+                       (t4 < 0 || t4 == type2))
+                       break;
+               if ((t1 < 0 || t1 == type4) &&
+                       (t2 < 0 || t2 == type2) &&
+                       (t4 < 0 || t4 == type1))
+                       break;
+       }
+       return idx;
+}
+
+int
+md_check_abnormal_bond(MDArena *arena, Molecule *mol, int idx)
+{
+       BondPar *bp;
+       Atom *ap1, *ap2;
+       Int idx2;
+       Double d;
+       if (mol == NULL)
+               mol = arena->mol;
+       if (arena->par == NULL || idx < 0 || idx >= mol->nbonds || (idx2 = arena->bond_par_i[idx]) < 0 || idx2 >= arena->par->nbondPars)
+               return -1;
+       ap1 = ATOM_AT_INDEX(mol->atoms, mol->bonds[idx * 2]);
+       ap2 = ATOM_AT_INDEX(mol->atoms, mol->bonds[idx * 2 + 1]);
+       bp = arena->par->bondPars + idx2;
+       if (bp->k == 0.0 || bp->r0 == 0.0)
+               return 0;
+       d = MoleculeMeasureBond(mol, &(ap1->r), &(ap2->r));
+       return (fabs(d / bp->r0 - 1.0) >= 0.2);
+}
+
+int
+md_check_abnormal_angle(MDArena *arena, Molecule *mol, int idx)
+{
+       AnglePar *anp;
+       Atom *ap1, *ap2, *ap3;
+       Int idx2;
+       Double d;
+       if (mol == NULL)
+               mol = arena->mol;
+       if (arena->par == NULL || idx < 0 || idx >= mol->nangles || (idx2 = arena->angle_par_i[idx]) < 0 || idx2 >= arena->par->nanglePars)
+               return -1;
+       ap1 = ATOM_AT_INDEX(mol->atoms, mol->angles[idx * 3]);
+       ap2 = ATOM_AT_INDEX(mol->atoms, mol->angles[idx * 3 + 1]);
+       ap3 = ATOM_AT_INDEX(mol->atoms, mol->angles[idx * 3 + 2]);
+       anp = arena->par->anglePars + idx2;
+       if (anp->k == 0.0 || anp->a0 == 0.0)
+               return 0;
+       d = MoleculeMeasureAngle(mol, &(ap1->r), &(ap2->r), &(ap3->r));
+       return (fabs(d - anp->a0 * kRad2Deg) >= 20.0);
+}
+
+int
+md_check_abnormal_dihedral(MDArena *arena, Molecule *mol, int idx)
+{
+       TorsionPar *tp;
+       Atom *ap1, *ap2, *ap3, *ap4;
+       Int idx2;
+       Double d;
+       if (mol == NULL)
+               mol = arena->mol;
+       if (arena->par == NULL || idx < 0 || idx >= mol->ndihedrals || (idx2 = arena->dihedral_par_i[idx]) < 0 || idx2 >= arena->par->ndihedralPars)
+               return -1;
+       ap1 = ATOM_AT_INDEX(mol->atoms, mol->dihedrals[idx * 4]);
+       ap2 = ATOM_AT_INDEX(mol->atoms, mol->dihedrals[idx * 4 + 1]);
+       ap3 = ATOM_AT_INDEX(mol->atoms, mol->dihedrals[idx * 4 + 2]);
+       ap4 = ATOM_AT_INDEX(mol->atoms, mol->dihedrals[idx * 4 + 3]);
+       tp = arena->par->dihedralPars + idx2;
+       if (tp->k[0] == 0.0)
+               return 0;
+       d = MoleculeMeasureDihedral(mol, &(ap1->r), &(ap2->r), &(ap3->r), &(ap4->r));
+       if (tp->period[0] == 0)
+               return (fabs(d - tp->phi0[0] * kRad2Deg) >= 20.0);
+       else {
+               d = (tp->period[0] * (d - tp->phi0[0] * kRad2Deg)) / 360.0 + 0.5;
+               d = (d - floor(d) - 0.5) * 360.0; /* map to [-180, 180] */
+               return (fabs(d) >= 20.0);
+       }
+}
+
+int
+md_check_abnormal_improper(MDArena *arena, Molecule *mol, int idx)
+{
+       TorsionPar *tp;
+       Atom *ap1, *ap2, *ap3, *ap4;
+       Int idx2;
+       Double d;
+       if (mol == NULL)
+               mol = arena->mol;
+       if (arena->par == NULL || idx < 0 || idx >= mol->ndihedrals || (idx2 = arena->improper_par_i[idx]) < 0 || idx2 >= arena->par->nimproperPars)
+               return -1;
+       ap1 = ATOM_AT_INDEX(mol->atoms, mol->impropers[idx * 4]);
+       ap2 = ATOM_AT_INDEX(mol->atoms, mol->impropers[idx * 4 + 1]);
+       ap3 = ATOM_AT_INDEX(mol->atoms, mol->impropers[idx * 4 + 2]);
+       ap4 = ATOM_AT_INDEX(mol->atoms, mol->impropers[idx * 4 + 3]);
+       tp = arena->par->improperPars + idx2;
+       if (tp->k[0] == 0.0)
+               return 0;
+       d = MoleculeMeasureDihedral(mol, &(ap1->r), &(ap2->r), &(ap3->r), &(ap4->r));
+       if (tp->period[0] == 0)
+               return (fabs(d - tp->phi0[0] * kRad2Deg) >= 20.0);
+       else {
+               d = (tp->period[0] * (d - tp->phi0[0] * kRad2Deg)) / 360.0 + 0.5;
+               d = (d - floor(d) - 0.5) * 360.0; /* map to [-180, 180] */
+               return (fabs(d) >= 20.0);
+       }
+}
+
+static int
+s_search_bond(MDArena *arena, int n1, int n2)
+{
+       int i, *ip;
+       if (n1 < 0 || n1 >= arena->mol->natoms || n2 < 0 || n2 >= arena->mol->natoms)
+               return -1;
+       for (i = 0, ip = arena->mol->bonds; i < arena->mol->nbonds; i++, ip += 2) {
+               if ((ip[0] == n1 && ip[1] == n2) || (ip[0] == n2 && ip[1] == n1))
+                       return i;
+       }
+       return -1;
+}
+
+static int
+s_search_angle(MDArena *arena, int n1, int n2, int n3)
+{
+       int i, *ip;
+       if (n1 < 0 || n1 >= arena->mol->natoms || n2 < 0 || n2 >= arena->mol->natoms || n3 < 0 || n3 >= arena->mol->natoms)
+               return -1;
+       for (i = 0, ip = arena->mol->angles; i < arena->mol->nangles; i++, ip += 3) {
+               if (ip[1] == n2 && ((ip[0] == n1 && ip[2] == n3) || (ip[0] == n3 && ip[2] == n1)))
+                       return i;
+       }
+       return -1;
+}
+
+static int
+s_search_dihedral(MDArena *arena, int n1, int n2, int n3, int n4)
+{
+       int i, *ip;
+       if (n1 < 0 || n1 >= arena->mol->natoms || n2 < 0 || n2 >= arena->mol->natoms || n3 < 0 || n3 >= arena->mol->natoms || n4 < 0 || n4 >= arena->mol->natoms)
+               return -1;
+       for (i = 0, ip = arena->mol->dihedrals; i < arena->mol->ndihedrals; i++, ip += 4) {
+               if ((ip[0] == n1 && ip[1] == n2 && ip[2] == n3 && ip[3] == n4) || (ip[0] == n4 && ip[1] == n3 && ip[2] == n2 && ip[3] == n1))
+                       return i;
+       }
+       return -1;
+}
+
+static int
+s_search_improper(MDArena *arena, int n1, int n2, int n3, int n4)
+{
+       int i, *ip;
+       if (n1 < 0 || n1 >= arena->mol->natoms || n2 < 0 || n2 >= arena->mol->natoms || n3 < 0 || n3 >= arena->mol->natoms || n4 < 0 || n4 >= arena->mol->natoms)
+               return -1;
+       for (i = 0, ip = arena->mol->impropers; i < arena->mol->nimpropers; i++, ip += 4) {
+               if ((ip[0] == n1 && ip[1] == n2 && ip[2] == n3 && ip[3] == n4) || (ip[0] == n4 && ip[1] == n3 && ip[2] == n2 && ip[3] == n1))
+                       return i;
+       }
+       return -1;
+}
+
+/*  Find vdw parameters and build the in-use list */
+static int
+s_find_vdw_parameters(MDArena *arena)
+{
+       Int idx, i, j, type1, type2, t1, t2, nmissing;
+       Double cutoff6, cutoff12;
+       Parameter *par = arena->par;
+       Molecule *mol = arena->mol;
+       Atom *ap;
+       VdwPar *vp;
+
+       if (arena->vdw_par_i != NULL)
+               free(arena->vdw_par_i);
+       arena->vdw_par_i = (Int *)calloc(sizeof(Int), mol->natoms);
+       if (arena->vdw_par_i == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+
+       /*  Find the vdw parameter; priority: (1) variant-aware in mol->par, (2) mol->par ignoring variants,
+           (3) variant->aware in global par, (4) global par ignoring variants  */
+       for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (mol->par != NULL) {
+                       vp = ParameterLookupVdwPar(mol->par, ap->type, 0);
+               /*      vp = ParameterLookupVdwPar(mol->par, i, kParameterLookupLocal | kParameterLookupNoWildcard);
+                       if (vp == NULL)
+                               vp = ParameterLookupVdwPar(mol->par, ap->type, kParameterLookupLocal | kParameterLookupGlobal); */
+                       if (vp != NULL) {
+                               arena->vdw_par_i[i] = (vp - mol->par->vdwPars) + ATOMS_MAX_NUMBER * 2;
+                               continue;
+                       }
+               }
+               vp = ParameterLookupVdwPar(gBuiltinParameters, ap->type, 0);
+               if (vp != NULL) {
+                       arena->vdw_par_i[i] = (vp - gBuiltinParameters->vdwPars) + ATOMS_MAX_NUMBER;
+                       continue;
+               }
+               /*  Record as missing  */
+               vp = ParameterLookupVdwPar(par, ap->type, kParameterLookupMissing | kParameterLookupNoBaseAtomType);
+               if (vp == NULL) {
+                       vp = AssignArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), par->nvdwPars, NULL);
+                       vp->src = -1;
+                       vp->type1 = ap->type;
+               }
+               arena->vdw_par_i[i] = (vp - par->vdwPars);
+       }
+       nmissing = par->nvdwPars;
+
+       /*  Copy the vdw parameters  */
+       /*  Atoms with the same vdw parameters point to one common entry  */
+       /*  Except when the atom appears in one of the pair-specific vdw parameters with
+           atom index specification; in that case, that atom is given a separate entry  */
+       for (i = 0, idx = par->nvdwPars; i < mol->natoms; i++) {
+               t1 = arena->vdw_par_i[i];
+               if (t1 < ATOMS_MAX_NUMBER)
+                       continue;
+               arena->vdw_par_i[i] = idx;
+               if (mol->par != NULL) {
+                       /*  Look up the pair-specific vdw parameters with atom index specification  */
+                       for (j = mol->par->nvdwpPars - 1; j >= 0; j--) {
+                               VdwPairPar *vpp = mol->par->vdwpPars + j;
+                               if (vpp->type1 == i || vpp->type2 == i)
+                                       break;
+                       }
+               } else j = -1;
+               if (j < 0) {
+                       /*  Not found: all other entries with the same vdw parameters share the entry  */
+                       for (j = i + 1; j < mol->natoms; j++) {
+                               if (arena->vdw_par_i[j] == t1)
+                                       arena->vdw_par_i[j] = idx;
+                       }
+               }
+               if (t1 >= ATOMS_MAX_NUMBER * 2)
+                       vp = mol->par->vdwPars + (t1 - ATOMS_MAX_NUMBER * 2);
+               else vp = gBuiltinParameters->vdwPars + (t1 - ATOMS_MAX_NUMBER);
+               AssignArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), idx, vp);
+               idx++;
+       }
+       
+       /*  Debug output  */
+       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+               fprintf(arena->debug_result, "\n  Atom van der Waals parameters\n");
+               fprintf(arena->debug_result, "  No. type vdw_i   sigma    eps    sigma14  eps14\n");
+               for (i = 0; i < mol->natoms; i++) {
+                       char s[8];
+                       idx = arena->vdw_par_i[i];
+                       fprintf(arena->debug_result, "%5d %-4s %5d ", i+1, AtomTypeDecodeToString(mol->atoms[i].type, s), idx);
+                       if (idx >= 0) {
+                               Double sigma, eps, sigma2, eps2;
+                               sigma = par->vdwPars[idx].A;
+                               eps = par->vdwPars[idx].B;
+                               if (sigma != 0.0) {
+                                       eps = 0.25 * eps * eps / sigma;
+                                       sigma = pow(sigma / eps * 0.25, 1.0/12.0);
+                               } else {
+                                       eps = sigma = 0.0;
+                               }
+                               sigma2 = par->vdwPars[idx].A14;
+                               eps2 = par->vdwPars[idx].B14;
+                               if (sigma2 != 0.0) {
+                                       eps2 = 0.25 * eps2 * eps2 / sigma2;
+                                       sigma2 = pow(sigma2 / eps2 * 0.25, 1.0/12.0);
+                               } else {
+                                       eps2 = sigma2 = 0.0;
+                               }
+                               fprintf(arena->debug_result, " %7.3f %7.3f %7.3f %7.3f\n", sigma, eps*INTERNAL2KCAL, sigma2, eps2*INTERNAL2KCAL);
+                       } else {
+                               fprintf(arena->debug_result, " (not available)\n");
+                       }
+               }
+       }
+
+       /*  Build cache  */
+       if (arena->vdw_cache != NULL)
+               free(arena->vdw_cache);
+       arena->vdw_cache = (MDVdwCache *)calloc(sizeof(MDVdwCache), par->nvdwPars * par->nvdwPars + 1);
+       if (arena->vdw_cache == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       cutoff6 = arena->cutoff * arena->cutoff;
+       cutoff6 = 1.0 / (cutoff6 * cutoff6 * cutoff6);
+       cutoff12 = cutoff6 * cutoff6;
+       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+               fprintf(arena->debug_result, "\n  van der Waals parameter cache table\n");
+               fprintf(arena->debug_result, " idx1  idx2  type1 type2  sigma    eps    sigma14  eps14\n");
+       }
+       for (i = 0; i < par->nvdwPars; i++) {
+               t1 = arena->vdw_par_i[i];
+               type1 = par->vdwPars[t1].type1;
+               for (j = i; j < par->nvdwPars; j++) {
+                       VdwPairPar newpar, *vpp;
+                       Double sigma1, sigma2, eps1, eps2;
+                       vpp = NULL;
+                       t2 = arena->vdw_par_i[j];
+                       type2 = par->vdwPars[t2].type1;  /*  Not type2  */
+                       if (i != j) {
+                               /*  Look up the pair-specific van der Waals parameters  */
+                               if (mol->par != NULL) {
+                                       vpp = ParameterLookupVdwPairPar(mol->par, type1, type2, 0);
+                               }
+                               if (vpp == NULL)
+                                       vpp = ParameterLookupVdwPairPar(gBuiltinParameters, type1, type2, 0);
+                       }
+                       if (vpp != NULL) {
+                               newpar = *vpp;
+                       } else {
+                               /*  Create a pair-specific VdwPar for this pair  */
+                               VdwPar *p1, *p2;
+                               p1 = par->vdwPars + i;
+                               p2 = par->vdwPars + j;
+                               if (p1->A < 1e-15 && p1->A > -1e-15) {
+                                       sigma1 = 1.0;
+                                       eps1 = 0.0;
+                               } else {
+                                       eps1 = 0.25 * p1->B * p1->B / p1->A;
+                                       sigma1 = pow(p1->B * 0.25 / eps1, 1.0/6.0);
+                               }
+                               if (p2->A < 1e-15 && p2->A > -1e-15) {
+                                       sigma2 = 1.0;
+                                       eps2 = 0.0;
+                               } else {
+                                       eps2 = 0.25 * p2->B * p2->B / p2->A;
+                                       sigma2 = pow(p2->B * 0.25 / eps2, 1.0/6.0);
+                               }
+                               sigma1 = (sigma1 + sigma2) * 0.5;
+                               eps1 = sqrt(eps1 * eps2);
+                               sigma1 = sigma1 * sigma1 * sigma1;
+                               sigma1 = sigma1 * sigma1;
+                               newpar.B = 4.0 * sigma1 * eps1;
+                               newpar.A = newpar.B * sigma1;
+                               if (p1->A14 < 1e-15 && p1->A14 > -1e-15) {
+                                       sigma1 = 1.0;
+                                       eps1 = 0.0;
+                               } else {
+                                       eps1 = 0.25 * p1->B14 * p1->B14 / p1->A14;
+                                       sigma1 = pow(p1->B14 * 0.25 / eps1, 1.0/6.0);
+                               }
+                               if (p2->A14 < 1e-15 && p2->A14 > -1e-15) {
+                                       sigma2 = 1.0;
+                                       eps2 = 0.0;
+                               } else {
+                                       eps2 = 0.25 * p2->B14 * p2->B14 / p2->A14;
+                                       sigma2 = pow(p2->B14 * 0.25 / eps2, 1.0/6.0);
+                               }
+                               sigma1 = (sigma1 + sigma2) * 0.5;
+                               eps1 = sqrt(eps1 * eps2);
+                               sigma1 = sigma1 * sigma1 * sigma1;
+                               sigma1 = sigma1 * sigma1;
+                               newpar.B14 = 4.0 * sigma1 * eps1;
+                               newpar.A14 = newpar.B14 * sigma1;
+                               newpar.type1 = type1;
+                               newpar.type2 = type2;
+                       }
+                       idx = i * par->nvdwPars + j;
+                       arena->vdw_cache[idx].par = newpar;
+                       /*  Cache the value at the cutoff distance  */
+                       arena->vdw_cache[idx].vcut = newpar.A * cutoff12 - newpar.B * cutoff6;
+                       arena->vdw_cache[idx].vcut14 = newpar.A14 * cutoff12 - newpar.B * cutoff6;
+                       arena->vdw_cache[j * par->nvdwPars + i] = arena->vdw_cache[idx];
+                       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+                               char s1[8], s2[8];
+                               eps1 = 0.25 * newpar.B * newpar.B / newpar.A;
+                               sigma1 = pow(newpar.B * 0.25 / eps1, 1.0/6.0);
+                               eps2 = 0.25 * newpar.B14 * newpar.B14 / newpar.A14;
+                               sigma2 = pow(newpar.B14 * 0.25 / eps2, 1.0/6.0);                                
+                               fprintf(arena->debug_result, "%5d %5d %-4s  %-4s  %7.3f %7.3f %7.3f %7.3f\n", idx, i, AtomTypeDecodeToString(type1, s1), AtomTypeDecodeToString(type2, s2), sigma1, eps1*INTERNAL2KCAL, sigma2, eps2*INTERNAL2KCAL);
+                       }
+               } /* end loop j */
+       } /* end loop i */
+       arena->nmissing += nmissing;
+       return nmissing;
+}
+
+/*  Find bond parameters  */
+static int
+s_find_bond_parameters(MDArena *arena)
+{
+       Int i, j, idx, type1, type2, t1, nmissing = 0;
+       Molecule *mol = arena->mol;
+       Parameter *par = arena->par;
+       BondPar *bp;
+
+       if (mol->nbonds > 0) {
+               if (arena->bond_par_i != NULL)
+                       free(arena->bond_par_i);
+               arena->bond_par_i = (Int *)calloc(sizeof(Int), mol->nbonds);
+               if (arena->bond_par_i == NULL)
+                       md_panic(arena, ERROR_out_of_memory);
+               if (arena->anbond_r0 != NULL)
+                       free(arena->anbond_r0);
+               arena->anbond_r0 = (Double *)calloc(sizeof(Double), mol->nbonds);
+               if (arena->anbond_r0 == NULL)
+                       md_panic(arena, ERROR_out_of_memory);
+
+               /*  Find the bond parameter; priority: (1) atom index specific in mol->par, (2)
+                atom type specific in mol->par, (3) atom type specific in built-in  */
+               for (i = 0; i < mol->nbonds; i++) {
+                       Int i1, i2;
+                       Atom *ap1, *ap2;
+                       i1 = mol->bonds[i * 2];
+                       i2 = mol->bonds[i * 2 + 1];
+                       ap1 = ATOM_AT_INDEX(mol->atoms, i1);
+                       ap2 = ATOM_AT_INDEX(mol->atoms, i2);
+                       type1 = ap1->type;
+                       type2 = ap2->type;
+                       bp = NULL;
+                       if (mol->par != NULL) {
+                               bp = ParameterLookupBondPar(mol->par, type1, type2, 0);
+                               if (bp != NULL) {
+                                       arena->bond_par_i[i] = (bp - mol->par->bondPars) + ATOMS_MAX_NUMBER * 2;
+                                       continue;
+                               }
+                       }
+                       bp = ParameterLookupBondPar(gBuiltinParameters, type1, type2, 0);
+                       if (bp != NULL) {
+                               arena->bond_par_i[i] = (bp - gBuiltinParameters->bondPars) + ATOMS_MAX_NUMBER;
+                               continue;
+                       }
+                       bp = ParameterLookupBondPar(par, type1, type2, kParameterLookupMissing | kParameterLookupNoBaseAtomType);
+                       if (bp == NULL) {
+                               /*  Record as missing  */
+                               bp = AssignArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), par->nbondPars, NULL);
+                               bp->src = -1;
+                               bp->type1 = type1;
+                               bp->type2 = type2;
+                       }
+                       arena->bond_par_i[i] = (bp - par->bondPars);
+               }
+               nmissing = par->nbondPars;
+       
+               /*  Copy the bond parameters  */
+               for (i = 0, idx = par->nbondPars; i < mol->nbonds; i++) {
+                       t1 = arena->bond_par_i[i];
+                       if (t1 < ATOMS_MAX_NUMBER)
+                               continue;
+                       arena->bond_par_i[i] = idx;
+                       for (j = i + 1; j < mol->nbonds; j++) {
+                               if (arena->bond_par_i[j] == t1)
+                                       arena->bond_par_i[j] = idx;
+                       }
+                       if (t1 >= ATOMS_MAX_NUMBER * 2)
+                               bp = mol->par->bondPars + (t1 - ATOMS_MAX_NUMBER * 2);
+                       else bp = gBuiltinParameters->bondPars + (t1 - ATOMS_MAX_NUMBER);
+                       AssignArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), idx, bp);
+                       idx++;
+               }
+       }
+       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+               char s1[8], s2[8];
+               fprintf(arena->debug_result, "\n  Bond parameters\n");
+               fprintf(arena->debug_result, "  No. atom1 atom2 type1 type2     r0      k\n");
+               for (i = 0; i < mol->nbonds; i++) {
+                       idx = arena->bond_par_i[i];
+                       fprintf(arena->debug_result, "%5d %5d %5d ", i+1, mol->bonds[i*2]+1, mol->bonds[i*2+1]+1);
+                       if (idx < 0) {
+                               fprintf(arena->debug_result, "<< missing >>\n");
+                               continue;
+                       }
+                       fprintf(arena->debug_result, " %-4s  %-4s %7.3f %7.3f\n", AtomTypeDecodeToString(par->bondPars[idx].type1, s1), AtomTypeDecodeToString(par->bondPars[idx].type2, s2), par->bondPars[idx].r0, par->bondPars[idx].k*INTERNAL2KCAL);
+               }
+       }
+       arena->nmissing += nmissing;
+       return nmissing;
+}
+
+/*  Find angle parameters  */
+static int
+s_find_angle_parameters(MDArena *arena)
+{
+       Int i, j, idx, type1, type2, type3, t1, nmissing = 0;
+       Molecule *mol = arena->mol;
+       Parameter *par = arena->par;
+       AnglePar *ap;
+       
+       if (mol->nangles > 0) {
+               if (arena->angle_par_i != NULL)
+                       free(arena->angle_par_i);
+               arena->angle_par_i = (Int *)calloc(sizeof(Int), mol->nangles);
+               if (arena->angle_par_i == NULL)
+                       md_panic(arena, ERROR_out_of_memory);
+               
+               /*  Find the angle parameter; priority: (1) atom index specific in mol->par, (2)
+                atom type specific in mol->par, (3) atom type specific in built-in  */
+               for (i = 0; i < mol->nangles; i++) {
+                       Int i1, i2, i3;
+                       i1 = mol->angles[i * 3];
+                       i2 = mol->angles[i * 3 + 1];
+                       i3 = mol->angles[i * 3 + 2];
+                       type1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
+                       type2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
+                       type3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
+                       if (mol->par != NULL) {
+                       /*      ap = ParameterLookupAnglePar(mol->par, i1, i2, i3, kParameterLookupLocal | kParameterLookupNoWildcard);
+                               if (ap == NULL)
+                                       ap = ParameterLookupAnglePar(mol->par, type1, type2, type3, kParameterLookupLocal | kParameterLookupGlobal); */
+                               ap = ParameterLookupAnglePar(mol->par, type1, type2, type3, 0);
+                               if (ap != NULL) {
+                                       arena->angle_par_i[i] = (ap - mol->par->anglePars) + ATOMS_MAX_NUMBER * 2;
+                                       continue;
+                               }
+                       }
+                       ap = ParameterLookupAnglePar(gBuiltinParameters, type1, type2, type3, 0);
+                       if (ap != NULL) {
+                               arena->angle_par_i[i] = (ap - gBuiltinParameters->anglePars) + ATOMS_MAX_NUMBER;
+                               continue;
+                       }
+                       /*  Record as missing  */
+                       ap = ParameterLookupAnglePar(par, type1, type2, type3, kParameterLookupMissing | kParameterLookupNoBaseAtomType);
+                       if (ap == NULL) {
+                               ap = AssignArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), par->nanglePars, NULL);
+                               ap->src = -1;
+                               ap->type1 = type1;
+                               ap->type2 = type2;
+                               ap->type3 = type3;
+                       }
+                       arena->angle_par_i[i] = (ap - par->anglePars);
+               }
+               nmissing = par->nanglePars;
+               
+               /*  Copy the angle parameters  */
+               for (i = 0, idx = par->nanglePars; i < mol->nangles; i++) {
+                       t1 = arena->angle_par_i[i];
+                       if (t1 < ATOMS_MAX_NUMBER)
+                               continue;
+                       arena->angle_par_i[i] = idx;
+                       for (j = i + 1; j < mol->nangles; j++) {
+                               if (arena->angle_par_i[j] == t1)
+                                       arena->angle_par_i[j] = idx;
+                       }
+                       if (t1 >= ATOMS_MAX_NUMBER * 2)
+                               ap = mol->par->anglePars + (t1 - ATOMS_MAX_NUMBER * 2);
+                       else ap = gBuiltinParameters->anglePars + (t1 - ATOMS_MAX_NUMBER);
+                       AssignArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), idx, ap);
+                       idx++;
+               }
+       }
+       
+       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+               char s1[8], s2[8], s3[8];
+               fprintf(arena->debug_result, "\n  Angle parameters\n");
+               fprintf(arena->debug_result, "  No. atom1 atom2 atom3 type1 type2 type3      a0      k\n");
+               for (i = 0; i < mol->nangles; i++) {
+                       idx = arena->angle_par_i[i];
+                       fprintf(arena->debug_result, "%5d %5d %5d %5d ", i+1, mol->angles[i*3]+1, mol->angles[i*3+1]+1, mol->angles[i*3+2]+1);
+                       if (idx < 0) {
+                               fprintf(arena->debug_result, "<< missing >>\n");
+                               continue;
+                       }
+                       fprintf(arena->debug_result, " %-4s  %-4s  %-4s %7.3f %7.3f\n",  AtomTypeDecodeToString(par->anglePars[idx].type1, s1), AtomTypeDecodeToString(par->anglePars[idx].type2, s2), AtomTypeDecodeToString(par->anglePars[idx].type3, s3), par->anglePars[idx].a0 * 180.0 / 3.1415927, par->anglePars[idx].k*INTERNAL2KCAL);
+               }
+       }
+       arena->nmissing += nmissing;
+       return nmissing;
+}
+
+/*  Find dihedral parameters  */
+static int
+s_find_dihedral_parameters(MDArena *arena)
+{
+       Int i, j, idx, type1, type2, type3, type4, t1, nmissing = 0;
+       Molecule *mol = arena->mol;
+       Parameter *par = arena->par;
+       TorsionPar *tp;
+       
+       if (mol->ndihedrals > 0) {
+               if (arena->dihedral_par_i != NULL)
+                       free(arena->dihedral_par_i);
+               arena->dihedral_par_i = (Int *)calloc(sizeof(Int), mol->ndihedrals);
+               if (arena->dihedral_par_i == NULL)
+                       md_panic(arena, ERROR_out_of_memory);
+               
+               /*  Find the dihedral parameter; priority: (1) atom index specific in mol->par, (2)
+                atom type specific in mol->par, (3) atom type specific in built-in  */
+               for (i = 0; i < mol->ndihedrals; i++) {
+                       Int i1, i2, i3, i4;
+                       i1 = mol->dihedrals[i * 4];
+                       i2 = mol->dihedrals[i * 4 + 1];
+                       i3 = mol->dihedrals[i * 4 + 2];
+                       i4 = mol->dihedrals[i * 4 + 3];
+                       type1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
+                       type2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
+                       type3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
+                       type4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
+                       if (mol->par != NULL) {
+                       /*      tp = ParameterLookupDihedralPar(mol->par, i1, i2, i3, i4, kParameterLookupLocal | kParameterLookupNoWildcard);
+                               if (tp == NULL)
+                                       tp = ParameterLookupDihedralPar(mol->par, type1, type2, type3, type4, kParameterLookupLocal | kParameterLookupGlobal); */
+                               tp = ParameterLookupDihedralPar(mol->par, type1, type2, type3, type4, 0);
+                               if (tp != NULL) {
+                                       arena->dihedral_par_i[i] = (tp - mol->par->dihedralPars) + ATOMS_MAX_NUMBER * 2;
+                                       continue;
+                               }
+                       }
+                       tp = ParameterLookupDihedralPar(gBuiltinParameters, type1, type2, type3, type4, 0);
+                       if (tp != NULL) {
+                               arena->dihedral_par_i[i] = (tp - gBuiltinParameters->dihedralPars) + ATOMS_MAX_NUMBER;
+                               continue;
+                       }
+                       /*  Record as missing  */
+                       tp = ParameterLookupDihedralPar(par, type1, type2, type3, type4, kParameterLookupMissing | kParameterLookupNoBaseAtomType);
+                       if (tp == NULL) {
+                               tp = AssignArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), par->ndihedralPars, NULL);
+                               tp->src = -1;
+                               tp->type1 = type1;
+                               tp->type2 = type2;
+                               tp->type3 = type3;
+                               tp->type4 = type4;
+                       }
+                       arena->dihedral_par_i[i] = (tp - par->dihedralPars);
+               }
+               nmissing = par->ndihedralPars;
+       
+               /*  Copy the dihedral parameters  */
+               for (i = 0, idx = par->ndihedralPars; i < mol->ndihedrals; i++) {
+                       t1 = arena->dihedral_par_i[i];
+                       if (t1 < ATOMS_MAX_NUMBER)
+                               continue;
+                       arena->dihedral_par_i[i] = idx;
+                       for (j = i + 1; j < mol->ndihedrals; j++) {
+                               if (arena->dihedral_par_i[j] == t1)
+                                       arena->dihedral_par_i[j] = idx;
+                       }
+                       if (t1 >= ATOMS_MAX_NUMBER * 2)
+                               tp = mol->par->dihedralPars + (t1 - ATOMS_MAX_NUMBER * 2);
+                       else tp = gBuiltinParameters->dihedralPars + (t1 - ATOMS_MAX_NUMBER);
+                       AssignArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), idx, tp);
+                       idx++;
+               }
+       }
+       
+       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+               Int j;
+               char s1[8], s2[8], s3[8], s4[8];
+               fprintf(arena->debug_result, "\n  Dihedral parameters\n");
+               fprintf(arena->debug_result, "  No. atom1 atom2 atom3 atom4 type1 type2 type3 type4 mult  phi0      k     per\n");
+               for (i = 0; i < mol->ndihedrals; i++) {
+                       idx = arena->dihedral_par_i[i];
+                       fprintf(arena->debug_result, "%5d %5d %5d %5d %5d ", i+1, mol->dihedrals[i*4]+1, mol->dihedrals[i*4+1]+1, mol->dihedrals[i*4+2]+1, mol->dihedrals[i*4+3]+1);
+                       if (idx < 0) {
+                               fprintf(arena->debug_result, "<< missing >>\n");
+                               continue;
+                       }
+                       fprintf(arena->debug_result, "%-4s  %-4s  %-4s  %-4s  %2d ", AtomTypeDecodeToString(par->dihedralPars[idx].type1, s1), AtomTypeDecodeToString(par->dihedralPars[idx].type2, s2), AtomTypeDecodeToString(par->dihedralPars[idx].type3, s3), AtomTypeDecodeToString(par->dihedralPars[idx].type4, s4), par->dihedralPars[idx].mult);
+                       for (j = 0; j < par->dihedralPars[idx].mult; j++) {
+                               fprintf(arena->debug_result, "%7.3f %7.3f %1d ", par->dihedralPars[idx].phi0[j]*180/PI, par->dihedralPars[idx].k[j]*INTERNAL2KCAL, par->dihedralPars[idx].period[j]);
+                       }
+                       fprintf(arena->debug_result, "\n");
+               }
+       }
+       arena->nmissing += nmissing;
+       return nmissing;
+}
+
+/*  Find improper parameters  */
+static int
+s_find_improper_parameters(MDArena *arena)
+{
+       Int i, j, idx, type1, type2, type3, type4, t1, nmissing = 0;
+       Molecule *mol = arena->mol;
+       Parameter *par = arena->par;
+       TorsionPar *tp;
+       
+       if (mol->nimpropers > 0) {
+               if (arena->improper_par_i != NULL)
+                       free(arena->improper_par_i);
+               arena->improper_par_i = (Int *)calloc(sizeof(Int), mol->nimpropers);
+               if (arena->improper_par_i == NULL)
+                       md_panic(arena, ERROR_out_of_memory);
+               
+               /*  Find the improper parameter; priority: (1) atom index specific in mol->par, (2)
+                atom type specific in mol->par, (3) atom type specific in built-in  */
+               for (i = 0; i < mol->nimpropers; i++) {
+                       Int i1, i2, i3, i4;
+                       i1 = mol->impropers[i * 4];
+                       i2 = mol->impropers[i * 4 + 1];
+                       i3 = mol->impropers[i * 4 + 2];
+                       i4 = mol->impropers[i * 4 + 3];
+                       type1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
+                       type2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
+                       type3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
+                       type4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
+                       if (mol->par != NULL) {
+                       /*      tp = ParameterLookupImproperPar(mol->par, i1, i2, i3, i4, kParameterLookupLocal | kParameterLookupNoWildcard);
+                               if (tp == NULL)
+                                       tp = ParameterLookupImproperPar(mol->par, type1, type2, type3, type4, kParameterLookupLocal | kParameterLookupGlobal); */
+                               tp = ParameterLookupImproperPar(mol->par, type1, type2, type3, type4, 0);
+                               if (tp != NULL) {
+                                       arena->improper_par_i[i] = (tp - mol->par->improperPars) + ATOMS_MAX_NUMBER * 2;
+                                       continue;
+                               }
+                       }
+                       tp = ParameterLookupImproperPar(gBuiltinParameters, type1, type2, type3, type4, 0);
+                       if (tp != NULL) {
+                               arena->improper_par_i[i] = (tp - gBuiltinParameters->improperPars) + ATOMS_MAX_NUMBER;
+                               continue;
+                       }
+                       /*  Record as missing  */
+                       tp = ParameterLookupImproperPar(par, type1, type2, type3, type4, kParameterLookupMissing | kParameterLookupNoBaseAtomType);
+                       if (tp == NULL) {
+                               tp = AssignArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), par->nimproperPars, NULL);
+                               tp->src = -1;
+                               tp->type1 = type1;
+                               tp->type2 = type2;
+                               tp->type3 = type3;
+                               tp->type4 = type4;
+                       }
+                       arena->improper_par_i[i] = (tp - par->improperPars);
+               }
+               nmissing = par->nimproperPars;
+       
+               /*  Copy the improper parameters  */
+               for (i = 0, idx = par->nimproperPars; i < mol->nimpropers; i++) {
+                       t1 = arena->improper_par_i[i];
+                       if (t1 < ATOMS_MAX_NUMBER)
+                               continue;
+                       arena->improper_par_i[i] = idx;
+                       for (j = i + 1; j < mol->nimpropers; j++) {
+                               if (arena->improper_par_i[j] == t1)
+                                       arena->improper_par_i[j] = idx;
+                       }
+                       if (t1 >= ATOMS_MAX_NUMBER * 2)
+                               tp = mol->par->improperPars + (t1 - ATOMS_MAX_NUMBER * 2);
+                       else tp = gBuiltinParameters->improperPars + (t1 - ATOMS_MAX_NUMBER);
+                       AssignArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), idx, tp);
+                       idx++;
+               }
+       }
+       
+       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+               Int j;
+               char s1[8], s2[8], s3[8], s4[8];
+               fprintf(arena->debug_result, "\n  Improper parameters\n");
+               fprintf(arena->debug_result, "  No. atom1 atom2 atom3 atom4 type1 type2 type3 type4 mult  phi0      k   per\n");
+               for (i = 0; i < mol->nimpropers; i++) {
+                       idx = arena->improper_par_i[i];
+                       fprintf(arena->debug_result, "%5d %5d %5d %5d %5d ", i+1, mol->impropers[i*4]+1, mol->impropers[i*4+1]+1, mol->impropers[i*4+2]+1, mol->impropers[i*4+3]+1);
+                       if (idx < 0) {
+                               fprintf(arena->debug_result, "<< missing >>\n");
+                               continue;
+                       }
+                       fprintf(arena->debug_result, "%-4s  %-4s  %-4s  %-4s  %2d ", AtomTypeDecodeToString(par->improperPars[idx].type1, s1), AtomTypeDecodeToString(par->improperPars[idx].type2, s2), AtomTypeDecodeToString(par->improperPars[idx].type3, s3), AtomTypeDecodeToString(par->improperPars[idx].type4, s4), par->improperPars[idx].mult);
+                       for (j = 0; j < par->improperPars[idx].mult; j++) {
+                               fprintf(arena->debug_result, "%7.3f %7.3f %1d", par->improperPars[idx].phi0[j]*180/PI, par->improperPars[idx].k[j]*INTERNAL2KCAL, par->improperPars[idx].period[j]);
+                       }
+                       fprintf(arena->debug_result, "\n");
+               }
+       }       
+       arena->nmissing += nmissing;
+       return nmissing;
+}
+
+/*  Find one fragment, starting from start_index  */
+static void
+s_find_fragment_sub(MDArena *arena, Int start_index, Int fragment_index)
+{
+       Atom *atoms = arena->mol->atoms;
+       int i, j;
+       for (i = 0; i < atoms[start_index].nconnects; i++) {
+               j = atoms[start_index].connects[i];
+               if (j >= 0 && j < arena->natoms_uniq) {
+                       int n = arena->fragment_indices[j];
+                       if (n < 0) {
+                               arena->fragment_indices[j] = fragment_index;
+                               s_find_fragment_sub(arena, j, fragment_index);
+                       } else if (n != fragment_index) {
+                               if (arena->log_result != NULL)
+                                       fprintf(arena->log_result, "Warning: internal inconsistency in finding fragment at atom %d\n", j + 1);
+                       }
+               }
+       }
+}
+
+/*  Find fragments  */
+void
+md_find_fragments(MDArena *arena)
+{
+       int i, idx, nuniq;
+       if (arena->fragment_indices != NULL)
+               free(arena->fragment_indices);
+       arena->fragment_indices = (Int *)malloc(sizeof(Int) * arena->natoms_uniq);
+       if (arena->fragment_indices == NULL)
+               md_panic(arena, "Low memory in md_find_fragments");
+       nuniq = arena->natoms_uniq;
+       for (i = 0; i < nuniq; i++)
+               arena->fragment_indices[i] = -1;
+       idx = 0;
+       for (i = 0; i < nuniq; i++) {
+               if (arena->fragment_indices[i] >= 0)
+                       continue;
+               s_find_fragment_sub(arena, i, idx);
+               idx++;
+       }
+       arena->nfragments = idx;
+       if (arena->fragment_info != NULL)
+               free(arena->fragment_info);
+       arena->fragment_info = (struct MDFragmentInfo *)calloc(sizeof(struct MDFragmentInfo), idx);
+       if (arena->fragment_info == NULL)
+               md_panic(arena, "Low memory in md_find_fragments");
+}
+
+/*  Calculate center-of-mass  */
+void
+md_center_of_mass(MDArena *arena, Vector *cp)
+{
+       int i, n;
+       Atom *ap;
+       Double sumw;
+       VecZero(*cp);
+       sumw = 0.0;
+       n = arena->mol->natoms;
+       for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+               VecScaleInc(*cp, ap->r, ap->weight);
+               sumw += ap->weight;
+       }
+       VecScaleSelf(*cp, 1.0 / sumw);
+}
+
+/*  Relocate center-of-mass  */
+int
+md_relocate_center(MDArena *arena)
+{
+       int i, n;
+       Atom *ap;
+       Vector cm;
+       n = arena->mol->natoms;
+       md_center_of_mass(arena, &cm);
+       VecDec(cm, arena->initial_center);
+       for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+               VecDec(ap->r, cm);
+       }
+       return 0;
+}
+
+/*  Quench translational and rotational momenta  */
+int
+md_quench_momenta(MDArena *arena)
+{
+       int i, n;
+       Atom *ap;
+       Vector am, cm, m, v, v1, v2;
+       Double w, sumw, kinetic;
+       Mat33 mi;
+       
+       if (!arena->quench_angular_momentum && !arena->quench_translational_momentum)
+               return 0;
+
+       n = arena->mol->natoms;
+
+       /*  Center of mass  */
+       md_center_of_mass(arena, &cm);
+       
+       /*  Initial kinetic energy (times 2)  */
+       kinetic = 0.0;
+       for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+               kinetic += ap->weight * VecLength2(ap->v);
+       }
+
+       /*  Quench the angular momentum  */
+       /*  Calculate the total angular momentum  */
+       
+       if (arena->quench_angular_momentum) {
+               VecZero(am);
+               for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+                       VecSub(v, ap->r, cm);
+                       VecCross(v1, v, ap->v);
+                       VecScaleInc(am, v1, ap->weight);
+               }
+               /*  Moment of inertia  */
+               memset(mi, 0, sizeof(mi));
+               for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+                       Double x = ap->r.x - cm.x;
+                       Double y = ap->r.y - cm.y;
+                       Double z = ap->r.z - cm.z;
+                       w = ap->weight;
+                       mi[0] += w * (y * y + z * z);
+                       mi[4] += w * (z * z + x * x);
+                       mi[8] += w * (x * x + y * y);
+                       mi[1] -= w * x * y;
+                       mi[5] -= w * y * z;
+                       mi[2] -= w * z * x;
+               }
+               mi[3] = mi[1];
+               mi[6] = mi[2];
+               mi[7] = mi[5];
+               /*  Calculate the angular velocity as a rigid body  */
+               /*  I * w = L; I, moment of inertia; w, angular velocity; L, angular momentum  */
+               MatrixInvert(mi, mi);
+               MatrixVec(&v, mi, &am);
+               /*  Subtract the velocity at the atom position derived from the angular velocity  */
+               for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+                       VecSub(v1, ap->r, cm);
+                       VecCross(v2, v, v1);
+                       VecDec(ap->v, v2);
+               }
+       }
+       
+       /*  Quench the translational momentum  */
+       if (arena->quench_translational_momentum) {
+               VecZero(m);
+               sumw = 0.0;
+               for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+                       VecScaleInc(m, ap->v, ap->weight);
+                       sumw += ap->weight;
+               }
+               VecScaleSelf(m, 1.0 / sumw);
+               for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++)
+                       VecDec(ap->v, m);
+       }
+
+       /*  Current kinetic energy (times 2) */
+       w = 0.0;
+       for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+               w += ap->weight * VecLength2(ap->v);
+       }
+       w = sqrt(kinetic / w);
+       
+       /*  Scale the velocities to keep the kinetic energy  */
+       for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+               VecScaleSelf(ap->v, w);
+       }
+       return 0;
+}
+
+/*  Initilize the runtime fields that are affected by the atom positions  */
+/*  (Should be called after coordinates are updated without changing structure info)  */
+void
+md_init_for_positions(MDArena *arena)
+{
+       Molecule *mol;
+       Parameter *par;
+       int i, j, idx;
+
+       if (arena == NULL || (mol = arena->mol) == NULL || (par = arena->par) == NULL)
+               return;
+       
+       /*  Initialize fix positions  */
+       for (i = j = 0; i < mol->natoms; i++) {
+               Atom *ap = &(mol->atoms[i]);
+               if (ap->fix_force != 0.0) {
+                       ap->fix_pos = ap->r;
+                       j++;
+               }
+       }
+       if (j > 0)
+               md_log(arena, "Number of fixed atoms = %d\n", j);
+/*     if (arena->fix_atoms != NULL) {
+               for (i = 0; i < arena->nfix_atoms; i++) {
+                       j = arena->fix_atoms[i].index;
+                       if (j >= 0 && j < mol->natoms)
+                               arena->fix_atoms[i].pos = mol->atoms[j].r;
+               }
+               printf("Number of fixed atoms = %d\n", arena->nfix_atoms);
+       } */
+
+       /*  Abnormal bonds  */
+       if (arena->anbond_thres > 0.0) {
+               Vector r12;
+               Double r;
+               for (i = 0; i < mol->nbonds; i++) {
+                       idx = arena->bond_par_i[i];
+                       if (idx < 0)
+                               continue;
+                       VecSub(r12, mol->atoms[mol->bonds[i*2]].r, mol->atoms[mol->bonds[i*2+1]].r);
+                       r = VecLength(r12);
+                       if (r >= (1 + arena->anbond_thres) * par->bondPars[idx].r0)
+                               arena->anbond_r0[i] = r - par->bondPars[idx].r0;
+                       else
+                               arena->anbond_r0[i] = 0.0;
+               }
+       }
+       
+       /*  Center of mass  */
+       md_center_of_mass(arena, &(arena->initial_center));
+}
+
+const char *
+md_prepare(MDArena *arena, int check_only)
+{
+       Int i, idx;
+       Int t1, t2;
+       Molecule *mol;
+
+       if (arena->xmol->needsMDRebuild) {
+               /*  Set molecule again  */
+               md_arena_set_molecule(arena, arena->xmol);
+               arena->xmol->needsMDRebuild = 0;
+       } else {
+               md_copy_coordinates_to_internal(arena);
+       }
+       mol = arena->mol;
+
+       arena->errmsg[0] = 0;
+       arena->setjmp_buf = NULL;
+
+       /*  Table of missing parameters  */
+/*     Int *missing = NULL; */
+/*     Int nmissing = 0;  */
+       
+       if (mol == NULL)
+               return "molecule is not defined";
+       if (mol->natoms == 0 || mol->atoms == NULL)
+               return "molecule has no atoms";
+
+       /*  Open files  */
+       if (!check_only) {
+               if (arena->log_result_name != NULL && arena->log_result == NULL) {
+                       arena->log_result = fopen(arena->log_result_name, "wb");
+                       if (arena->log_result == NULL)
+                               return "cannot create log file";
+               }
+               if (arena->coord_result_name != NULL && arena->coord_result == NULL) {
+                       arena->coord_result = fopen(arena->coord_result_name, "wb");
+                       if (arena->coord_result == NULL)
+                               return "cannot create coord file";
+               }
+               if (arena->vel_result_name != NULL && arena->vel_result == NULL) {
+                       arena->vel_result = fopen(arena->vel_result_name, "wb");
+                       if (arena->vel_result == NULL)
+                               return "cannot create vel file";
+               }
+               if (arena->force_result_name != NULL && arena->force_result == NULL) {
+                       arena->force_result = fopen(arena->force_result_name, "wb");
+                       if (arena->force_result == NULL)
+                               return "cannot create force file";
+               }
+               if (arena->extend_result_name != NULL && arena->extend_result == NULL) {
+                       arena->extend_result = fopen(arena->extend_result_name, "wb");
+                       if (arena->extend_result == NULL)
+                               return "cannot create extend file";
+               }
+               if (arena->debug_result_name != NULL && arena->debug_result == NULL) {
+                       arena->debug_result = fopen(arena->debug_result_name, "wb");
+                       if (arena->debug_result == NULL)
+                               return "cannot create debug file";
+               }
+       }
+       
+       /*  Count symmetry unique atoms  */
+       /*  Atoms should be ordered so that all symmetry related atoms come after symmetry unique atoms  */
+       for (i = t1 = 0, t2 = -1; i < mol->natoms; i++) {
+               Atom *ap = mol->atoms + i;
+               Symop symop = ap->symop;
+               if (SYMOP_ALIVE(symop)) {
+                       if (t2 == -1)
+                               t2 = i;  /*  The index of the first non-unique atom  */
+               } else {
+                       t1++;        /*  The number of unique atoms  */
+               }
+       }
+       if (t2 >= 0 && t1 != t2)
+               return "all symmetry related atoms should be after symmetry unique atoms";
+/*     if (t1 > mol->natoms && mol->box == NULL)
+               return "symmetry operation is used but no periodic box is defined"; */
+
+       if (mol->cell != NULL) {
+               arena->periodic_a = (mol->cell->flags[0] != 0);
+               arena->periodic_b = (mol->cell->flags[1] != 0);
+               arena->periodic_c = (mol->cell->flags[2] != 0);
+       }
+
+       arena->natoms_uniq = t1;
+       
+       if (arena->debug_result != NULL) {
+               time_t loc_time;
+               time(&loc_time);
+               fprintf(arena->debug_result, "---- LWMD Started at %s", ctime(&loc_time));
+       }
+
+       /*  Recalc angle/dihedral/improper tables from the bond table and parameters  */
+/*     s_rebuild_angles(arena);
+       s_rebuild_dihedrals(arena);
+       s_rebuild_impropers(arena); */
+
+       /*  Statistics of the molecule  */
+       md_log(arena,
+                  "Number of bonds = %d\n"
+                  "Number of angles = %d\n"
+                  "Number of dihedrals = %d\n"
+                  "Number of impropers = %d\n", mol->nbonds, mol->nangles, mol->ndihedrals, mol->nimpropers);
+       
+       t1 = t2 = 0;
+       for (i = 0; i < mol->natoms; i++) {
+               if (mol->atoms[i].fix_force > 0)
+                       t1++;
+               if (mol->atoms[i].fix_force < 0)
+                       t2++;
+       }
+       if (t1 > 0)
+               md_log(arena, "Number of constrained atoms = %d\n", t1);
+       if (t2 > 0)
+               md_log(arena, "Number of fixed atoms = %d\n", t2);
+
+
+       if (arena->natoms_uniq < mol->natoms) {
+               md_log(arena, "Number of symmetry-unique atoms = %d\n", arena->natoms_uniq);
+               t2 = 0;
+               for (i = 0; i < arena->natoms_uniq; i++) {
+                       if (mol->atoms[i].fix_force < 0)
+                               t2++;
+               }
+       }
+               
+       arena->degree_of_freedom = 3 * (arena->natoms_uniq - t2);
+       md_log(arena, "Degree of freedom = %d\n", arena->degree_of_freedom);
+
+       /*  Build local cache of the used parameters  */
+       if (arena->par != NULL)
+               ParameterRelease(arena->par);
+       arena->par = ParameterNew();
+       arena->nmissing = arena->nsuspicious = 0;
+       s_find_vdw_parameters(arena);
+       s_find_bond_parameters(arena);
+       s_find_angle_parameters(arena);
+       s_find_dihedral_parameters(arena);
+       s_find_improper_parameters(arena);
+       
+       if (arena->nmissing > 0) {
+       /*      for (i = 0; i < nmissing; i++) {
+                       char s1[6], s2[6], s3[6], s4[6];
+                       type1 = missing[i * 5];
+                       AtomTypeDecodeToString(missing[i * 5 + 1], s1);
+                       AtomTypeDecodeToString(missing[i * 5 + 2], s2);
+                       AtomTypeDecodeToString(missing[i * 5 + 3], s3);
+                       AtomTypeDecodeToString(missing[i * 5 + 4], s4);
+                       if (type1 == 0)
+                               md_warning(arena, "Missing vdw parameter for %s\n", s1, s2);
+                       else if (type1 == 1)
+                               md_warning(arena, "Missing bond parameter for %s-%s\n", s1, s2);
+                       else if (type1 == 2)
+                               md_warning(arena, "Missing angle parameter for %s-%s-%s\n", s1, s2, s3);
+                       else if (type1 == 3)
+                               md_warning(arena, "Missing dihedral parameter for %s-%s-%s-%s\n", s1, s2, s3, s4);
+                       else if (type1 == 4)
+                               md_warning(arena, "Missing improper parameter for %s-%s-%s-%s\n", s1, s2, s3, s4);
+               }
+               free(missing); */
+               return "some parameters are missing";
+       }
+       
+       /*  Parameter checking only  */
+       if (check_only) {
+               arena->is_initialized = 0;
+               return NULL;
+       }
+               
+       /*  Build the exclusion table  */
+       s_make_exclusion_list(arena);
+       if (arena->debug_result != NULL && arena->debug_output_level > 0) {
+               fprintf(arena->debug_result, "\n  MDExclusion table for each atom\n");
+               fprintf(arena->debug_result, "  No.  {self and special} {1-2} {1-3} {1-4}\n");
+               for (i = 0; i < mol->natoms; i++) {
+                       fprintf(arena->debug_result, "%5d ", i+1);
+                       for (idx = arena->exinfo[i].index0; idx <= arena->exinfo[i+1].index0; idx++) {
+                               int n = 0;
+                               if (idx == arena->exinfo[i].index0)
+                                       n += fprintf(arena->debug_result, "{");
+                               if (idx == arena->exinfo[i].index1)
+                                       n += fprintf(arena->debug_result, "} {");
+                               if (idx == arena->exinfo[i].index2)
+                                       n += fprintf(arena->debug_result, "} {");
+                               if (idx == arena->exinfo[i].index3)
+                                       n += fprintf(arena->debug_result, "} {");
+                               if (idx < arena->exinfo[i+1].index0) {
+                                       if (n == 0)
+                                               fprintf(arena->debug_result, " ");
+                                       fprintf(arena->debug_result, "%d", arena->exlist[idx]+1);
+                               }
+                       }
+                       fprintf(arena->debug_result, "}\n");
+               }
+       }
+
+       /*  Allocate storage for Verlet list  */
+       arena->max_nverlets = mol->natoms;
+       if (arena->verlets != NULL)
+               free(arena->verlets);
+       arena->verlets = (MDVerlet *)calloc(sizeof(MDVerlet), arena->max_nverlets);
+       arena->nverlets = 0;
+       if (arena->verlets_dr != NULL)
+               free(arena->verlets_dr);
+       arena->verlets_dr = (Vector *)calloc(sizeof(Vector), mol->natoms);
+       if (arena->verlets == NULL || arena->verlets_dr == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       arena->last_verlet_step = -1;
+       if (arena->verlet_i != NULL)
+               free(arena->verlet_i);
+       arena->verlet_i = (Int *)calloc(sizeof(Int), mol->natoms + 1);
+       
+       /*  Allocate storage for partial energy/force  */
+       if (arena->energies != NULL)
+               free(arena->energies);
+       arena->energies = (Double *)calloc(sizeof(Double), kEndIndex);
+       if (arena->forces != NULL)
+               free(arena->forces);
+       arena->forces = (Vector *)calloc(sizeof(Vector), kKineticIndex * mol->natoms);
+       if (arena->energies == NULL || arena->forces == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+
+       /*  Initialize storage for pair interaction calculations */
+       /*  (The storage will be allocated when necessary)  */
+/*     if (arena->group_flags != NULL) {
+               free(arena->group_flags);
+               arena->group_flags = NULL;
+       } */
+       if (arena->pair_forces != NULL) {
+               free(arena->pair_forces);
+               arena->pair_forces = NULL;
+       }
+
+       /*  Initialize temperature statistics  */
+       arena->sum_temperature = 0.0;
+       arena->nsum_temperature = 0;
+       
+       /*  Initialize unique bond/angle/dihedral/improper table  */
+/*     s_find_symmetry_unique(arena); */
+
+       /*  Initialize the position-dependent fields  */
+       md_init_for_positions(arena);
+
+       /*  Clear the snapshot storage  */
+/*     if (arena->snapshots != NULL) {
+               for (i = 0; i < arena->nsnapshots; i++) {
+                       if (arena->snapshots[i] != NULL)
+                               free(arena->snapshots[i]);
+               }
+               free(arena->snapshots);
+               arena->snapshots = NULL;
+       }
+       arena->nsnapshots = 0;
+*/
+       
+       /*  Random number seed  */
+       {
+               unsigned int seed = arena->random_seed;
+               if (seed == 0)
+                       seed = (unsigned int)time(NULL);
+               md_srand(seed);
+               md_log(arena, "Random number seed = %u\n", seed);
+       }
+       
+       /*  Clear the surface area record (will be reallocated within calc_surface_force() if necessary) */
+       if (arena->sp_arena != NULL) {
+               clear_sp_arena(arena);
+       }
+/*     if (arena->sp2_arena != NULL) {
+               clear_sp2_arena(arena);
+       }
+ */
+
+       /*  Graphite potential  */
+       if (arena->use_graphite) {
+               if (arena->graphite != NULL)
+                       graphite_release(arena->graphite);
+               arena->graphite = graphite_new();
+       }
+
+       if (arena->velocities_read == 0)
+               md_init_velocities(arena);
+
+       /*  Fragment analysis  */
+       md_find_fragments(arena);
+       md_log(arena, "%d fragments found\n", arena->nfragments);
+       
+       /*  Pressure control statistics  */
+       if (arena->pressure != NULL) {
+               if (mol->cell == NULL)
+                       return "pressure control is requested but no unit cell is defined";
+               pressure_prepare(arena);
+       }
+
+       arena->is_initialized = 1;
+       arena->mol->needsMDRebuild = 0;
+       arena->request_abort = 0;
+
+       return NULL;
+}
+
+#if __WXMSW__
+#define random rand
+#define srandom srand
+#define kRandMax ((double)RAND_MAX)
+#else
+#define kRandMax 2147483648.0
+#endif
+
+/*  A uniform random number in [0,1]  */
+Double
+md_rand(void)
+{
+       return (double)random() / kRandMax;
+}
+
+/*  A random number with gaussian distribution  */
+Double
+md_gaussian_rand(void)
+{
+       static int waiting = 0;
+       static Double next_value = 0;
+       Double f, r, v1, v2;
+       if (waiting) {
+               waiting = 0;
+               return next_value;
+       }
+       r = 2.0;
+       while (r >= 1.0 || r < 1.523e-8) {
+               v1 = 2.0 * md_rand() - 1.0;
+               v2 = 2.0 * md_rand() - 1.0;
+               r = v1 * v1 + v2 * v2;
+       }
+       f = sqrt(-2.0 * log(r) / r);
+       waiting = 1;
+       next_value = v1 * f;
+       return v2 * f;
+}
+
+/*  Seed the random number  */
+void
+md_srand(unsigned int seed)
+{
+       srandom(seed);
+}
+
+/*  Scale velocities to match the given temperature  */
+void
+md_scale_velocities(MDArena *arena)
+{
+       int i;
+       Atom *ap;
+       Double ttemp, scale;
+       if (arena->nsum_temperature == 0) {
+               Double kinetic = 0.0;
+               for (i = 0, ap = arena->mol->atoms; i < arena->natoms_uniq; i++, ap++) {
+                       if (ap->fix_force < 0)
+                               continue;
+                       kinetic += ap->weight * VecLength2(ap->v);
+               }
+               kinetic *= 0.5;
+               ttemp = 2.0 * kinetic / (arena->degree_of_freedom * BOLTZMANN);
+       } else {
+               ttemp = arena->sum_temperature / arena->nsum_temperature;
+       }
+       scale = sqrt(arena->temperature / ttemp);
+       for (i = 0, ap = arena->mol->atoms; i < arena->mol->natoms; i++, ap++) {
+               if (ap->fix_force < 0)
+                       continue;
+               VecScaleSelf(ap->v, scale);
+       }
+       arena->sum_temperature = 0;
+       arena->nsum_temperature = 0;
+}
+
+/*  Give random velocities that matches the given temperature  */
+void
+md_init_velocities(MDArena *arena)
+{
+       int i, n;
+       Double w;
+/*     Double temp = arena->temperature; */
+       Atom *ap = arena->mol->atoms;
+       n = arena->mol->natoms;
+       for (i = 0; i < n; i++, ap++) {
+               if (ap->fix_force < 0 || fabs(ap->weight) < 1e-6) {
+                       ap->v.x = ap->v.y = ap->v.z = 0;
+               } else {
+                       w = sqrt(arena->temperature * BOLTZMANN / ap->weight);
+                       ap->v.x = w * md_gaussian_rand();
+                       ap->v.y = w * md_gaussian_rand();
+                       ap->v.z = w * md_gaussian_rand();
+               /*      ap->v.x = md_rand() - 0.5;
+                       ap->v.y = md_rand() - 0.5;
+                       ap->v.z = md_rand() - 0.5; */
+               }
+       }
+       arena->sum_temperature = 0;
+       arena->nsum_temperature = 0;
+
+       /*  Quench the total momentum  */
+       md_quench_momenta(arena);
+
+       /*  Adjust the temperature  */
+       md_scale_velocities(arena);
+       
+       arena->velocities_read = 0;
+}
+
+void
+md_bootstrap(MDArena *arena)
+{
+       /*  Set the initial velocities and calculate the current force  */
+       md_init_velocities(arena);
+       calc_force(arena);
+}
+
+static int
+s_md_output_extend_info(MDArena *arena, FILE *fp)
+{
+       Vector *ap, *bp, *cp, *op;
+       static Vector zero = {0.0, 0.0, 0.0};
+       if (arena->coord_result_frame % 10 == 0 || ftello(fp) == 0) {
+               fprintf(fp, "EXLABEL: %7s %7s %11s ", "FRAME", "STEP", "POT_ENERGY");
+               fprintf(fp, "%7s %7s %7s ", "CELL_AX", "CELL_AY", "CELL_AZ");
+               fprintf(fp, "%7s %7s %7s ", "CELL_BX", "CELL_BY", "CELL_BZ");
+               fprintf(fp, "%7s %7s %7s ", "CELL_CX", "CELL_CY", "CELL_CZ");
+               fprintf(fp, "%7s %7s %7s\n", "CELL_OX", "CELL_OY", "CELL_OZ");
+       }
+               
+       fprintf(fp, "EXINFO:  %7d %7d %11.5f ", arena->coord_result_frame, arena->step, arena->total_energy * INTERNAL2KCAL);
+       if (arena->mol->cell != NULL) {
+               ap = &(arena->mol->cell->axes[0]);
+               bp = &(arena->mol->cell->axes[1]);
+               cp = &(arena->mol->cell->axes[2]);
+               op = &(arena->mol->cell->origin);
+       } else {
+               ap = bp = cp = op = &zero;
+       }
+       fprintf(fp, "%7.3f %7.3f %7.3f ", ap->x, ap->y, ap->z);
+       fprintf(fp, "%7.3f %7.3f %7.3f ", bp->x, bp->y, bp->z);
+       fprintf(fp, "%7.3f %7.3f %7.3f ", cp->x, cp->y, cp->z);
+       fprintf(fp, "%7.3f %7.3f %7.3f\n", op->x, op->y, op->z);
+
+       fflush(fp);
+       
+       return 0;
+}
+
+int
+md_output_results(MDArena *arena)
+{
+       int i, j, natoms;
+       Atom *ap;
+       Vector *av, *bv, *cv;
+
+       natoms = (arena->output_expanded_atoms ? arena->mol->natoms : arena->natoms_uniq);
+
+#if 0
+       if (arena->coord_result == NULL && arena->coord_result_name != NULL) {
+               arena->coord_result = fopen(arena->coord_result_name, "wb");
+               if (arena->coord_result == NULL) {
+                       snprintf(sErrBuf, sizeof sErrBuf, "Cannot open coordinate result file %s", arena->coord_result_name);
+                       arena->errmsg = sErrBuf;
+                       return 1;
+               }
+               fprintf(arena->coord_result, "TITLE: coordinates for %d atoms\n", natoms);
+               arena->coord_result_frame = 0;
+       }
+       if (arena->vel_result == NULL && arena->vel_result_name != NULL) {
+               arena->vel_result = fopen(arena->vel_result_name, "wb");
+               if (arena->vel_result == NULL) {
+                       snprintf(sErrBuf, sizeof sErrBuf, "Cannot open velocity result file %s", arena->vel_result_name);
+                       arena->errmsg = sErrBuf;
+                       return 1;
+               }
+               fprintf(arena->vel_result, "TITLE: velocities for %d atoms\n", natoms);
+       }
+       if (arena->force_result == NULL && arena->force_result_name != NULL) {
+               arena->force_result = fopen(arena->force_result_name, "wb");
+               if (arena->force_result == NULL) {
+                       snprintf(sErrBuf, sizeof sErrBuf, "Cannot open force result file %s", arena->force_result_name);
+                       arena->errmsg = sErrBuf;
+                       return 1;
+               }
+               fprintf(arena->force_result, "TITLE: force for %d atoms\n", natoms);
+       }
+       if (arena->extend_result == NULL && arena->extend_result_name != NULL) {
+               arena->extend_result = fopen(arena->extend_result_name, "wb");
+               if (arena->extend_result == NULL) {
+                       snprintf(sErrBuf, sizeof sErrBuf, "Cannot open extend info file %s", arena->extend_result_name);
+                       arena->errmsg = sErrBuf;
+                       return 1;
+               }
+               fprintf(arena->extend_result, "# Frame step energy ax ay az bx by bz cx cy cz ox oy oz\n");
+       }
+#endif
+
+       ap = arena->mol->atoms;
+       if (arena->wrap_coordinates && arena->mol->cell != NULL) {
+               av = &(arena->mol->cell->axes[0]);
+               bv = &(arena->mol->cell->axes[1]);
+               cv = &(arena->mol->cell->axes[2]);
+       } else {
+               av = bv = cv = NULL;
+       }
+       if (arena->coord_result != NULL) {
+               if (arena->wrap_coordinates)
+                       md_wrap_coordinates(arena);
+               for (i = j = 0; i < natoms; i++, j = (j + 3) % 10) {
+                       Vector r = ap[i].r;
+                       if (av != NULL) {
+                               VecScaleInc(r, *av, ap[i].wrap_dx);
+                               VecScaleInc(r, *bv, ap[i].wrap_dy);
+                               VecScaleInc(r, *cv, ap[i].wrap_dz);
+                       }
+                       fprintf(arena->coord_result, " %7.3f%s %7.3f%s %7.3f%s",
+                               r.x, (j == 9 ? "\n" : ""),
+                               r.y, (j == 8 ? "\n" : ""),
+                               r.z, (j == 7 ? "\n" : ""));
+               }
+               if (j != 0)
+                       fprintf(arena->coord_result, "\n");
+               fflush(arena->coord_result);
+       }
+       if (arena->vel_result != NULL) {
+               for (i = j = 0; i < natoms; i++, j = (j + 3) % 10) {
+                       fprintf(arena->vel_result, " %7.3f%s %7.3f%s %7.3f%s",
+                               ap[i].v.x*1000, (j == 9 ? "\n" : ""),
+                               ap[i].v.y*1000, (j == 8 ? "\n" : ""),
+                               ap[i].v.z*1000, (j == 7 ? "\n" : ""));
+               }
+               if (j != 0)
+                       fprintf(arena->vel_result, "\n");
+               fflush(arena->vel_result);
+       }
+       if (arena->force_result != NULL) {
+               for (i = j = 0; i < natoms; i++, j = (j + 3) % 10) {
+                       fprintf(arena->force_result, " %7.3f%s %7.3f%s %7.3f%s",
+                               ap[i].f.x, (j == 9 ? "\n" : ""),
+                               ap[i].f.y, (j == 8 ? "\n" : ""),
+                               ap[i].f.z, (j == 7 ? "\n" : ""));
+               }
+               if (j != 0)
+                       fprintf(arena->force_result, "\n");
+               fflush(arena->force_result);
+       }
+       if (arena->extend_result != NULL) {
+               s_md_output_extend_info(arena, arena->extend_result);
+       } else if (arena->log_result != NULL) {
+               s_md_output_extend_info(arena, arena->log_result);
+       }
+       arena->coord_result_frame++;
+       return 0;
+}
+
+void
+md_output_energies(MDArena *arena)
+{
+       int i;
+       int periodic = (arena->mol->cell != NULL) && (arena->periodic_a || arena->periodic_b || arena->periodic_c);
+/*     if (arena->log_result == NULL)
+               return; */
+       if ((arena->step / arena->energy_output_freq) % 10 == 0) {
+               md_log(arena, "ELABEL:  %11s %11s %11s %11s %11s %11s", "STEP", "TOTAL_POT", "BOND", "ANGLE", "DIHEDRAL", "IMPROPER");
+               md_log(arena, " %11s %11s %11s %11s %11s %11s %11s %11s", "VDW", "ELECT", "AUX", "SURFACE", "KINETIC", "NET", "TEMP", "TEMP_AVG");
+               if (periodic)
+                       md_log(arena, " %11s", "VOLUME");
+               md_log(arena, "\n");
+       }
+       md_log(arena, "ENERGY:  %11d %11.5f", arena->step, arena->total_energy * INTERNAL2KCAL);
+       for (i = 0; i < kEndIndex; i++)
+               md_log(arena, " %11.5f", arena->energies[i] * INTERNAL2KCAL);
+       md_log(arena, " %11.5f", (arena->energies[kKineticIndex] + arena->total_energy) * INTERNAL2KCAL);
+       md_log(arena, " %11.5f", arena->transient_temperature);
+       md_log(arena, " %11.5f", (arena->nsum_temperature > 0 ? arena->sum_temperature / arena->nsum_temperature : 0.0));
+       if (periodic) {
+               Vector v, *av, *bv, *cv;
+               av = &(arena->mol->cell->axes[0]);
+               bv = &(arena->mol->cell->axes[1]);
+               cv = &(arena->mol->cell->axes[2]);
+               VecCross(v, *av, *bv);
+               md_log(arena, " %11.5f", VecDot(v, *cv));
+       }
+       md_log(arena, "\n");
+       md_log(arena, NULL);
+}
+
+void
+md_update_velocities(MDArena *arena)
+{
+       int i, natoms;
+       Byte use_sym;
+       Atom *ap;
+       Double w, wt;
+/*     Double wsum; */
+/*     Vector m1, m2; */
+       Double halftimestep = arena->timestep * 0.5;
+       ap = arena->mol->atoms;
+       natoms = arena->mol->natoms;
+       use_sym = (arena->mol->nsyms > 0);
+       Double limit = arena->velocity_limit;
+       limit *= limit;
+/*     VecZero(m1);
+       VecZero(m2);
+       wsum = 0.0; */
+       for (i = 0; i < natoms; i++, ap++) {
+               if (use_sym && ap->symop.alive)
+                       continue;
+               if (ap->fix_force < 0)
+                       continue;
+               wt = ap->weight;
+               if (fabs(wt) < 1e-6)
+                       continue;
+               w = halftimestep / wt;
+       /*      VecScaleInc(m1, ap->v, wt); */
+               VecScaleInc(ap->v, ap->f, w);
+               w = VecLength2(ap->v);
+               if (w > limit || !isfinite(w))  /*  Is isfinite() available in other platforms?? */
+                       md_panic(arena, "the velocity for atom %d exceeded limit at step %d", i+1, arena->step);
+       /*      VecScaleInc(m2, ap->v, wt); */
+       /*      wsum += wt; */
+       }
+/*
+       VecDec(m2, m1);
+       w = 1.0 / wsum;
+       VecScaleSelf(m2, w);
+       for (i = 0, ap = arena->mol->atoms; i < natoms; i++, ap++) {
+               if (use_sym && ap->symop.alive)
+                       continue;
+               if (ap->fix_force < 0)
+                       continue;
+               VecDec(ap->v, m2);
+       }
+*/
+       arena->velocities_read = 0;
+       /*  For debug  */
+/*     VecZero(m2);
+       for (i = 0, ap = arena->mol->atoms; i < natoms; i++, ap++) {
+               if (use_sym && ap->sym_op > 0)
+                       continue;
+               wt = ap->weight;
+               VecScaleInc(m2, ap->v, wt);
+       } */
+}
+
+void
+md_update_positions(MDArena *arena)
+{
+       int i, natoms;
+       Atom *ap;
+       Double timestep = arena->timestep;
+       Vector *vdr = arena->verlets_dr;
+       Vector dr;
+/*     Double w, limit;
+       Double kinetic, kinetic_uniq; */
+       Byte use_sym;
+       ap = arena->mol->atoms;
+       natoms = arena->mol->natoms;
+/*     limit = arena->velocity_limit;
+       limit *= limit; */
+       use_sym = (arena->mol->nsyms > 0);
+/*     kinetic = kinetic_uniq = 0.0; */
+       for (i = 0; i < natoms; i++, ap++, vdr++) {
+               if (use_sym && ap->symop.alive)
+                       continue;
+               if (ap->fix_force < 0)
+                       continue;
+               VecScale(dr, ap->v, timestep);
+               VecInc(ap->r, dr);
+               VecInc(*vdr, dr);
+       }
+
+       /*  Update the abnormal bond parameters  */
+       if (arena->anbond_thres > 0.0) {
+               Double *fp = arena->anbond_r0;
+               for (i = 0; i < arena->mol->nbonds; i++) {
+                       if (fp[i] <= 0.0)
+                               continue;
+                       fp[i] -= arena->anbond_anneal_rate;
+                       if (fp[i] < 0.0)
+                               fp[i] = 0.0;
+               }
+       }
+       
+       /*  Relocate the center of mass (if necessary)  */
+       if (arena->relocate_center) {
+               md_relocate_center(arena);
+       }
+}
+
+void
+md_calc_kinetic_energy(MDArena *arena)
+{
+       int i, n, nuniq;
+       Double w, kinetic, kinetic_uniq;
+       Atom *ap;
+       n = arena->mol->natoms;
+       nuniq = arena->natoms_uniq;
+       kinetic = kinetic_uniq = 0;
+       for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+               if (ap->fix_force < 0)
+                       continue;
+               w = VecLength2(ap->v) * ap->weight;
+               kinetic += w;
+               if (i < nuniq)
+                       kinetic_uniq += w; 
+       }
+       arena->energies[kKineticIndex] = kinetic * 0.5;
+       arena->transient_temperature = kinetic_uniq / (arena->degree_of_freedom * BOLTZMANN);
+       arena->sum_temperature += arena->transient_temperature;
+       arena->nsum_temperature++;
+}
+
+/*  Andersen thermostat  */
+void
+md_andersen_thermostat(MDArena *arena)
+{
+       int n = arena->natoms_uniq;
+       int i;
+       Double w, de;
+       Atom *ap;
+       Double q = arena->andersen_thermo_coupling;
+       de = 0.0;
+       for (i = 0, ap = arena->mol->atoms; i < n; i++, ap++) {
+               if (ap->fix_force < 0 || fabs(ap->weight) < 1e-6) {
+                       ap->v.x = ap->v.y = ap->v.z = 0;
+               } else if (md_rand() < q) {
+                       de -= VecLength2(ap->v) * ap->weight;
+                       w = sqrt(arena->temperature * BOLTZMANN / ap->weight);
+                       ap->v.x = w * md_gaussian_rand();
+                       ap->v.y = w * md_gaussian_rand();
+                       ap->v.z = w * md_gaussian_rand();
+                       de += VecLength2(ap->v) * ap->weight;
+               }
+       }
+       de *= 0.5;
+       arena->energies[kKineticIndex] += de;
+}
+
+void
+md_transform_vec_by_symmetry(MDArena *arena, Vector *dst, const Vector *src, Symop rec, int no_translation)
+{
+       Transform temp;
+       if (!rec.alive)
+               return;
+       if (rec.sym > 0 && rec.sym <= arena->mol->nsyms) {
+               memmove(temp, arena->cellsyms[rec.sym - 1], sizeof(temp));
+               if (no_translation)
+                       temp[9] = temp[10] = temp[11] = 0.0;
+               TransformVec(dst, temp, src);
+       } else *dst = *src;
+       if (!no_translation) {
+               Vector *vp = arena->mol->cell->axes;
+               VecScaleInc(*dst, vp[0], rec.dx);
+               VecScaleInc(*dst, vp[1], rec.dy);
+               VecScaleInc(*dst, vp[2], rec.dz);
+       }
+}
+
+void
+md_wrap_coordinates(MDArena *arena)
+{
+       /*  Calculate the offset for each fragment to wrap into the unit cell (0,0,0)-(1,1,1) */
+       int i, n;
+       int last_n;
+       Symop last_symop;
+       Molecule *mol = arena->mol;
+       Atom *ap;
+
+       if (mol == NULL || mol->natoms == 0)
+               return;
+               
+       if (arena->nfragments == 0)
+               md_find_fragments(arena);
+
+       /*  Calculate the center of mass for each fragment  */
+       for (n = 0; n < arena->nfragments; n++) {
+               VecZero(arena->fragment_info[n].pos);
+               arena->fragment_info[n].mass = 0.0;
+       }
+       for (i = 0, ap = mol->atoms; i < arena->natoms_uniq; i++, ap++) {
+               n = arena->fragment_indices[i];
+               VecScaleInc(arena->fragment_info[n].pos, ap->r, ap->weight);
+               arena->fragment_info[n].mass += ap->weight;
+       }
+       for (n = 0; n < arena->nfragments; n++) {
+               VecScaleSelf(arena->fragment_info[n].pos, 1.0/arena->fragment_info[n].mass);
+       }
+       
+       /*  Calculate the offset  */
+       /*  A trick: atoms belonging to one fragment are almost always located in a consequent
+               positions. So we cache the 'last' information to avoid duplicate calculation
+               efficiently.  */
+       last_n = -1;
+       last_symop.alive = 0;
+       for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap++) {
+               if (ap->symop.alive)
+                       n = arena->fragment_indices[ap->symbase];
+               else n = arena->fragment_indices[i];
+               if (n == last_n && ap->symop.alive == last_symop.alive
+               && (!last_symop.alive || (ap->symop.dx == last_symop.dx && ap->symop.dy == last_symop.dy && ap->symop.dz == last_symop.dz && ap->symop.sym == last_symop.sym))) {
+                       /*  Belongs to the 'last' fragment  */
+                       ap->wrap_dx = ap[-1].wrap_dx;
+                       ap->wrap_dy = ap[-1].wrap_dy;
+                       ap->wrap_dz = ap[-1].wrap_dz;
+               } else {
+                       /*  Calculate the offset from the position of the center of mass */
+                       Vector r = arena->fragment_info[n].pos;
+                       TransformVec(&r, arena->mol->cell->rtr, &r);
+                       if (ap->symop.alive && ap->symop.sym > 0 && ap->symop.sym <= arena->mol->nsyms) {
+                               /*  The translational components of symop are not included  */
+                               TransformVec(&r, arena->mol->syms[ap->symop.sym - 1], &r);
+                       }
+                       ap->wrap_dx = (arena->periodic_a ? -floor(r.x) : 0);
+                       ap->wrap_dy = (arena->periodic_b ? -floor(r.y) : 0);
+                       ap->wrap_dz = (arena->periodic_c ? -floor(r.z) : 0);
+               }
+               last_n = n;
+               last_symop = ap->symop;
+       }
+}
+
+void
+md_amend_by_symmetry(MDArena *arena)
+{
+       int i, natoms;
+       Atom *ap, *ap0;
+       if (arena->mol->nsyms == 0)
+               return;
+       ap = ap0 = arena->mol->atoms;
+       natoms = arena->mol->natoms;
+       for (i = 0; i < natoms; i++, ap++) {
+               Symop symop;
+               if (!ap->symop.alive)
+                       continue;
+               symop = ap->symop;
+               ap0 = arena->mol->atoms + ap->symbase;
+               md_transform_vec_by_symmetry(arena, &(ap->r), &(ap0->r), symop, 0);
+               md_transform_vec_by_symmetry(arena, &(ap->f), &(ap0->f), symop, 1);
+               md_transform_vec_by_symmetry(arena, &(ap->v), &(ap0->v), symop, 1);
+       }
+       
+}
+
+void
+md_snapshot(MDArena *arena, int idx)
+{
+       Atom *ap;
+       Vector *vp;
+       MDSnapshot **spp;
+       size_t size;
+       int i, natoms;
+       if (idx < 0)
+               return;
+       spp = (MDSnapshot **)AssignArray(&arena->snapshots, &arena->nsnapshots, sizeof(*spp), idx, NULL);
+       if (spp == NULL)
+               goto low_memory;
+       natoms = arena->mol->natoms;
+       size = sizeof(**spp) + sizeof(Vector) * (natoms - 1) * 3;
+       if (*spp == NULL)
+               *spp = (MDSnapshot *)malloc(size);
+       else
+               *spp = (MDSnapshot *)realloc(*spp, size);
+       if (*spp == NULL)
+               goto low_memory;
+       memset(*spp, 0, size);
+       (*spp)->step = arena->step;
+       (*spp)->natoms = natoms;
+       vp = (*spp)->rvf;
+       for (i = 0, ap = arena->mol->atoms; i < natoms; i++, ap++) {
+               *vp++ = ap->r;
+               *vp++ = ap->v;
+               *vp++ = ap->f;
+       }
+       return;
+  low_memory:
+       md_panic(arena, "Low memory while saving snapshot");
+}
+
+void
+md_restore(MDArena *arena, int idx)
+{
+       int i, natoms, natoms1;
+       Atom *ap;
+       Vector *vp;
+/*     MDSnapshot **spp; */
+
+       if (idx < 0 || idx >= arena->nsnapshots)
+               return;
+       vp = arena->snapshots[idx]->rvf;
+       natoms = arena->mol->natoms;
+       natoms1 = arena->snapshots[idx]->natoms;
+       if (natoms != natoms1) {
+               md_warning(arena, "restore: the number of atoms in snapshot (%d) is different from the current number of atoms (%d)\n", natoms1, natoms);
+       }
+       for (i = 0, ap = arena->mol->atoms; i < natoms && i < natoms1; i++, ap++) {
+               ap->r = *vp++;
+               ap->v = *vp++;
+               ap->f = *vp++;
+       }
+       arena->last_verlet_step = -1;  /*  The Verlet list needs update  */
+}
+
+int
+md_step(MDArena *arena)
+{
+       md_update_velocities(arena);
+       md_update_positions(arena);
+       /* md_rattle_coordinate(arena); */
+       md_amend_by_symmetry(arena);
+       calc_force(arena);
+/*     md_calc_kinetic_energy(arena); */
+       md_update_velocities(arena);
+       /* md_rattle_velocity(arena); */
+       return 0;
+}
+
+void
+md_minimize_init(MDArena *arena)
+{
+       int i;
+       static const Vector zerov = {0, 0, 0};
+       if (arena->old_forces != NULL)
+               free(arena->old_forces);
+       arena->old_forces = (Vector *)calloc(sizeof(Vector), arena->mol->natoms * 2);
+       if (arena->old_forces == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       arena->old_pos = arena->old_forces + arena->mol->natoms;
+       arena->f_len2 = arena->old_f_len2 = arena->max_gradient = 0.0;
+       for (i = 0; i < arena->mol->natoms; i++) {
+               arena->mol->atoms[i].v = arena->mol->atoms[i].f = zerov;
+       }
+       arena->conv_flag = 0;
+}
+
+int
+md_minimize_step(MDArena *arena)
+{
+       Double bk, w1, w2, w3, dump;
+       Double low, mid, high, low_energy, mid_energy, high_energy, lambda;
+       Double low_limit, high_limit;
+       Int i, j, retval, natoms_movable;
+       Atom *atoms = arena->mol->atoms;
+       Atom *ap;
+       Int natoms = arena->mol->natoms;
+       Vector *vp;
+       const Double phi = 0.618033988749895;  /*  The golden ratio  */
+
+       md_amend_by_symmetry(arena);
+
+       w1 = w2 = 0.0;
+       retval = 0;
+       natoms_movable = 0;
+       for (i = 0, ap = atoms, vp = arena->old_forces; i < natoms; i++, ap++, vp++) {
+               if (ap->fix_force < 0)
+                       continue;
+               w1 += VecLength2(ap->f);
+               w2 += VecDot(ap->f, *vp);
+               natoms_movable++;
+       }
+       arena->f_len2 = w1;
+       if (arena->old_f_len2 == 0.0) {
+               /*  New direction  */
+               bk = 0.0;
+       } else {
+               bk = (w1 - w2) / arena->old_f_len2;
+               if (bk < 0.0)
+                       bk = 0.0;  /*  New direction  */
+       }
+       /*  Update the search direction  */
+       arena->old_f_len2 = arena->f_len2;
+       w2 = w3 = 0.0;
+       dump = 1.0;
+       for (i = 0, ap = atoms; i < natoms; i++, ap++) {
+               if (ap->fix_force < 0)
+                       continue;
+               w1 = VecLength2(ap->f) * dump;
+               if (!isfinite(w1))  /*  Is isfinite() available in other platforms?? */
+                       md_panic(arena, "the gradient at atom %d exceeded limit at step %d", i+1, arena->step);
+               if (w1 > 1e4)
+                       dump *= 1e4 / w1;
+       }
+       dump = sqrt(dump);
+       for (i = 0, ap = atoms, vp = arena->old_forces; i < natoms; i++, ap++, vp++) {
+               if (ap->fix_force < 0)
+                       continue;
+               *vp = ap->f;
+               *(vp + natoms) = ap->r;
+               ap->v.x = ap->v.x * bk + ap->f.x * dump;
+               ap->v.y = ap->v.y * bk + ap->f.y * dump;
+               ap->v.z = ap->v.z * bk + ap->f.z * dump;
+               w1 = VecLength2(ap->v);
+               w2 += w1;
+       /*      if (w1 > 1e4 || !isfinite(w1))
+                       md_panic(arena, "the gradient at atom %d exceeded limit at step %d", i+1, arena->step); */
+               if (w1 > w3)
+                       w3 = w1;
+       }
+       w3 = sqrt(w3);
+       arena->max_gradient = w3;
+       arena->v_len2 = w2;
+/*     printf("f_len2 = %g, v_len2 = %g, bk = %g, max_grad = %g\n", arena->f_len2, arena->v_len2, bk, w3); */
+       if (bk == 0.0 && w3 < arena->gradient_convergence)
+               return 1;  /*  Gradient is sufficiently small  */
+
+       /*  Proceed along ap->v until the energy increases  */
+       low_limit = arena->coordinate_convergence / arena->max_gradient;
+       high_limit = 0.1 / arena->max_gradient;
+       low = 0.0;
+       low_energy = arena->total_energy;
+/*     lambda = 1e-3 * arena->f_len2 / w2; */
+       lambda = high_limit;
+       high = lambda;
+       while (1) {
+               for (j = 0, ap = atoms, vp = arena->old_pos; j < natoms; j++, ap++, vp++) {
+                       if (ap->fix_force < 0)
+                               continue;
+                       ap->r.x = vp->x + ap->v.x * lambda;
+                       ap->r.y = vp->y + ap->v.y * lambda;
+                       ap->r.z = vp->z + ap->v.z * lambda;
+               }
+               calc_force(arena);
+               mid = lambda;
+               mid_energy = arena->total_energy;
+               if (mid_energy < low_energy) {
+                       /*  mid is the 'sufficiently large' step to give lower total energy  */
+                       if (mid == high) {
+                               /*  Higher limit: move by this amount  */
+                               retval = 0;
+                               goto cleanup;
+                       }
+                       break;
+               }
+               high = mid;
+               high_energy = mid_energy;
+               lambda *= 0.25;
+               if (lambda < low_limit) {
+                       /*  Cannot find point with lower energy than the starting point  */
+                       /*  Restore the original position  */
+                       for (j = 0, ap = atoms, vp = arena->old_pos; j < natoms; j++, ap++, vp++) {
+                               if (ap->fix_force < 0)
+                                       continue;
+                               ap->r = *vp;
+                       }
+                       calc_force(arena);
+                       lambda = 0.0;
+                       if (bk == 0.0)
+                               retval = 2;  /*  Atom movement is sufficiently small  */
+                       goto cleanup;
+               }
+       }
+/*     printf("Line minimization [%g, %g, %g] (energy [%g, %g, %g]) ", low, mid, high, low_energy, mid_energy, high_energy); */
+       /*  low_energy >= mid_energy < high_energy  */
+       /*  Binary search for minimum  */
+       for (i = 0; i < 10; i++) {
+               if (high - mid > mid - low) {
+                       lambda = high - (high - mid) * phi;
+                       for (j = 0, ap = atoms, vp = arena->old_pos; j < natoms; j++, ap++, vp++) {
+                               if (ap->fix_force < 0)
+                                       continue;
+                               ap->r.x = vp->x + ap->v.x * lambda;
+                               ap->r.y = vp->y + ap->v.y * lambda;
+                               ap->r.z = vp->z + ap->v.z * lambda;
+                       }       
+                       calc_force(arena);
+                       if (arena->total_energy < mid_energy) {
+                               low = mid;
+                               low_energy = mid_energy;
+                               mid = lambda;
+                               mid_energy = arena->total_energy;
+                       } else {
+                               high = lambda;
+                               high_energy = arena->total_energy;
+                       }
+               } else {
+                       lambda = mid - (mid - low) * phi;
+                       for (j = 0, ap = atoms, vp = arena->old_pos; j < natoms; j++, ap++, vp++) {
+                               if (ap->fix_force < 0)
+                                       continue;
+                               ap->r.x = vp->x + ap->v.x * lambda;
+                               ap->r.y = vp->y + ap->v.y * lambda;
+                               ap->r.z = vp->z + ap->v.z * lambda;
+                       }       
+                       calc_force(arena);
+                       if (arena->total_energy < mid_energy) {
+                               high = mid;
+                               high_energy = mid_energy;
+                               mid = lambda;
+                               mid_energy = arena->total_energy;
+                       } else {
+                               low = lambda;
+                               low_energy = arena->total_energy;
+                       }
+               }
+               if (bk == 0.0 && (high - low) * arena->max_gradient < arena->coordinate_convergence) {
+                       retval = 2;  /*  Atom movement is sufficiently small  */
+                       break;
+               }
+       }
+  cleanup:
+/*     printf("Final lambda = %g (%g)\n", lambda, arena->total_energy); */
+       return retval;
+}
+
+void
+md_cell_recalculate(MDArena *arena)
+{
+       Molecule *mol = arena->mol;
+       XtalCell *cell = mol->cell;
+       int i;
+       MoleculeCalculateCellFromAxes(mol->cell, 1);
+       for (i = 0; i < mol->nsyms; i++) {
+               Transform temp;
+               TransformMul(temp, mol->syms[i], cell->rtr);
+               TransformMul(arena->cellsyms[i], cell->tr, temp);
+       }
+}
+
+/*  scale_atoms = 1: symmetry-unique atoms are transformed to keep the fractional coordinate constant */
+/*  scale_atoms = 2: same as scale_atoms = 1, except that the center of mass of each fragment of connected
+       atoms are scaled and the atoms within one fragment are moved by the same amount */
+/*  NOTE: only symmetry-unique atoms are moved, so don't forget to do md_symmetry_amend()!  */
+void
+md_scale_cell(MDArena *arena, const Transform tf, int scale_atoms)
+{
+       Transform grad, grad_inv, grad_tr, grad_inv_tr;
+       int i;
+       Double volume;
+       Vector v;
+       Atom *ap;
+       XtalCell *cell = arena->mol->cell;
+       memmove(grad, cell->rtr, sizeof(grad));
+       VecCross(v, cell->axes[0], cell->axes[1]);
+       volume = VecDot(v, cell->axes[2]);
+       TransformVec(&cell->axes[0], tf, &cell->axes[0]);
+       TransformVec(&cell->axes[1], tf, &cell->axes[1]);
+       TransformVec(&cell->axes[2], tf, &cell->axes[2]);
+       md_cell_recalculate(arena);
+
+       /*  Deformation gradient (temp) = celltr * old_rcelltr  */
+       TransformMul(grad, cell->tr, grad);
+/*     grad[9] = grad[10] = grad[11] = 0.0;  */
+       TransformInvert(grad_inv, grad);
+       memmove(grad_tr, grad, sizeof(grad));
+       MatrixTranspose(grad_tr, grad_tr);
+       memmove(grad_inv_tr, grad_inv, sizeof(grad_inv));
+       MatrixTranspose(grad_inv_tr, grad_inv_tr);
+
+       if (scale_atoms == 1) {
+               /*  Scale atom positions and velocities  */
+               for (i = 0, ap = arena->mol->atoms; i < arena->natoms_uniq; i++, ap++) {
+                       if (ap->periodic_exclude)
+                               continue;
+                       TransformVec(&ap->r, grad, &ap->r);
+                       MatrixVec(&ap->f, grad, &ap->f);         /* No translational component */
+                       MatrixVec(&ap->v, grad_inv_tr, &ap->v);  /* No translational component */
+               }
+       } else if (scale_atoms == 2) {
+               int j;
+               /*  Scale atom positions and velocities by fragments  */
+               for (i = 0; i < arena->nfragments; i++) {
+                       VecZero(arena->fragment_info[i].pos);
+                       arena->fragment_info[i].mass = 0.0;
+               }
+               /*  Get the center of mass for each fragment  */
+               for (i = 0, ap = arena->mol->atoms; i < arena->natoms_uniq; i++, ap++) {
+                       j = arena->fragment_indices[i];
+                       if (j < 0 || j >= arena->nfragments)
+                               continue;
+                       VecInc(arena->fragment_info[j].pos, ap->r);
+                       arena->fragment_info[j].mass += ap->weight;
+               }
+               /*  Calculate the offset for each fragment  */
+               for (j = 0; j < arena->nfragments; j++) {
+                       Vector *vp = &(arena->fragment_info[j].pos);
+                       if (arena->fragment_info[j].mass > 0.0) {
+                               VecScaleSelf(*vp, 1.0 / arena->fragment_info[j].mass);
+                               TransformVec(&v, grad, vp);
+                               VecSub(*vp, v, *vp);
+                       } else VecZero(*vp);
+               }
+               /*  Transform  */
+               for (i = 0, ap = arena->mol->atoms; i < arena->natoms_uniq; i++, ap++) {
+                       if (ap->periodic_exclude)
+                               continue;
+                       MatrixVec(&ap->f, grad, &ap->f);  /*  This is not exact  */
+                       MatrixVec(&ap->v, grad_inv_tr, &ap->v);
+                       j = arena->fragment_indices[i];
+                       if (j < 0 || j >= arena->nfragments)
+                               continue;
+                       VecInc(ap->r, arena->fragment_info[j].pos);
+               }
+       }
+       
+       arena->last_verlet_step = -1;
+}
+
+void
+md_finish(MDArena *arena)
+{
+       if (arena->coord_result != NULL) {
+               fflush(arena->coord_result);
+       }
+       if (arena->vel_result != NULL) {
+               fflush(arena->vel_result);
+       }
+       if (arena->force_result != NULL) {
+               fflush(arena->force_result);
+       }
+       if (arena->extend_result != NULL) {
+               fflush(arena->extend_result);
+       }
+       
+       if (arena->debug_result != NULL) {
+               fflush(arena->debug_result);
+       }
+}
+
+int
+md_copy_coordinates_from_internal(MDArena *arena)
+{
+       /*  Copy the internal r/v/f to xmol  */
+       int i;
+       Atom *ap1, *ap2;
+       if (arena->mol == NULL || arena->xmol == NULL)
+               return -1;  /*  Not initialized  */
+       if (arena->mol->natoms != arena->xmol->natoms)
+               return -2;  /*  Number of atoms does not match  */
+       for (i = 0, ap1 = arena->mol->atoms, ap2 = arena->xmol->atoms; i < arena->mol->natoms; i++, ap1 = ATOM_NEXT(ap1), ap2 = ATOM_NEXT(ap2)) {
+               ap2->r = ap1->r;
+               ap2->v = ap1->v;
+               ap2->f = ap1->f;
+       }
+       if (arena->mol->cell != NULL && arena->xmol->cell != NULL)
+               memmove(arena->xmol->cell, arena->mol->cell, sizeof(XtalCell));
+       return 0;
+}
+
+int
+md_copy_coordinates_to_internal(MDArena *arena)
+{
+       /*  Copy the xmol coordinates to internal  */
+       int i;
+       Atom *ap1, *ap2;
+       if (arena->mol == NULL || arena->xmol == NULL)
+               return -1;  /*  Not initialized  */
+       if (arena->mol->natoms != arena->xmol->natoms)
+               return -2;  /*  Number of atoms does not match  */
+       for (i = 0, ap1 = arena->mol->atoms, ap2 = arena->xmol->atoms; i < arena->mol->natoms; i++, ap1 = ATOM_NEXT(ap1), ap2 = ATOM_NEXT(ap2)) {
+               ap1->r = ap2->r;
+       }
+       if (arena->mol->cell != NULL && arena->xmol->cell != NULL)
+               memmove(arena->mol->cell, arena->xmol->cell, sizeof(XtalCell));
+       return 0;
+}
+
+int
+md_is_running(MDArena *arena)
+{
+       if (arena != NULL && arena->is_running)
+               return 1;
+       else return 0;
+}
+
+int
+md_main(MDArena *arena, int minimize)
+{
+//     extern int do_callback(MDArena *);
+       jmp_buf env;
+       const char *msg;
+       int retval = 0;
+       int (*md_step_func)(MDArena *);
+
+       if (!arena->is_initialized || arena->xmol->needsMDRebuild) {
+               /*  Prepare MD parameters and runtime fields  */
+               msg = md_prepare(arena, 0);
+               if (msg != NULL) {
+                       snprintf(arena->errmsg, sizeof(arena->errmsg), "%s", msg);
+                       return 1;
+               }
+               /*  Copy coordinates  */
+               MoleculeLock(arena->xmol);
+               retval = md_copy_coordinates_to_internal(arena);
+               MoleculeUnlock(arena->xmol);
+               if (retval != 0)
+                       return retval;
+       }
+       
+       arena->is_running = 1;
+       
+       if (setjmp(env) == 0) {
+               arena->setjmp_buf = &env;
+               if (minimize) {
+                       md_minimize_init(arena);
+                       md_step_func = md_minimize_step;
+                       arena->minimize_complete = 0;
+               } else {
+                       md_step_func = md_step;
+               }
+
+               /*  Calculate initial energies and forces  */
+               arena->step = arena->start_step;
+               md_amend_by_symmetry(arena);
+               calc_force(arena);
+               md_calc_kinetic_energy(arena);
+               if (arena->step == 0 || arena->start_step == arena->end_step) {
+                       md_output_results(arena);
+                       md_output_energies(arena);
+               }
+               if (arena->step == 0 && arena->md_callback_func != NULL) {
+                       retval = (*(arena->md_callback_func))(arena);
+                       if (retval != 0) {
+                               snprintf(arena->errmsg, sizeof(arena->errmsg), "MD Interrupt");
+                               goto cleanup;
+                       }
+               }
+
+               /*  Run simulation  */
+               for (arena->step = arena->start_step + 1; arena->step <= arena->end_step; arena->step++) {
+
+                       /*  Molecules may be modified from the callback procedure  */
+                       if (!arena->is_initialized || arena->xmol->needsMDRebuild) {
+                               msg = md_prepare(arena, 0);
+                               if (msg != NULL) {
+                                       snprintf(arena->errmsg, sizeof(arena->errmsg), "%s", msg);
+                                       retval = 1;
+                                       goto cleanup;
+                               }
+                       }
+
+                       retval = (*md_step_func)(arena);
+                       md_calc_kinetic_energy(arena);
+                       if (arena->rescale_temp_freq > 0 && arena->step % arena->rescale_temp_freq == 0)
+                               md_scale_velocities(arena);
+                       if (arena->reinit_temp_freq > 0 && arena->step % arena->reinit_temp_freq == 0)
+                               md_init_velocities(arena);
+                       if (arena->andersen_thermo_freq > 0 && arena->step % arena->andersen_thermo_freq == 0)
+                               md_andersen_thermostat(arena);
+                       if (arena->pressure != NULL)
+                               pressure_control(arena);
+
+                       if (arena->coord_output_freq > 0 && arena->step % arena->coord_output_freq == 0)
+                               md_output_results(arena);
+                       if (arena->energy_output_freq > 0 && arena->step % arena->energy_output_freq == 0)
+                               md_output_energies(arena);
+
+                       if (retval != 0) {
+                               if (minimize) {
+                                       switch (retval) {
+                                               case 1:
+                                                       md_log(arena, "Minimize: minimization converged because the maximum gradient becomes less than the threshold.\n");
+                                                       retval = 0;
+                                                       arena->minimize_complete = 1;
+                                                       break;
+                                               case 2:
+                                                       md_log(arena, "Minimize: minimization converged because the maximum movement of atoms becomes less than the threshold.\n");
+                                                       retval = 0;
+                                                       arena->minimize_complete = 1;
+                                                       break;
+                                       }
+                               }
+                               goto cleanup;
+                       }
+                       if (arena->request_abort != 0) {
+                               snprintf(arena->errmsg, sizeof(arena->errmsg), "MD Abort by request");
+                               retval = 1;
+                               goto cleanup;
+                       }
+
+                       if (arena->md_callback_func != NULL && (arena->callback_freq == 0 || arena->step % arena->callback_freq == 0)) {
+                               retval = (*(arena->md_callback_func))(arena);
+                               if (retval != 0) {
+                                       snprintf(arena->errmsg, sizeof(arena->errmsg), "MD Interrupt");
+                                       goto cleanup;
+                               }
+                       }
+                       
+               }
+               arena->step--;
+
+       } else {
+               /*  Return from md_panic()  */
+               retval = -1;  /*  Some fatal error  */
+       }
+
+cleanup:
+       if (retval == 0) {
+               arena->start_step = arena->step;  /*  Prepare for next run  */
+       /*      MoleculeLock(arena->xmol);
+               retval = md_copy_coordinates_from_internal(arena);
+               MoleculeUnlock(arena->xmol); */
+       }
+       
+       arena->setjmp_buf = NULL;
+
+       if (arena->is_running) {
+               arena->is_running = 0;
+               md_finish(arena);
+       }
+
+       return retval;
+}
+
+void
+md_set_default(MDArena *arena)
+{
+       arena->start_step = 0;
+       arena->end_step = 1000;
+       arena->timestep = 1;
+
+       arena->coord_output_freq = 10;
+       arena->energy_output_freq = 10;
+       arena->cutoff = 9.0;
+       arena->electro_cutoff = 9.0;
+       arena->pairlist_distance = 10.0;
+       arena->use_xplor_shift = 1;
+       arena->scale14_vdw = 0.5;
+       arena->scale14_elect = 0.83;
+       arena->temperature = 300.0;
+       arena->velocities_read = 0;
+       arena->dielectric = 4.8;
+/*     arena->probe_radius = 3.2; */
+       arena->probe_radius = 0.0;
+       arena->surface_tension = -0.005;
+       arena->surface_potential_freq = 5;
+
+       arena->velocity_limit = 100.0;
+       arena->sym_tolerance = 5e-3;
+       arena->gradient_convergence = 1e-6;
+       arena->coordinate_convergence = 1e-8;
+       arena->relocate_center = 1;
+       arena->andersen_thermo_freq = 50;
+       arena->andersen_thermo_coupling = 0.1;
+       arena->pressure_freq = 0;
+/*     arena->pressure_coupling = 0.4;
+       arena->pressure_trial_width = 0.01;
+       arena->pressure_control_algorithm = 0;
+       arena->pressure_fluctuate_cell_origin = 0.01;
+       arena->pressure_fluctuate_cell_orientation = 0.01;
+       arena->pressure[0] = 1;
+       arena->pressure[1] = 0;
+       arena->pressure[2] = 0;
+       arena->pressure[3] = 0;
+       arena->pressure[4] = 1;
+       arena->pressure[5] = 0;
+       arena->pressure[6] = 0;
+       arena->pressure[7] = 0;
+       arena->pressure[8] = 1;
+       arena->cell_flexibility[0] = -1;
+       arena->cell_flexibility[1] = -1;
+       arena->cell_flexibility[2] = -1;
+       arena->cell_flexibility[3] = 0;
+       arena->cell_flexibility[4] = 0;
+       arena->cell_flexibility[5] = 0;
+       arena->cell_flexibility[6] = 0;
+       arena->cell_flexibility[7] = 0; */
+/*     arena->cella.x = 1;
+       arena->cella.y = 0;
+       arena->cella.z = 0;
+       arena->cellb.x = 0;
+       arena->cellb.y = 1;
+       arena->cellb.z = 0;
+       arena->cellc.x = 0;
+       arena->cellc.y = 0;
+       arena->cellc.z = 1; */
+/*     if (arena->mol != NULL && arena->mol->box != NULL)
+               md_cell_recalculate(arena); */
+}
+
+MDArena *
+md_arena_new(Molecule *xmol)
+{
+       MDArena *arena;
+       arena = (MDArena *)calloc(sizeof(MDArena), 1);
+       if (arena == NULL)
+               return NULL;
+       arena->refCount = 1;
+       md_set_default(arena);
+       if (xmol != NULL) {
+               md_arena_set_molecule(arena, xmol);
+       }
+       return arena;
+}
+
+MDArena *
+md_arena_set_molecule(MDArena *arena, Molecule *xmol)
+{
+       /*  xmol is set to mol  */
+       if (arena == NULL)
+               return NULL;
+       if (arena->xmol != xmol) {
+               if (arena->xmol != NULL) {
+                       if (arena->xmol->arena == arena)
+                               arena->xmol->arena = NULL;
+                       MoleculeRelease(arena->xmol);
+               }
+               arena->xmol = xmol;
+               if (xmol != NULL) {
+                       MoleculeRetain(arena->xmol);
+                       arena->xmol->arena = arena;
+               }
+       }
+       
+       /*  Dispose the internal cache  */
+       if (arena->mol != NULL) {
+               MoleculeRelease(arena->mol);
+               arena->mol = NULL;
+       }
+       
+       if (xmol != NULL) {
+               /*  Create an internal copy  */
+               Molecule *mol = MoleculeNew();
+               Atom *ap;
+               int i;
+               memset(mol, 0, sizeof(Molecule));
+               NewArray(&mol->atoms, &mol->natoms, gSizeOfAtomRecord, xmol->natoms);
+               memmove(mol->atoms, xmol->atoms, gSizeOfAtomRecord * xmol->natoms);
+               /*  Note: aniso and frames are unnecessary  */
+               for (i = 0, ap = mol->atoms; i < xmol->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       ap->aniso = NULL;
+                       ap->frames= NULL;
+                       ap->nframes = 0;
+               }
+               NewArray(&mol->bonds, &mol->nbonds, sizeof(Int) * 2, xmol->nbonds);
+               memmove(mol->bonds, xmol->bonds, sizeof(Int) * 2 * xmol->nbonds);
+               NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, xmol->nangles);
+               memmove(mol->angles, xmol->angles, sizeof(Int) * 3 * xmol->nangles);
+               NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, xmol->ndihedrals);
+               memmove(mol->dihedrals, xmol->dihedrals, sizeof(Int) * 4 * xmol->ndihedrals);
+               NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, xmol->nimpropers);
+               memmove(mol->impropers, xmol->impropers, sizeof(Int) * 4 * xmol->nimpropers);
+               NewArray(&mol->syms, &mol->nsyms, sizeof(Transform), xmol->nsyms);
+               memmove(mol->syms, xmol->syms, sizeof(Transform) * xmol->nsyms);
+               if (xmol->cell != NULL) {
+                       mol->cell = (XtalCell *)malloc(sizeof(XtalCell));
+                       memmove(mol->cell, xmol->cell, sizeof(XtalCell));
+               }
+/*             if (xmol->box != NULL) {
+                       mol->box = (PeriodicBox *)malloc(sizeof(PeriodicBox));
+                       memmove(mol->box, xmol->box, sizeof(PeriodicBox));
+               } */
+               mol->arena = arena;
+               mol->par = xmol->par;
+               if (mol->par != NULL)
+                       ParameterRetain(mol->par);
+               arena->mol = mol;
+       }
+       return arena;
+}
+
+MDArena *
+md_arena_retain(MDArena *arena)
+{
+       if (arena != NULL)
+               arena->refCount++;
+       return arena;
+}
+
+void
+md_arena_release(MDArena *arena)
+{
+       int i;
+       if (arena == NULL)
+               return;
+       if (--arena->refCount != 0)
+               return;
+       if (arena->mol != NULL) {
+               if (arena->mol->arena == arena)
+                       arena->mol->arena = NULL;
+               MoleculeRelease(arena->mol);
+       }
+       if (arena->xmol != NULL) {
+               if (arena->xmol->arena == arena)
+                       arena->xmol->arena = NULL;
+               MoleculeRelease(arena->xmol);
+       }
+       if (arena->par != NULL)
+               ParameterRelease(arena->par);
+       if (arena->log_result_name != NULL)
+               free((void *)arena->log_result_name);
+       if (arena->coord_result_name != NULL)
+               free((void *)arena->coord_result_name);
+       if (arena->vel_result_name != NULL)
+               free((void *)arena->vel_result_name);
+       if (arena->force_result_name != NULL)
+               free((void *)arena->force_result_name);
+       if (arena->extend_result_name != NULL)
+               free((void *)arena->extend_result_name);
+       if (arena->debug_result_name != NULL)
+               free((void *)arena->debug_result_name);
+       if (arena->log_result != NULL)
+               fclose(arena->log_result);
+       if (arena->coord_result != NULL)
+               fclose(arena->coord_result);
+       if (arena->vel_result != NULL)
+               fclose(arena->vel_result);
+       if (arena->force_result != NULL)
+               fclose(arena->force_result);
+       if (arena->extend_result != NULL)
+               fclose(arena->extend_result);
+       if (arena->debug_result != NULL)
+               fclose(arena->debug_result);
+       if (arena->custom_bond_pars != NULL)
+               free(arena->custom_bond_pars);
+       if (arena->custom_pars != NULL)
+               free(arena->custom_pars);
+       if (arena->bond_par_i != NULL)
+               free(arena->bond_par_i);
+       if (arena->angle_par_i != NULL)
+               free(arena->angle_par_i);
+       if (arena->dihedral_par_i != NULL)
+               free(arena->dihedral_par_i);
+       if (arena->improper_par_i != NULL)
+               free(arena->improper_par_i);
+       if (arena->vdw_par_i != NULL)
+               free(arena->vdw_par_i);
+       if (arena->vdw_cache != NULL)
+               free(arena->vdw_cache);
+       if (arena->energies != NULL)
+               free(arena->energies);
+       if (arena->forces != NULL)
+               free(arena->forces);
+       if (arena->pair_forces != NULL)
+               free(arena->pair_forces);
+       if (arena->cellsyms != NULL)
+               free(arena->cellsyms);
+       if (arena->pressure != NULL)
+               pressure_release(arena->pressure);
+       if (arena->fragment_indices != NULL)
+               free(arena->fragment_indices);
+       if (arena->fragment_info != NULL)
+               free(arena->fragment_info);
+#warning "TODO: Is arena->exatoms really necessary? "
+       if (arena->exatoms != NULL)
+               free(arena->exatoms);
+       if (arena->special_positions != NULL)
+               free(arena->special_positions);
+       if (arena->exlist != NULL)
+               free(arena->exlist);
+       if (arena->exinfo != NULL)
+               free(arena->exinfo);
+       if (arena->verlets != NULL)
+               free(arena->verlets);
+       if (arena->verlets_dr != NULL)
+               free(arena->verlets_dr);
+       if (arena->verlet_i != NULL)
+               free(arena->verlet_i);
+       if (arena->snapshots != NULL) {
+               for (i = 0; i < arena->nsnapshots; i++) {
+                       if (arena->snapshots[i] != NULL)
+                               free(arena->snapshots[i]);
+               }
+               free(arena->snapshots);
+       }
+       if (arena->old_forces != NULL)
+               free(arena->old_forces);
+       if (arena->old_pos != NULL)
+               free(arena->old_pos);
+       if (arena->graphite != NULL)
+               graphite_release(arena->graphite);
+       free(arena);
+}
+
+/*
+PROGRAM
+{
+       call force(f)
+       do loop=1,nstep
+         v = v + (dt/2) * (f/m)
+         r = r + dt*v;
+         call rattle_coodinate
+         call force(f)
+         v = v + (dt/2) * (f/m)
+         call rattle_velocity
+    enddo
+}
+*/
diff --git a/MolLib/MD/MDCore.h b/MolLib/MD/MDCore.h
new file mode 100644 (file)
index 0000000..9329d3a
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ *  MDCore.h
+ *
+ *  Created by Toshi Nagata on 2005/06/06.
+ *  Copyright 2005 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MDCORE_H__
+#define __MDCORE_H__
+
+#include "../Molecule.h"
+#include "../Parameter.h"
+
+#include <stdarg.h>
+#include <time.h>
+#include <setjmp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+#define UNITCHARGE 1.602e-19
+/*  Vacuum permittability in internal units  */
+#define EPSILON0 0.572798
+/* = 8.854e-12*1e-10/(6.023e23*1e-3*KJ2INTERNAL)/(UNITCHARGE*UNITCHARGE)  */
+/* = 8.854e-12 (C^2 kg^-1 m^-3 s^2) * (UNITCHARGE C/e)^(-2) * (1e10 ang/m)^(-3) * (6.02e26 am/kg)^(-1) * (1e15 fs/s)^2  */
+
+#define COULOMBIC (1/(4*PI*EPSILON0))
+
+/*  1 bar = 1e5 Pa = 1e2 kJ m^(-3) = (1e2*6.023e23*KJ2INTERNAL)internal_eu * (1e10 ang)^(-3) = 1e-32*6.023e23 internal_eu ang^(-3)  */
+#define BAR2INTERNALPRESSURE (6.02e-9)
+
+#define ERROR_out_of_memory "Low memory"
+
+/*  Index enumeration for storing partial energy/force  */
+enum {
+       kBondIndex = 0,
+       kAngleIndex,
+       kDihedralIndex,
+       kImproperIndex,
+       kVDWIndex,
+       kElectrostaticIndex,
+       kAuxiliaryIndex,
+       kSurfaceIndex,
+       kKineticIndex,
+       kEndIndex,
+       kSlowIndex = kSurfaceIndex
+};
+
+/*  Type of custom bond parameter  */
+enum {
+       kTCLProcType = 255,
+       kMorseType = 0
+};
+
+typedef struct MDCustomPar {
+       Byte   type;  /* 0:bond, 1:angle, 2:dihedral, 3:improper, 4:vdw */
+       Int    n1, n2, n3, n4;  /*  Atom indices  */
+       Int    index; /* index of bond_pars, angle_pars, etc. */
+} MDCustomPar;
+
+/*  Verlet list item (for non-bonding force)  */
+typedef struct MDVerlet {
+       Int    n1, n2;
+       Symop symop;         /*  The symmetry operation for the 2nd atom.
+        Only the translational components are used
+        in the force calculations, because the non-
+        translational components should be handled
+        in amend_by_symmetry() operation.  */
+       Int    mult;                /*  How many equivalent interactions will appear */
+       Byte   vdw_type;            /*  0: vdw, 1: scaled (1-4) vdw  */
+       unsigned int index;         /*  The index to arena->vdw_cache  */
+       Double  vdw_cutoff;          /*  Specific vdw cutoff  */
+       /*      Double  vcut;   */           /*  Value at the specific cutoff distance  */
+} MDVerlet;
+
+/*  MDExclusion list  */
+/*  exlist[exinfo[i].index0 .. exinfo[i].index1-1] : special exclusion  */
+/*  exlist[exinfo[i].index1 .. exinfo[i].index2-1] : 1-2 (directly bonded)  */
+/*  exlist[exinfo[i].index2 .. exinfo[i].index3-1] : 1-3  */
+/*  exlist[exinfo[i].index3 .. exinfo[i+1].index0-1] : 1-4  */
+/*  exlist[] is a (long) array of Int; exinfo[] is MDExclusion[natoms+1]; +1 needed because exinfo[natoms].index0 must be accessible  */
+/*  1-4 comes last because it sometimes needs special treatments  */
+typedef struct MDExclusion {
+       Int index0, index1, index2, index3;
+} MDExclusion;
+
+/*  van der Waals parameter cache  */
+typedef struct MDVdwCache {
+       VdwPairPar par;
+       Double vcut, vcut14;  /*  The values at the cutoff distance  */
+} MDVdwCache;
+
+/*  Custom bond parameter  */
+typedef struct MDCustomBondPar {
+       Byte type;
+       union {
+               void *proc;  /*  TCL callback proc (stored as a TCL object)  */
+               /*  The TCL callback proc takes one parameter (interatomic distance) and returns a list of two floating numbers (the energy and force). Force = -d(energy)/dr; note the minus sign. */
+               struct { /*  The Morse potential  */
+                       /*  V(r) = D(1-s)^2, F(r) = -2aDs(1-s); s = exp(-a(r-r0)) */
+                       Double D, a, r0;
+               } morse;
+       } u;
+} MDCustomBondPar;
+
+/*  Snapshots  */
+typedef struct MDSnapshot {
+       Int    step;
+       Int    natoms;
+       Vector rvf[3];    /*  Variable length array; r[0], v[0], f[0], r[1], ... */
+} MDSnapshot;
+
+/*  Group flags  */
+typedef struct MDGroupFlags {
+       unsigned char *flags;
+       int natoms;
+} MDGroupFlags;
+#define group_flag_mask "\001\002\004\010\020\040\100\200"
+#define get_group_flag(gf, n) (n < (gf)->natoms ? (gf)->flags[(n)/8] & group_flag_mask[(n)%8] : 0)
+#define set_group_flag(gf, n, bool) (n < (gf)->natoms ? (bool ? ((gf)->flags[(n)/8] |= group_flag_mask[(n)%8]) : ((gf)->flags[(n)/8] &= ~(group_flag_mask[(n)%8]))) : 0)
+
+/*  Everything needed for MD  */
+typedef struct MDArena {
+       Int refCount;
+       Molecule *xmol;         /*  Molecule given from the outside world  */
+       Molecule *mol;          /*  Private copy of the Molecule for MD calc  */
+
+       const char *coord_input_name;
+       FILE *coord_input;
+       Byte  coord_input_type;
+       Int   coord_input_lineno;
+       Int   coord_input_frame;
+       void *coord_input_data;  /*  pointer to dcd_header_record etc. */
+
+       /*  Results output streams  */
+       const char *log_result_name;
+       const char *coord_result_name;
+       const char *vel_result_name;
+       const char *force_result_name;
+       const char *extend_result_name;
+       const char *debug_result_name;
+       FILE *log_result;
+       FILE *coord_result;
+       FILE *vel_result;
+       FILE *force_result;
+       FILE *extend_result;
+       FILE *debug_result;
+       Int debug_output_level;
+       
+       /*  Error and interrupt handling  */
+       void (*md_panic_func)(struct MDArena *arena, const char *msg);
+       int (*md_callback_func)(struct MDArena *arena);
+       Int callback_freq;
+
+       char errmsg[256];
+       jmp_buf *setjmp_buf;
+       
+       /*  Time  */
+       time_t start_time;
+       
+       /*  MD parameters  */
+       Int step, start_step, end_step;
+       Int coord_output_freq;
+       Int energy_output_freq;
+       Int coord_result_frame;
+       Double timestep;
+       Double cutoff;
+       Double electro_cutoff;
+       Double pairlist_distance;
+       Double temperature;
+       Int rescale_temp_freq;
+       Int reinit_temp_freq;
+       Int velocities_read;
+       Int random_seed;
+       Double dielectric;  /*  Bulk dielectric constant of the medium  */
+       Double gradient_convergence;    /*  Convergent criterion for energy minimization  */
+       Double coordinate_convergence;  /*  Convergent criterion for energy minimization  */
+       Int use_xplor_shift; /* Use X-Plor type shift function? (default = 1)  */
+       
+       Double scale14_vdw;   /* Scaling factor for 1-4 vdw interactions (default = 1)  */
+       Double scale14_elect; /* Scaling factor for 1-4 electrostatic interactions (default = 1) */
+       
+       Int relocate_center; /* Fix center of mass at the original position (default = 1) */
+       Int quench_translational_momentum;
+       Int quench_angular_momentum;
+       Int output_expanded_atoms;  /*  Include symmetry-expanded atoms in output  */
+       
+       /*  Andersen thermostat  */
+       Int andersen_thermo_freq;
+       Double andersen_thermo_coupling;
+       
+       /*  Surface potential  */
+       Double probe_radius;  /*  The probe radius for surface area calculation  */
+       Double surface_tension; /*  The microscopic surface tension for SASA potential  */
+       Int surface_potential_freq;
+//     Int sphere_points;   /*  The number of sphere points  */
+       
+       /*  Attempt to handle _extremely_ distorted structures. */
+       Double anbond_thres;  /* The bond with initial length >= (1+anbond_thres)*r0 is an abnormal bond */
+       Double anbond_anneal_rate; /* For the abnormal bonds, r0 is set to the initial length, and decreased by this amount every timestep */
+       
+       /*  Custom bond potential  */
+       Int    ncustom_bond_pars;
+       MDCustomBondPar *custom_bond_pars;
+       Int    *custom_bond_par_i;  /*  Int[nbonds]  */
+       /*  0: no custom, >=1: custom_bond_pars[i-1]  */
+       
+       /*  Custom parameters  */
+       Int    ncustom_pars;
+       MDCustomPar *custom_pars;
+       
+       /*  Fix atoms  */
+       /*      Int nfix_atoms;
+        fix_atom_record *fix_atoms; */
+       
+       /*  Switch the bond potential from harmonic to linear at r >= (1 + bond_switch) * r0  */
+       /*      Double bond_switch; */
+       /*      Double bond_switch_thres; *//* Bond switch is ON only for those bonds whose initial length >= (1 + bond_switch_thres) * r0  */
+       
+       /*  Artificial, quadratic potential centered at a certain atom - OBSOLETE  */
+       /*      Double  centric_potential_force;
+        Int    centric_potential_center;
+        Double  centric_potential_inner_limit;  *//*  potential = 0 for r < inner_limit  */
+       /*      Double  centric_potential_outer_limit;  *//*  Switch the potential to linear at r > outer_limit  */
+       
+       /*  Spherical boundary condition  */
+       Double  spherical_bc_force;
+       Vector spherical_bc_center;
+       Double  spherical_bc_inner_limit; /*  potential = 0 for r < inner_limit  */
+       Double  spherical_bc_outer_limit; /*  Switch the potential to linear at r > outer_limit  */
+       
+       /*  Artificial box potential centered at the origin  */
+       /*  The box is [-xsize, -ysize, -zsize]-[xsize, ysize, zsize]  */
+       /*  Inside the box potential = 0. Outside the box, a potential k{(x-xsize)^2+(y-ysize)^2+(z-zsize)^2} is applied.  */
+       Double  box_potential_xsize, box_potential_ysize, box_potential_zsize;
+       Double  box_potential_force;
+       
+       /*  Graphite potential  */
+       Int use_graphite;
+
+       /*  Velocity limit  */
+       Double  velocity_limit;  /*  Default = 100  */
+       
+       /*  TCL interface  */
+//     void *tcl_interp;       /*  TCL interpreter; the type is actually (Tcl_Interp *) */
+//     void *tcl_retval;       /*  The return value from the callback procedure; set only by md_abort. */
+//     const char *callback;   /*  TCL callback procedure  */
+       
+       /*  Runtime fields  */
+       
+       /*  Initialize flag  */
+       Byte   is_initialized;
+       Byte   is_running;
+       
+       Byte   request_abort;   /*  If early return is necesarry, assert this flag.  */
+       Byte   minimize_complete;  /*  Becomes non-zero if minimization completed.  */
+
+       Int    natoms_uniq;     /*  Number of symmetry-unique atoms  */
+       
+       /*  Parameters are copied from mol->par and gBuiltinParameters for each call to md_prepare()  */
+       Parameter *par;
+       
+       /*  Number of missing parameters and suspicious parameters  */
+       Int    nmissing;        /*  This must be zero  */
+       Int    nsuspicious;     /*  This can be non-zero, but attention should be paid  */
+       
+       /*  Indices to the parameter records  */
+       Int    *bond_par_i;  /*  The index of the bond parameter record: Int[nbonds]  */
+       Int    *angle_par_i; /*  The index of the angle parameter record: Int[nangles]  */
+       Int    *dihedral_par_i;  /*  The index of the dihedral parameter record: Int[ndihedrals]  */
+       Int    *improper_par_i;  /*  The index of the improper parameter record: Int[nimpropers]  */
+       
+       /*  The van der Waals parameters are precalculated for each pair of atom types  */
+       /*  For each pair, the A/B parameters are calculated from the values for the component types  */
+       /*  If the pair parameters are specifically given those are used  */
+       /*  The precalculated parameters for the atom pair (i,j) are given as 
+           vdw_cache[vdw_par_i[i] * par->nvdwPars + vdw_par_i[j]]  */
+       Int    *vdw_par_i;
+       MDVdwCache *vdw_cache;
+       
+       Double  *anbond_r0;   /*  The r0 parameters for abnormal bonds  */
+       
+       /*  Energies and partial forces  */
+       Double  *energies;    /*  bond, angle, dihedral, improper, */
+       /*  vdw, electrostatic, surface, auxiliary  */
+       Double   total_energy;
+       Vector *forces;      /*  forces[idx*natoms+i], idx=bond, angle, etc. i=atom index */
+       
+       /*  Handling transient temperature  */
+       Int    degree_of_freedom;  /*  3*(natoms - (number of fixed atoms))  */
+       Double  sum_temperature;    /*  For rescaling temperature  */
+       Int    nsum_temperature;   /*  ditto  */
+       Double  transient_temperature;
+       
+       /*  Temporary storage for pair interaction calculations  */
+       /*      char   *group_flags; */
+//     void *group_flags_1, *group_flags_2;  /*  hold the TCL objects  */
+       Double   pair_energies[2];  /* [0]: vdw, [1]: electrostatic */
+       Vector *pair_forces;
+       
+       /*  Symmetry operation  */
+//     Int    nsyms;
+//     Transform  *syms;
+       Transform  *cellsyms;  /*  celltr * syms[i] * rcelltr  */
+       
+       /*  Unit cell vectors  */
+//     Vector cella, cellb, cellc, cello;
+//     Transform celltr;  /*  fractional coordinate -> cartesian  */
+//     Transform rcelltr; /*  cartesian -> fractional coordinate  */
+       
+       /*  Use periodic boundary conditions along a/b/c axes?  */
+       Int    periodic_a, periodic_b, periodic_c;
+       
+       /*  Wrap the coordinates to the unit cell. This affects only the output (not internal coordinates)  */
+       Int    wrap_coordinates;
+       
+       /*  Pressure control  */
+       Int    pressure_freq;
+       struct MDPressureArena *pressure;
+       
+       /*  Fragment informations  */
+       /*  Fragment is a cluster of symmetry-unique atoms that are connected by bonds */
+       Int    nfragments;        /*  Number of fragments  */
+       Int    *fragment_indices; /*  Int[natoms_uniq]; the fragment index for each atom */
+       struct MDFragmentInfo {
+               Vector pos;
+               Double  mass;
+       } *fragment_info;     /*  array[nfragments]; internally used  */
+       
+       /*  "Image" atoms for non-bonding calculations under periodic boundary conditions */
+       Int    nexatoms;          /*  May fluctuate during simulation  */
+       Atom  *exatoms;
+       Int    nexatoms_current;  /*  Current number of "image" atoms  */
+       
+       /*  sym_relate[i*nsyms+j] is the index of atom that coincide with atom i transformed by inverse of syms[j] */
+       /*      Int   *sym_relate; */
+       
+       /*  The tolerance (in angstrom) to find symmetry-equivalent atoms. Default = 5e-3  */
+       Double sym_tolerance;
+       
+       /*  Multiplicity of each atom implied by symmetry operation  */
+       /*      Int   *sym_mult; */
+       
+       /*  The following 4 arrays are allocated as a contiguous chunk. Call free() for the first one only. */
+       /*  Meaning of the value (v) in sym_****_uniq (**** = bond, angle, dihedral, improper):
+        v < 0: the **** is unique and its multiplicity is -v.
+        v >= 0: the **** is not unique and equivalent to v-th ****.  */
+       /*      Int   *sym_bond_uniq;
+        Int   *sym_angle_uniq;
+        Int   *sym_dihedral_uniq;
+        Int   *sym_improper_uniq; */
+       
+       /*  Information for special positions  */
+       Int    nspecial_positions;
+       Int    (*special_positions)[ATOMS_MAX_SYMMETRY];
+       
+       /*  MDExclusion list  */
+       Int    nexlist;
+       Int    *exlist;
+       MDExclusion *exinfo;
+       
+       /*  Verlet list (the pairlist for non-bonding interaction)  */
+       Int    max_nverlets;
+       Int    nverlets;
+       MDVerlet *verlets; /* Variable size; the current limit is max_nverlets */
+       Vector *verlets_dr;  /*  The movement of atoms since the Verlet list was updated last  */
+       Int    last_verlet_step;  /*  The timestep when the Verlet list was updated last */
+       Int    *verlet_i;    /*  The verlet entries for atom i begins at verlet_i[i]  */
+       
+       /*  Save snapshot and restore  */
+       Int    nsnapshots;
+       MDSnapshot **snapshots;
+       
+       /*  For relocating center-of-mass  */
+       Vector initial_center;
+       
+       /*  Minimize: conjugate gradient algorithm  */
+       Vector *old_forces;               /*  The forces in the previous step  */
+       Vector *old_pos;          /*  = old_forces + natoms, The original positions before trial movements  */
+       Double  f_len2;            /*  Sum of square lengths of force  */
+       Double  old_f_len2;        /*  Old f_len2 */
+       Double  v_len2;            /*  Sum of square lengths of "velocity" (= current searching direction)  */
+       Double  max_gradient;      /*  maximum gradient  */
+       Int    conv_flag;         /*  0: not converged, 1: converged by coordinate, 2: converged by gradient  */
+       
+       /*  Surface potential calculation  */
+       struct SPArena *sp_arena;
+//     Double  surface_potential; /*  The surface potential  */
+//     Vector *sp_grad;          /*  The gradient of the surface potential for each atom coordinates */
+       
+       /*  Graphite calculation  */
+       struct MDGraphiteArena *graphite;
+       
+} MDArena;
+
+Double md_rand(void);
+void md_srand(unsigned int seed);
+
+int md_fit_coordinates(MDArena *arena, Int refno, Double *weights, Transform trans);
+
+void md_scale_velocities(MDArena *arena);
+void md_init_velocities(MDArena *arena);
+/* int md_symmetry_relate(MDArena *arena, int n1, int n2, int sym_op); */
+void md_transform_vec_by_symmetry(MDArena *arena, Vector *dst, const Vector *src, Symop rec, int no_transform);
+
+void md_init_for_positions(MDArena *arena);
+const char *md_prepare(MDArena *arena, int check_only);
+int md_check_abnormal_bond(MDArena *arena, Molecule *mol, int idx);
+int md_check_abnormal_angle(MDArena *arena, Molecule *mol, int idx);
+int md_check_abnormal_dihedral(MDArena *arena, Molecule *mol, int idx);
+int md_check_abnormal_improper(MDArena *arena, Molecule *mol, int idx);
+
+void md_amend_by_symmetry(MDArena *arena);
+void md_cell_recalculate(MDArena *arena);
+void md_scale_cell(MDArena *arena, const Transform tf, int scale_atoms);
+void md_wrap_coordinates(MDArena *arena);
+
+void md_snapshot(MDArena *arena, int idx);
+void md_restore(MDArena *arena, int idx);
+
+void md_calc_kinetic_energy(MDArena *arena);
+
+int md_output_results(MDArena *arena);
+
+int md_copy_coordinates_from_internal(MDArena *arena);
+
+int md_is_running(MDArena *arena);
+       
+int md_main(MDArena *arena, int minimize);
+void md_set_default(MDArena *arena);
+
+MDArena *md_arena_new(Molecule *mol);
+MDArena *md_arena_set_molecule(MDArena *arena, Molecule *mol);
+MDArena *md_arena_retain(MDArena *arena);
+void md_arena_release(MDArena *arena);
+void md_finish(MDArena *arena);
+
+void md_panic(MDArena *arena, const char *fmt,...);
+void md_debug(MDArena *arena, const char *fmt,...);
+int md_log(MDArena *arena, const char *fmt, ...);
+int md_warning(MDArena *arena, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __MDCORE_H__ */
diff --git a/MolLib/MD/MDForce.c b/MolLib/MD/MDForce.c
new file mode 100644 (file)
index 0000000..a0796b2
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ *  MDForce.c
+ *
+ *  Created by Toshi Nagata on 2005/06/07.
+ *  Copyright 2005 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MDForce.h"
+#include "MDGraphite.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+extern int do_custom_bond_callback(MDArena *arena, Double r, void *procObj, Double *energy, Double *force);
+
+static void
+s_custom_bond_force(MDArena *arena, Double r, MDCustomBondPar *cp, Double *energy, Double *force)
+{
+       float s;
+       switch (cp->type) {
+               case kMorseType:
+                       s = exp(-cp->u.morse.a * (r - cp->u.morse.r0));
+                       *energy = cp->u.morse.D * (1.0 + s * (s - 2.0));
+                       *force = -2.0 * cp->u.morse.a * cp->u.morse.D * s * (1.0 - s);
+                       break;
+               case kTCLProcType:
+               /*      retval = do_custom_bond_callback(arena, r, cp->u.proc, energy, force);
+                       if (retval != 0)
+                               md_panic(arena, NULL); */
+                       break;
+               default:
+                       *energy = *force = 0.0;
+                       break;
+       }
+}
+
+static void
+s_calc_bond_force(MDArena *arena)
+{
+       Atom *ap = arena->mol->atoms;
+       Int nbonds = arena->mol->nbonds;
+       Int *bonds = arena->mol->bonds;
+       Int *bond_par_i = arena->bond_par_i;
+       Int *custom_bond_par_i = arena->custom_bond_par_i;
+       BondPar *bond_pars = arena->par->bondPars;
+       Double *anbond_r0 = arena->anbond_r0;
+       Double *energies = &arena->energies[kBondIndex];
+       Vector *forces = &arena->forces[kBondIndex * arena->mol->natoms];
+       int i;
+       for (i = 0; i < nbonds; i++, bonds += 2, bond_par_i++) {
+               Vector r12;
+               Double k0, k1, w1, w2, r0;
+               Int idx;
+               BondPar *bp;
+               if (*bond_par_i < 0)
+                       continue;  /*  Ignore this entry  */
+       /*      if (bond_uniq != NULL && bond_uniq[i] >= 0)
+                       continue;  *//*  Non-unique bond  */
+               VecSub(r12, ap[bonds[1]].r, ap[bonds[0]].r);
+               w1 = VecLength(r12);
+               if (custom_bond_par_i != NULL && (idx = custom_bond_par_i[i]) > 0 && idx <= arena->ncustom_bond_pars) {
+                       Double energy, force;
+                       s_custom_bond_force(arena, w1, arena->custom_bond_pars + (idx - 1), &energy, &force);
+                       k0 = energy;
+                       k1 = force / w1;
+                       r0 = 0.0;  /*  To keep complier happy  */
+               } else {
+                       bp = bond_pars + *bond_par_i;
+                       r0 = bp->r0;
+                       if (anbond_r0 != NULL) {
+                               if (anbond_r0[i] > 0.0)
+                                       r0 += anbond_r0[i];
+                       }
+                       w2 = w1 - r0;
+                       k0 = bp->k * w2 * w2;         /*  Energy  */
+                       k1 = -2.0 * bp->k * w2 / w1;  /*  Force / r  */
+               }
+               VecScaleSelf(r12, k1);
+               *energies += k0;
+               VecDec(forces[bonds[0]], r12);
+               VecInc(forces[bonds[1]], r12);
+               if (arena->debug_result && arena->debug_output_level > 1) {
+                       fprintf(arena->debug_result, "bond force %d-%d: r=%f, r0=%f, k1=%f, {%f %f %f}\n", bonds[0]+1, bonds[1]+1, w1, r0, k1, r12.x, r12.y, r12.z);
+               }
+       }
+}
+
+static void
+s_calc_angle_force(MDArena *arena)
+{
+       Atom *ap = arena->mol->atoms;
+       Int nangles = arena->mol->nangles;
+       Int *angles = arena->mol->angles;
+       Int *angle_par_i = arena->angle_par_i;
+       AnglePar *angle_pars = arena->par->anglePars;
+/*     Int *angle_uniq = (arena->nsyms > 0 ? arena->sym_angle_uniq : NULL); */
+       Double *energies = &arena->energies[kAngleIndex];
+       Vector *forces = &arena->forces[kAngleIndex * arena->mol->natoms];
+       int i;
+       for (i = 0; i < nangles; i++, angles += 3, angle_par_i++) {
+               Vector r21, r23, f1, f2, v1;
+               Double k0, k1, w1, w2, w3;
+               Double cost, sint, t;
+               AnglePar *anp;
+               if (*angle_par_i < 0)
+                       continue;  /*  Ignore this entry  */
+       /*      if (angle_uniq != NULL && angle_uniq[i] >= 0)
+                       continue; */ /*  Non-unique angle  */
+               anp = angle_pars + *angle_par_i;
+               VecSub(r21, ap[angles[0]].r, ap[angles[1]].r);
+               VecSub(r23, ap[angles[2]].r, ap[angles[1]].r);
+               w1 = 1.0 / VecLength(r21);
+               w2 = 1.0 / VecLength(r23);
+               VecScaleSelf(r21, w1);
+               VecScaleSelf(r23, w2);
+               cost = VecDot(r21, r23);
+               if (cost > 0.999999 || cost < -0.999999) {
+               /*      printf("Cannot handle linear angle %d-%d-%d: skipped.\n", angles[0]+1, angles[1]+1, angles[2]+1); */
+                       continue;  /*  Cannot handle linear angle  */
+               }
+               sint = sqrt(1.0 - cost * cost);
+       /*      if (sint < 1e-5 && sint > -1e-5)
+                       continue;  *//*  Cannot handle linear angle  */
+               t = atan2(sint, cost) - anp->a0;
+               k0 = anp->k * t * t;
+       
+               if (sint < 0.1 && sint > -0.1) {
+                       /* ---- Method 1 ---- */
+                       /* This is slower than method 2, but it is safer when sint is close to 0 */
+                       k1 = -2 * anp->k * t;
+                       VecCross(v1, r21, r23);
+                       VecCross(f1, r21, v1);
+                       w3 = w1 * k1 / VecLength(f1);
+                       if (!isfinite(w3))
+                               continue;
+                       VecScaleSelf(f1, w3);
+                       VecCross(f2, v1, r23);
+                       w3 = w2 * k1 / VecLength(f2);
+                       if (!isfinite(w3))
+                               continue;
+                       VecScaleSelf(f2, w3);
+               } else {
+                       /* ---- Method 2 ---- */
+                       k1 = -2 * anp->k * t / sint;
+                       VecScale(f1, r21, cost);
+                       VecDec(f1, r23);
+                       w3 = w1 * k1;
+                       VecScaleSelf(f1, w3);
+                       VecScale(f2, r23, cost);
+                       VecDec(f2, r21);
+                       w3 = w2 * k1;
+                       VecScaleSelf(f2, w3);
+               }
+
+               *energies += k0;
+               VecInc(forces[angles[0]], f1);
+               VecInc(forces[angles[2]], f2);
+               VecInc(f1, f2);
+               VecDec(forces[angles[1]], f1);
+
+               if (arena->debug_result && arena->debug_output_level > 1) {
+                       fprintf(arena->debug_result, "angle force %d-%d-%d: a=%f, a0=%f, k1=%f, {%f %f %f}, {%f %f %f}\n", angles[0]+1, angles[1]+1, angles[2]+1, (t+anp->a0)*180/PI, anp->a0*180/PI, k1, f1.x, f1.y, f1.z, f2.x, f2.y, f2.z);
+               }
+
+       }
+}
+
+static void
+s_calc_dihedral_force_sub(MDArena *arena, Atom *ap, Int ndihedrals, Int *dihedrals, Int *dihedral_par_i, TorsionPar *dihedral_pars, Int *dihedral_uniq, Double *energies, Vector *forces)
+{
+       int i;
+       if (arena->mol->nsyms == 0)
+               dihedral_uniq = NULL;  /*  Ignore the symmetry info  */
+       for (i = 0; i < ndihedrals; i++, dihedrals += 4, dihedral_par_i++) {
+               Vector r21, r32, r43;
+               Vector v1, v2, v3;
+               Double w1, w2, w3, k0, k1;
+               Double cosphi, sinphi, phi;
+               Vector f1, f2, f3;
+               TorsionPar *tp;
+               int n;
+
+               if (*dihedral_par_i < 0)
+                       continue;  /*  Ignore this entry  */
+       /*      if (dihedral_uniq != NULL && dihedral_uniq[i] >= 0)
+                       continue;  *//*  Non-unique dihedral  */
+               else tp = dihedral_pars + *dihedral_par_i;
+               VecSub(r21, ap[dihedrals[0]].r, ap[dihedrals[1]].r);
+               VecSub(r32, ap[dihedrals[1]].r, ap[dihedrals[2]].r);
+               VecSub(r43, ap[dihedrals[2]].r, ap[dihedrals[3]].r);
+               VecCross(v1, r21, r32);
+               VecCross(v2, r32, r43);
+               VecCross(v3, r32, v1);
+               w1 = VecLength(v1);
+               w2 = VecLength(v2);
+               w3 = VecLength(v3);
+               if (w1 < 1e-5 || w2 < 1e-5 || w3 < 1e-5)
+                       continue;  /*  The dihedral cannot be defined  */
+               w1 = 1.0/w1;
+               w2 = 1.0/w2;
+               w3 = 1.0/w3;
+               VecScaleSelf(v1, w1);
+               VecScaleSelf(v2, w2);
+               VecScaleSelf(v3, w3);
+               
+               /*  The dihedral angle value  */
+               cosphi = VecDot(v1, v2);
+               sinphi = VecDot(v3, v2);
+               phi = -atan2(sinphi, cosphi);
+               
+               /*  Repeat for multiple dihedral terms  */
+               k0 = k1 = 0.0;
+               for (n = 0; n < tp->mult; n++) {
+                       Double k = tp->k[n];
+                       Double phi0 = tp->phi0[n];
+                       Int period = tp->period[n];
+                       if (period > 0) {
+                               k0 += k * (1 + cos(period * phi + phi0));
+                               k1 -= period * k * sin(period * phi + phi0);
+                       } else {
+                               /*  A simple quadratic term  */
+                               phi -= phi0;
+                               if (phi < -PI)
+                                       phi += 2 * PI;
+                               else if (phi > PI)
+                                       phi -= 2 * PI;
+                               k0 += k * phi * phi;
+                               k1 += 2 * k * phi;
+                       }
+               }
+               
+               if (sinphi < -0.1 || sinphi > 0.1) {
+                       /*  The sin form  */
+                       Vector v4, v5, vw0;
+                       VecScale(v4, v1, cosphi);
+                       VecDec(v4, v2);
+                       VecScaleSelf(v4, w1);
+                       VecScale(v5, v2, cosphi);
+                       VecDec(v5, v1);
+                       VecScaleSelf(v5, w2);
+                       k1 /= sinphi;
+                       VecCross(f1, r32, v4);
+                       VecCross(f3, v5, r32);
+                       VecCross(f2, v4, r21);
+                       VecCross(vw0, r43, v5);
+                       VecInc(f2, vw0);
+                       VecScaleSelf(f1, k1);
+                       VecScaleSelf(f2, k1);
+                       VecScaleSelf(f3, k1);
+               } else {
+                       /*  The cos form  */
+                       Vector v6, v7, vw1, vw2;
+                       VecScale(v6, v3, sinphi);
+                       VecDec(v6, v2);
+                       VecScaleSelf(v6, w3);
+                       VecScale(v7, v2, sinphi);
+                       VecDec(v7, v3);
+                       VecScaleSelf(v7, w2);
+                       k1 = -k1 / cosphi;
+                       VecCross(vw1, v6, r32);
+                       VecCross(f1, r32, vw1);
+                       VecCross(f3, v7, r32);
+                       VecCross(f2, r43, v7);
+                       VecCross(vw1, r32, r21);
+                       VecCross(vw2, v6, vw1);
+                       VecInc(f2, vw2);
+                       VecCross(vw1, r32, v6);
+                       VecCross(vw2, r21, vw1);
+                       VecInc(f2, vw2);
+                       VecScaleSelf(f1, k1);
+                       VecScaleSelf(f2, k1);
+                       VecScaleSelf(f3, k1);
+               }
+       /*      if (dihedral_uniq != NULL)
+                       k0 *= -dihedral_uniq[i]; */
+               *energies += k0;
+               VecInc(forces[dihedrals[0]], f1);
+               VecDec(f1, f2);
+               VecDec(forces[dihedrals[1]], f1);
+               VecDec(f2, f3);
+               VecDec(forces[dihedrals[2]], f2);
+               VecDec(forces[dihedrals[3]], f3);
+
+               if (arena->debug_result && arena->debug_output_level > 1) {
+                       fprintf(arena->debug_result, "dihedral(improper) force %d-%d-%d-%d: phi=%f, k1=%f, {%f %f %f}, {%f %f %f}, {%f %f %f}\n", dihedrals[0]+1, dihedrals[1]+1, dihedrals[2]+1, dihedrals[3]+1, phi*180/PI, k1, f1.x, f1.y, f1.z, f2.x, f2.y, f2.z, f3.x, f3.y, f3.z);
+               }
+       }
+}
+
+static void
+s_calc_dihedral_force(MDArena *arena)
+{
+       Molecule *mol = arena->mol;
+       Parameter *par = arena->par;
+       Double *energies = &arena->energies[kDihedralIndex];
+       Vector *forces = &arena->forces[kDihedralIndex * arena->mol->natoms];
+       s_calc_dihedral_force_sub(arena, mol->atoms, mol->ndihedrals, mol->dihedrals, arena->dihedral_par_i, par->dihedralPars, NULL/*arena->sym_dihedral_uniq*/, energies, forces);
+}
+
+static void
+s_calc_improper_force(MDArena *arena)
+{
+       Molecule *mol = arena->mol;
+       Parameter *par = arena->par;
+       Double *energies = &arena->energies[kImproperIndex];
+       Vector *forces = &arena->forces[kImproperIndex * arena->mol->natoms];
+       s_calc_dihedral_force_sub(arena, mol->atoms, mol->nimpropers, mol->impropers, arena->improper_par_i, par->improperPars, NULL/*arena->sym_improper_uniq*/, energies, forces);
+}
+
+/*  Update the Verlet list (for non-bonding interaction)  */
+static void
+s_make_verlet_list(MDArena *arena)
+{
+       int i, j, k, n, natoms;
+       int dx, dy, dz, ndx, ndy, ndz;
+       Molecule *mol = arena->mol;
+       Atom *atoms = mol->atoms;
+       Atom *api, *apj;
+       MDVerlet *vl = arena->verlets;
+       Vector *vdr = arena->verlets_dr;
+       Double limit;
+       Byte use_sym;
+       XtalCell *cell = mol->cell;
+
+       natoms = mol->natoms;
+       for (i = 0; i < natoms; i++)
+               VecZero(vdr[i]);
+
+       ndx = (arena->periodic_a ? 1 : 0);
+       ndy = (arena->periodic_b ? 1 : 0);
+       ndz = (arena->periodic_c ? 1 : 0);
+
+       limit = arena->pairlist_distance * arena->pairlist_distance;
+       n = 0;
+       use_sym = (arena->mol->nsyms > 0);
+       for (i = 0, api = atoms; i < natoms; i++, api++) {
+               MDExclusion *exinfo = arena->exinfo + i;
+               Int index4 = (exinfo + 1)->index0;
+               Int vdw_idx1 = arena->vdw_par_i[i];
+               arena->verlet_i[i] = n;
+
+       /*      for (dx = -ndx; dx <= ndx; dx++) {
+                       for (dy = -ndy; dy <= ndy; dy++) {
+                               for (dz = -ndz; dz <= ndz; dz++) { */
+
+
+               for (j = i + 1, apj = atoms + j; j < natoms; j++, apj++) {
+                       Vector rij;
+                       Double lenij2, w;
+                       Int *ip, index0;
+                       Double *rtp;
+                       int exflag = 0;
+                       int mult = 1;
+               /*      int uniq; */
+
+                       /*  Fixed atoms  */
+                       if (api->fix_force < 0 && apj->fix_force < 0)
+                               continue;
+
+                       VecSub(rij, apj->r, api->r);
+
+                       /*  Calculate the cell offset for the nearest neighbor  */
+                       /*  NOTE: the offset is calculated independently for each axis. This may result
+                           in unexpected choice when the angles between the axes are far from 90 deg */
+                       if (apj->periodic_exclude == 0) {
+                               rtp = cell->rtr;
+                               if (arena->periodic_a) {
+                                       w = rtp[0] * rij.x + rtp[1] * rij.y + rtp[2] * rij.z;
+                                       dx = floor(-w + 0.5);
+                               } else dx = 0;
+                               if (arena->periodic_b) {
+                                       w = rtp[3] * rij.x + rtp[4] * rij.y + rtp[5] * rij.z;
+                                       dy = floor(-w + 0.5);
+                               } else dy = 0;
+                               if (arena->periodic_c) {
+                                       w = rtp[6] * rij.x + rtp[7] * rij.y + rtp[8] * rij.z;
+                                       dz = floor(-w + 0.5);
+                               } else dz = 0;
+                       } else dx = dy = dz = 0;
+
+                       /*  Non unique atom pair  */
+               /*      if (use_sym && api->symop.alive && apj->symop.alive)
+                               continue; */
+                       
+                       if (dx == 0 && dy == 0 && dz == 0) {
+                               /*  Pair within the unit cell  */
+                       /*      if (i >= j)
+                                       continue; */
+                               /*  Is this pair to be excluded?  */
+                               for (index0 = exinfo->index0, ip = arena->exlist + index0; index0 < index4; index0++, ip++) {
+                                       if (*ip == j)
+                                               break;
+                               }
+                               if (index0 < exinfo->index3)
+                                       continue;  /*  Special exclusion, 1-2, 1-3  */
+                               if (index0 < index4)
+                                       exflag = 1;  /*  1-4 interaction  */
+                       } else {
+                               VecScaleInc(rij, cell->axes[0], dx);
+                               VecScaleInc(rij, cell->axes[1], dy);
+                               VecScaleInc(rij, cell->axes[2], dz);
+                               exflag = 0;
+                       /*      if (i != j)
+                                       mult = 2; */
+                       }
+
+                       lenij2 = VecLength2(rij);
+                       if (lenij2 <= limit) {
+                               Parameter *par = arena->par;
+                               Int vdw_idx2 = arena->vdw_par_i[j];
+                               Int vdw_idx;
+                               Double vdw_cutoff = arena->cutoff;
+                               if (vdw_idx1 < 0 || vdw_idx2 < 0)
+                                       vdw_idx = par->nvdwPars * par->nvdwPars;  /*  A null record  */
+                               else if (vdw_idx1 < vdw_idx2)
+                                       vdw_idx = vdw_idx1 * par->nvdwPars + vdw_idx2;
+                               else
+                                       vdw_idx = vdw_idx2 * par->nvdwPars + vdw_idx1;
+                               /*  Check the specific cutoff table  */
+                               for (k = par->nvdwCutoffPars - 1; k >= 0; k--) {
+                                       VdwCutoffPar *cr = par->vdwCutoffPars + k;
+                                       if (cr->type == 0) {
+                                               if (((cr->n1 < 0 || cr->n1 == api->type) && (cr->n2 < 0 || cr->n2 == apj->type)) ||
+                                                       ((cr->n1 < 0 || cr->n1 == apj->type) && (cr->n2 < 0 || cr->n2 == api->type))) {
+                                                       vdw_cutoff = cr->cutoff;
+                                                       break;
+                                               }
+                                       } else {
+                                               if ((cr->n1 <= i && i <= cr->n2 && cr->n3 <= j && j <= cr->n4)||
+                                                       (cr->n3 <= i && i <= cr->n4 && cr->n1 <= j && j <= cr->n2)) {
+                                                       vdw_cutoff = cr->cutoff;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (vdw_cutoff < 0) {
+                                       /*  A negative value of specific cutoff means "cutoff at r_eq * (-specific_cutoff)  */
+                                       MDVdwCache *cp = &(arena->vdw_cache[vdw_idx]);
+                                       Double r_eq = pow(cp->par.A / cp->par.B * 2.0, 1.0/6.0);
+                                       vdw_cutoff = r_eq * (-vdw_cutoff);
+                               }
+                               if (n >= arena->max_nverlets) {
+                                       arena->max_nverlets += 32;
+                                       vl = (MDVerlet *)realloc(vl, sizeof(MDVerlet) * arena->max_nverlets);
+                                       if (vl == NULL)
+                                               md_panic(arena, "Low memory");
+                               }
+                               vl[n].vdw_type = (exflag ? 1 : 0);
+                               vl[n].mult = mult;
+                               vl[n].symop.dx = dx;
+                               vl[n].symop.dy = dy;
+                               vl[n].symop.dz = dz;
+                               vl[n].symop.sym = 0;
+                               vl[n].symop.alive = (dx != 0 || dy != 0 || dz != 0);
+                               vl[n].index = vdw_idx;
+                               vl[n].n1 = i;
+                               vl[n].n2 = j;
+                               vl[n].vdw_cutoff = vdw_cutoff;
+                               n++;
+                       } /* end if lenij2 <= limit */
+               } /* end loop j */
+
+       } /* end loop i */
+       arena->verlet_i[natoms] = n;
+       arena->nverlets = n;
+       arena->verlets = vl;
+       arena->last_verlet_step = arena->step;
+               
+       if (arena->debug_result && arena->debug_output_level > 2) {
+               fprintf(arena->debug_result, "\n  Verlet list at step %d\n", arena->step);
+               fprintf(arena->debug_result, "  {atom1 atom2 vdw_type (=1 if 1-4 bonded) vdw_index}\n");
+               for (i = 0; i < arena->nverlets; i++) {
+                       fprintf(arena->debug_result, "{%d %d %d %d}%c", vl[i].n1+1, vl[i].n2+1, vl[i].vdw_type, vl[i].index, (i % 4 == 3 ? '\n' : ' '));
+               }
+               if (i % 4 != 0)
+                       fprintf(arena->debug_result, "\n");
+       }
+}
+
+/*  Calculate the nonbonded force  */
+/*  group_flags[] is used for calculation of pair-specific interaction between
+    two groups of atoms  */
+static void
+s_calc_nonbonded_force_sub(MDArena *arena, Double *energies, Double *eenergies, Vector *forces, Vector *eforces, const MDGroupFlags *group_flags_1, const MDGroupFlags *group_flags_2)
+{
+       int i;
+       Vector *vdr;
+       Double limit, elimit, dielec_r;
+       MDVerlet *vl;
+/*     MDVdwCache *vdw_cache = arena->vdw_cache; */
+       Atom *atoms = arena->mol->atoms;
+       XtalCell *cell = arena->mol->cell;
+
+       /*  Check whether the Verlet list needs update  */
+       if (arena->last_verlet_step >= 0) {
+               i = arena->mol->natoms - 1;
+               vdr = arena->verlets_dr + i;
+               if (arena->cutoff > arena->electro_cutoff)
+                       limit = (arena->pairlist_distance - arena->cutoff) * 0.5;
+               else
+                       limit = (arena->pairlist_distance - arena->electro_cutoff) * 0.5;
+               limit = limit * limit;
+               for ( ; i >= 0; i--, vdr--) {
+                       if (VecLength2(*vdr) >= limit)
+                               break;
+               }
+       } else i = 0;
+       if (i >= 0)
+               s_make_verlet_list(arena);
+       
+       /*  Calculate the non-bonded interaction for each pair in the Verlet list  */
+/*     limit = arena->cutoff * arena->cutoff; */
+       elimit = arena->electro_cutoff * arena->electro_cutoff;
+       dielec_r = COULOMBIC / arena->dielectric;
+       for (i = arena->nverlets - 1, vl = arena->verlets + i; i >= 0; i--, vl--) {
+               Double A, B, vofs, k0, k1;
+               MDVdwCache *vp = arena->vdw_cache + vl->index;
+               Atom *ap1, *ap2;
+               Vector rij, fij;
+               Double r2, w2, w6, w12;
+               if (group_flags_1 != NULL && group_flags_2 != NULL) {
+                       if (!(get_group_flag(group_flags_1, vl->n1) && get_group_flag(group_flags_2, vl->n2))
+                       && !(get_group_flag(group_flags_1, vl->n2) && get_group_flag(group_flags_2, vl->n1)))
+                               continue;
+               }
+               if (vl->vdw_type == 1) {
+                       A = vp->par.A14;
+                       B = vp->par.B14;
+               /*      vofs = vp->vcut14; */
+               } else {
+                       A = vp->par.A;
+                       B = vp->par.B;
+               /*      vofs = vp->vcut; */
+               }
+               ap1 = &atoms[vl->n1];
+               ap2 = &atoms[vl->n2];
+               rij = ap2->r;
+               if (vl->symop.alive) {
+                       VecScaleInc(rij, cell->axes[0], vl->symop.dx);
+                       VecScaleInc(rij, cell->axes[1], vl->symop.dy);
+                       VecScaleInc(rij, cell->axes[2], vl->symop.dz);
+               }
+               VecDec(rij, ap1->r);
+               r2 = VecLength2(rij);
+               if (r2 >= elimit)
+                       continue;
+               limit = vl->vdw_cutoff * vl->vdw_cutoff;
+               if (r2 >= limit)
+                       continue;
+               
+               fij.x = fij.y = fij.z = 0.0;
+               
+               /*  Coulombic force  */
+               w12 = ap1->charge * ap2->charge * dielec_r;
+               if (w12 != 0.0) {
+                       if (arena->use_xplor_shift) {
+                               w2 = 1.0 / sqrt(r2);
+                               k0 = r2 / elimit - 1.0;
+                               k0 = k0 * k0;
+                               k0 = w12 * k0 * w2;
+                               k1 = (3.0 * r2 / elimit - 2.0) / elimit - 1.0 / r2;
+                               k1 = -w12 * k1 * w2;
+                       } else {
+                               w2 = 1.0 / sqrt(r2);
+                               w6 = w2 / r2;
+                               k0 = w12 * w2;
+                               k1 = w12 * w6;
+                               vofs = -w12 / arena->electro_cutoff;
+                               k0 += vofs;
+                       }
+                       if (vl->vdw_type == 1) {
+                               k0 *= arena->scale14_elect;
+                               k1 *= arena->scale14_elect;
+                       }
+                       VecScale(fij, rij, k1);
+                       *eenergies += k0 / vl->mult;
+                       if (eforces != NULL) {
+                               VecDec(eforces[vl->n1], fij);
+                               VecInc(eforces[vl->n2], fij);
+                       }
+                       if (arena->debug_result && arena->debug_output_level > 1) {
+                               fprintf(arena->debug_result, "nonbonded(electrostatic) force %d-%d: r=%f, k0=%f, k1=%f, {%f %f %f}\n", vl->n1+1, vl->n2+1, sqrt(r2), k0/KCAL2INTERNAL, k1*sqrt(r2)/KCAL2INTERNAL, fij.x/KCAL2INTERNAL, fij.y/KCAL2INTERNAL, fij.z/KCAL2INTERNAL);
+                       }
+               }
+
+               /*  van der Waals force  */
+               w2 = 1.0 / r2;
+               w6 = w2 * w2 * w2;
+               w12 = w6 * w6;
+               k0 = A * w12 - B * w6;
+               k1 = 6 * w2 * (2.0 * A * w12 - B * w6);
+               w2 = 1.0 / limit;
+               w6 = w2 * w2 * w2;
+               w12 = w6 * w6;
+               vofs = -A * w12 + B * w6;
+               k0 += vofs;
+               if (vl->vdw_type == 1) {
+                       k0 *= arena->scale14_vdw;
+                       k1 *= arena->scale14_vdw;
+               }
+               VecScale(fij, rij, k1);
+               *energies += k0 / vl->mult;
+               if (forces != NULL) {
+                       VecDec(forces[vl->n1], fij);
+                       VecInc(forces[vl->n2], fij);
+               }
+               if (arena->debug_result && arena->debug_output_level > 1) {
+                       fprintf(arena->debug_result, "nonbonded(vdw) force %d-%d: r=%f, k0=%f, k1=%f, {%f %f %f}\n", vl->n1+1, vl->n2+1, sqrt(r2), k0/KCAL2INTERNAL, k1*sqrt(r2)/KCAL2INTERNAL, fij.x/KCAL2INTERNAL, fij.y/KCAL2INTERNAL, fij.z/KCAL2INTERNAL);
+               }
+       }
+}
+
+static void
+s_calc_nonbonded_force(MDArena *arena)
+{
+       Double *energies = &arena->energies[kVDWIndex];
+       Double *eenergies = &arena->energies[kElectrostaticIndex];
+       Vector *forces = &arena->forces[kVDWIndex * arena->mol->natoms];
+       Vector *eforces = &arena->forces[kElectrostaticIndex * arena->mol->natoms];
+       s_calc_nonbonded_force_sub(arena, energies, eenergies, forces, eforces, NULL, NULL);
+}
+
+static void
+s_calc_auxiliary_force(MDArena *arena)
+{
+       Double *energies = &arena->energies[kAuxiliaryIndex];
+       Vector *forces = &arena->forces[kAuxiliaryIndex * arena->mol->natoms];
+       Atom *atoms = arena->mol->atoms;
+       int natoms = arena->mol->natoms;
+       int i, j;
+
+       /*  Centric force - OBSOLETE */
+/*     if (arena->centric_potential_force > 0) {
+               Vector center = arena->mol->atoms[arena->centric_potential_center].r;
+               Double rin = arena->centric_potential_inner_limit;
+               Double rout = arena->centric_potential_outer_limit;
+               Double k = arena->centric_potential_force;
+               for (i = 0; i < natoms; i++) {
+                       Vector r21;
+                       Double w1, w2, k0, k1;
+                       VecSub(r21, atoms[i].r, center);
+                       w2 = VecLength2(r21);
+                       w1 = sqrt(w2);
+                       if (w1 <= rin) {
+                               k0 = k1 = 0.0;
+                       } else if (rout > 0 && w1 > rout) {
+                               k0 = k * (rout - rin) * (w1 + w1 - rout + rin);
+                               k1 = -2.0 * k * (rout - rin) / w1;
+                       } else {
+                               k0 = k * (w1 - rin) * (w1 - rin);
+                               k1 = -2.0 * k * (1 - rin / w1);
+                       }
+                       *energies += k0;
+                       VecScaleSelf(r21, k1);
+                       VecInc(forces[i], r21);
+#if DEBUG
+                       if (arena->debug_result && arena->debug_output_level > 1) {
+                               fprintf(arena->debug_result, "auxiliary(centric) force %d: r=%f, k1=%f, {%f %f %f}\n", i, w1, k1*w1, r21.x, r21.y, r21.z);
+                       }
+#endif
+               }
+       }
+       */
+
+       /*  Spherical boundary conditions  */
+       if (arena->spherical_bc_force > 0) {
+               Vector center = arena->spherical_bc_center;
+               Double rin = arena->spherical_bc_inner_limit;
+               Double rout = arena->spherical_bc_outer_limit;
+               Double k = arena->spherical_bc_force;
+               for (i = 0; i < natoms; i++) {
+                       Vector r21;
+                       Double w1, w2, k0, k1;
+                       VecSub(r21, atoms[i].r, center);
+                       w2 = VecLength2(r21);
+                       w1 = sqrt(w2);
+                       if (w1 <= rin) {
+                               k0 = k1 = 0.0;
+                       } else if (rout > 0 && w1 > rout) {
+                               k0 = k * (rout - rin) * (w1 + w1 - rout + rin);
+                               k1 = -2.0 * k * (rout - rin) / w1;
+                       } else {
+                               k0 = k * (w1 - rin) * (w1 - rin);
+                               k1 = -2.0 * k * (1 - rin / w1);
+                       }
+                       *energies += k0;
+                       VecScaleSelf(r21, k1);
+                       VecInc(forces[i], r21);
+                       if (arena->debug_result && arena->debug_output_level > 1) {
+                               fprintf(arena->debug_result, "auxiliary(spherical BC) force %d: r=%f, k1=%f, {%f %f %f}\n", i, w1, k1*w1, r21.x, r21.y, r21.z);
+                       }
+               }
+       }
+
+       /*  Box force  */
+       if (arena->box_potential_force > 0) {
+               Double xsize = arena->box_potential_xsize;
+               Double ysize = arena->box_potential_ysize;
+               Double zsize = arena->box_potential_zsize;
+               Double k = arena->box_potential_force;
+               for (i = 0; i < natoms; i++) {
+                       Vector r = atoms[i].r;
+                       if (r.x > xsize)
+                               r.x -= xsize;
+                       else if (r.x < -xsize)
+                               r.x += xsize;
+                       else r.x = 0.0;
+                       if (r.y > ysize)
+                               r.y -= ysize;
+                       else if (r.y < -ysize)
+                               r.y += ysize;
+                       else r.y = 0.0;
+                       if (r.z > zsize)
+                               r.z -= zsize;
+                       else if (r.z < -zsize)
+                               r.z += zsize;
+                       else r.z = 0.0;
+                       *energies += k * (r.x * r.x + r.y * r.y + r.z * r.z);
+                       VecScaleInc(forces[i], r, -2);
+                       if (arena->debug_result && arena->debug_output_level > 1) {
+                               fprintf(arena->debug_result, "auxiliary(box) force %d: {%f %f %f}\n", i, -2*r.x, -2*r.y, -2*r.z);
+                       }
+               }
+       }
+
+       /*  Fix atoms  */
+       for (i = j = 0; i < natoms; i++) {
+               Atom *ap = &atoms[i];
+               Vector r21;
+               Double w1, w2, k0, k1;
+               if (ap->fix_force <= 0.0)
+                       continue;
+               VecSub(r21, ap->r, ap->fix_pos);
+               w2 = VecLength2(r21);
+               w1 = sqrt(w2);
+               k0 = ap->fix_force * w2;
+               k1 = -2.0 * ap->fix_force * w1;
+               *energies += k0;
+               VecScaleSelf(r21, k1);
+               VecInc(forces[i], r21);
+               j++;
+               if (arena->debug_result && arena->debug_output_level > 1) {
+                       fprintf(arena->debug_result, "auxiliary(fix) force %d: r=%f, k1=%f, {%f %f %f}\n", i, w1, k1*w1, r21.x, r21.y, r21.z);
+               }
+       }
+       
+       /*  Graphite  */
+       if (arena->graphite != NULL && arena->use_graphite) {
+               graphite_force(arena->graphite, arena, energies, forces);
+       }
+}
+
+static void
+s_md_debug_output_positions(MDArena *arena)
+{
+       int i;
+       if (arena->debug_result == NULL || arena->debug_output_level == 0)
+               return;
+       fprintf(arena->debug_result, "\n  Atom positions, velocities, and forces at step %d\n", arena->step);
+       fprintf(arena->debug_result, "%5s %7s %7s %7s %7s %7s %7s %7s %7s %7s\n", "No.", "x", "y", "z", "vx", "vy", "vz", "fx", "fy", "fz");
+       for (i = 0; i < arena->mol->natoms; i++) {
+               Atom *ap = &arena->mol->atoms[i];
+               fprintf(arena->debug_result, "%5d %7.3f %7.3f %7.3f %7.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n", i+1, ap->r.x, ap->r.y, ap->r.z, ap->v.x, ap->v.y, ap->v.z, ap->f.x, ap->f.y, ap->f.z);
+       }
+}
+
+void
+calc_force(MDArena *arena)
+{
+       Int i, j, natoms;
+       Vector *ff, *fa;
+       Molecule *mol = arena->mol;
+       Int doSurface = 0;
+
+       natoms = mol->natoms;
+
+       /*  Clear the energy/force storage  */
+       for (i = 0; i < natoms; i++)
+               VecZero(mol->atoms[i].f);
+       
+       memset(arena->energies, 0, sizeof(Double) * kSlowIndex);
+       memset(arena->forces, 0, sizeof(Vector) * kSlowIndex * natoms);
+       arena->total_energy = 0.0;
+       
+       if (arena->step == arena->start_step || arena->step % arena->surface_potential_freq == 0) {
+               doSurface = 1;
+               arena->energies[kSurfaceIndex] = 0.0;
+               memset(arena->forces + kSurfaceIndex * natoms, 0, sizeof(Vector) * natoms);
+       }
+
+       s_calc_bond_force(arena);
+       s_calc_angle_force(arena);
+       s_calc_dihedral_force(arena);
+       s_calc_improper_force(arena);
+       s_calc_nonbonded_force(arena);
+       s_calc_auxiliary_force(arena);
+
+       if (doSurface && arena->probe_radius > 0.0) {
+       /*      if (arena->sphere_points == 0)
+                       calc_surface_force(arena);
+               else 
+                       calc_surface_force_2(arena); */
+               calc_surface_force(arena);
+       }
+       
+       /*  Sum up all partial forces and energies  */
+       arena->total_energy = 0.0;
+       for (i = 0; i < kKineticIndex; i++)
+               arena->total_energy += arena->energies[i];
+
+       for (i = 0; i < natoms; i++) {
+               fa = &mol->atoms[i].f;
+               ff = &arena->forces[i];
+               for (j = 0; j < kKineticIndex; j++) {
+                       VecInc(*fa, *ff);
+                       ff += natoms;
+               }
+       }
+       
+       /*  The total (internal) force must be zero  */
+/*     {
+               Vector f;
+               Double w;
+               VecZero(f);
+               for (i = 0; i < natoms; i++) {
+                       Vector *fp = &(mol->atoms[i].f);
+                       VecInc(f, *fp);
+               }
+               w = (natoms > 0 ? 1.0 / natoms : 0.0);
+               VecScaleSelf(f, w);
+               for (i = 0; i < natoms; i++) {
+                       Vector *fp = &(mol->atoms[i].f);
+                       VecDec(*fp, f);
+               }
+       } */
+       
+       s_md_debug_output_positions(arena);
+}
+
+void
+calc_pair_interaction(MDArena *arena, const MDGroupFlags *group_flags_1, const MDGroupFlags *group_flags_2)
+{
+       Vector *forces, *eforces;
+       if (arena->pair_forces != NULL) {
+               forces = arena->pair_forces;
+               eforces = forces + arena->mol->natoms;
+               memset(forces, 0, sizeof(Vector) * arena->mol->natoms * 2);
+       } else forces = eforces = NULL;
+       arena->pair_energies[0] = arena->pair_energies[1] = 0.0;
+       s_calc_nonbonded_force_sub(arena, &arena->pair_energies[0], &arena->pair_energies[1], forces, eforces, group_flags_1, group_flags_2);
+}
diff --git a/MolLib/MD/MDForce.h b/MolLib/MD/MDForce.h
new file mode 100644 (file)
index 0000000..7b5ccbf
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  MDForce.h
+ *
+ *  Created by Toshi Nagata on 2005/06/07.
+ *  Copyright 2005 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MDFORCE_H__
+#define __MDFORCE_H__
+
+#include "MDCore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+void calc_force(MDArena *arena);
+void calc_pair_interaction(MDArena *arena, const MDGroupFlags *group_flags_1, const MDGroupFlags *group_flags_2);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __MD_FORCE_H__ */
diff --git a/MolLib/MD/MDGraphite.c b/MolLib/MD/MDGraphite.c
new file mode 100644 (file)
index 0000000..d4bbd16
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ *  MDGraphite.c
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 07/10/07.
+ *  Copyright 2007 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MDGraphite.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+/*  Table entry  */
+/*  The table should be created for each type of atom of different vdW parameters.
+       So we need to keep the vdW parameters and the table address.  */
+typedef struct MDGraphiteTable {
+       Double  A, B;    /*  A = 4*pow(r0, 12)*eps, B = 4*pow(r0, 6)*eps  */
+       Double  offset;  /*  Offset for cutoff truncation */
+       Double  cutoff2; /*  Square of the cutoff distance  */
+       Double *cache;  /*  Double[Sx*Sy*Sz*4]  */
+} MDGraphiteTable;
+
+struct MDGraphiteArena {
+       Int natoms_uniq;   /*  Must be the same as arena->natoms_uniq; otherwise, all internal information is rebuilt. */
+
+       Int needs_update;   /* If non-zero, then the internal information is rebuilt at the next force calculation  */
+       Int *table_indices; /* Index to tables[] for each symmetry-unique atom  */
+       Double *energies;    /* Cache energies of symmetry-unique atoms */
+       Double last_energy;
+       Vector *last_forces;
+       Int ntables;
+       MDGraphiteTable *tables;
+       Vector origin;               /*  Origin of the graphite plane in cartesian coordinates */
+       Vector xaxis, yaxis, zaxis;  /*  The axes of the graphite plane in cartesian coordinates */
+       Mat33 rcell;                 /*  Convert cartesian to graphite internal coordinates  */
+
+       Double R;  /* C-C bond length. Default: 1.23  */
+       Double C_eps;  /* LJ epsilon for graphite C atoms. Default: 0.0860 * KCAL2INTERNAL  */
+       Double C_sig;  /* LJ sigma for graphite C atoms. Default: 3.3997  */
+
+/*  The energies and forces at selected points in the asymmetric unit is
+       cached in graphite->table[]. Sx, Sy, Sz and Sz0 specifies how these points
+       are selected.
+       The asymmetric unit is a rectangle (0, 0)-(0.75R, sqrt(3)*0.5R). This rectangle
+       area (including borders) is split into an (Sx * Sy) lattice, and the lattice 
+       points (i, j) are selected as sample points. For each point, the z-coordinate
+       in the range [Z_min, Z_lim] is sampled, so that there are Sz0 points span 
+       [Z_min, Z_switch) and (Sz - Sz0) points span [Z_switch, Z_lim]. Thus, the
+       cartesian coordinates of a sample point (i, j, k) are:
+               x = 0.75*R*i/(Sx-1),
+               y = sqrt(3)*0.5R*j/(Sy-1),
+               z = (k >= Sz0 ? Z_switch+(Z_lim-Z_switch)*(k-Sz0)/(Sz-Sz0-1)
+                                         : Z_min+(Z_switch-Z_min)*k/Sz0)
+       The cached value can be accessed by:
+               table[((i*Sy + j)*Sz + k)*4 + n]
+       where n is 0, 1, 2, 3 for the energy, force.x, force.y, force.z, respectively.
+*/
+       Int use_table;
+       Int Sx;  /*  Default: 16  */
+       Int Sy;  /*  Default: 16  */
+       Int Sz;  /*  Default: 64  */
+       Int Sz0; /*  Default: 52  */
+       Double Z_min;     /*  Default: 1.0  */
+       Double Z_switch;  /*  Default: 5.0  */
+       Double Z_lim;     /*  Default: 10.0  */
+       
+       /*  If periodic_average > 0 and periodic boundary conditions are enabled, then
+               the graphite potential will be averaged for periodic_average number of image
+               atoms for each periodic cell axes. */
+       Int periodic_average;
+
+#if DEBUG
+       FILE *debug_fp;
+#endif
+
+};
+
+/*  A = 4*pow(r0, 12)*eps, B = 4*pow(r0, 6)*eps  */
+static Double
+s_graphite_lj(Vector r, const MDGraphiteTable *tr, Vector *force)
+{
+       Double r2 = r.x*r.x + r.y*r.y + r.z*r.z;
+       Double w2, w6, w12, k0, k1;
+       if (r2 >= tr->cutoff2) {
+               force->x = force->y = force->z = 0.0;
+               return 0.0;
+       }
+       w2 = 1.0 / r2;
+       w6 = w2 * w2 * w2;
+       w12 = w6 * w6;
+       k0 = tr->A * w12 - tr->B * w6 + tr->offset;
+       k1 = 6 * w2 * (2.0 * tr->A * w12 - tr->B * w6);
+       if (force != NULL) {
+               force->x = r.x * k1;
+               force->y = r.y * k1;
+               force->z = r.z * k1;
+       }
+       return k0;
+}
+
+/*  Calculate the total potential and force between an atom and the graphite sheet.
+       The graphite sheet is assumed as a perfect hexagonal array of carbon atoms, and
+       only van der Waals force is considered.
+       The origin is at the center of a hexagon, and all carbon atoms lie on the xy plane.
+       The x-axis points from the origin to one carbon atom. The carbon atoms can be
+       indexed as:
+               (0.5R*(3*i+1), sqrt(3)*0.5R*j), where i+j is odd
+       This function calculates the vdW potential and force from the graphite 
+       atoms in the range (i,j)=(-10,-15) to (+10,+15).
+       It is expected that the target atom is close to the z-axis. In the present code,
+       this function is only invoked for the position in the asymmetric unit, (0,0)..
+       (1.5R, sqrt(3)*0.5R).
+*/
+static Double
+s_graphite_asymunit(MDGraphiteArena *graphite, Vector r, const MDGraphiteTable *tr, Vector *force)
+{
+       Int i, j;
+       Double e0, en;
+       Vector r0, f0;
+       force->x = force->y = force->z = 0.0;
+       en = 0.0;
+       for (i = -10; i <= 10; i++) {
+               for (j = -15; j <= 15; j++) {
+                       if ((i + j) % 2 == 0)
+                               continue;
+                       r0.x = r.x - 0.5 * graphite->R * (3 * i + 1);
+                       r0.y = r.y - graphite->R * 0.866025403784439 * j;
+                       r0.z = r.z;
+                       e0 = s_graphite_lj(r0, tr, &f0);
+                       VecInc(*force, f0);
+                       en += e0;
+                       r0.x = r.x - 0.5 * graphite->R * (3 * i - 1);
+                       e0 = s_graphite_lj(r0, tr, &f0);
+                       VecInc(*force, f0);
+                       en += e0;
+               }
+       }
+       return en;
+}
+
+/*  Nearest graphite atoms; x, y coordinates should be multiplied by
+R/2 and (sqrt(3)/2)R, respectively  */
+/*
+static Int sNearestTable[] = {
+       -1, -3,  1, -3,
+       -4, -2, -2, -2,  2, -2,  4, -2,
+       -5, -1, -1, -1,  1, -1,  5, -1,
+       -4,  0, -2,  0,  2,  0,  4,  0,
+       -5,  1, -1,  1,  1,  1,  5,  1,
+       -4,  2, -2,  2,  2,  2,  4,  2,
+       -1,  3,  1,  3
+}; */
+static Int sNearestTable[] = {
+       -2, -4,  2, -4,
+       -5, -3, -1, -3,  1, -3,  5, -3,
+       -4, -2, -2, -2,  2, -2,  4, -2,
+       -7, -1, -5, -1, -1, -1,  1, -1,  5, -1,  7, -1,
+       -8,  0, -4,  0, -2,  0,  2,  0,  4,  0,  8,  0,
+       -7,  1, -5,  1, -1,  1,  1,  1,  5,  1,  7,  1,
+       -4,  2, -2,  2,  2,  2,  4,  2,
+       -5,  3, -1,  3,  1,  3,  5,  3,
+       -2,  4,  2, -4
+};
+#define kSizeOfNearestTable (sizeof(sNearestTable)/sizeof(sNearestTable[0])/2)
+
+/*  The nearest 24 graphite atoms are always explicitly calculated  */
+Double
+s_graphite_nearest_asymunit(MDGraphiteArena *graphite, Vector r, const MDGraphiteTable *tr, Vector *force)
+{
+       Int i;
+       Double en;
+       Vector q, dr, f;
+       en = force->x = force->y = force->z = 0.0;
+       for (i = 0; i < kSizeOfNearestTable; i++) {
+               q.x = sNearestTable[i*2] * graphite->R * 0.5;
+               q.y = sNearestTable[i*2+1] * 0.866025403784439 * graphite->R;
+               dr.x = r.x - q.x;
+               dr.y = r.y - q.y;
+               dr.z = r.z;
+               en += s_graphite_lj(dr, tr, &f);
+               force->x += f.x;
+               force->y += f.y;
+               force->z += f.z;
+       }
+       return en;
+}
+
+static void
+s_graphite_make_table(MDGraphiteArena *graphite, MDGraphiteTable *tr)
+{
+       Int i, j, k;
+       Vector v, f, f0;
+       Double en, en0, *fp;
+       k = graphite->Sx * graphite->Sy * graphite->Sz * sizeof(Double) * 4;
+       if (tr->cache == NULL)
+               tr->cache = (Double *)malloc(k);
+       else
+               tr->cache = (Double *)realloc(tr->cache, k);
+       memset(tr->cache, 0, k);
+       for (k = 0; k < graphite->Sz; k++) {
+               if (k >= graphite->Sz0)
+                       v.z = graphite->Z_switch + (graphite->Z_lim - graphite->Z_switch) * (k - graphite->Sz0) / (graphite->Sz - graphite->Sz0 - 1);
+               else
+                       v.z = graphite->Z_min + (graphite->Z_switch - graphite->Z_min) * k / graphite->Sz0;  /*  Not (Sz0 - 1)  */
+               for (i = 0; i < graphite->Sx; i++) {
+                       for (j = 0; j < graphite->Sy; j++) {
+                               v.x = 0.75 * graphite->R * i / (graphite->Sx - 1.0);
+                               v.y = 0.866025403784439 * graphite->R * j / (graphite->Sy - 1.0);
+                               en = s_graphite_asymunit(graphite, v, tr, &f);
+                               en0 = s_graphite_nearest_asymunit(graphite, v, tr, &f0);
+                               fp = tr->cache + ((i * graphite->Sy + j) * graphite->Sz + k) * 4;
+                               *fp++ = en - en0;
+                               *fp++ = f.x - f0.x;
+                               *fp++ = f.y - f0.y;
+                               *fp++ = f.z - f0.z;
+                       }
+               }
+       }
+}
+
+static inline Double
+s_linear_interpolate(MDGraphiteArena *graphite, Double *base, Double dx, Double dy, Double dz)
+{
+       Int Sy = graphite->Sy;
+       Int Sz = graphite->Sz;
+       Double a0 = base[0];
+       Double a1 = base[Sy * Sz * 4] - a0;
+       Double a2 = base[Sz * 4] - a0;
+       Double a3 = base[4] - a0;
+       Double a4 = base[(Sy + 1) * Sz * 4] - a0 - a1 - a2;
+       Double a5 = base[(Sz + 1) * 4] - a0 - a2 - a3;
+       Double a6 = base[(Sy * Sz + 1) * 4] - a0 - a1 - a3;
+       Double a7 = base[((Sy + 1) * Sz + 1) * 4] - a0 - a1 - a2 - a3 - a4 - a5 - a6;
+       return a0 + a1 * dx + a2 * dy + a3 * dz + a4 * dx * dy + a5 * dy * dz + a6 * dz * dx + a7 * dx * dy * dz;
+}
+
+static Double
+s_graphite(MDGraphiteArena *graphite, Vector v, MDGraphiteTable *tr, Vector *f)
+{
+       Int i, j, k;
+       Double cella, cellb;
+       Double en, dx, dy, dz, *fp;
+       Int ix, iy, negx, negy, negz, symop;
+       Vector v0, f0;
+       
+       if (v.z < 0) {
+               negz = 1;
+               v.z = -v.z;
+       } else negz = 0;
+       if (v.z > graphite->Z_lim) {
+               f->x = f->y = f->z = 0.0;
+               return 0.0;
+       }
+       
+       cella = 0.75 * graphite->R;
+       cellb = 0.866025403784439 * graphite->R;
+       dx = v.x / (cella * 4);
+       ix = floor(dx + 0.5);
+       dx -= ix;
+       dy = v.y / (cellb * 4);
+       iy = floor(dy + 0.5);
+       dy -= iy;
+       
+       /*  Move vector to the asymmetric unit (0,0)-(0.25,0.25) */
+       if (dx < 0) {
+               dx = -dx;
+               negx = 1;
+       } else negx = 0;
+       if (dy < 0) {
+               dy = -dy;
+               negy = 1;
+       } else negy = 0;
+       if (dy > 0.25) {
+               if (dx > 0.25) {
+                       dx = 0.5 - dx;
+                       dy -= 0.25;
+                       symop = 3;
+               } else {
+                       dy = 0.5 - dy;
+                       symop = 2;
+               }
+       } else if (dx > 0.25) {
+               dx = 0.5 - dx;
+               dy = 0.25 - dy;
+               symop = 1;
+       } else symop = 0;
+       v0.x = dx * cella * 4;
+       v0.y = dy * cellb * 4;
+       v0.z = v.z;
+       
+       if (tr->cache != NULL) {
+               /* 0 <= dx <= 0.25, 0 <= dy <= 0.25, 1.0 <= v.z <= 10.0 */
+               /* 0 <= i <= Sx-1, 0 <= j <= Sy-1, 0 <= k <= Sz-1 */
+               dx = dx * (graphite->Sx - 1.0) / 0.25;
+               dy = dy * (graphite->Sy - 1.0) / 0.25;
+               if (v.z >= graphite->Z_switch)
+                       dz = (v.z - graphite->Z_switch) * (graphite->Sz - graphite->Sz0 - 1) / (graphite->Z_lim - graphite->Z_switch) + graphite->Sz0;
+               else
+                       dz = (v.z - graphite->Z_min) * graphite->Sz0 / (graphite->Z_switch - graphite->Z_min);
+               i = floor(dx);
+               dx -= i;
+               j = floor(dy);
+               dy -= j;
+               k = floor(dz);
+               dz -= k;
+               if (i >= graphite->Sx - 1) {
+                       i = graphite->Sx - 2;
+                       dx = 1.0;
+               }
+               if (j >= graphite->Sy - 1) {
+                       j = graphite->Sy - 2;
+                       dy = 1.0;
+               }
+               if (k < 0) {
+                       k = 0;
+                       dz = 0.0;
+               } else if (k >= graphite->Sz - 1) {
+                       k = graphite->Sz - 2;
+                       dz = 1.0;
+               }
+               fp = tr->cache + ((i*graphite->Sy + j)*graphite->Sz + k)*4;
+               en = s_linear_interpolate(graphite, fp, dx, dy, dz);
+               f0.x = s_linear_interpolate(graphite, fp + 1, dx, dy, dz);
+               f0.y = s_linear_interpolate(graphite, fp + 2, dx, dy, dz);
+               f0.z = s_linear_interpolate(graphite, fp + 3, dx, dy, dz);
+               en += s_graphite_nearest_asymunit(graphite, v0, tr, f);
+               f->x += f0.x;
+               f->y += f0.y;
+               f->z += f0.z;
+       } else {
+               en = s_graphite_asymunit(graphite, v0, tr, f);
+       }
+       
+       /*  Back transform to original axis system  */
+       switch (symop) {
+               case 3: negx = !negx; break;
+               case 2: negy = !negy; break;
+               case 1: negx = !negx; negy = !negy; break;
+               default: break;
+       }
+       if (negx)
+               f->x = -f->x;
+       if (negy)
+               f->y = -f->y;
+       if (negz)
+               f->z = -f->z;
+       return en;
+}
+
+MDGraphiteArena *
+graphite_new(void)
+{
+       static Vector x = {1, 0, 0}, y = {0, 1, 0}, z = {0, 0, 1};
+       static Mat33 u = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+       MDGraphiteArena *graphite = (MDGraphiteArena *)calloc(sizeof(MDGraphiteArena), 1);
+       graphite->use_table = 1;
+       graphite->Sx = 16;
+       graphite->Sy = 16;
+       graphite->Sz = 64;
+       graphite->Sz0 = 52;
+       graphite->Z_min = 1.0;
+       graphite->Z_switch = 5.0;
+       graphite->Z_lim = 10.0;
+       graphite->R = 1.23;
+       graphite->C_eps = 0.0860 * KCAL2INTERNAL;
+       graphite->C_sig = 3.3997;
+       graphite->xaxis = x;
+       graphite->yaxis = y;
+       graphite->zaxis = z;
+       memmove(graphite->rcell, u, sizeof(Mat33));
+       return graphite;
+}
+
+static void
+s_graphite_release_tables(MDGraphiteArena *graphite)
+{
+       int i;
+       if (graphite == NULL || graphite->ntables == 0 || graphite->tables == NULL)
+               return;
+       for (i = 0; i < graphite->ntables; i++) {
+               if (graphite->tables[i].cache != NULL)
+                       free(graphite->tables[i].cache);
+       }
+       free(graphite->tables);
+       graphite->tables = NULL;
+       graphite->ntables = 0;
+}
+
+void
+graphite_release(MDGraphiteArena *graphite)
+{
+       if (graphite != NULL) {
+               if (graphite->table_indices != NULL)
+                       free(graphite->table_indices);
+               if (graphite->tables != NULL)
+                       s_graphite_release_tables(graphite);
+       }
+       free(graphite);
+}
+
+void
+graphite_set_origin(MDGraphiteArena *graphite, const Vector *vp)
+{
+       if (graphite == NULL)
+               return;
+       graphite->origin = *vp;
+}
+
+/*  (Re)initialize the MDGraphiteArena  */
+static void
+s_graphite_init(MDGraphiteArena *graphite, MDArena *arena)
+{
+       MDGraphiteTable *tp;
+       Atom *ap;
+       int nuniq = arena->natoms_uniq;
+       int i, j;
+       
+       /*  Reallocate the internal storage  */
+       if (graphite->table_indices == NULL)
+               graphite->table_indices = (Int *)malloc(sizeof(Int) * nuniq);
+       else
+               graphite->table_indices = (Int *)realloc(graphite->table_indices, sizeof(Int) * nuniq);
+       if (graphite->energies == NULL)
+               graphite->energies = (Double *)malloc(sizeof(Double) * nuniq);
+       else
+               graphite->energies = (Double *)realloc(graphite->energies, sizeof(Double) * nuniq);
+       if (graphite->last_forces == NULL)
+               graphite->last_forces = (Vector *)malloc(sizeof(Vector) * nuniq);
+       else
+               graphite->last_forces = (Vector *)realloc(graphite->last_forces, sizeof(Vector) * nuniq);
+       if (graphite->tables != NULL)
+               s_graphite_release_tables(graphite);
+       
+       /*  Calculate the tables  */
+       md_log(arena, "Building the graphite table...\n");
+       
+       for (i = 0, ap = arena->mol->atoms; i < nuniq; i++, ap++) {
+               Int vdw_idx;
+               VdwPar *vp;
+               Double sig, eps, sigc, epsc, A, B;
+               vdw_idx = arena->vdw_par_i[i];
+               if (vdw_idx < 0 || vdw_idx >= arena->par->nvdwPars) {
+                       /*  Out of range  */
+                       graphite->table_indices[i] = -1;
+                       continue;
+               }
+               vp = &(arena->par->vdwPars[vdw_idx]);
+               if (fabs(vp->A) < 1e-15) {
+                       sig = 1.0;
+                       eps = 0.0;
+               } else {
+                       eps = 0.25 * vp->B * vp->B / vp->A;
+                       sig = pow(vp->B * 0.25 / eps, 1.0/6.0);
+               }
+               sigc = (graphite->C_sig + sig) * 0.5;
+               epsc = sqrt(graphite->C_eps * eps);
+               A =     4.0 * pow(sigc, 12.0) * epsc;
+               B = 4.0 * pow(sigc, 6.0) * epsc;
+               /*  Are there entries with similar A/B values?  */
+               for (j = 0; j < graphite->ntables; j++) {
+                       tp = graphite->tables + j;
+                       if (fabs(tp->A - A) < 1e-10 && fabs(tp->B - B) < 1e-10)
+                               break;
+               }
+               if (j >= graphite->ntables) {
+                       /*  Create a new entry with a table  */
+                       Vector v;
+                       j = graphite->ntables;
+                       tp = (MDGraphiteTable *)AssignArray(&graphite->tables, &graphite->ntables, sizeof(MDGraphiteTable), j, NULL);
+                       tp->A = A;
+                       tp->B = B;
+                       /*  Calculate the offset: set 0 to offset temporarily, and call s_graphite_lj */
+                       tp->cutoff2 = arena->cutoff * arena->cutoff + 1.0;  /* +1.0 to avoid cutoff */
+                       tp->offset = 0.0;
+                       v.x = arena->cutoff;
+                       v.y = v.z = 0.0;
+                       tp->offset = -s_graphite_lj(v, tp, &v);
+                       tp->cutoff2 = arena->cutoff * arena->cutoff; /* This is the real value */
+                       if (graphite->use_table)
+                               s_graphite_make_table(graphite, tp);
+                       else tp->cache = NULL;
+               }
+               graphite->table_indices[i] = j;
+       }
+       graphite->natoms_uniq = arena->natoms_uniq;
+       md_log(arena, "%d table(s) were created %s cached data.\n", graphite->ntables, (graphite->use_table ? "with" : "without"));     
+       graphite->needs_update = 0;
+       
+#if DEBUG
+       if (arena->debug_result && arena->debug_output_level > 1) {
+               graphite->debug_fp = arena->debug_result;
+       } else graphite->debug_fp = NULL;
+#endif 
+       
+}
+
+void
+graphite_force(MDGraphiteArena *graphite, MDArena *arena, Double *energy, Vector *forces)
+{
+       Molecule *mol = arena->mol;
+       int i;
+       int ix, iy, iz, px, py, pz;
+       Double pxpypz_1;
+       Vector *avp, *bvp, *cvp;
+       if (graphite == NULL || mol == NULL || mol->natoms == 0 || arena->natoms_uniq == 0)
+               return;
+       if (graphite->natoms_uniq != arena->natoms_uniq || graphite->needs_update)
+               s_graphite_init(graphite, arena);
+       graphite->last_energy = 0;
+       memset(graphite->last_forces, 0, sizeof(Vector) * arena->natoms_uniq);
+       if (graphite->periodic_average && arena->mol->cell != NULL) {
+               px = (arena->periodic_a ? graphite->periodic_average : 1);
+               py = (arena->periodic_b ? graphite->periodic_average : 1);
+               pz = (arena->periodic_c ? graphite->periodic_average : 1);
+       } else {
+               px = py = pz = 1;
+       }
+       pxpypz_1 = 1.0 / (px * py * pz);
+       if (mol->cell != NULL) {
+               avp = &(mol->cell->axes[0]);
+               bvp = &(mol->cell->axes[1]);
+               cvp = &(mol->cell->axes[2]);
+       }
+       for (i = 0; i < arena->natoms_uniq; i++) {
+               Vector f;
+               Double en;
+               Vector v = mol->atoms[i].r;
+               if (mol->atoms[i].fix_force < 0)
+                       continue;
+               en = f.x = f.y = f.z = 0.0;
+               for (ix = 0; ix < px; ix++) {
+                       for (iy = 0; iy < py; iy++) {
+                               for (iz = 0; iz < pz; iz++) {
+                                       Vector f0, v0;
+                                       Double en0;
+                                       if (mol->cell != NULL) {
+                                               v0.x = v.x + avp->x * ix + bvp->x * iy + cvp->x * iz;
+                                               v0.y = v.y + avp->y * ix + bvp->y * iy + cvp->y * iz;
+                                               v0.z = v.z + avp->z * ix + bvp->z * iy + cvp->z * iz;
+                                       } else v0 = v;
+                                       VecDec(v0, graphite->origin);
+                                       MatrixVec(&v0, graphite->rcell, &v0);
+                                       en0 = s_graphite(graphite, v0, graphite->tables + graphite->table_indices[i], &f0);
+                                       en += en0;
+                                       VecInc(f, f0);
+                               }
+                       }
+               }
+               en *= pxpypz_1;
+               VecScaleSelf(f, pxpypz_1);
+               graphite->energies[i] = en;
+               graphite->last_energy += en;
+               graphite->last_forces[i] = f;
+       }
+       for (i = arena->natoms_uniq; i < mol->natoms; i++) {
+               Symop symop = mol->atoms[i].symop;
+               if (mol->atoms[i].fix_force < 0)
+                       continue;
+               if (symop.alive) {
+                       int n = mol->atoms[i].symbase;
+                       if (n >= 0 && n < arena->natoms_uniq)
+                               graphite->last_energy += graphite->energies[n];
+               }
+       }
+       if (energy != NULL)
+               *energy += graphite->last_energy;
+       if (forces != NULL) {
+               for (i = 0; i < arena->natoms_uniq; i++)
+                       VecInc(forces[i], graphite->last_forces[i]);
+       }
+}
+
+void
+graphite_get_axes(MDGraphiteArena *graphite, Vector *op, Vector *xp, Vector *yp, Vector *zp, Double *rp)
+{
+       if (graphite != NULL) {
+               if (op != NULL)
+                       *op = graphite->origin;
+               if (xp != NULL)
+                       *xp = graphite->xaxis;
+               if (yp != NULL)
+                       *yp = graphite->yaxis;
+               if (zp != NULL)
+                       *zp = graphite->zaxis;
+               if (rp != NULL)
+                       *rp = graphite->R;
+       }
+}
+
diff --git a/MolLib/MD/MDGraphite.h b/MolLib/MD/MDGraphite.h
new file mode 100644 (file)
index 0000000..34b74f7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  MDGraphite.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 07/10/07.
+ *  Copyright 2007 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MDGRAPHITE_H__
+#define __MDGRAPHITE_H__
+
+#include "MDCore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+typedef struct MDGraphiteArena MDGraphiteArena;
+
+MDGraphiteArena *graphite_new(void);
+void graphite_release(MDGraphiteArena *graphite);
+void graphite_set_origin(MDGraphiteArena *graphite, const Vector *vp);
+void graphite_force(MDGraphiteArena *graphite, MDArena *arena, Double *energy, Vector *force);
+void graphite_get_axes(MDGraphiteArena *graphite, Vector *op, Vector *xp, Vector *yp, Vector *zp, Double *rp);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __MDGRAPHITE_H__ */
diff --git a/MolLib/MD/MDPressure.c b/MolLib/MD/MDPressure.c
new file mode 100644 (file)
index 0000000..5647bed
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ *  MDPressure.c
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 07/10/12.
+ *  Copyright 2007 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MDPressure.h"
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+/*  Calculate the lattice deformation stress  */
+static Double
+s_lattice_deformation_stress(const Mat33 apply, const Transform celltr, const Transform old_celltr)
+{
+       Mat33 grad, grad_tr, grad_inv, grad_inv_tr;
+       Mat33 strain, tension;
+       Double eng, volume;
+       Int i;
+
+       /*  Some of the calculations duplicate with md_scale_cell(), but it will not cause
+               a serious slowdown  */
+       volume = fabs(MatrixDeterminant(celltr));
+
+       /*  Deformation gradient (temp) = celltr * old_rcelltr  */
+       MatrixInvert(grad, old_celltr);
+       MatrixMul(grad, celltr, grad);
+       MatrixTranspose(grad_tr, grad);
+       MatrixInvert(grad_inv, grad);
+       MatrixTranspose(grad_inv_tr, grad_inv);
+       MatrixMul(strain, grad_tr, grad);
+       strain[0] -= 1.0;
+       strain[4] -= 1.0;
+       strain[8] -= 1.0;
+       MatrixScale(strain, strain, 0.5);
+       MatrixMul(tension, apply, grad_inv_tr);
+       MatrixMul(tension, grad_inv, tension);
+       MatrixScale(tension, tension, MatrixDeterminant(grad));
+       eng = 0.0;
+       for (i = 0; i < 9; i++)
+               eng += strain[i] * tension[i];
+       eng *= volume * BAR2INTERNALPRESSURE;
+       return eng;
+}
+
+MDPressureArena *
+pressure_new(void)
+{
+       MDPressureArena *pressure = (MDPressureArena *)calloc(sizeof(MDPressureArena), 1);
+       if (pressure == NULL)
+               return NULL;
+       pressure->freq = 20;
+       pressure->coupling = 0.3;
+       pressure->trial_width = 0.001;
+       pressure->control_algorithm = 0;
+       pressure->fluctuate_cell_origin = 0.01;
+       pressure->fluctuate_cell_orientation = 0.01;
+       pressure->apply[0] = 1;
+       pressure->apply[1] = 0;
+       pressure->apply[2] = 0;
+       pressure->apply[3] = 0;
+       pressure->apply[4] = 1;
+       pressure->apply[5] = 0;
+       pressure->apply[6] = 0;
+       pressure->apply[7] = 0;
+       pressure->apply[8] = 1;
+       pressure->cell_flexibility[0] = -1;
+       pressure->cell_flexibility[1] = -1;
+       pressure->cell_flexibility[2] = -1;
+       pressure->cell_flexibility[3] = 0;
+       pressure->cell_flexibility[4] = 0;
+       pressure->cell_flexibility[5] = 0;
+       pressure->cell_flexibility[6] = 0;
+       pressure->cell_flexibility[7] = 0;
+       return pressure;
+}
+
+void
+pressure_release(MDPressureArena *pressure)
+{
+       if (pressure == NULL)
+               return;
+       free(pressure);
+}
+
+void
+pressure_prepare(MDArena *arena)
+{
+       MDPressureArena *pressure = arena->pressure;
+       if (pressure == NULL)
+               return;
+       pressure->mc_accept = pressure->mc_reject = 0;
+       if (pressure->temporary_energies != NULL)
+               free(pressure->temporary_energies);
+       pressure->temporary_energies = (Double *)calloc(sizeof(Double), arena->natoms_uniq);
+       if (pressure->temporary_energies == NULL)
+               md_panic(arena, "Low memory");
+       if (pressure->temporary_velocities != NULL)
+               free(pressure->temporary_velocities);
+       pressure->temporary_velocities = (Vector *)calloc(sizeof(Vector), arena->natoms_uniq);
+       if (pressure->temporary_velocities == NULL)
+               md_panic(arena, "Low memory");
+}
+
+/*  Pressure control  */
+void
+pressure_control(MDArena *arena)
+{
+       Vector cella_save, cellb_save, cellc_save, cello_save;
+       Double de, total_energy_save, energies_save[kEndIndex];
+       MDPressureArena *pressure = arena->pressure;
+       Atom *ap;
+       Transform tf;
+       Vector v;
+       Vector cello_new;
+       Mat33 celltr_save;
+       Double w, w0;
+       int i;
+       int needs_cell_recalculate = 0;
+       XtalCell *cell = arena->mol->cell;
+       
+       if (pressure == NULL || (!arena->periodic_a && !arena->periodic_b && !arena->periodic_c))
+               return;
+       if (pressure->freq == 0 || arena->step % pressure->freq != 0)
+               return;
+
+       while (md_rand() < pressure->coupling) {
+
+               Double flex;
+               Transform tf0;
+       
+               /*  Trial move of the periodic cell  */
+               memset(tf, 0, sizeof(tf));
+               tf[0] = tf[4] = tf[8] = 1;
+               memmove(tf0, tf, sizeof(tf));
+               w0 = (md_rand() - 0.5) * pressure->trial_width;
+               for (i = 0; i < 3; i++) {
+                       static char idx[] = {4, 8, 5, 7, 0, 8, 2, 6, 0, 4, 1, 3};
+                       flex = pressure->cell_flexibility[i + 3];
+                       if (flex > 0) {
+                               w = w0 = (md_rand() + md_rand() - 1.0) * pressure->trial_width * 0.5;
+                       } else if (flex < 0) {
+                               w = w0;
+                       } else w = 0;
+                       w *= fabs(flex);
+                       tf[idx[i * 4]] = tf[idx[i * 4 + 1]] = sqrt(1 - w * w);
+                       tf[idx[i * 4 + 2]] = tf[idx[i * 4 + 3]] = w;
+                       TransformMul(tf0, tf0, tf);
+                       tf[idx[i * 4]] = tf[idx[i * 4 + 1]] = 1;
+                       tf[idx[i * 4 + 2]] = tf[idx[i * 4 + 3]] = 0;
+               }
+               memmove(tf, tf0, sizeof(tf));
+               w0 = (md_rand() - 0.5) * pressure->trial_width;
+               for (i = 0; i < 3; i++) {
+                       int j;
+                       flex = pressure->cell_flexibility[i];
+                       if (flex > 0) {
+                               w = w0 = (md_rand() - 0.5) * pressure->trial_width;
+                       } else if (flex < 0) {
+                               w = w0;
+                       } else w = 0;
+                       for (j = 0; j < 3; j++) {
+                               tf[j * 3 + i] *= 1 + w * fabs(flex);
+                       }
+               }
+               
+               /*  The new cell vector (bij) = (tik)(akj), so B = A*T  */
+               /*  The cell transform matrix R = A*T*(A^-1)  */
+               MatrixMul(tf, cell->tr, tf);
+               MatrixMul(tf, tf, cell->rtr);
+       /*      MatrixInvert(mat, arena->celltr);
+               MatrixMul(tf, tf, mat); */
+
+               cella_save = cell->axes[0];
+               cellb_save = cell->axes[1];
+               cellc_save = cell->axes[2];
+               cello_save = cell->origin;
+               memmove(celltr_save, cell->tr, sizeof(celltr_save));
+
+               /*  Save a snapshot  */
+               md_snapshot(arena, 0);
+               total_energy_save = arena->total_energy;
+               memmove(energies_save, arena->energies, sizeof(Double) * kEndIndex);
+
+               /*  Transform the cell  */
+               md_scale_cell(arena, tf, (pressure->use_atomic_move ? 1 : 2));
+
+               /*  Fluctuate cell origin and orientation if requested  */
+               if (pressure->cell_flexibility[6] != 0) {
+                       cello_new = cell->origin;
+                       cello_new.x += (md_rand() - 0.5) * pressure->fluctuate_cell_origin;
+                       cello_new.y += (md_rand() - 0.5) * pressure->fluctuate_cell_origin;
+                       cello_new.z += (md_rand() - 0.5) * pressure->fluctuate_cell_origin;
+                       cell->origin = cello_new;
+                       needs_cell_recalculate = 1;
+               }
+               if (pressure->cell_flexibility[7] != 0) {
+                       Vector axis;
+                       Mat33 mat2;
+                       switch ((int)pressure->cell_flexibility[7]) {
+                               case 1: axis.x = 1.0; axis.y = axis.z = 0.0; break;
+                               case 2: axis.y = 1.0; axis.x = axis.z = 0.0; break;
+                               case 3: axis.z = 1.0; axis.x = axis.y = 0.0; break;
+                               case 4:
+                                       axis.x = md_rand() - 0.5;
+                                       axis.y = md_rand() - 0.5;
+                                       axis.z = md_rand() - 0.5;
+                                       w = 1.0 / VecLength(axis);
+                                       VecScaleSelf(axis, w);
+                                       break;
+                               default:
+                                       axis.x = axis.y = axis.z = 0.0; /* Just to keep compiler happy */
+                                       break;
+                       }
+                       MatrixRotation(mat2, &axis, (md_rand() - 0.5) * pressure->fluctuate_cell_orientation);
+                       MatrixVec(&cell->axes[0], mat2, &cell->axes[0]);
+                       MatrixVec(&cell->axes[1], mat2, &cell->axes[1]);
+                       MatrixVec(&cell->axes[2], mat2, &cell->axes[2]);
+                       needs_cell_recalculate = 1;
+               }
+               if (needs_cell_recalculate)
+                       md_cell_recalculate(arena);
+
+               /*  Recalc the energies  */
+               md_amend_by_symmetry(arena);
+               calc_force(arena);
+               md_calc_kinetic_energy(arena);
+               pressure->mc_delta_potential = arena->total_energy - total_energy_save;
+               pressure->mc_delta_kinetic = arena->energies[kKineticIndex] - energies_save[kKineticIndex];
+               pressure->mc_stress = -s_lattice_deformation_stress(pressure->apply, cell->tr, celltr_save);
+               de = pressure->mc_delta_potential + pressure->mc_delta_kinetic + pressure->mc_stress;
+               pressure->mc_delta_volume = fabs(MatrixDeterminant(cell->tr)) - fabs(MatrixDeterminant(celltr_save));
+               
+               /*  Metropolis scheme  */
+               if (de > 0 && md_rand() > exp(-de / (BOLTZMANN * arena->temperature))) {
+                       /*  Reject  */
+                       cell->axes[0] = cella_save;
+                       cell->axes[1] = cellb_save;
+                       cell->axes[2] = cellc_save;
+                       cell->origin = cello_save;
+                       md_cell_recalculate(arena);
+                       if (pressure->control_algorithm != 1) {
+                               md_restore(arena, 0);
+                               arena->total_energy = total_energy_save;
+                               memmove(arena->energies, energies_save, sizeof(Double) * kEndIndex);
+                       }
+                       pressure->mc_reject++;
+               } else {
+                       pressure->mc_accept++;
+                       if (pressure->control_algorithm == 1) {
+                               Vector dr;
+                               int j, k;
+                               /*  Update positions and velocities  */
+                               for (i = 0, ap = arena->mol->atoms; i < arena->mol->natoms; i++, ap++) {
+                                       if (ap->periodic_exclude)
+                                               continue;
+                                       if (i < arena->natoms_uniq) {
+                                               k = arena->fragment_indices[i];
+                                               v = pressure->temporary_velocities[i];
+                                       } else if (ap->symop.alive && (j = ap->symbase) >= 0 && j < arena->natoms_uniq) {
+                                               k = arena->fragment_indices[j];
+                                               v = pressure->temporary_velocities[j];
+                                       } else continue;
+                                       dr = arena->fragment_info[k].pos;
+                                       if (ap->symop.alive) {
+                                               md_transform_vec_by_symmetry(arena, &dr, &dr, ap->symop, 1);
+                                               md_transform_vec_by_symmetry(arena, &v, &v, ap->symop, 1);
+                                       }
+                                       VecInc(ap->r, dr);
+                                       VecInc(arena->verlets_dr[i], dr);
+                                       ap->v = v;
+                               }
+                       }
+               }
+       /*      if (pressure->mc_callback != NULL) {
+                       if (Tcl_EvalObjEx((Tcl_Interp *)(arena->tcl_interp), pressure->mc_callback, 0) != TCL_OK) {
+                               arena->request_abort = 1;
+                               return;
+                       }
+               } */
+       } /* end while (mrand() < pressure->coupling) */
+}
+
diff --git a/MolLib/MD/MDPressure.h b/MolLib/MD/MDPressure.h
new file mode 100644 (file)
index 0000000..9608f98
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  MDPressure.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 07/10/12.
+ *  Copyright 2007 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MDPRESSURE_H__
+#define __MDPRESSURE_H__
+
+#include "MDCore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+struct MDPressureArena {
+       Int    disabled;
+       Int    freq;
+       Double  coupling;
+       Double  trial_width;    /* Move width for MC trials. Default 0.01.  */
+       Mat33  apply;          /*  A tensor. The unit is bar (NOT internal unit)  */
+       Int    mc_accept, mc_reject;  /*  Monte Carlo statistics  */
+       Double  cell_flexibility[8];  /*  How the cell vectors are modified in each trial step  */
+       /*  cell_flexibility[0..2] determines whether the cell vector is modified along its
+        axis. If the value is 0, the cell vector is not changed in that direction. If the
+        value is positive, the cell vector is changed. If the value is negative, the cell 
+        vector is changed, but the amount of change is the same as the last axis which has
+        a positive flexibility value.
+        cell_flexibility[3..5] determines whether two axes interact each other. The
+        index 3, 4, 5 corresponds to b/c, c/a, a/b pair, respectively. If the value is
+        0, then the two axes do not interact. If the value is positive, then the two axes are
+        modified along the other axis; for b/c pair, they will be modifed as b->b + xc,
+        c->c + xb. To avoid unphysical rotation of the axes system, the same modifier
+        coefficient x is used for both transformation.
+        cell_flexibility[6] allows (if non-zero) the cell origin to fluctuate.
+        pressure_fluctuate_cell_origin can be set to control the amount of fluctuation.
+        cell_flexibility[7] allows the cell orientation to fluctuate. 1: rotation
+        around x axis, 2; rotation around y axis, 3: rotation around z axis, 4: free
+        rotation. The amount of fluctuation is controlled by pressure_fluctuate_cell_orientation.
+        The absolute value of cell_flexibility[0..5] are multiplied to trial_width, so that
+        different trial_width for the cell parameters are allowed. */
+       Int    control_algorithm;
+       Double  *temporary_energies;  /*  Double[natoms_uniq]  */
+       Vector *temporary_velocities; /*  Vector[natoms_uniq]  */
+       Int    use_atomic_move;  /*  If non-zero, all atoms positions are scaled. Otherwise, only the center of mass of each molecule is scaled.  */
+       Double  fluctuate_cell_origin;  /*  Default 0.01. */
+       Double  fluctuate_cell_orientation; /*  Default 0.01.  */
+       Double  mc_delta_potential;
+       Double  mc_delta_kinetic;
+       Double  mc_stress;
+       Double  mc_delta_volume;
+};
+
+typedef struct MDPressureArena MDPressureArena;
+
+MDPressureArena *pressure_new(void);
+void pressure_prepare(MDArena *arena);
+void pressure_release(MDPressureArena *pressure);
+void pressure_control(MDArena *arena);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __MDPRESSURE_H__ */
diff --git a/MolLib/MD/MDSurface.c b/MolLib/MD/MDSurface.c
new file mode 100644 (file)
index 0000000..beffc19
--- /dev/null
@@ -0,0 +1,1555 @@
+/*
+ *  MDSurface.c
+ *
+ *  Created by Toshi Nagata on 2005/06/22.
+ *  Copyright 2005 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MDSurface.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#if DEBUG
+#define SP_DEBUG 0   /* Change to non-zero for very verbose debug output  */
+#else
+#undef SP_DEBUG
+#endif
+
+#if SP_DEBUG
+#include <stdarg.h>
+#endif
+
+#if SP_DEBUG
+static void
+s_sp_debug(MDArena *arena, const char *fmt,...)
+{
+       va_list ap;
+       if (arena == NULL || arena->debug_result == NULL)
+               return;
+       fprintf(arena->debug_result, "SP_DEBUG: ");
+       va_start(ap, fmt);
+       vfprintf(arena->debug_result, fmt, ap);
+       va_end(ap);
+       fprintf(arena->debug_result, "\n");
+       fflush(arena->debug_result);
+}
+#endif
+
+static void
+s_allocate_sp_arena(MDArena *arena)
+{
+       SPArena *sarena;
+       Int natoms, i;
+       sarena = (SPArena *)calloc(sizeof(SPArena), 1);
+       if (sarena == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       arena->sp_arena = sarena;
+       natoms = arena->mol->natoms;
+       sarena->atom_rad = (Double *)calloc(sizeof(Double), natoms);
+       if (sarena->atom_rad == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       sarena->atom_pot = (Double *)calloc(sizeof(Double), natoms);
+       if (sarena->atom_pot == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       sarena->atom_area = (Double *)calloc(sizeof(Double), natoms);
+       if (sarena->atom_area == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       sarena->atom_buried = (Byte *)calloc(sizeof(Byte), natoms);
+       if (sarena->atom_buried == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       sarena->sphere_idx = (Int *)calloc(sizeof(Int), natoms*2);
+       if (sarena->sphere_idx == NULL)
+               md_panic(arena, ERROR_out_of_memory);
+       for (i = 0; i < natoms; i++) {
+               Int idx;
+               Double r;
+               sarena->atom_pot[i] = arena->surface_tension * KCAL2INTERNAL;
+               idx = arena->vdw_par_i[i];
+               if (idx < 0) {
+                       r = 1.0;  /*  Assume 1.0 angstrom if the vdW parameter is missing  */
+               } else {
+                       VdwPar *vp = &(arena->par->vdwPars[idx]);
+                       r = pow(vp->A * 2.0 / vp->B, 1.0/6.0) * 0.5;
+               }
+               sarena->atom_rad[i] = arena->probe_radius + r;
+       #if SP_DEBUG
+               s_sp_debug(arena, "atom_rad[%d] = %f", i+1, sarena->atom_rad[i]);
+       #endif
+       }
+}
+
+/*  Register a pair of crossing spheres  */
+static void
+s_register_sphere_record(SPArena *sarena, Int i, Int j, const Vector *vijp, Double rhi, Double rhj, Double dij)
+{
+       crossing_sphere_record csr;
+       Vector vw1, vw2, qij;
+       Double w;
+
+       csr.i = i;
+       csr.j = j;
+       csr.dij = dij;
+       csr.vij = *vijp;
+
+       /*  gj is the (signed) distance from the center of sphere i to the crossing plane */
+       csr.gj = 0.5 * (rhi * rhi - rhj * rhj + csr.dij * csr.dij) / csr.dij;
+       /*  The radius of the intersection circle  */
+       csr.rj = sqrt(rhi * rhi - csr.gj * csr.gj);
+
+       /*  Calculate the rotation matrix  */
+       /*  (cos(t)*cos(p), cos(t)*sin(p), -sin(t)) -> (1,0,0)
+        *  (-sin(p), cos(p), 0) -> (0,1,0)
+        *  (sin(t)*cos(p), sin(t)*sin(p), cos(t)) -> (0,0,1)
+        *  where vij=dij*(sin(t)*cos(p), sin(t)*sin(p), cos(t) */
+       w = 1.0 / csr.dij;
+       VecScale(vw1, csr.vij, w);
+       w = vw1.x * vw1.x + vw1.y * vw1.y;
+       if (w > 1e-10) {
+               w = sqrt(w);
+               vw2.x = -vw1.y / w;
+               vw2.y = vw1.x / w;
+       } else {
+               vw2.x = 0.0;
+               vw2.y = 1.0;
+       }
+       vw2.z = 0.0;
+       VecCross(qij, vw2, vw1);
+       MatrixGeneralRotation(csr.rot_c, &qij, &vw2, &vw1);
+       MatrixTranspose(csr.rot, csr.rot_c);                    
+       csr.int_start = csr.int_end = 0;
+       csr.skip = 0;
+       AssignArray(&sarena->spheres, &sarena->maxspheres, sizeof(crossing_sphere_record), sarena->nspheres++, &csr);
+}
+
+/*  Comparator for sorting crossing_sphere_record  */
+static int
+s_compare_sphere_records(const void *a, const void *b)
+{
+       const crossing_sphere_record *csa = (const crossing_sphere_record *)a;
+       const crossing_sphere_record *csb = (const crossing_sphere_record *)b;
+       if (csa->i > csb->i)
+               return 1;
+       else if (csa->i < csb->i)
+               return -1;
+       else if (csa->gj > csb->gj)
+               return 1;
+       else if (csa->gj < csb->gj)
+               return -1;
+       else return 0;
+}
+
+#if SP_DEBUG
+/*  Check the sanity of the crossing sphere list  */
+static int
+s_sanity_check_sphere_records(MDArena *arena)
+{
+       Int natoms = arena->mol->natoms;
+       SPArena *sarena = arena->sp_arena;
+       crossing_sphere_record *spheres = sarena->spheres;
+       Int i, j, k, start, end;
+       end = 0;
+       for (i = 0; i < natoms; i++) {
+               start = sarena->sphere_idx[i*2];
+               if (end != start)
+                       warning("Sanity check: sphere_idx[%d*2] != sphere_idx[%d*2-1]", i+1, i+1);
+               end = sarena->sphere_idx[i*2+1];
+               for (j = start; j < end; j++) {
+                       /*  Check the partner record  */
+                       Int jstart, jend;
+                       jstart = sarena->sphere_idx[spheres[j].j*2];
+                       jend = sarena->sphere_idx[spheres[j].j*2+1];
+                       for (k = jstart; k < jend; k++) {
+                               if (spheres[k].j == i)
+                                       break;
+                       }
+                       if (k == jend)
+                               warning("Sanity check: (%d,%d) cross but (%d,%d) do not", i+1, j+1, j+1, i+1);
+               }
+       }
+}
+#endif
+
+/*  List all pairs of crossing spheres  */
+static void
+s_list_crossing_spheres(MDArena *arena)
+{
+       Int i, j;
+       Double rhi, rhj, w;
+       Int natoms = arena->mol->natoms;
+       Atom *atoms = arena->mol->atoms;
+       SPArena *sarena = arena->sp_arena;
+       /*  Check the buried atoms  */
+       for (i = 0; i < natoms; i++) {
+               rhi = sarena->atom_rad[i];
+               for (j = i + 1; j < natoms; j++) {
+                       Vector vij;
+                       VecSub(vij, atoms[j].r, atoms[i].r);
+                       w = VecLength(vij);
+                       rhj = sarena->atom_rad[j];
+                       if (w + rhi <= rhj) {
+                               sarena->atom_buried[i] = 1;
+                               break;
+                       }
+                       if (w + rhj <= rhi)
+                               sarena->atom_buried[j] = 1;
+               }
+       }
+       /*  Look for the crossing pairs of spheres  */
+       for (i = 0; i < natoms; i++) {
+               if (sarena->atom_buried[i]) {
+                       /*  Do not count the buried atoms  */
+               /*      sarena->sphere_idx[i * 2] = sarena->sphere_idx[i * 2 + 1] = start; */
+                       continue;
+               }
+               rhi = sarena->atom_rad[i];
+               for (j = i + 1; j < natoms; j++) {
+                       Vector vij;
+                       Double dij;
+                       if (j == i || sarena->atom_buried[j])
+                               continue;
+                       VecSub(vij, atoms[j].r, atoms[i].r);
+                       dij = VecLength(vij);
+                       rhj = sarena->atom_rad[j];
+                       if (dij >= rhi + rhj || dij <= fabs(rhi - rhj))
+                               continue;  /*  These two spheres do not intersect  */
+                       s_register_sphere_record(sarena, i, j, &vij, rhi, rhj, dij);
+                       VecScaleSelf(vij, -1);
+                       s_register_sphere_record(sarena, j, i, &vij, rhj, rhi, dij);
+               }
+       /*      if (end - start >= 2)
+                       qsort(&sarena->spheres[start], end - start, sizeof(crossing_sphere_record), s_compare_sphere_records);
+               sarena->sphere_idx[i * 2] = start;
+               sarena->sphere_idx[i * 2 + 1] = end; */
+       }
+       /*  Sort by (1) i, and (2) gj (i.e. the sphere with larger enclosing volume comes earlier) */
+       qsort(sarena->spheres, sarena->nspheres, sizeof(crossing_sphere_record), s_compare_sphere_records);
+       /*  Set the sphere_idx[]  */
+       j = 0;
+       for (i = 0; i < natoms; i++) {
+               sarena->sphere_idx[i*2] = j;
+               while (j < sarena->nspheres && sarena->spheres[j].i <= i)
+                       j++;
+               sarena->sphere_idx[i*2+1] = j;
+       }
+#if SP_DEBUG
+       s_sp_debug(arena, "Number of crossing spheres: %d", sarena->nspheres);
+       for (i = 0; i < natoms; i++) {
+               for (j = sarena->sphere_idx[i*2]; j < sarena->sphere_idx[i*2+1]; j++) {
+                       crossing_sphere_record *csp = &sarena->spheres[j];
+                       s_sp_debug(arena, "Atom %d: entry %d, {i=%d,j=%d,vij={%f,%f,%f},dij=%f,rj=%f,gj=%f,rot={%f,%f,%f,%f,%f,%f,%f,%f,%f},int_start=%d,int_end=%d",
+                               i+1, j, csp->i+1, csp->j+1, csp->vij.x, csp->vij.y, csp->vij.z, csp->dij,
+                               csp->rj, csp->gj,
+                               csp->rot[0], csp->rot[1], csp->rot[2], csp->rot[3], csp->rot[4],
+                               csp->rot[5], csp->rot[6], csp->rot[7], csp->rot[8],
+                               csp->int_start, csp->int_end);
+               }
+       }
+       s_sanity_check_sphere_records(arena);
+#endif
+}
+
+/*  Examine whether a point on the sphere i is within the crossing circle j  */
+static int
+s_is_inside(const crossing_sphere_record *csp, const Vector *p)
+{
+       Double w;
+       /*  w = rad * cos(t), rad is the radius of sphere i and t is the angle between csp->vij and p */
+       w = VecDot(csp->vij, *p) / csp->dij;
+/*     if ((w >= 0 && csp->gj > 0 && w > csp->gj) || (w <= 0 && csp->gj < 0 && w < csp->gj)) */
+       if (w >= csp->gj)
+               return 1;
+       else return 0;
+}
+
+/*  Register an intersection point  */
+static void
+s_register_intersect(MDArena *arena, Int i, const crossing_sphere_record *csj, const crossing_sphere_record *csk, Int sgn, const Vector *qp, const Vector *pp, Int point_id)
+{
+       intersect_record intersect;
+       SPArena *sarena = arena->sp_arena;
+       intersect.i = i;
+       intersect.j = csj->j;
+       intersect.k = csk->j;
+       intersect.idx = csj - sarena->spheres;
+       if (pp != NULL)
+               intersect.s = atan2(pp->y, pp->x);
+       else {
+               Vector p = arena->mol->atoms[i].r;
+               VecSub(p, *qp, p);
+               MatrixVec(&p, csj->rot, &p);
+               intersect.s = atan2(p.y, p.x);
+       }
+       intersect.flag = 0;
+       intersect.point_id = point_id;
+       AssignArray(&sarena->intersects, &sarena->maxintersects, sizeof(intersect_record), sarena->nintersects++, &intersect);
+#if DEBUG || 1
+       if (csj->i != i || csk->i != i) {
+               md_warning(arena, "Internal inconsistency: point (%d,%d,%d;%d) is registered but circle indices are (%d,%d) and (%d,%d)", i+1, csj->j+1, csk->j+1, point_id, csj->i+1, csj->j+1, csk->i+1, csk->j+1);
+       }
+#endif
+       if (point_id >= sarena->npoints) {
+               intersect_point_record ipr;
+               ipr.sign = sgn;
+               ipr.i = i;
+               ipr.idx_j = csj - sarena->spheres;
+               ipr.idx_k = csk - sarena->spheres;
+               ipr.v = *qp;
+               AssignArray(&sarena->points, &sarena->maxpoints, sizeof(intersect_point_record), sarena->npoints++, &ipr);
+       }
+}
+
+/*  Comparator for sorting intersect_records  */
+static int
+s_compare_intersect_records(const void *a, const void *b)
+{
+       const intersect_record *ia = (const intersect_record *)a;
+       const intersect_record *ib = (const intersect_record *)b;
+       if (ia->i < ib->i)
+               return -1;
+       else if (ia->i > ib->i)
+               return 1;
+       else if (ia->idx < ib->idx)
+               return -1;
+       else if (ia->idx > ib->idx)
+               return 1;
+       else if (ia->s < ib->s)
+               return -1;
+       else if (ia->s > ib->s)
+               return 1;
+       else return 0;
+}
+
+/*  Simpler comparator for sorting intersect_records  */
+static int
+s_compare_intersect_records_s_only(const void *a, const void *b)
+{
+       const intersect_record *ia = (const intersect_record *)a;
+       const intersect_record *ib = (const intersect_record *)b;
+       if (ia->s < ib->s)
+               return -1;
+       else if (ia->s > ib->s)
+               return 1;
+       else return 0;
+}
+
+/*  Calculate the intersection point of three spheres  */
+static int
+s_calc_intersection_points(const crossing_sphere_record *csj, const crossing_sphere_record *csk, Vector *p1, Vector *p2)
+{
+       Vector vk;
+       Double w1, w2, w3, w4, w5;
+       /*  vk is the center of circle k in (i,j)-local coordinates */
+       w1 = csk->gj / csk->dij;
+       MatrixVec(&vk, csj->rot, &csk->vij); /*quat_rotate(&vk, &csk->vij, &csj->quat);*/
+       VecScaleSelf(vk, w1);
+       w1 = vk.x * vk.x + vk.y * vk.y;
+       if (w1 < 1e-10) {
+               /*  Special case: vk is parallel to (0,0,1)  */
+               /*  They never intersect  */
+               return 0;
+       }
+       w2 = csk->gj * csk->gj - vk.z * csj->gj;
+       w3 = csj->rj * csj->rj * w1 - w2 * w2;
+       /*  The intersection point is given in (i,j)-coordinates
+        *    x = (vk.x*w2 +- vk.y*sqrt(w3))/w1
+        *    y = (vk.y*w2 -+ vk.x*sqrt(w3))/w1
+        *    z = csj->gj  */
+       if (w3 <= 0.0) {
+               /*  The discriminant is non-positive; no intersection points */
+               return 0;
+       }
+       w2 /= w1;
+       w3 = sqrt(w3) / w1;
+       w4 = vk.x * w2;
+       w5 = vk.y * w3;
+       w2 *= vk.y;
+       w3 *= vk.x;
+       p1->x = w4 + w5;
+       p1->y = w2 - w3;
+       p1->z = csj->gj;
+       p2->x = w4 - w5;
+       p2->y = w2 + w3;
+       p2->z = csj->gj;
+       return 1;
+}
+
+/*  List the relevant intersection points of crossing circles  */
+/*  Algorithm 2  */
+static void
+s_list_intersection_points(MDArena *arena)
+{
+       Int i, j, k;
+       Int natoms = arena->mol->natoms;
+       Atom *atoms = arena->mol->atoms;
+       SPArena *sarena = arena->sp_arena;
+
+       for (i = 0; i < natoms; i++) {
+               Int start, end;
+               Vector oij = atoms[i].r;  /*  origin of the sphere i  */
+               start = sarena->sphere_idx[i * 2];
+               end = sarena->sphere_idx[i * 2 + 1];
+               /*  Calculate the intersection points of two circles j and k  */
+               /*  Only those points with sphere (atom) number i < j < k are 
+                *  explicitly calculated, and other five points are generated by
+                *  transforming coordinates  */
+               for (j = start; j < end; j++) {
+                       crossing_sphere_record *csj = &sarena->spheres[j];
+                       if (csj->skip || i >= csj->j)
+                               continue;
+                       for (k = j + 1; k < end; k++) {
+                               crossing_sphere_record *csk = &sarena->spheres[k];
+                               Vector p[2];
+                               Int ii;
+                               if (csk->skip || i >= csk->j)
+                                       continue;
+                               if (s_calc_intersection_points(csj, csk, &p[0], &p[1]) == 0) {
+                                       /*  No intersection points  */
+                                       /*  One circle may include the other, in which case
+                                        *  the circle k does not need further processing  */
+                                       p[0].x = csj->rj;
+                                       p[0].y = 0.0;
+                                       p[0].z = csj->gj;
+                                       MatrixVec(&p[0], csj->rot_c, &p[0]); /*quat_rotate(&p[0], &p[0], &csj->quat_c);*/
+                                       if (s_is_inside(csk, &p[0])) {
+                                               csj->skip = 1;
+                                       #if SP_DEBUG
+                                               s_sp_debug(arena, "Circle (%d,%d) is buried within circle (%d,%d)", i+1, csj->j+1, i+1, csk->j+1);
+                                       #endif
+                                               break;
+                                       } else {
+                                               p[1].x = csk->rj;
+                                               p[1].y = 0.0;
+                                               p[1].z = csk->gj;
+                                               MatrixVec(&p[1], csk->rot_c, &p[1]); /*quat_rotate(&p[1], &p[1], &csk->quat_c);*/
+                                               if (s_is_inside(csj, &p[1])) {
+                                                       csk->skip = 1;
+                                               #if SP_DEBUG
+                                                       s_sp_debug(arena, "Circle (%d,%d) is buried within circle (%d,%d)", i+1, csk->j+1, i+1, csj->j+1);
+                                               #endif
+                                               }
+                                       }
+                                       continue;
+                               }
+                               for (ii = 0; ii < 2; ii++) {
+                                       Vector q;
+                                       Int jj;
+                                       /*  q is the global coordinates of the crossing points  */
+                                       MatrixVec(&q, csj->rot_c, &p[ii]); /*quat_rotate(&q, &p[ii], &csj->quat_c);*/
+                                       VecInc(q, oij);
+                                       /*  Check whether this point is inside any crossing sphere  */
+                                       for (jj = start; jj < end; jj++) {
+                                               crossing_sphere_record *csjj = &sarena->spheres[jj];
+                                               Vector qk;
+                                               Double rhoj;
+                                               if (j == jj || k == jj || csjj->skip)
+                                                       continue;
+                                               VecSub(qk, q, atoms[csjj->j].r);
+                                               rhoj = sarena->atom_rad[csjj->j];
+                                               if (VecLength2(qk) < rhoj * rhoj) {
+                                               #if SP_DEBUG
+                                                       VecDec(q, oij);
+                                                       s_sp_debug(arena, "Point {%f,%f,%f} is rejected because it is within the circle (%d,%d)", q.x, q.y, q.z, csjj->i+1, csjj->j+1);
+                                                       VecInc(q, oij);
+                                               #endif
+                                                       break;
+                                               }
+                                       }
+                                       if (jj >= end) {
+                                               /*  Register this point  */
+                                               /*  The same point is registered as 6 different indices  */
+                                               Int point_id = sarena->npoints;
+                                               Int i1;
+                                               crossing_sphere_record *csj1, *csk1, *csstart, *csend;
+                                               /*  Examine whether circles j and k cross
+                                                * (Mathematically they should, but the (j,k) pair is independently
+                                                *  calculated from the (i,j) and (i,k) pairs, so the numerical
+                                                *  roundups may result in inconsistency)  */
+                                               i1 = csj->j;
+                                               csstart = &sarena->spheres[sarena->sphere_idx[i1 * 2]];
+                                               csend = &sarena->spheres[sarena->sphere_idx[i1 * 2 + 1]];
+                                               for (csk1 = csstart; csk1 < csend; csk1++) {
+                                                       if (csk1->j == csk->j)
+                                                               break;
+                                               }
+                                               if (csk1 == csend)
+                                                       continue;  /*  Ignore this point  */
+                                               for (csj1 = csstart; csj1 < csend; csj1++) {
+                                                       if (csj1->j == i)
+                                                               break;
+                                               }
+                                               if (csj1 == csend)
+                                                       md_panic(arena, "BUG ALERT! Internal inconsistency: spheres (%d,%d)=(i,j) cross but (%d,%d) do not; file %s, line %d", i+1, csj->j+1, csj->j+1, i+1, __FILE__, __LINE__);                                               
+                                               /*  (i,j,k)  */
+                                               s_register_intersect(arena, i, csj, csk, ii, &q, &p[ii], point_id);
+                                               /*  (i,k,j)  */
+                                               s_register_intersect(arena, i, csk, csj, ii, &q, NULL, point_id);
+                                               /*  (j,i,k)  */
+                                               s_register_intersect(arena, i1, csj1, csk1, ii, &q, NULL, point_id);
+                                               /*  (j,k,i)  */
+                                               s_register_intersect(arena, i1, csk1, csj1, ii, &q, NULL, point_id);
+                                               /*  (k,i,j)  */
+                                               i1 = csk->j;
+                                               csstart = &sarena->spheres[sarena->sphere_idx[i1 * 2]];
+                                               csend = &sarena->spheres[sarena->sphere_idx[i1 * 2 + 1]];
+                                               for (csj1 = csstart; csj1 < csend; csj1++) {
+                                                       if (csj1->j == i)
+                                                               break;
+                                               }
+                                               for (csk1 = csstart; csk1 < csend; csk1++) {
+                                                       if (csk1->j == csj->j)
+                                                               break;
+                                               }
+                                               if (csj1 == csend)
+                                                       md_panic(arena, "BUG ALERT! Internal inconsistency: spheres (%d,%d)=(i,k) cross but (%d,%d) do not; file %s, line %d", i+1, csk->j+1, csk->j+1, i+1, __FILE__, __LINE__);
+                                               if (csk1 == csend)
+                                                       md_panic(arena, "BUG ALERT! Internal inconsistency: spheres (%d,%d)=(j,k) cross but (%d,%d) do not; file %s, line %d", csj->j+1, csk->j+1, csk->j+1, csj->j+1, __FILE__, __LINE__);
+                                               s_register_intersect(arena, i1, csj1, csk1, ii, &q, NULL, point_id);
+                                               /*  (k,j,i)  */
+                                               s_register_intersect(arena, i1, csk1, csj1, ii, &q, NULL, point_id);
+                                       }
+                               } /*  end loop ii  */
+                       } /*  end loop k  */
+               } /*  end loop j  */
+       } /*  end loop i  */
+       /*  Sort by (1) index i, (2) sphere index idx, (3) arc parameter s  */
+       qsort(sarena->intersects, sarena->nintersects, sizeof(intersect_record), s_compare_intersect_records);
+       /*  Record the index range for each crossing_sphere_record  */
+       j = 0;
+       for (i = 0; i < sarena->nspheres; i++) {
+               crossing_sphere_record *csi = &sarena->spheres[i];
+               csi->int_start = j;
+               while (j < sarena->nintersects && sarena->intersects[j].idx <= i)
+                       j++;
+               csi->int_end = j;
+               if (csi->int_start == csi->int_end) {
+                       /*  A circle with no intersection points  */
+                       /*  Check if this circle is buried  */
+                       Vector p1;
+                       Int idx, idx_end;
+                       /*  p1 is a point on this circle  */
+                       p1.x = csi->rj;
+                       p1.y = 0.0;
+                       p1.z = csi->gj;
+                       MatrixVec(&p1, csi->rot_c, &p1); /*quat_rotate(&p1, &p1, &csi->quat_c);*/
+                       idx_end = sarena->sphere_idx[csi->i*2+1];
+                       for (idx = sarena->sphere_idx[csi->i*2]; idx < idx_end; idx++) {
+                               /*  Is it inside any crossing circle on sphere csi->i?  */
+                               if (i == idx)
+                                       continue;
+                               if (s_is_inside(&sarena->spheres[idx], &p1)) {
+                                       csi->skip = 1;
+                               #if SP_DEBUG
+                                       s_sp_debug(arena, "Circle (%d,%d) is buried within multiple circles on sphere %d", csi->i+1, csi->j+1, i+1);
+                               #endif
+                                       break;
+                               }
+                       }
+               }
+       }
+#if SP_DEBUG
+       s_sp_debug(arena, "Number of intersection points: %d", sarena->nintersects);
+       for (i = 0; i < sarena->nspheres; i++) {
+               crossing_sphere_record *csi = &sarena->spheres[i];
+               for (j = csi->int_start; j < csi->int_end; j++) {
+                       intersect_record *irj = &sarena->intersects[j];
+                       Vector p = sarena->points[irj->point_id].v;
+               /*      s_calc_intersection_coord(&p, irj, arena); */
+               /*      p.x = csi->rj * cos(irj->s);
+                       p.y = csi->rj * sin(irj->s);
+                       p.z = csi->gj;
+                       quat_rotate(&p, &p, &csi->quat); */
+               /*      VecInc(p, arena->mol->atoms[csi->i].r); */
+                       s_sp_debug(arena, "Circle %d: entry %d, {i=%d,j=%d,k=%d,s=%f,id=%d,p={%f,%f,%f},idx=%d",
+                               i, j, irj->i+1,irj->j+1, irj->k+1, irj->s, irj->point_id, p.x, p.y, p.z,
+                               irj->idx);
+               }
+       }
+#endif
+}
+
+/*  Re-calculate the coordinates and arc parameters of intersection points  */
+/*  Note: the intersection points are sorted to maintain the ascending order of
+ *  the arc parameters  */
+/*  If calculation failed, then returns zero. In that case, the lists of the
+ *  crossing spheres and intersection points must be rebuilt  */
+static int
+s_recalc_intersection_points(MDArena *arena)
+{
+       Int i;
+       Atom *atoms = arena->mol->atoms;
+       SPArena *sarena = arena->sp_arena;
+       Int npoints = sarena->npoints;
+       intersect_point_record *ipr;
+       intersect_record *ir;
+       crossing_sphere_record *csr;
+       Vector p1, p2;
+
+       /*  Recalculate the intersection points  */
+       for (i = 0, ipr = sarena->points; i < npoints; i++, ipr++) {
+               if (s_calc_intersection_points(&sarena->spheres[ipr->idx_j], &sarena->spheres[ipr->idx_k], &p1, &p2) == 0)
+                       return 0;  /*  Failure  */
+               if (ipr->sign == 0) {
+                       ipr->v = p1;
+                       if (i < npoints - 1 && ipr[1].i == ipr->i && ipr[1].idx_j == ipr->idx_j && ipr[1].idx_k == ipr->idx_k) {
+                               ipr[1].v = p2;
+                               i++;
+                               ipr++;
+                       }
+               } else {
+                       ipr->v = p2;
+               }
+       }
+       
+       /*  Recalculate the arc parameters  */
+       for (i = 0, ir = sarena->intersects; i < sarena->nintersects; i++, ir++) {
+               ipr = &sarena->points[ir->point_id];
+               csr = &sarena->spheres[ir->idx];
+               p1 = atoms[ir->i].r;
+               VecSub(p2, ipr->v, p1);
+               MatrixVec(&p2, csr->rot, &p2);
+               ir->s = atan2(p2.y, p2.x);
+       }
+       
+       /*  Sort the points by arc parameters  */
+       for (i = 0, csr = sarena->spheres; i < sarena->nspheres; i++) {
+               Int start = csr->int_start;
+               Int end = csr->int_end;
+               if (start - end >= 2) {
+                       qsort(&sarena->intersects[start], end - start, sizeof(intersect_record), s_compare_intersect_records_s_only);
+               }
+       }
+       
+       return 0;
+}
+
+#if SP_DEBUG
+static void
+s_print_internal_information(MDArena *arena)
+{
+       struct temp_record {
+               int i, j, k, count;
+               Vector p;
+       };
+       int i;
+       Int ntemps = 0;
+       struct temp_record *temps = NULL;
+       Vector p, dp;
+       int k, ii, jj, kk;
+       struct temp_record *tp = NULL;
+
+       if (arena->debug_result == NULL || arena->debug_output_level == 0)
+               return;
+       
+       fprintf(arena->debug_result, "The spheres and the crossing points:\n");
+       fprintf(arena->debug_result, "%d\n", arena->mol->natoms + arena->sp_arena->nintersects / 6);
+       for (i = 0; i < arena->mol->natoms; i++) {
+               Vector *rp = &arena->mol->atoms[i].r;
+               fprintf(arena->debug_result, "XX %f %f %f %f\n", rp->x, rp->y, rp->z, arena->sp_arena->atom_rad[i]);
+       }
+       for (i = 0; i < arena->sp_arena->nintersects; i++) {
+               intersect_record *ip = &arena->sp_arena->intersects[i];
+               crossing_sphere_record *csi = &arena->sp_arena->spheres[ip->idx];
+               p.x = csi->rj * cos(ip->s);
+               p.y = csi->rj * sin(ip->s);
+               p.z = csi->gj;
+               MatrixVec(&p, csi->rot_c, &p); /*quat_rotate(&p, &p, &csi->quat_c);*/
+               VecInc(p, arena->mol->atoms[csi->i].r);
+               ii = ip->i;
+               jj = ip->j;
+               kk = ip->k;
+               if (ii > jj) { k = ii; ii = jj; jj = k; }
+               if (jj > kk) { k = jj; jj = kk; kk = k; }
+               if (ii > jj) { k = ii; ii = jj; jj = k; }
+               /*  Search for temps[], and register this point if not present  */
+               for (k = 0; k < ntemps; k++) {
+                       tp = &temps[k];
+                       VecSub(dp, p, tp->p);
+                       if (VecLength2(dp) < 1e-6)
+                               break;
+               }
+               if (k < ntemps) {
+                       if (tp->i != ii || tp->j != jj || tp->k != kk) {
+                               fprintf(arena->debug_result, "!!! The point {%f,%f,%f} appeared twice with difference indices {%d,%d,%d} and {%d,%d,%d}\n",
+                                       p.x, p.y, p.z, tp->i+1, tp->j+1, tp->k+1, ii+1, jj+1, kk+1);
+                       } else {
+                               tp->count++;
+                       }
+               } else {
+                       tp = (struct temp_record *)AssignArray(&temps, &ntemps, sizeof(temps[0]), ntemps, NULL);
+                       tp->i = ii;
+                       tp->j = jj;
+                       tp->k = kk;
+                       tp->count = 1;
+                       tp->p = p;
+               }
+       }
+       for (i = 0; i < ntemps; i++) {
+               p = temps[i].p;
+               fprintf(arena->debug_result, "XX %f %f %f %f", p.x, p.y, p.z, 0.500);
+               if (temps[i].count != 6) {
+                       fprintf(arena->debug_result, "  !!! count = %d != 6 (i=%d,j=%d,k=%d)", temps[i].count, temps[i].i+1, temps[i].j+1, temps[i].k+1);
+               }
+               fprintf(arena->debug_result, "\n");
+       }
+       fflush(arena->debug_result);
+       if (temps != NULL)
+               free(temps);
+}
+#endif
+
+/*  The gradient vectors for force calculation  */
+/*  For force calculation we need three gradients, namely for vi, vj, vk 
+ *  (these are the center of spheres i, j, k in global coordinates).
+ *  Actually we calculate two gradients for xj and xk, where xj = vj - vi
+ *  and xk = vk - vi. Gradients for vi, vj, vk can then be calculated 
+ *  by grad_vi = -(grad_xj + grad_xk), grad_vj = grad_xj, grad_vk = grad_xk.
+ */
+/*  Notations:
+ *  q : the position of the intersection point in the (i,j) local coordinates
+ *  (i.e. the coordinates in which the center of sphere i is (0,0,0) and
+ *  the center of sphere j is (0,0,csr1->gj) )
+ *  gvj : (0,0,csr1->gj) = the center of circle j in the (i,j) local coordinates
+ *  gvk : the center of circle k in the (i,j) local coordinates
+ *  gvj and gvk are related to xj and xk by the following equations:
+ *  gvj = quat_rotate((csr1->gj / csr1->dij) * csr1->vij, csr1->quat)
+ *  gvk = quat_rotate((csr2->gj / csr2->dij) * csr2->vij, csr1->quat)
+ *  where csr1 and csr2 are the crossing_sphere_records for (i,j) and (i,k)
+ *  pairs, respectively. Note that the quaternion in the gvk equation is
+ *  csr1->quat, not csr2->quat!
+ */
+/*  The following gradients are calculated here:
+ *  qx_xj = grad_xj(qx) = (d(q.x)/d(xj.x), d(q.x)/d(xj.y), d(q.x)/d(xj.z))
+ *  qy_xj = grad_xj(qy) = (d(q.y)/d(xj.x), d(q.y)/d(xj.y), d(q.y)/d(xj.z))
+ *  gj_xj = grad_xj(gj) = (d(gj)/d(xj.x), d(gj)/d(xj.y), d(gj)/d(xj.z))
+ *  s_xj = grad_xj(s) = (ds/d(xj.x), ds/d(xj.y), ds/d(xj.z))
+ *  qx_xk = grad_xk(qx) = (d(q.x)/d(xk.x), d(q.x)/d(xk.y), d(q.x)/d(xk.z))
+ *  qy_xk = grad_xk(qy) = (d(q.y)/d(xk.x), d(q.y)/d(xk.y), d(q.y)/d(xk.z))
+ *  gk_xk = grad_xk(gk) = (d(gk)/d(xk.x), d(gk)/d(xk.y), d(gk)/d(xk.z))
+ *  s_xk = grad_xk(s) = (ds/d(xk.x), ds/d(xk.y), ds/d(xk.z))
+ *  (s is the arc parameter on circle j.)
+ *  Note that grad_xk(gj) and grad_xj(gk) are zero.
+ */
+static void
+s_calc_gradient_vectors(gradient_record *gp, const MDArena *arena, const crossing_sphere_record *csr1, const crossing_sphere_record *csr2, const Vector *qp)
+{
+       Vector q, gvj, gvk;
+       Vector gvkx_xj, gvky_xj, gvkz_xj;
+       Vector gvkx_xk, gvky_xk, gvkz_xk;
+       Vector va1, va2;
+       Double rho_i, rho_j, rho_k;
+       Double w1, w2, w3 ,w4, w5, w6;
+       const Double *atom_rad;
+
+       /*  The crossing point in (i,j) local coordinates  */
+       MatrixVec(&q, csr1->rot, qp); /*quat_rotate(&q, qp, &csr1->quat); *//* *qp is the crossing point in global coords */
+       
+       /*  The radii of the spheres  */
+       atom_rad = arena->sp_arena->atom_rad;
+       rho_i = atom_rad[csr1->i];
+       rho_j = atom_rad[csr1->j];
+       rho_k = atom_rad[csr2->j];
+       
+       /*  The center of sphere k in (i,j) local coordinates  */
+       gvj.x = gvj.y = 0.0;
+       gvj.z = csr1->gj;
+       w1 = csr2->gj / csr2->dij;
+       VecScale(gvk, csr2->vij, w1);
+       MatrixVec(&gvk, csr1->rot, &gvk); /*quat_rotate(&gvk, &gvk, &csr1->quat);*/
+
+       /*  Gradients by xj  */
+
+       /*  Gradients of local-to-global matrix M  */
+       /*  M converts the (i,j)-local coordinates to the global coordinates as:
+        *   x(global).x = M[0]*x(local).x + M[1]*x(local).y + M[2]*x(local).z, etc.
+        *  mx, my, mz are defined as follows:
+        *   mx[n] = d(M[n])/d(xj.x)
+        *   my[n] = d(M[n])/d(xj.y)
+        *   mz[n] = d(M[n])/d(xj.z)
+       */
+       {
+               Double wxx, wyy, wzz, wdd, w2sq;
+               wxx = csr1->vij.x * csr1->vij.x;
+               wyy = csr1->vij.y * csr1->vij.y;
+               wzz = csr1->vij.z * csr1->vij.z;
+               wdd = csr1->dij * csr1->dij;
+               w2 = wxx + wyy;
+               w2sq = sqrt(w2);
+               w3 = 1.0 / (wdd * csr1->dij);
+               w4 = 1.0 / (w2 * w2sq);
+               w5 = w3 * w4;
+               gp->mx[0] = csr1->vij.z * w5 * (-wxx * w2 + wyy * wdd);
+               gp->mx[3] = -csr1->vij.x * csr1->vij.y * csr1->vij.z * w5 * (w2 + wdd);
+               w6 = csr1->vij.z * csr1->vij.z * w2 * w5;
+               gp->mx[6] = -csr1->vij.x * w6;
+               gp->my[0] = gp->mx[3];
+               gp->my[3] = csr1->vij.z * w5 * (-wyy * w2 + wxx * wdd);
+               gp->my[6] = -csr1->vij.y * w6;
+               w6 = w2sq * w3;
+               gp->mz[0] = csr1->vij.x * w6;
+               gp->mz[3] = csr1->vij.y * w6;
+               gp->mz[6] = csr1->vij.z * w6;
+               gp->mx[1] = csr1->vij.x * csr1->vij.y * w4;
+               gp->mx[4] = wyy * w4;
+               gp->my[1] = -wxx * w4;
+               gp->my[4] = -gp->mx[1];
+               gp->mx[7] = gp->my[7] = 0.0;
+               gp->mz[1] = gp->mz[4] = gp->mz[7] = 0.0;
+               gp->mx[2] = (wyy + wzz) * w3;
+               gp->mx[5] = -csr1->vij.x * csr1->vij.y * w3;
+               gp->mx[8] = -csr1->vij.x * csr1->vij.z * w3;
+               gp->my[2] = gp->mx[5];
+               gp->my[5] = (wxx + wzz) * w3;
+               gp->my[8] = -csr1->vij.y * csr1->vij.z * w3;
+               gp->mz[2] = gp->mx[8];
+               gp->mz[5] = gp->my[8];
+               gp->mz[8] = (wxx + wyy) * w3;
+       }
+       
+       /*  gvkx_xj = grad_xj(gvk.x) = gk/vk*grad_xj((M^-1)*xk)
+        *   = gk/vk*grad_xj((M[0],M[3],M[6])*xk)
+        *   = gk/vk*{(mx[0],mx[3],mx[6])*xk, (my[0],my[3],my[6])*xk, (mz[0],mz[3],mz[6])*xk}
+        *  Also for gvky_xj, gvkz_xj  */
+       va1 = csr2->vij;
+       w1 = csr2->gj / csr2->dij;
+       gvkx_xj.x = w1 * (gp->mx[0] * va1.x + gp->mx[3] * va1.y + gp->mx[6] * va1.z);
+       gvkx_xj.y = w1 * (gp->my[0] * va1.x + gp->my[3] * va1.y + gp->my[6] * va1.z);
+       gvkx_xj.z = w1 * (gp->mz[0] * va1.x + gp->mz[3] * va1.y + gp->mz[6] * va1.z);
+       gvky_xj.x = w1 * (gp->mx[1] * va1.x + gp->mx[4] * va1.y + gp->mx[7] * va1.z);
+       gvky_xj.y = w1 * (gp->my[1] * va1.x + gp->my[4] * va1.y);
+       gvky_xj.z = 0.0;
+       gvkz_xj.x = w1 * (gp->mx[2] * va1.x + gp->mx[5] * va1.y + gp->mx[8] * va1.z);
+       gvkz_xj.y = w1 * (gp->my[2] * va1.x + gp->my[5] * va1.y + gp->my[8] * va1.z);
+       gvkz_xj.z = w1 * (gp->mz[2] * va1.x + gp->mz[5] * va1.y + gp->mz[8] * va1.z);
+
+#if 0
+       /*  Intermediate vectors: gradients of gvk.x, gvk.y, gvk.y by xj  */
+       /*  gvkx_xj = grad_xj(gvk.x)  */
+       w2 = csr1->vij.x * csr1->vij.x + csr1->vij.y * csr1->vij.y;
+       w2sq = sqrt(w2);
+       w3 = w1 / (csr1->dij * csr1->dij * csr1->dij * w2 * w2sq);
+       w4 = csr1->vij.z * (-csr1->vij.x * csr1->vij.x * w2 + csr1->vij.y * csr1->vij.y * csr1->dij * csr1->dij);
+       w5 = -csr1->vij.x * csr1->vij.y * csr1->vij.z * (w1 + csr1->dij * csr1->dij);
+       w6 = -csr1->vij.z * csr1->vij.z * w2;
+       gvkx_xj.x = w3 * (csr2->vij.x * w4 + csr2->vij.y * w5 + csr2->vij.z * csr1->vij.x * w6);
+       w4 = csr1->vij.z * (-csr1->vij.y * csr1->vij.y * w2 + csr1->vij.x * csr1->vij.x * csr1->dij * csr1->dij);
+       gvkx_xj.y = w3 * (csr2->vij.x * w5 + csr2->vij.y * w4 + csr2->vij.z * csr1->vij.y * w6);
+       gvkx_xj.z = w3 * w2 * w2 * VecDot(csr2->vij, csr1->vij);
+       /*  gvky_xj = grad_xj(gvk.y)  */
+       w3 = w1 / (w2 * w2sq) * (csr2->vij.x * csr1->vij.x + csr2->vij.y * csr1->vij.y);
+       gvky_xj.x = csr1->vij.y * w3;
+       gvky_xj.y = -csr1->vij.x * w3;
+       gvky_xj.z = 0.0;
+       /*  gvkz_xj = grad_xj(gvk.z)  */
+       w3 = w1 / (csr1->dij * csr1->dij * csr1->dij);
+       gvkz_xj.x = w3 * csr2->vij.x * (csr1->dij * csr1->dij - csr1->vij.x * csr1->vij.x);
+       gvkz_xj.y = w3 * csr2->vij.y * (csr1->dij * csr1->dij - csr1->vij.y * csr1->vij.y);
+       gvkz_xj.z = w3 * csr2->vij.z * (csr1->dij * csr1->dij - csr1->vij.z * csr1->vij.z);
+#endif
+
+       /*  Intermediate vectors:
+        *   va1 = gvk.x * qx_xj + gvk.y * qy_xj
+        *   va2 = q.x * qx_xj + qy * qy_xj  */
+       w1 = csr1->dij;
+       w1 = (rho_j * rho_j - rho_i * rho_i + w1 * w1) / (2.0 * w1 * w1 * w1);
+       w2 = -gvk.z * w1;
+       VecScale(va1, csr1->vij, w2);
+       VecScaleSelf(gvkx_xj, q.x);
+       VecScaleSelf(gvky_xj, q.y);
+       VecScaleSelf(gvkz_xj, csr1->gj);
+       va1.x -= gvkx_xj.x + gvky_xj.x + gvkz_xj.x;
+       va1.y -= gvkx_xj.y + gvky_xj.y + gvkz_xj.y;
+       va1.z -= gvkx_xj.z + gvky_xj.z + gvkz_xj.z;
+       w2 = -csr1->gj * w1;
+       VecScale(va2, csr1->vij, w2);
+       w2 = 1.0 / (gvk.x * q.y - gvk.y * q.x);
+       w3 = q.y * w2;
+       w4 = -gvk.y * w2;
+       w5 = -q.x * w2;
+       w6 = gvk.x * w2;
+       VecScale(gp->qx_xj, va1, w3);
+       VecScaleInc(gp->qx_xj, va2, w4);
+       VecScale(gp->qy_xj, va1, w5);
+       VecScaleInc(gp->qy_xj, va2, w6);
+       VecScale(gp->gj_xj, csr1->vij, w1);
+
+/*     w2 = 1.0 / (q.x * gvk.y - q.y * gvk.x);
+       w3 = w1 * w2;
+       w4 = (-gvk.y * csr1->gj + gvk.z * q.y) * w3;
+       VecScale(gp->qx_xj, csr1->vij, w4);
+       w4 = (-gvk.x * csr1->gj - gvk.z * q.x) * w3;
+       VecScale(gp->qy_xj, csr1->vij, w4);
+       VecScale(gp->gj_xj, csr1->vij, w1); */
+/*     gp->qx_xj.x = gp->qx_xj.y = 0.0;
+       gp->qx_xj.z = (-gvk.y * csr1->gj + gvk.z * q.y) * w3;
+       gp->qy_xj.x = gp->qy_xj.y = 0.0;
+       gp->qy_xj.z = (gvk.x * csr1->gj - gvk.z * q.x) * w3;
+       gp->gj_xj.x = gp->gj_xj.y = 0.0;
+       gp->gj_xj.z = w1;
+       quat_rotate(&gp->qx_xj, &gp->qx_xj, &csr1->quat);
+       quat_rotate(&gp->qy_xj, &gp->qy_xj, &csr1->quat);
+       quat_rotate(&gp->gj_xj, &gp->gj_xj, &csr1->quat); */
+
+       /*  Gradients by xk  */
+
+       /*  Intermediate vectors: gradients of gvk.x, gvk.y, gvk.y by xk  */
+       /*  gvkx_xk = grad_xk(gvk.x)  */
+       w1 = csr2->dij * csr2->dij;
+       w2 = (rho_i * rho_i - rho_k * rho_k + w1) / (2.0 * w1);
+       w3 = -(rho_i * rho_i - rho_k * rho_k) / (w1 * w1);
+       va1.x = 1.0;
+       va1.y = va1.z = 0.0;
+       MatrixVec(&va1, csr1->rot_c, &va1); /*quat_rotate(&va1, &va1, &csr1->quat_c);*/
+       w4 = VecDot(va1, csr2->vij) * w3;
+       VecScale(gvkx_xk, va1, w2);
+       VecScaleInc(gvkx_xk, csr2->vij, w4);
+       /*  gvky_xk = grad_xk(gvk.y)  */
+       va1.y = 1.0;
+       va1.x = va1.z = 0.0;
+       MatrixVec(&va1, csr1->rot_c, &va1); /*quat_rotate(&va1, &va1, &csr1->quat_c);*/
+       w4 = VecDot(va1, csr2->vij) * w3;
+       VecScale(gvky_xk, va1, w2);
+       VecScaleInc(gvky_xk, csr2->vij, w4);
+       /*  gvkz_xk = grad_xk(gvk.z)  */
+       va1.z = 1.0;
+       va1.x = va1.y = 0.0;
+       MatrixVec(&va1, csr1->rot_c, &va1); /*quat_rotate(&va1, &va1, &csr1->quat_c);*/
+       w4 = VecDot(va1, csr2->vij) * w3;
+       VecScale(gvkz_xk, va1, w2);
+       VecScaleInc(gvkz_xk, csr2->vij, w4);
+
+       /*   va1 = gvk.x * qx_xk + gvk.y * qy_xk  */
+       w1 = csr2->dij * csr2->dij;
+       w1 = (rho_k * rho_k - rho_i * rho_i + w1) / (2.0 * w1 * csr2->dij);
+       w2 = 2.0 * csr2->gj * w1;
+       VecScale(va1, csr2->vij, w2);
+       VecScaleSelf(gvkx_xk, q.x);
+       VecScaleSelf(gvky_xk, q.y);
+       VecScaleSelf(gvkz_xk, csr1->gj);
+       va1.x -= gvkx_xk.x + gvky_xk.x + gvkz_xk.x;
+       va1.y -= gvkx_xk.y + gvky_xk.y + gvkz_xk.y;
+       va1.z -= gvkx_xk.z + gvky_xk.z + gvkz_xk.z;
+       
+       w2 = 1.0 / (gvk.x * q.y - gvk.y * q.x);
+       w3 = q.y * w2;
+       w5 = -q.x * w2;
+       VecScale(gp->qx_xk, va1, w3);
+       VecScale(gp->qy_xk, va1, w5);
+       VecScale(gp->gk_xk, csr2->vij, w1);
+
+#if 0
+       w1 = csr2->dij;
+       w1 = (rho_k * rho_k - rho_i * rho_i + w1 * w1) / (2.0 * w1 * w1 * w1);
+       w2 = 1.0 / (q.x * gvk.y - q.y * gvk.x);
+       w3 = w1 * w2;
+       gp->qx_xk.x = (2.0 * gvk.x - q.x) * w3;
+       gp->qx_xk.y = (2.0 * gvk.y - q.y) * w3;
+       gp->qx_xk.z = (2.0 * gvk.z - csr1->gj) * w3;
+       gp->qy_xk = gp->qx_xk;
+       VecScaleSelf(gp->qx_xk, q.y);
+       VecScaleSelf(gp->qy_xk, q.x);
+/*     w3 = w1 / csr2->dij;  */
+       VecScale(gp->gk_xk, csr2->vij, w1);
+       MatrixVec(&gp->qx_xk, csr1->rot, &gp->qx_xk); /*quat_rotate(&gp->qx_xk, &gp->qx_xk, &csr1->quat);*/
+       MatrixVec(&gp->qy_xk, csr1->rot, &gp->qy_xk); /*quat_rotate(&gp->qy_xk, &gp->qy_xk, &csr1->quat);*/
+/*     quat_rotate(&gp->gk_xk, &gp->gk_xk, &csr1->quat); */
+#endif
+
+       /*  Gradients of the arc parameter s  */
+       w1 = 1.0 / (csr1->rj * csr1->rj);
+       gp->s_xj.x = (-q.y * gp->qx_xj.x + q.x * gp->qy_xj.x) * w1;
+       gp->s_xj.y = (-q.y * gp->qx_xj.y + q.x * gp->qy_xj.y) * w1;
+       gp->s_xj.z = (-q.y * gp->qx_xj.z + q.x * gp->qy_xj.z) * w1;
+       gp->s_xk.x = (-q.y * gp->qx_xk.x + q.x * gp->qy_xk.x) * w1;
+       gp->s_xk.y = (-q.y * gp->qx_xk.y + q.x * gp->qy_xk.y) * w1;
+       gp->s_xk.z = (-q.y * gp->qx_xk.z + q.x * gp->qy_xk.z) * w1;
+
+#if SP_DEBUG
+       if (arena->debug_result != NULL && arena->debug_output_level > 2) {
+               Int i, j, k, c;
+               FILE *fp = arena->debug_result;  /*  Just an alias for shorter typing */
+               i = csr1->i + 1;
+               j = csr1->j + 1;
+               k = csr2->j + 1;
+               c = (gp->sign ? '+' : '-');
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: qx    =  %10.6f\n", i, j, k, c, q.x);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: qx_xj = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->qx_xj.x, gp->qx_xj.y, gp->qx_xj.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: qx_xk = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->qx_xk.x, gp->qx_xk.y, gp->qx_xk.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: qy    =  %10.6f\n", i, j, k, c, q.y);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: qy_xj = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->qy_xj.x, gp->qy_xj.y, gp->qy_xj.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: qy_xk = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->qy_xk.x, gp->qy_xk.y, gp->qy_xk.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gj    =  %10.6f\n", i, j, k, c, csr1->gj);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gj_xj = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->gj_xj.x, gp->gj_xj.y, gp->gj_xj.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gk    =  %10.6f\n", i, j, k, c, csr2->gj);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gk_xk = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->gk_xk.x, gp->gk_xk.y, gp->gk_xk.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gvk   = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gvk.x, gvk.y, gvk.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gvkx_xk={%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gvkx_xk.x/q.x, gvkx_xk.y/q.x, gvkx_xk.z/q.x);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gvky_xk={%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gvky_xk.x/q.y, gvky_xk.y/q.y, gvky_xk.z/q.y);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: gvkz_xk={%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gvkz_xk.x/csr1->gj, gvkz_xk.y/csr1->gj, gvkz_xk.z/csr1->gj);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: s     =  %10.6f\n", i, j, k, c, atan2(q.y, q.x));
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: s_xj  = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->s_xj.x, gp->s_xj.y, gp->s_xj.z);
+               fprintf(fp, "CALC_GRADIENT[%d,%d,%d,%c]: s_xk  = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, gp->s_xk.x, gp->s_xk.y, gp->s_xk.z);
+       }
+#endif
+}                      
+
+/*  Find the intersection point with the same point_id  */
+static int
+s_find_partner_point(MDArena *arena, const SPArena *sarena, const intersect_record *irp)
+{
+       Int i = irp->i;
+       Int start = sarena->sphere_idx[i * 2];
+       Int end = sarena->sphere_idx[i * 2 + 1];
+       Int j, n;
+       const crossing_sphere_record *csj;
+       for (j = start; j < end; j++) {
+               if (j == irp->idx)
+                       continue;
+               csj = &sarena->spheres[j];
+               for (n = csj->int_start; n < csj->int_end; n++) {
+                       if (sarena->intersects[n].point_id == irp->point_id)
+                               break;
+               }
+               if (n < csj->int_end)
+                       return n;
+       }
+       md_warning(arena, "Internal inconsistency: the intersection point (%d,%d,%d,%d) appears only once", irp->i+1, irp->j+1, irp->k+1, irp->point_id);
+#if DEBUG
+       for (j = 0; j < sarena->nspheres; j++) {
+               csj = &sarena->spheres[j];
+               for (n = csj->int_start; n < csj->int_end; n++) {
+                       intersect_record *irn = &sarena->intersects[n];
+                       md_warning(arena, "DEBUG: point %d = (%d,%d,%d;%d;%d), sphere %d = (%d,%d)", n, irn->i+1, irn->j+1, irn->k+1, irn->point_id, irn->idx, j, csj->i+1, csj->j+1);
+                       if (irn->i != csj->i || irn->j != csj->j)
+                               md_warning(arena, "DEBUG: internal inconsistency, point %d does not belong to the correct crossing sphere record", n);
+               }
+       }
+#endif                 
+       return -1;
+}
+
+/*  Print the debug information  */
+#if SP_DEBUG
+static void
+s_print_surface_area_debug(const MDArena *arena, const gradient_record *gp, const intersect_record *ip, Double da, const char *tag)
+{
+       if (arena->debug_result != NULL && arena->debug_output_level > 2) {
+               Int i, j, k, c;
+               FILE *fp = arena->debug_result;  /*  Just an alias for shorter typing */
+               i = ip->i + 1;
+               j = ip->j + 1;
+               k = ip->k + 1;
+               c = (gp->sign ? '+' : '-');
+               fprintf(fp, "SURF_AREA_GRAD[%d,%d,%d,%c]: %-6s: da   =  %10.6f\n", i, j, k, c, tag, da);
+               fprintf(fp, "SURF_AREA_GRAD[%d,%d,%d,%c]: %-6s: a_xj = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, tag, gp->a_xj.x, gp->a_xj.y, gp->a_xj.z);
+               fprintf(fp, "SURF_AREA_GRAD[%d,%d,%d,%c]: %-6s: a_xk = {%10.6f,%10.6f,%10.6f}\n", i, j, k, c, tag, gp->a_xk.x, gp->a_xk.y, gp->a_xk.z);
+       }
+}
+#define SA_DEBUG(tag, ir) s_print_surface_area_debug(arena, &grad, ir, da, tag)
+#else
+#define SA_DEBUG(tag, ir)
+#endif
+
+/*  Calculate the surface area for each atom  */
+static void
+s_calc_surface_area(MDArena *arena)
+{
+       Int i;      /*  The atom index  */
+       Int idx1, idx2;   /*  The circle (or crossing sphere) indices  */
+       Int n1, n2; /*  The intersection point index  */
+       Int count;  /*  The number of intersection points processed  */
+       intersect_record *ir1, *ir2;
+       Vector p1, p2;
+       gradient_record grad;
+       Double cosomega, sinomega, sinomega_inv, omega, ss;
+       Int natoms = arena->mol->natoms;
+       SPArena *sarena = arena->sp_arena;
+       Double *energies = &arena->energies[kSurfaceIndex];
+       Vector *forces = &arena->forces[kSurfaceIndex * arena->mol->natoms];
+
+#if SP_DEBUG
+       s_sp_debug(arena, "SURFACE AREA CALCULATION");
+#endif
+       memset(forces, 0, sizeof(Vector) * arena->mol->natoms);
+       for (i = 0; i < natoms; i++) {
+               Int start, end;
+               Double area = 0.0;  /*  Accessible area  */
+               Double rho = sarena->atom_rad[i];  /*  The radius of atom i  */
+               Double da;
+               crossing_sphere_record *csr1, *csr2;
+               start = sarena->sphere_idx[i * 2];
+               end = sarena->sphere_idx[i * 2 + 1];
+               /*  Treat the special cases  */
+               if (sarena->atom_buried[i]) {
+                       sarena->atom_area[i] = 0.0;
+               #if SP_DEBUG
+                       s_sp_debug(arena, "atom_area[%d] = 0.0, because atom[%d] is buried", i+1, i+1);
+               #endif
+                       continue;
+               }
+               if (start == end) {
+                       /*  No crossing spheres (isolated sphere)  */
+                       sarena->atom_area[i] = 4 * PI * rho * rho;
+               #if SP_DEBUG
+                       s_sp_debug(arena, "atom_area[%d] = 4*PI*rho**2, because atom[%d] is isolated", i+1, i+1);
+               #endif
+                       continue;
+               }
+               /*  Find the circles without any intersection points */
+               for (idx1 = start; idx1 < end; idx1++) {
+                       csr1 = &sarena->spheres[idx1];
+                       if (csr1->skip)
+                               continue;
+                       if (csr1->int_end == csr1->int_start) {
+                               /*  Is this circle buried? */
+                       /*      p1.x = csr1->rj;
+                               p1.y = 0.0;
+                               p1.z = csr1->gj;
+                               quat_rotate(&p1, &p1, &csr1->quat_c);
+                               for (idx2 = start; idx2 < end; idx2++) {
+                                       if (idx1 == idx2)
+                                               continue;
+                                       if (s_is_inside(sarena, &sarena->spheres[idx2], &p1))
+                                               break;
+                               }
+                               if (idx2 < end) {
+                               } else {
+                               #if SP_DEBUG
+                                       s_sp_debug(arena, "Circle (%d,%d) is buried", i+1, csr1->j+1);
+                               #endif
+                               }
+                       */
+                               Double w1 = sarena->atom_pot[i];
+                       /*      Double w2 = sarena->atom_rad[csr1->j];  */
+                               da = 2 * PI * (1.0 + csr1->gj / rho);
+                               area += da;
+                       /*      w1 = w1 * (csr1->dij * csr1->dij + w2 * w2 - rho * rho) / (2.0 * csr1->dij * csr1->dij * csr1->dij); */
+                               w1 = w1 * PI * rho / csr1->dij;
+                               VecScale(p1, csr1->vij, w1);
+                               VecDec(forces[csr1->j], p1);
+                               VecInc(forces[i], p1);
+                       #if SP_DEBUG
+                               s_sp_debug(arena, "Circle (%d,%d) forms a boundary itself, dA/(rho^2) = %f", i+1, csr1->j+1, da);
+                       #endif
+                       }
+               }
+               /*  Traverse the set of arcs divided by the intersection points  */
+               n1 = -1;
+               ir1 = NULL;
+               csr1 = NULL;
+               count = 0;
+               while (1) {
+                       Vector q1, pw1, pw2;
+                       Double w1, w2, w3, w4;
+                       int do_grad = 1;
+                       if (n1 < 0) {
+                               /*  Find the first unprocessed intersection point  */
+                               ir1 = NULL;
+                       #if SP_DEBUG
+                               s_sp_debug(arena, "Trying to find a new intersection point...");
+                       #endif
+                               for (idx1 = start; idx1 < end; idx1++) {
+                                       csr1 = &sarena->spheres[idx1];
+                                       for (n1 = csr1->int_start; n1 < csr1->int_end; n1++) {
+                                               if (sarena->intersects[n1].flag == 0) {
+                                                       ir1 = &sarena->intersects[n1];
+                                                       break;
+                                               }
+                                       }
+                                       if (ir1 != NULL)
+                                               break;
+                               }
+                               if (ir1 == NULL) {
+                               #if SP_DEBUG
+                                       s_sp_debug(arena, "All points have been processed.");
+                               #endif
+                                       break;  /*  All points have been processed  */
+                               }
+                       #if SP_DEBUG
+                               s_sp_debug(arena, "A new intersection point (%d,%d,%d;%d) was found.", ir1->i+1, ir1->j+1, ir1->k+1, ir1->point_id);
+                       #endif
+                               /*  Determine whether this point is starting point or ending point */
+                               /*  ss is the point between this point and the next point  */
+                               if (n1 == csr1->int_end - 1) {
+                                       ss = 0.5 * (ir1->s + sarena->intersects[csr1->int_start].s + 2 * PI);
+                               } else {
+                                       ss = 0.5 * (ir1->s + (ir1 + 1)->s);
+                               }
+                               p1.x = csr1->rj * cos(ss);
+                               p1.y = csr1->rj * sin(ss);
+                               p1.z = csr1->gj;
+                               MatrixVec(&p1, csr1->rot_c, &p1); /*quat_rotate(&p1, &p1, &csr1->quat_c);*/
+                       #if SP_DEBUG
+                               s_sp_debug(arena, "Examining the point for s=%f, {%f,%f,%f}, whether it is inside any circle", ss, p1.x, p1.y, p1.z);
+                       #endif
+                               /*  Is this point in any of the circle on the same sphere? */
+                               for (idx2 = start; idx2 < end; idx2++) {
+                                       if (idx2 == idx1) /*|| sarena->spheres[idx2].j == ir1->k)*/
+                                               continue;
+                                       if (s_is_inside(&sarena->spheres[idx2], &p1)) {
+                                       #if SP_DEBUG
+                                               s_sp_debug(arena, "The point is within the circle (%d,%d)", sarena->spheres[idx2].i+1, sarena->spheres[idx2].j+1);
+                                       #endif
+                                               break;
+                                       }
+                               }
+                               /*  If yes, then this is the end point. Otherwise, it is the start point. */
+                               /*  We start traversing from the end point; end point->next arc's start
+                                *  point->traverse the arc->end point->next arc's start point->... */
+                               if (idx2 >= end) {
+                                       /*  It is the start point, so start from the partner point */
+                                       n1 = s_find_partner_point(arena, sarena, ir1);
+                                       if (n1 >= 0) {
+                                               ir1 = &sarena->intersects[n1];
+                                               idx1 = ir1->idx;
+                                               csr1 = &sarena->spheres[idx1];
+                                       }
+                               }
+                       #if SP_DEBUG
+                               s_sp_debug(arena, "Start traversing from point (%d,%d,%d;%d)", ir1->i+1, ir1->j+1, ir1->k+1, ir1->point_id);
+                       #endif
+                       } /* end if (n1 < 0) */
+                       if (n1 < 0)
+                               break; /* Abnormal end */
+
+                       /*  Now we are at the 'end point' of a particular arc. */
+
+                       /*  Find the partner point, which is the 'start point' of the next arc. */
+                       n2 = s_find_partner_point(arena, sarena, ir1);
+                       if (n2 < 0)
+                               break;  /* Abnormal end */
+                       ir2 = &sarena->intersects[n2];
+                       idx2 = ir2->idx;
+                       csr2 = &sarena->spheres[idx2];
+               #if SP_DEBUG
+                       s_sp_debug(arena, "The partner point is (%d,%d,%d;%d).", ir2->i+1, ir2->j+1, ir2->k+1, ir2->point_id);
+               #endif
+
+                       /*  The tangent vector (p1) and position vector (q1) at this point  */
+                       /*  (in global coordinates)  */
+                       p1.x = -sin(ir1->s);
+                       p1.y = cos(ir1->s);
+                       p1.z = 0.0;
+                       q1.y = -csr1->rj * p1.x;
+                       q1.x = csr1->rj * p1.y;
+                       q1.z = csr1->gj;
+                       MatrixVec(&p1, csr1->rot_c, &p1); /*quat_rotate(&p1, &p1, &csr1->quat_c);*/
+                       MatrixVec(&q1, csr1->rot_c, &q1); /*quat_rotate(&q1, &q1, &csr1->quat_c);*/
+
+                       /*  The tangent vector at the partner point  */
+                       p2.x = -sin(ir2->s);
+                       p2.y = cos(ir2->s);
+                       p2.z = 0.0;
+                       MatrixVec(&p2, csr2->rot_c, &p2); /*quat_rotate(&p2, &p2, &csr2->quat_c);*/
+
+                       /*  The external angle (omega)  */
+                       cosomega = VecDot(p1, p2)/(VecLength(p1) * VecLength(p2));
+                       sinomega = sqrt(1.0 - cosomega * cosomega);
+                       omega = atan2(sinomega, cosomega);
+
+                       /*  If omega is close to 0 or PI, then avoid calculation of the gradient
+                        *  because the surface area function becomes singular  */
+                       if (cosomega > 0.95 || cosomega < -0.95) {
+                               VecZero(grad.a_xj);
+                               VecZero(grad.a_xk);
+                               do_grad = 0;
+                       } else do_grad = 1;
+
+                       /*  Gradients of s (arc parameter) at this point */
+               #if SP_DEBUG
+                       grad.sign = 0;
+               #endif
+                       if (do_grad)
+                               s_calc_gradient_vectors(&grad, arena, csr1, csr2, &q1);
+
+                       /*  The arc parameter term of the surface area */
+                       /*  (The arc parameter term is added at each 'end point', and
+                        *  subtracted at each 'start point'.)  */
+                       da = ir1->s * csr1->gj / rho;
+               #if SP_DEBUG
+                       s_sp_debug(arena, "The arc term [end point] = %f (s = %f)", ir1->s * csr1->gj / rho, ir1->s);
+               #endif
+
+                       /*  Gradients of the arc parameter term  */
+                       if (do_grad) {
+                               grad.a_xj.x = (csr1->gj * grad.s_xj.x + ir1->s * grad.gj_xj.x) / rho;
+                               grad.a_xj.y = (csr1->gj * grad.s_xj.y + ir1->s * grad.gj_xj.y) / rho;
+                               grad.a_xj.z = (csr1->gj * grad.s_xj.z + ir1->s * grad.gj_xj.z) / rho;
+                               grad.a_xk.x = (csr1->gj * grad.s_xk.x) / rho;
+                               grad.a_xk.y = (csr1->gj * grad.s_xk.y) / rho;
+                               grad.a_xk.z = (csr1->gj * grad.s_xk.z) / rho;
+                               SA_DEBUG("ARC1", ir1);
+                       }
+
+                       /*  The omega (external angle) term of the surface area  */
+                       VecCross(pw2, p1, p2);
+                       if (VecDot(q1, pw2) < 0) {
+                               omega = -omega;
+                               sinomega = -sinomega;
+                       }
+                       da += omega;
+               #if SP_DEBUG
+                       s_sp_debug(arena, "The external angle is %f (%f deg); the tangent vectors are {%f,%f,%f}, {%f,%f,%f}", omega, omega*180/PI, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
+               #endif
+
+                       sinomega_inv = 0.0;  /*  Not significant; just for suppress compiler warnings */
+
+                       /*  pw1(pw2): vector from the center of the circle j(k) to q1 */
+                       w1 = -csr1->gj / csr1->dij;
+                       pw1 = q1;
+                       VecScaleInc(pw1, csr1->vij, w1);
+                       w1 = -csr2->gj / csr2->dij;
+                       pw2 = q1;
+                       VecScaleInc(pw2, csr2->vij, w1);
+
+                       /*  Gradients of the omega term, the first half  */
+                       if (do_grad) {
+                               sinomega_inv = (fabs(sinomega) < 1e-5 ? 0.0 : 1.0 / sinomega);
+                               w2 = sinomega_inv * VecDot(pw1, p2) / csr1->rj;
+                               w3 = -sin(ir1->s) * sinomega_inv;
+                               w4 = cos(ir1->s) * sinomega_inv;
+                               grad.a_xj.x += w2 * grad.s_xj.x
+                                       - w3 * (grad.mx[0]*p2.x + grad.mx[3]*p2.y + grad.mx[6]*p2.z)
+                                       - w4 * (grad.mx[1]*p2.x + grad.mx[4]*p2.y + grad.mx[7]*p2.z);
+                               grad.a_xj.y += w2 * grad.s_xj.y
+                                       - w3 * (grad.my[0]*p2.x + grad.my[3]*p2.y + grad.my[6]*p2.z)
+                                       - w4 * (grad.my[1]*p2.x + grad.my[4]*p2.y + grad.my[7]*p2.z);
+                               grad.a_xj.z += w2 * grad.s_xj.z
+                                       - w3 * (grad.mz[0]*p2.x + grad.mz[3]*p2.y + grad.mz[6]*p2.z)
+                                       - w4 * (grad.mz[1]*p2.x + grad.mz[4]*p2.y + grad.mz[7]*p2.z);
+                               VecScaleInc(grad.a_xk, grad.s_xk, w2);
+                       }
+
+               #if 0
+               {
+                       s_sp_debug(arena, "[%d,%d,%d] p1.x = %10.6f, d(p1.x)/dxj = {%10.6f,%10.6f,%10.6f} d(p1.x)/dxk = {%10.6f,%10.6f,%10.6f}", ir1->i+1, ir1->j+1, ir1->k+1, p1.x,
+                               (grad.mx[0]*w3+grad.mx[1]*w4)*sinomega - pw1.x/csr1->rj*grad.s_xj.x,
+                               (grad.my[0]*w3+grad.my[1]*w4)*sinomega - pw1.x/csr1->rj*grad.s_xj.y,
+                               (grad.mz[0]*w3+grad.mz[1]*w4)*sinomega - pw1.x/csr1->rj*grad.s_xj.z,
+                               -pw1.x/csr1->rj*grad.s_xk.x,
+                               -pw1.x/csr1->rj*grad.s_xk.y,
+                               -pw1.x/csr1->rj*grad.s_xk.z);
+                       s_sp_debug(arena, "[%d,%d,%d] p1.y = %10.6f, d(p1.y)/dxj = {%10.6f,%10.6f,%10.6f} d(p1.y)/dxk = {%10.6f,%10.6f,%10.6f}", ir1->i+1, ir1->j+1, ir1->k+1, p1.y,
+                               (grad.mx[3]*w3+grad.mx[4]*w4)*sinomega - pw1.y/csr1->rj*grad.s_xj.x,
+                               (grad.my[3]*w3+grad.my[4]*w4)*sinomega - pw1.y/csr1->rj*grad.s_xj.y,
+                               (grad.mz[3]*w3+grad.mz[4]*w4)*sinomega - pw1.y/csr1->rj*grad.s_xj.z,
+                               -pw1.y/csr1->rj*grad.s_xk.x,
+                               -pw1.y/csr1->rj*grad.s_xk.y,
+                               -pw1.y/csr1->rj*grad.s_xk.z);
+                       s_sp_debug(arena, "[%d,%d,%d] p1.z = %10.6f, d(p1.z)/dxj = {%10.6f,%10.6f,%10.6f} d(p1.z)/dxk = {%10.6f,%10.6f,%10.6f}", ir1->i+1, ir1->j+1, ir1->k+1, p1.y,
+                               (grad.mx[6]*w3+grad.mx[7]*w4)*sinomega - pw1.z/csr1->rj*grad.s_xj.x,
+                               (grad.my[6]*w3+grad.my[7]*w4)*sinomega - pw1.z/csr1->rj*grad.s_xj.y,
+                               (grad.mz[6]*w3+grad.mz[7]*w4)*sinomega - pw1.z/csr1->rj*grad.s_xj.z,
+                               -pw1.z/csr1->rj*grad.s_xk.x,
+                               -pw1.z/csr1->rj*grad.s_xk.y,
+                               -pw1.z/csr1->rj*grad.s_xk.z);
+               }
+               #endif
+
+                       /*  Calculate the gradients for the partner point */
+               #if SP_DEBUG
+                       grad.sign = 1;
+               #endif
+                       if (do_grad)
+                               s_calc_gradient_vectors(&grad, arena, csr2, csr1, &q1);
+
+                       /*  Gradients of the omega term, the second half  */
+                       if (do_grad) {
+                               w2 = sinomega_inv * VecDot(pw2, p1) / csr2->rj;
+                               w3 = -sin(ir2->s) * sinomega_inv;
+                               w4 = cos(ir2->s) * sinomega_inv;
+                               grad.a_xk.x += w2 * grad.s_xj.x
+                                       - w3 * (grad.mx[0]*p1.x + grad.mx[3]*p1.y + grad.mx[6]*p1.z)
+                                       - w4 * (grad.mx[1]*p1.x + grad.mx[4]*p1.y + grad.mx[7]*p1.z);
+                               grad.a_xk.y += w2 * grad.s_xj.y
+                                       - w3 * (grad.my[0]*p1.x + grad.my[3]*p1.y + grad.my[6]*p1.z)
+                                       - w4 * (grad.my[1]*p1.x + grad.my[4]*p1.y + grad.my[7]*p1.z);
+                               grad.a_xk.z += w2 * grad.s_xj.z
+                                       - w3 * (grad.mz[0]*p1.x + grad.mz[3]*p1.y + grad.mz[6]*p1.z)
+                                       - w4 * (grad.mz[1]*p1.x + grad.mz[4]*p1.y + grad.mz[7]*p1.z);
+                               VecScaleInc(grad.a_xj, grad.s_xk, w2);
+                               SA_DEBUG("OMEGA", ir1);
+                       }
+
+               #if 0
+               {
+                       s_sp_debug(arena, "[%d,%d,%d] p2.x = %10.6f, d(p2.x)/dxj = {%10.6f,%10.6f,%10.6f} d(p2.x)/dxk = {%10.6f,%10.6f,%10.6f}", ir1->i+1, ir1->j+1, ir1->k+1, p2.x,
+                               -pw2.x/csr2->rj*grad.s_xk.x,
+                               -pw2.x/csr2->rj*grad.s_xk.y,
+                               -pw2.x/csr2->rj*grad.s_xk.z,
+                               (grad.mx[0]*w3+grad.mx[1]*w4)*sinomega - pw2.x/csr2->rj*grad.s_xj.x,
+                               (grad.my[0]*w3+grad.my[1]*w4)*sinomega - pw2.x/csr2->rj*grad.s_xj.y,
+                               (grad.mz[0]*w3+grad.mz[1]*w4)*sinomega - pw2.x/csr2->rj*grad.s_xj.z);
+                       s_sp_debug(arena, "[%d,%d,%d] p2.y = %10.6f, d(p2.y)/dxj = {%10.6f,%10.6f,%10.6f} d(p2.y)/dxk = {%10.6f,%10.6f,%10.6f}", ir1->i+1, ir1->j+1, ir1->k+1, p2.y,
+                               -pw2.y/csr2->rj*grad.s_xk.x,
+                               -pw2.y/csr2->rj*grad.s_xk.y,
+                               -pw2.y/csr2->rj*grad.s_xk.z,
+                               (grad.mx[3]*w3+grad.mx[4]*w4)*sinomega - pw2.y/csr2->rj*grad.s_xj.x,
+                               (grad.my[3]*w3+grad.my[4]*w4)*sinomega - pw2.y/csr2->rj*grad.s_xj.y,
+                               (grad.mz[3]*w3+grad.mz[4]*w4)*sinomega - pw2.y/csr2->rj*grad.s_xj.z);
+                       s_sp_debug(arena, "[%d,%d,%d] p2.z = %10.6f, d(p2.z)/dxj = {%10.6f,%10.6f,%10.6f} d(p2.z)/dxk = {%10.6f,%10.6f,%10.6f}", ir1->i+1, ir1->j+1, ir1->k+1, p2.z,
+                               -pw2.z/csr2->rj*grad.s_xk.x,
+                               -pw2.z/csr2->rj*grad.s_xk.y,
+                               -pw2.z/csr2->rj*grad.s_xk.z,
+                               (grad.mx[6]*w3+grad.mx[7]*w4)*sinomega - pw2.z/csr2->rj*grad.s_xj.x,
+                               (grad.my[6]*w3+grad.my[7]*w4)*sinomega - pw2.z/csr2->rj*grad.s_xj.y,
+                               (grad.mz[6]*w3+grad.mz[7]*w4)*sinomega - pw2.z/csr2->rj*grad.s_xj.z);
+               }
+               #endif
+
+                       /*  The arc parameter term for the partner point  */
+                       /*  Substract 2*PI if this is the last point registered for circle k,
+                        *  because the s parameter of next point will cross the [-PI,PI] boundary. */
+                       ss = ir2->s;
+                       if (n2 == csr2->int_end - 1)
+                               ss -= 2 * PI;
+                       da -= ss * csr2->gj / rho;
+               #if SP_DEBUG
+                       s_sp_debug(arena, "The arc term [start point] = %f (s = %f%s)", ss * csr2->gj / rho, ir2->s, (n2 == csr2->int_end - 1 ? " - 2*PI" : ""));
+               #endif
+                       /*  Gradients of the arc parameter term  */
+                       if (do_grad) {
+                               grad.a_xk.x -= (csr2->gj * grad.s_xj.x + ss * grad.gj_xj.x) / rho;
+                               grad.a_xk.y -= (csr2->gj * grad.s_xj.y + ss * grad.gj_xj.y) / rho;
+                               grad.a_xk.z -= (csr2->gj * grad.s_xj.z + ss * grad.gj_xj.z) / rho;
+                               grad.a_xj.x -= (csr2->gj * grad.s_xk.x) / rho;
+                               grad.a_xj.y -= (csr2->gj * grad.s_xk.y) / rho;
+                               grad.a_xj.z -= (csr2->gj * grad.s_xk.z) / rho;
+                               SA_DEBUG("ARC2", ir1);
+                       }
+
+                       /*  Mark this point (two entries) as processed  */
+                       ir1->flag = ir2->flag = 1;
+
+                       /*  Add up the partial area and force  */
+                       w1 = sarena->atom_pot[ir1->i] * rho * rho;
+                       area += da;
+                       VecScale(pw1, grad.a_xj, w1);
+                       VecScale(pw2, grad.a_xk, w1);
+                       VecDec(forces[ir1->j], pw1);
+                       VecDec(forces[ir1->k], pw2);
+                       VecInc(pw1, pw2);
+                       VecInc(forces[ir1->i], pw1);
+
+               #if DEBUG
+                       if (VecLength2(pw1) > 1.0 || VecLength2(pw2) > 1.0) {
+                               warning("Warning: The surface force becomes very large. Maybe something is wrong?");
+                               warning("Warning: STEP %d, (i,j,k,id)=(%d,%d,%d,%d), pw1={%f,%f,%f}, pw2={%f,%f,%f}", arena->step, ir1->i+1, ir1->j+1, ir1->k+1, ir1->point_id, pw1.x, pw1.y, pw1.z, pw2.x, pw2.y, pw2.z);
+                       }
+               #endif
+               
+                       count++;
+
+                       /*  Find the next point on circle k */
+                       n1 = n2 + 1;
+                       if (n1 >= csr2->int_end)
+                               n1 = csr2->int_start;
+                       ir1 = &sarena->intersects[n1];
+               #if SP_DEBUG
+                       s_sp_debug(arena, "The next point on the arc is (%d,%d,%d;%d)%s", ir1->i+1, ir1->j+1, ir1->k+1, ir1->point_id, (ir1->flag ? ", which is already processed" : "."));
+               #endif
+                       if (ir1->flag) {
+                               /*  This point is already processed; then close this boundary  */
+                               n1 = -1;
+                       } else {
+                               idx1 = idx2;
+                               csr1 = csr2;
+                       }
+                       
+
+               } /* end while (1) */
+               if (count > 0) { /* Some intersection points were processed */
+                       area += 2 * PI;
+               }
+               area -= floor(area / (4 * PI)) * (4 * PI);
+               sarena->atom_area[i] = area * rho * rho;
+               *energies += sarena->atom_area[i] * sarena->atom_pot[i];
+       #if SP_DEBUG
+               s_sp_debug(arena, "The total accessible area of atom %d is %f", i+1, area*rho*rho);
+       #endif
+       }
+}
+
+/*  Calculate the surface potential/force of a molecule */
+void
+calc_surface_force(MDArena *arena)
+{
+       if (arena->sp_arena == NULL)
+               s_allocate_sp_arena(arena);
+       arena->sp_arena->nspheres = 0;
+       arena->sp_arena->nintersects = 0;
+       arena->sp_arena->npoints = 0;
+
+       s_list_crossing_spheres(arena);
+       s_list_intersection_points(arena);
+#if SP_DEBUG
+       s_print_internal_information(arena);
+#endif
+       s_calc_surface_area(arena);
+
+#if DEBUG
+       if (arena->debug_result && arena->debug_output_level > 1) {
+               int i;
+               for (i = 0; i < arena->mol->natoms; i++) {
+                       Vector *fp = &arena->forces[kSurfaceIndex * arena->mol->natoms + i];
+                       fprintf(arena->debug_result, "surface of atom %d: area=%f, pot=%f, {%f %f %f}\n", i+1, arena->sp_arena->atom_area[i], arena->sp_arena->atom_area[i]*arena->sp_arena->atom_pot[i], fp->x, fp->y, fp->z);
+               }
+       }
+#endif
+}
+
+/*  Clear the surface potential record  */
+void
+clear_sp_arena(MDArena *arena)
+{
+       if (arena->sp_arena != NULL) {
+               SPArena *sarena = arena->sp_arena;
+               if (sarena->spheres != NULL)
+                       free(sarena->spheres);
+               if (sarena->intersects != NULL)
+                       free(sarena->intersects);
+               if (sarena->points != NULL)
+                       free(sarena->points);
+               if (sarena->atom_rad != NULL)
+                       free(sarena->atom_rad);
+               if (sarena->atom_pot != NULL)
+                       free(sarena->atom_pot);
+               if (sarena->atom_area != NULL)
+                       free(sarena->atom_area);
+               if (sarena->atom_buried != NULL)
+                       free(sarena->atom_buried);
+               if (sarena->sphere_idx != NULL)
+                       free(sarena->sphere_idx);
+               free(sarena);
+               arena->sp_arena = NULL;
+       }
+}
+
+/*  Print surface area information  */
+void
+print_surface_area(MDArena *arena)
+{
+       Int i, natoms;
+       if (arena->sp_arena == NULL)
+               return;
+       natoms = arena->mol->natoms;
+       printf("Atom     area    energy force\n");
+       for (i = 0; i < natoms; i++) {
+               Vector f = arena->forces[kSurfaceIndex * natoms + i];
+               Double area = arena->sp_arena->atom_area[i];
+               Double energy = area * arena->sp_arena->atom_pot[i];
+               VecScaleSelf(f, INTERNAL2KCAL);
+               energy *= INTERNAL2KCAL;
+               printf("%5d %11.5f %11.5f %11.5f %11.5f %11.5f\n",
+                       i+1, area, energy, f.x, f.y, f.z);
+       }
+}
diff --git a/MolLib/MD/MDSurface.h b/MolLib/MD/MDSurface.h
new file mode 100644 (file)
index 0000000..b152029
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  MDSurface.h
+ *
+ *  Created by Toshi Nagata on 2005/06/22.
+ *  Copyright 2005 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MDSURFACE_H__
+#define __MDSURFACE_H__
+
+#include "MDCore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+/*  Internal information for a pair of intercrossing spheres  */
+typedef struct crossing_sphere_record {
+       Int    i;        /*  The index of the target sphere  */
+       Int    j;        /*  The index of the crossing sphere  */
+       Vector vij;      /*  Center-to-center vector  */
+       Double  dij;      /*  The length of vij  */
+       Double  rj;       /*  The radius of the intersection circle  */
+       Double  gj;       /*  sqrt(atom_rad[i]**2 - rj**2)  */
+       Mat33  rot;      /*  The rotation matrix that convert vij->(0,0,1) and qij->(1,0,0) 
+                            where qij = vij x (1,0,0) {if vij is not parallel with (1,0,0)} 
+                                     or vij x (0,1,0) {otherwise}  */
+       Mat33  rot_c;    /*  The inverse rotation  */
+       Int    skip;     /*  This entry should be skipped (i.e. the intersection circle is included within another circle) */
+       Int    int_start, int_end;  /* The start and end indices of the intersect_record array (int_end is not included, so int_end - int_start is the number of records) */
+} crossing_sphere_record;
+
+/*  Intersection points on one particular crossing circle  */
+typedef struct intersect_record {
+       Int    i;        /*  The index of the target sphere  */
+       Int    j;        /*  The index of the sphere to define the crossing circle  */
+       Int    k;        /*  The index of the sphere that causes this intersection point */
+       Int    idx;      /*  The index to the spheres[] (to allow quick access) */
+       unsigned int flag : 1;      /*  The flag to show this point is already processed  */
+       unsigned int point_id : 31; /*  The id number of this point = index to points[] */
+       Double  s;        /*  The arc-length parameter (radian in [-pi,pi]) */
+} intersect_record;
+
+/*  One particular intersection point  */
+typedef struct intersect_point_record {
+       unsigned int sign : 1;  /*  The flag to distinguish the two crossing points on the same (i,j,k) sphere combination  */
+       unsigned int i : 31;    /*  The index of the target sphere  */
+       Int    idx_j, idx_k; /*  The crossing circles; indices to spheres[]  */
+       Vector v;        /*  The global coordinate  */
+} intersect_point_record;
+
+/*  A record for working on gradients  */
+typedef struct gradient_record {
+       Vector qx_xj, qy_xj, gj_xj, s_xj;
+       Vector qx_xk, qy_xk, gk_xk, s_xk;
+       Mat33  mx, my, mz;  /*  Differentials of (i,j)-local-to-global matrix M by xj.x, xj.y, xj.z  */
+       Vector a_xj, a_xk;
+#if DEBUG
+       /*  Not significant for calculation; only used for printing debug information. */
+       Byte sign;  /* 1 if this point is 'startint point', 0 otherwise. */
+#endif
+} gradient_record;
+
+typedef struct SPArena {
+       Int    nspheres;
+       Int    maxspheres;
+       crossing_sphere_record *spheres;
+       Int    nintersects;
+       Int    maxintersects;
+       intersect_record *intersects;
+       Int    npoints;
+       Int    maxpoints;
+       intersect_point_record *points;    /*  The global coordinates of the intersection points  */
+       Double  *atom_rad;  /*  The radius of the atoms (sum of the vdw and probe radii)  */
+       Double  *atom_pot;  /*  The contribution of each atom to the total surface potential */
+       Double  *atom_area; /*  The accessible surface of each atom  */
+       Byte   *atom_buried;  /*  1 if this atom is buried in another atom  */
+       Int    *sphere_idx; /*  The start/end indices of the crossing_sphere_record array (the size of the array is natoms*2) */
+} SPArena;
+
+void clear_sp_arena(MDArena *arena);
+void calc_surface_force(MDArena *arena);
+void print_surface_area(MDArena *arena);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __MDSURFACE_H__ */
+
diff --git a/MolLib/MainView.c b/MolLib/MainView.c
new file mode 100755 (executable)
index 0000000..bd8a2db
--- /dev/null
@@ -0,0 +1,3412 @@
+/*
+ *  MainView.c
+ *
+ *  Created by Toshi Nagata on 06/07/30.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MolLib.h"
+
+#include "MD/MDCore.h"
+#include "MD/MDGraphite.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define biso2radius(r) ((r) > 0.5 ? sqrt((r) / 78.9568352087147) : 0.08)
+
+/*  Invalid bond/angle/torsion value, used in internal cache  */
+const Double kInvalidValue = -10000000.0;
+
+#pragma mark ==== MainView public methods ====
+
+MainView *
+MainView_newMainView(void *ref)
+{
+       static GLdouble sIdentity[16] = {
+               1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
+       };
+       MainView *mview = (MainView *)malloc(sizeof(MainView));
+       if (mview != NULL) {
+               memset(mview, 0, sizeof(MainView));
+               mview->ref = ref;
+               mview->track = TrackballNew();
+               mview->mode = kTrackballRotateMode;
+               mview->tempAtoms[0] = mview->tempAtoms[1] = -1;
+               memmove(mview->modelview_matrix, sIdentity, sizeof(sIdentity));
+               memmove(mview->projection_matrix, sIdentity, sizeof(sIdentity));
+               mview->atomRadius = 0.4;
+               mview->bondRadius = 0.1;
+               mview->probabilityScale = 1.5382;
+               mview->dimension = 10.0;
+       /*      MainView_resizeToFit(mview); */
+               mview->showHydrogens = mview->showDummyAtoms = mview->showExpandedAtoms = 1;
+               mview->showPeriodicBox = 1;
+               mview->showGraphite = 5;
+               mview->tableCache = IntGroupNew();
+               mview->tableSelection = IntGroupNew();
+       }
+       return mview;
+}
+
+void
+MainView_release(MainView *mview)
+{
+       int i;
+       if (mview != NULL) {
+               MainView_setMolecule(mview, NULL);
+               TrackballRelease(mview->track);
+               IntGroupRelease(mview->tableCache);
+               IntGroupRelease(mview->tableSelection);
+               if (mview->nlabels > 0) {
+                       for (i = 0; i < mview->nlabels; i++) {
+                               MainViewCallback_releaseLabel(mview->labels[i].label);
+                       }
+                       free(mview->labels);
+                       free(mview->sortedLabels);
+               }
+               if (mview->rotateFragment != NULL)
+                       IntGroupRelease(mview->rotateFragment);
+               if (mview->rotateFragmentOldPos != NULL)
+                       free(mview->rotateFragmentOldPos);
+               if (mview->visibleFlags != NULL)
+                       free(mview->visibleFlags);
+       }
+       free(mview);
+}
+
+void
+MainView_setMolecule(MainView *mview, struct Molecule *mol)
+{
+       if (mview == NULL || mview->mol == mol)
+               return;
+       if (mview->mol != NULL) {
+               mview->mol->mview = NULL;  /*  No need to release  */
+               MoleculeRelease(mview->mol);
+       }
+       mview->mol = mol;
+       if (mol != NULL) {
+               MoleculeRetain(mol);
+               mol->mview = mview;  /*  No retain  */
+               MainViewCallback_moleculeReplaced(mview, mol);
+               MainView_resizeToFit(mview);
+               MoleculeCallback_notifyModification(mol, 0);
+       /*      MainViewCallback_setNeedsDisplay(mview, 1); */
+       }
+}
+
+int
+MainView_isAtomHidden(MainView *mview, int index)
+{
+       if (index < 0 || index >= mview->mol->natoms)
+               return 1;
+       else if (mview->visibleFlags == NULL)
+               return 0;
+       else return (mview->visibleFlags[index] == 0);
+/*     Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, index);
+       if (ap->exflags & kAtomHiddenFlag)
+               return 1;
+       if (!mview->showHydrogens && ap->atomicNumber == 1)
+               return 1;
+       if (!mview->showDummyAtoms && ap->atomicNumber == 0)
+               return 1;
+       return 0; */
+}
+
+void
+MainView_refreshCachedInfo(MainView *mview)
+{
+       Molecule *mol;
+       int i, j, n1, n2, n3, n4;
+       Byte f1, f2, f3, f4;
+       Atom *ap;
+
+       if (mview == NULL || (mol = mview->mol) == NULL)
+               return;
+
+       /*  Rebuild internal caches  */
+       
+       /*  Visible flags  */
+       if (mview->visibleFlags == NULL)
+               mview->visibleFlags = (Byte *)malloc(mol->natoms);
+       else
+               mview->visibleFlags = (Byte *)realloc(mview->visibleFlags, mol->natoms);
+       memset(mview->visibleFlags, 0, mol->natoms);
+       mview->countHidden = 0;
+       for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (ap->exflags & kAtomHiddenFlag) {
+                       mview->countHidden++;
+                       continue;
+               }
+               if (!mview->showHydrogens && ap->atomicNumber == 1)
+                       continue;
+               if (!mview->showDummyAtoms && ap->atomicNumber == 0)
+                       continue;
+               mview->visibleFlags[i] = 1;
+       }
+       
+       /*  Selection (only temporary used within this function)  */
+       {
+               IntGroup *sel = MoleculeGetSelection(mol);
+               if (sel != NULL && IntGroupGetCount(sel) > 0) {
+                       for (i = 0; (n1 = IntGroupGetStartPoint(sel, i)) >= 0; i++) {
+                               n2 = IntGroupGetEndPoint(sel, i);
+                               if (n2 > mol->natoms)
+                                       n2 = mol->natoms;
+                               while (n1 < n2)
+                                       mview->visibleFlags[n1++] |= 2;
+                       }
+               }
+       }
+       
+       /*  Table caches  */
+       if (mview->tableCache == NULL)
+               mview->tableCache = IntGroupNew();
+       if (mview->tableSelection == NULL)
+               mview->tableSelection = IntGroupNew();
+       IntGroupClear(mview->tableCache);
+       IntGroupClear(mview->tableSelection);
+               
+       if (mview->tableIndex == 0) {  /* Atoms */
+               for (i = j = 0; i < mol->natoms; i++) {
+                       f1 = mview->visibleFlags[i];
+                       if ((f1 & 1) != 0) {
+                               IntGroupAdd(mview->tableCache, i, 1);
+                               if ((f1 & 2) != 0)
+                                       IntGroupAdd(mview->tableSelection, j, 1);
+                               j++;
+                       }
+               }
+       } else if (mview->tableIndex == 1) {  /* Bonds */
+               for (i = j = 0; i < mol->nbonds; i++) {
+                       n1 = mol->bonds[i * 2];
+                       n2 = mol->bonds[i * 2 + 1];
+                       f1 = mview->visibleFlags[n1];
+                       f2 = mview->visibleFlags[n2];
+                       if ((f1 & 1) != 0 && (f2 & 1) != 0) {
+                               IntGroupAdd(mview->tableCache, i, 1);
+                               if ((f1 & 2) != 0 && (f2 & 2) != 0)
+                                       IntGroupAdd(mview->tableSelection, j, 1);
+                               j++;
+                       }
+               }
+       } else if (mview->tableIndex == 2) {  /* Angles */
+               for (i = j = 0; i < mol->nangles; i++) {
+                       n1 = mol->angles[i * 3];
+                       n2 = mol->angles[i * 3 + 1];
+                       n3 = mol->angles[i * 3 + 2];
+                       f1 = mview->visibleFlags[n1];
+                       f2 = mview->visibleFlags[n2];
+                       f3 = mview->visibleFlags[n3];
+                       if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0) {
+                               IntGroupAdd(mview->tableCache, i, 1);
+                               if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0)
+                                       IntGroupAdd(mview->tableSelection, j, 1);
+                               j++;
+                       }
+               }
+       } else if (mview->tableIndex == 3) {  /* Dihedrals */
+               for (i = j = 0; i < mol->ndihedrals; i++) {
+                       n1 = mol->dihedrals[i * 4];
+                       n2 = mol->dihedrals[i * 4 + 1];
+                       n3 = mol->dihedrals[i * 4 + 2];
+                       n4 = mol->dihedrals[i * 4 + 3];
+                       f1 = mview->visibleFlags[n1];
+                       f2 = mview->visibleFlags[n2];
+                       f3 = mview->visibleFlags[n3];
+                       f4 = mview->visibleFlags[n4];
+                       if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
+                               IntGroupAdd(mview->tableCache, i, 1);
+                               if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
+                                       IntGroupAdd(mview->tableSelection, j, 1);
+                               j++;
+                       }
+               }
+       } else if (mview->tableIndex == 4) {  /* Impropers */
+               for (i = j = 0; i < mol->nimpropers; i++) {
+                       n1 = mol->impropers[i * 4];
+                       n2 = mol->impropers[i * 4 + 1];
+                       n3 = mol->impropers[i * 4 + 2];
+                       n4 = mol->impropers[i * 4 + 3];
+                       f1 = mview->visibleFlags[n1];
+                       f2 = mview->visibleFlags[n2];
+                       f3 = mview->visibleFlags[n3];
+                       f4 = mview->visibleFlags[n4];
+                       if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
+                               IntGroupAdd(mview->tableCache, i, 1);
+                               if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
+                                       IntGroupAdd(mview->tableSelection, j, 1);
+                               j++;
+                       }
+               }
+       } else if (mview->tableIndex == 5) {  /* MO infos  */
+               /*  Really no need to cache info, but create it anyway to simplify code  */
+               if (mol->bset != NULL && mol->bset->nmos > 0)
+                       IntGroupAdd(mview->tableCache, 0, mol->bset->nmos);
+       }
+       
+       /*  Clear internal selection flag  */
+       for (i = 0; i < mol->natoms; i++) {
+               mview->visibleFlags[i] &= ~2;
+       }
+}
+
+#pragma mark ====== 2D/3D transform operations ======
+
+void
+MainView_resizeToFit(MainView *mview)
+{
+       Vector p;
+       float f[4];
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       if (mview->mol->natoms == 0) {
+               TrackballReset(mview->track);
+               return;
+       }
+       MoleculeCenterOfMass(mview->mol, &p, NULL);
+/*     if (mview->mol->is_xtal_coord)
+               TransformVec(&p, mview->mol->cell->tr, &p); */
+       f[0] = -p.x / mview->dimension;
+       f[1] = -p.y / mview->dimension;
+       f[2] = -p.z / mview->dimension;
+       TrackballSetTranslate(mview->track, f);
+
+       /*  Set scale
+               r0: the longest distance from the center of mass
+               r0 > dimension: scale = -log(r0/dimension)/log(10)  (negative)
+               r0 < dimension: scale = -log(atan2(r0, dimension*cot(15deg))*180deg/pi*2/30deg)/log(10) (positive)
+       */
+       {
+               int i;
+               Vector q;
+               Atom *ap;
+               double r0 = 0.0, r1, scale;
+               for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       q = ap->r;
+               /*      if (mview->mol->is_xtal_coord)
+                               TransformVec(&q, mview->mol->cell->tr, &q); */
+                       VecDec(q, p);
+                       r1 = VecLength(q);
+                       if (r1 > r0)
+                               r0 = r1;
+               }
+               r0 /= mview->dimension;
+               if (r0 < 1e-6)
+                       scale = 0.0;
+               else if (r0 < 1.0)
+                       scale = -log(atan2(r0, kCot15Deg) * kRad2Deg * 2 / 30.0) / kLog10;
+               else
+                       scale = -log(r0) / kLog10;
+               TrackballSetScale(mview->track, scale);
+       }
+
+       MainViewCallback_setNeedsDisplay(mview, 1);
+
+#if 0
+       GLdouble mm[4][4];
+       GLdouble p0[4];
+       int i, j, natoms;
+       const Atom *ap;
+       const AtomPar *app;
+       GLdouble *pp, *p1;
+       GLdouble max[3], min[3], center[3];
+       float frame[4], width, height, cot15, rsin15, cot_th, rsin_th, d, trans[3];
+       Molecule *mol;
+       
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       mol = mview->mol;
+       
+       /*  Transform the coordinates of all atoms with the rotation part of
+        *  the model-view matrix  */
+       memmove((GLdouble *)mm, mview->modelview_matrix, sizeof(float) * 16);
+       natoms = mol->natoms;
+       pp = (GLdouble *)calloc(sizeof(GLdouble), 4 * natoms);
+       MALLOC_CHECK(pp, "resizing the model to fit");
+       for (i = 0; i < natoms; i++) {
+               ap = ATOM_AT_INDEX(mol->atoms, i);
+               if (ap == NULL)
+                       continue;
+               p0[0] = ap->r.x; p0[1] = ap->r.y; p0[2] = ap->r.z;
+               p0[3] = 0.0;  /*  This disables the translation part  */
+               p1 = &pp[i * 4];
+               MAT_DOT_VEC_4X4(p1, mm, p0);  /* #define'd in GLUT/vvector.h */
+       }
+       
+       /*  Determine the center for each axis  */
+       max[0] = max[1] = max[2] = -1e20;
+       min[0] = min[1] = min[2] = 1e20;
+       for (i = 0; i < natoms; i++) {
+               p1 = &pp[i * 4];
+               for (j = 0; j < 3; j++) {
+                       if (p1[j] > max[j])
+                               max[j] = p1[j];
+                       if (p1[j] < min[j])
+                               min[j] = p1[j];
+               }
+       }
+       for (j = 0; j < 3; j++)
+               center[j] = (min[j] + max[j]) * 0.5;
+       
+       /*  Get the frame size  */
+       MainViewCallback_frame(mview, frame);
+       width = frame[2] - frame[0];
+       height = frame[3] - frame[1];
+
+       /*  Calculate the minimum distance from which the view angle of
+        *  the atom becomes no more than 15 degree (for the y direction)
+        *  and theta degree (for the x direction, theta = arctan(h/w*tan(15 deg))) */
+       cot15 = 3.73205080756888;
+       rsin15 = 3.86370330515628;
+       cot_th = width / height * cot15;
+       rsin_th = sqrt(1.0 + cot_th * cot_th);
+       d = 0;
+       for (i = 0; i < natoms; i++) {
+               float dx, dy, z, r;
+               ap = ATOM_AT_INDEX(mol->atoms, i);
+               if (ap == NULL)
+                       continue;
+               app = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
+               if (app == NULL)
+                       continue;
+               r = app->radius * mview->atomRadius;
+               z = pp[i * 4 + 2] - center[2];
+               dx = pp[i * 4] * cot_th + r * rsin_th + z;
+               dy = pp[i * 4 + 1] * cot15 + r * rsin15 + z;
+               if (d < dx)
+                       d = dx;
+               if (d < dy)
+                       d = dy;
+       }
+       mview->dimension = d / cot15;
+       TrackballSetScale(mview->track, 1.0);
+       trans[0] = center[0] / mview->dimension;
+       trans[1] = center[1] / mview->dimension;
+       trans[2] = center[2] / mview->dimension;
+       TrackballSetTranslate(mview->track, trans);
+
+       free(pp);
+#endif
+}
+
+int
+MainView_convertScreenPositionToObjectPosition(MainView *mview, const GLfloat *screenPos, GLfloat *objectPos)
+{
+       float rect[4];
+    GLint viewport[4], n;
+    GLfloat winZ;
+    GLdouble posX, posY, posZ;
+       if (mview == NULL)
+               return 0;
+       MainViewCallback_frame(mview, rect);
+    viewport[0] = viewport[1] = 0;
+    viewport[2] = (GLint)(rect[2] - rect[0]);
+    viewport[3] = (GLint)(rect[3] - rect[1]);
+       MainViewCallback_lockFocus(mview);
+    if (screenPos[2] >= 0.0)
+        winZ = screenPos[2];
+    else
+        glReadPixels(screenPos[0], screenPos[1], 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
+    gluUnProject(screenPos[0], screenPos[1], winZ, mview->modelview_matrix, mview->projection_matrix, viewport, &posX, &posY, &posZ);
+    n = glGetError();
+       MainViewCallback_unlockFocus(mview);
+       objectPos[0] = posX;
+       objectPos[1] = posY;
+       objectPos[2] = posZ;
+    if (n != GL_NO_ERROR || winZ == 1.0)
+        return 0;
+    else
+        return 1;
+}
+
+int
+MainView_convertObjectPositionToScreenPosition(MainView *mview, const GLfloat *objectPos, GLfloat *screenPos)
+{
+       float rect[4];
+    GLint viewport[4];
+    GLdouble objX, objY, objZ;
+       if (mview == NULL)
+               return 0;
+       MainViewCallback_frame(mview, rect);
+    viewport[0] = viewport[1] = 0;
+    viewport[2] = (GLint)(rect[2] - rect[0]);
+    viewport[3] = (GLint)(rect[3] - rect[1]);
+    gluProject(objectPos[0], objectPos[1], objectPos[2], mview->modelview_matrix, mview->projection_matrix, viewport, &objX, &objY, &objZ);
+    if (glGetError() == GL_NO_ERROR) {
+               screenPos[0] = objX;
+               screenPos[1] = objY;
+               screenPos[2] = objZ;
+       /*      fprintf(stderr, "object(%.3f,%.3f,%.3f) screen(%.3f,%.3f,%.3f)\n", objectPos[0], objectPos[1], objectPos[2], screenPos[0], screenPos[1], screenPos[2]); */
+               return 1;
+       } else return 0;
+}
+
+int
+MainView_findObjectAtPoint(MainView *mview, const float *mousePos, int *outIndex1, int *outIndex2, int mouseCheck, int ignoreExpandedAtoms)
+{
+       float screenPos[3], op[3], oq[3], pqlen, pqlen2;
+       Vector pq, pa, v1, r1, r2;
+    int i, natoms, nbonds;
+       float r, d2, z;
+       const Atom *ap, *bp;
+       const AtomPar *dp;
+       const int *ip;
+       Molecule *mol;
+       float minDepth;
+       int n1, n2;
+
+       if (mview == NULL || mview->mol == NULL) {
+               *outIndex1 = *outIndex2 = -1;
+               return 0;
+       }
+       mol = mview->mol;
+
+#if 1
+       screenPos[0] = mousePos[0];
+       screenPos[1] = mousePos[1];
+       screenPos[2] = -1.0;
+       if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, op) == 0)
+               return 0;  /*  Nothing is here  */
+
+       /*  PQ is the part of the eyesight line in the visible area  */
+       screenPos[2] = 0.0;
+       MainView_convertScreenPositionToObjectPosition(mview, screenPos, op);
+       screenPos[2] = 1.0;
+       MainView_convertScreenPositionToObjectPosition(mview, screenPos, oq);
+       pq.x = oq[0] - op[0];
+       pq.y = oq[1] - op[1];
+       pq.z = oq[2] - op[2];
+       pqlen2 = VecLength2(pq);
+       pqlen = sqrt(pqlen2);
+       natoms = mol->natoms;
+       n1 = n2 = -1;
+       minDepth = 100.0;
+       for (i = 0; i < natoms; i++) {
+               Vector pq1, pa1;
+               float pq1len2, pq1len;
+               if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
+                       return 0;  /*  If mouse event is detected return immediately  */
+               /*  Examine if an atom is visible or not  */
+               /*  The distance of the atom center (A) from line PQ: */
+               /*    d = |VecCross(PA,PQ)|/|PQ|  */
+               /*  z = VecDot(PA,PQ)/|PQ|^2 - sqrt(r^2 - d^2)/|PQ|  */
+        ap = ATOM_AT_INDEX(mol->atoms, i);
+               if (ap == NULL)
+                       continue;
+               if (MainView_isAtomHidden(mview, i))
+                       continue;
+               if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
+                       continue;
+               r1 = ap->r;
+       /*      if (mol->is_xtal_coord)
+                       TransformVec(&r1, mol->cell->tr, &r1); */
+               pa.x = r1.x - op[0];
+               pa.y = r1.y - op[1];
+               pa.z = r1.z - op[2];
+               if (ap->aniso != NULL) {
+                       /*  Convert to ellipsoid principal axes  */
+                       Mat33 m1;
+                       Aniso *anp = ap->aniso;
+                       MatrixInvert(m1, anp->pmat);
+                       MatrixVec(&pq1, m1, &pq);
+                       MatrixVec(&pa1, m1, &pa);
+                       r = mview->probabilityScale;
+                       pq1len2 = VecLength2(pq1);
+                       pq1len = sqrt(pq1len2);
+               } else {
+                       if (mview->showEllipsoids) {
+                               r = biso2radius(ap->tempFactor) * mview->probabilityScale;
+                       } else {
+                               dp = &(gDispAtomParameters[ap->atomicNumber]);
+                               if (dp == NULL)
+                                       continue;
+                               r = dp->radius * mview->atomRadius;
+                       }
+                       pa1 = pa;
+                       pq1 = pq;
+                       pq1len2 = pqlen2;
+                       pq1len = pqlen;
+               }
+               VecCross(v1, pa1, pq1);
+               d2 = VecLength2(v1) / pq1len2;
+               if (d2 > r * r)
+                       continue;  /*  Not visible  */
+               z = VecDot(pa1, pq1) / pq1len2 - sqrt(r * r - d2) / pq1len;
+               if (z < 0.0 || z > 1.0)
+                       continue;  /*  Out of viewing volume  */
+               if (z < minDepth) {
+                       minDepth = z;
+                       n1 = i;
+               }
+       }
+       nbonds = mol->nbonds;
+       for (i = 0; i < nbonds; i++) {
+               Vector vx, vy, vz, vv, vp;
+               Double wb, wa, t, wx, blen;
+               if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
+                       return 0;  /*  If mouse event is detected return immediately  */
+               /*  Examine if a bond is visible or not  */
+               ip = &(mol->bonds[i * 2]);
+               ap = ATOM_AT_INDEX(mol->atoms, ip[0]);
+               bp = ATOM_AT_INDEX(mol->atoms, ip[1]);
+               if (MainView_isAtomHidden(mview, ip[0]) || MainView_isAtomHidden(mview, ip[1]))
+                       continue;
+               if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop) && SYMOP_ALIVE(bp->symop))
+                       continue;
+               /*  vx/vy/vz is a orthonormal base in which AB parallels the x-axis
+                   and AP in in the xy plane  */
+               /*  vp and vv is AP and PQ in that coordinate system  */
+               r1 = ap->r;
+               r2 = bp->r;
+       /*      if (mol->is_xtal_coord) {
+                       TransformVec(&r1, mol->cell->tr, &r1);
+                       TransformVec(&r2, mol->cell->tr, &r2);
+               } */
+               v1.x = op[0] - r1.x;
+               v1.y = op[1] - r1.y;
+               v1.z = op[2] - r1.z;
+               VecSub(vx, r2, r1);
+               blen = sqrt(VecLength2(vx));
+               if (blen < 1e-10)
+                       continue;
+               vx.x /= blen;
+               vx.y /= blen;
+               vx.z /= blen;
+               VecCross(vz, vx, v1);
+               if (NormalizeVec(&vz, &vz))
+                       continue;
+               VecCross(vy, vz, vx);
+               vp.x = VecDot(v1, vx);
+               vp.y = VecDot(v1, vy);
+               vp.z = VecDot(v1, vz);
+               vv.x = VecDot(pq, vx);
+               vv.y = VecDot(pq, vy);
+               vv.z = VecDot(pq, vz);
+               /*  The bond surface is y^2 + z^2 = r^2, 0 <= x <= 1  */
+               /*  The eyesight line is (x,y,z) = vp + t * vv, 0 <= t <= 1  */
+               /*  The crossing point: t = (-vv.y*vp.y - sqrt((vv.y*vp.y)^2 - (vv.y^2+vv.z^2)(vp.y^2-r^2)))/(vv.y^2+vv.z^2)  */
+               /*  (Note that vp.z = 0 by definition)  */
+               r = mview->bondRadius;
+               wb = vv.y * vp.y;
+               wa = vv.y * vv.y + vv.z * vv.z;
+               d2 = wb * wb - wa * (vp.y * vp.y - r * r);
+               if (d2 < 0)
+                       continue;  /*  Not visible  */
+               t = (-wb - sqrt(d2)) / wa;
+               if (t < 0 || t > 1)
+                       continue;  /*  Out of visible volume  */
+               wx = vp.x + t * vv.x;
+               if (wx < 0 || wx > blen)
+                       continue;  /*  Outside of bond  */
+               if (t < minDepth) {
+                       minDepth = t;
+                       n1 = ip[0];
+                       n2 = ip[1];
+               }
+       }
+       *outIndex1 = n1;
+       *outIndex2 = n2;
+       return (n1 >= 0 || n2 >= 0);
+               
+#else
+       /*  Examine if anything is drawn at the point  */
+       screenPos[0] = mousePos[0];
+       screenPos[1] = mousePos[1];
+       screenPos[2] = -1.0;
+       if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0)
+               return 0;  /*  Nothing is here  */
+       op[0] = objectPos[0];
+       op[1] = objectPos[1];
+       op[2] = objectPos[2];
+
+       /*  Examine the distance from the atom center with op, and select the one
+           with the smallest difference with the radius on the screen  */
+       atomRadius = mview->atomRadius;
+       bondRadius = mview->bondRadius;
+       natoms = mol->natoms;
+       mindr = 100.0;
+       n1 = n2 = -1;
+    for (i = 0; i < natoms; i++) {
+        ap = ATOM_AT_INDEX(mol->atoms, i);
+               if (ap == NULL)
+                       continue;
+               dp = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
+               if (dp == NULL)
+                       continue;
+               p[0] = objectPos[0] - ap->r.x;
+               p[1] = objectPos[1] - ap->r.y;
+               p[2] = objectPos[2] - ap->r.z;
+               rr = sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]) / (dp->radius * atomRadius);
+               dr = fabs(1.0 - rr);
+               if (dr < mindr) {
+                       mindr = dr;
+                       n1 = i;
+               }
+       }
+       if (mindr > 0.1)
+               n1 = -1;
+       nbonds = mol->nbonds;
+    for (i = 0; i < nbonds; i++) {
+               float q;
+               ip = &(mol->bonds[i * 2]);
+               ap = ATOM_AT_INDEX(mol->atoms, ip[0]);
+               bp = ATOM_AT_INDEX(mol->atoms, ip[1]);
+               p[0] = objectPos[0] - ap->r.x;
+               p[1] = objectPos[1] - ap->r.y;
+               p[2] = objectPos[2] - ap->r.z;
+               b[0] = bp->r.x - ap->r.x;
+               b[1] = bp->r.y - ap->r.y;
+               b[2] = bp->r.z - ap->r.z;
+               /*  ap.r + q*(b[0],b[1],b[2]) is the closest point on line AB to objectPos */
+               /*  If q is outside [0, 1] then the point is outside the line segment AB
+                   so it is ignored  */
+               q = (p[0]*b[0]+p[1]*b[1]+p[2]*b[2])/(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]);
+               if (q < 0 || q > 1)
+                       continue;
+               p[0] -= q * b[0];
+               p[1] -= q * b[1];
+               p[2] -= q * b[2];
+               dr = fabs(bondRadius - sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]));
+               if (dr < mindr) {
+                       mindr = dr;
+                       n1 = ip[0];
+                       n2 = ip[1];
+               }
+    }
+       *outIndex1 = n1;
+       *outIndex2 = n2;
+       return (n1 >= 0 || n2 >= 0);
+#endif
+}
+
+int
+MainView_screenCenterPointOfAtom(MainView *mview, int index, float *outScreenPos)
+{
+       const Atom *ap;
+       const AtomPar *dp;
+       Vector cv, pv, v;
+       Double rad, w;
+       float p[3];
+
+       if (mview == NULL || mview->mol == NULL || index < 0 || index >= mview->mol->natoms)
+               return 0;
+       ap = ATOM_AT_INDEX(mview->mol->atoms, index);
+       
+       /*  Camera position in object coordinates  */
+       cv = mview->camera;
+       
+       /*  The atom position (in Cartesian)  */
+       v = ap->r;
+/*     if (mview->mol->is_xtal_coord)
+               TransformVec(&v, mview->mol->cell->tr, &v); */
+
+       /*  The vector from atom center to camera  */
+       VecSub(pv, cv, v);
+       
+       /*  Get the surface point of the ellipsoid/sphere along the camera vector  */
+       if (mview->showEllipsoids) {
+               Mat33 m1;
+               Aniso *anp = ap->aniso;
+               if (anp != NULL) {
+                       Vector vx, vy, vz;
+                       MatrixInvert(m1, anp->pmat);
+                       /*  Convert the 'screen plane' vectors to ellipsoid principal axes  */
+                       vy = mview->up;
+                       VecCross(vx, mview->lookto, vy);
+                       MatrixVec(&vx, m1, &vx);
+                       MatrixVec(&vy, m1, &vy);
+                       /*  Touching point of the 'screen plane' to the ellipsoid  */
+                       VecCross(vz, vx, vy);
+                       w = mview->probabilityScale / VecLength(vz) * 1.1;
+                       VecScaleSelf(vz, w);
+                       MatrixVec(&vz, anp->pmat, &vz);
+                       /*  The crossing point of the camera vector with the touching plane */
+                       w = fabs(VecDot(pv, vz) / VecLength2(pv));
+                       VecScaleSelf(pv, w);
+               } else {
+                       w = mview->probabilityScale * biso2radius(ap->tempFactor) / VecLength(pv) * 1.1;
+                       VecScaleSelf(pv, w);
+               }
+       } else {
+               dp = &(gDispAtomParameters[ap->atomicNumber]);
+               rad = dp->radius * mview->atomRadius;
+               w = rad / VecLength(pv) * 1.1;
+               VecScaleSelf(pv, w);
+       }
+       VecInc(v, pv);
+       
+       /*  Transform to screen coordinate  */
+       p[0] = v.x;
+       p[1] = v.y;
+       p[2] = v.z;
+       return MainView_convertObjectPositionToScreenPosition(mview, p, outScreenPos);
+}
+
+void
+MainView_getCamera(MainView *mview, Vector *outCamera, Vector *outLookAt, Vector *outUp)
+{
+       if (mview != NULL) {
+               *outCamera = mview->camera;
+               *outLookAt = mview->lookat;
+               *outUp = mview->up;
+       }
+}
+
+#pragma mark ====== Draw model ======
+
+void
+MainView_initializeOpenGLView(MainView *mview)
+{
+       static GLfloat ambient[] = {0.6, 0.6, 0.6, 1.0};  // Some white ambient light.
+       static GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0};  // A white light.
+       
+       glEnable(GL_DEPTH_TEST);
+       glEnable(GL_LIGHTING);
+       
+       //  Set the ambient light
+       glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+       
+       //  Set the light and switch it on
+       glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
+       glEnable(GL_LIGHT0);
+       
+       //  Enable blending
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+       if (mview != NULL)
+               mview->isInitialized = 1;
+}
+
+/*  Get orthogonal unit vectors  */
+static int
+getOrthogonalVectors(const GLfloat *ap, GLfloat *bp, GLfloat *cp)
+{
+    Double ra, rb;
+    ra = sqrt(ap[0] * ap[0] + ap[1] * ap[1] + ap[2] * ap[2]);
+    if (ra < 1e-20)
+        return 0;
+    ra = 1 / ra;
+    if (fabs(ap[0]) < fabs(ap[1])) {
+        if (fabs(ap[0]) < fabs(ap[2])) {
+            bp[0] = 0;
+            bp[1] = -ap[2];
+            bp[2] = ap[1];
+        } else {
+            bp[0] = ap[1];
+            bp[1] = -ap[0];
+            bp[2] = 0;
+        }
+    } else {
+        if (fabs(ap[1]) < fabs(ap[2])) {
+            bp[0] = -ap[2];
+            bp[1] = 0;
+            bp[2] = ap[0];
+        } else {
+            bp[0] = ap[1];
+            bp[1] = -ap[0];
+            bp[2] = 0;
+        }
+    }
+    rb = 1 / sqrt(bp[0] * bp[0] + bp[1] * bp[1] + bp[2] * bp[2]);
+    bp[0] *= rb;
+    bp[1] *= rb;
+    bp[2] *= rb;
+    cp[0] = ra * (ap[1] * bp[2] - ap[2] * bp[1]);
+    cp[1] = ra * (ap[2] * bp[0] - ap[0] * bp[2]);
+    cp[2] = ra * (ap[0] * bp[1] - ap[1] * bp[0]);
+/*    printf("a = (%f, %f, %f) b = (%f, %f, %f) c = (%f, %f, %f)\n",
+        ap[0], ap[1], ap[2], bp[0], bp[1], bp[2], cp[0], cp[1], cp[2]); */
+    return 1;
+}
+
+static GLfloat sSinCache[81];
+static int sSinCacheSect = 0;
+
+static int
+setSinCache(int sect)
+{
+    int n, m, i;
+    m = sect / 4;
+    n = m * 4;
+    if (n >= 64)
+        n = 64;
+    if (n != sSinCacheSect) {
+        sSinCacheSect = n;
+        for (i = 0; i <= m * 5; i++)
+            sSinCache[i] = sin(3.14159265358979 * 2 / n * i);
+    }
+    return n;
+}
+
+static void
+drawCylinder(const GLfloat *a, const GLfloat *b, GLfloat r, int sect)
+{
+    GLfloat *c, *s;
+    int n, i;
+    GLfloat d[3], v[3], w[3];
+    n = setSinCache(sect);
+    if (n <= 0)
+        return;
+    s = sSinCache;
+    c = &sSinCache[n/4];
+    d[0] = b[0] - a[0];
+    d[1] = b[1] - a[1];
+    d[2] = b[2] - a[2];
+    if (getOrthogonalVectors(d, v, w) == 0)
+        return;
+    glBegin(GL_QUAD_STRIP);
+    for (i = 0; i <= n; i++) {
+        float nx, ny, nz;
+        nx = v[0] * c[i] + w[0] * s[i];
+        ny = v[1] * c[i] + w[1] * s[i];
+        nz = v[2] * c[i] + w[2] * s[i];
+        glNormal3f(nx, ny, nz);
+        glVertex3f(a[0] + r * nx, a[1] + r * ny, a[2] + r * nz);
+        glVertex3f(b[0] + r * nx, b[1] + r * ny, b[2] + r * nz);
+    }
+    glEnd();    
+}
+
+static void
+drawSphere(const GLfloat *p, GLfloat r, int sect)
+{
+    GLfloat *c, *s;
+    int n, i, j;
+    n = setSinCache(sect);
+    if (n <= 0)
+        return;
+    s = sSinCache;
+    c = &sSinCache[n/4];
+    for (i = 0; i <= n; i++) {
+        glBegin(GL_QUAD_STRIP);
+        for (j = 1; j <= n / 2 - 1; j++) {
+            glNormal3f(s[j] * c[i], s[j] * s[i], c[j]);
+            glVertex3f(r * s[j] * c[i] + p[0], r * s[j] * s[i] + p[1], r * c[j] + p[2]);
+            glNormal3f(s[j] * c[i+1], s[j] * s[i+1], c[j]);
+            glVertex3f(r * s[j] * c[i+1] + p[0], r * s[j] * s[i+1] + p[1], r * c[j] + p[2]);
+        }
+        glEnd();
+    }
+    glBegin(GL_TRIANGLE_FAN);
+    glNormal3f(0, 0, 1);
+    glVertex3f(p[0], p[1], r + p[2]);
+    for (i = n; i >= 0; i--) {
+        glNormal3f(s[1] * c[i], s[1] * s[i], c[1]);
+        glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], r * c[1] + p[2]);
+    }
+    glEnd();
+    glBegin(GL_TRIANGLE_FAN);
+    glNormal3f(0, 0, -1);
+    glVertex3f(p[0], p[1], -r + p[2]);
+    for (i = 0; i <= n; i++) {
+        glNormal3f(s[1] * c[i], s[1] * s[i], -c[1]);
+        glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], -r * c[1] + p[2]);
+    }
+    glEnd();
+}
+
+static void
+drawEllipsoid(const GLfloat *p, const GLfloat *v1, const GLfloat *v2, const GLfloat *v3, int sect)
+{
+       GLfloat mat[16];
+       static const GLfloat origin[3] = {0, 0, 0};
+       mat[0] = v1[0]; mat[1] = v1[1]; mat[2] = v1[2]; mat[3] = 0;
+       mat[4] = v2[0]; mat[5] = v2[1]; mat[6] = v2[2]; mat[7] = 0;
+       mat[8] = v3[0]; mat[9] = v3[1]; mat[10] = v3[2]; mat[11] = 0;
+       mat[12] = p[0]; mat[13] = p[1]; mat[14] = p[2]; mat[15] = 1;
+    glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+       glMultMatrixf(mat);
+       glEnable(GL_NORMALIZE);
+       //      glutSolidSphere(1, sect, sect); /* Is this faster than my code? */
+       drawSphere(origin, 1, sect);
+       glDisable(GL_NORMALIZE);
+       glPopMatrix();
+}
+
+static char *
+temporarySelection(MainView *mview, int flags, int clickCount, int ignoreExpandedAtoms)
+{
+       char *selectFlags;
+       int i, natoms;
+       const Atom *ap;
+       Double rect[4];
+       natoms = mview->mol->natoms;
+       selectFlags = (char *)calloc(sizeof(char), natoms);
+       if (selectFlags == NULL)
+               return NULL;
+       if (clickCount > 0) {
+               int n1, n2;
+               if (MainView_findObjectAtPoint(mview, mview->dragStartPos, &n1, &n2, 0, 0)) {
+                       if (n1 >= 0 && n1 < natoms)
+                               selectFlags[n1] = 1;
+                       if (n2 >= 0 && n2 < natoms)
+                               selectFlags[n2] = 1;
+               }
+       } else {
+               if (mview->dragStartPos[0] < mview->dragEndPos[0]) {
+                       rect[0] = mview->dragStartPos[0];
+                       rect[2] = mview->dragEndPos[0];
+               } else {
+                       rect[0] = mview->dragEndPos[0];
+                       rect[2] = mview->dragStartPos[0];
+               }
+               if (mview->dragStartPos[1] < mview->dragEndPos[1]) {
+                       rect[1] = mview->dragStartPos[1];
+                       rect[3] = mview->dragEndPos[1];
+               } else {
+                       rect[1] = mview->dragEndPos[1];
+                       rect[3] = mview->dragStartPos[1];
+               }
+               for (i = 0; i < natoms; i++) {
+                       ap = ATOM_AT_INDEX(mview->mol->atoms, i);
+                       if (ap == NULL)
+                               continue;
+                       if (MainView_isAtomHidden(mview, i))
+                               continue;
+                       if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
+                               continue;
+                       if (mview->draggingMode == kMainViewSelectingRegion) {
+                               /*  Check if this atom is within the selection rectangle  */
+                               GLfloat objectPos[3];
+                               GLfloat screenPos[3];
+                               Vector r1;
+                               r1 = ap->r;
+                       /*      if (mview->mol->is_xtal_coord)
+                                       TransformVec(&r1, mview->mol->cell->tr, &r1); */
+                               objectPos[0] = r1.x;
+                               objectPos[1] = r1.y;
+                               objectPos[2] = r1.z;
+                               if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) && screenPos[0] >= rect[0] && screenPos[0] <= rect[2] && screenPos[1] >= rect[1] && screenPos[1] <= rect[3])
+                                       selectFlags[i] = 1;
+                               else selectFlags[i] = 0;
+                       }
+               }
+       }
+       if (flags & kShiftKeyMask) {
+               for (i = 0; i < natoms; i++)
+                       selectFlags[i] ^= (MoleculeIsAtomSelected(mview->mol, i) != 0);
+       }
+       return selectFlags;
+}
+
+static void
+drawGraphite(MainView *mview)
+{
+       static GLfloat sDarkCyanColor[] = {0, 0.75, 0.75, 1};
+       MDArena *arena;
+       MDGraphiteArena *graphite;
+       if ((arena = mview->mol->arena) != NULL && (graphite = arena->graphite) != NULL) {
+               Vector xaxis, yaxis, zaxis, origin;
+               Double R;
+               int i, j, i0, i1, j0, j1, ir;
+               Double x, dx, y, dy, xx, yy;
+               GLfloat p[12];
+               graphite_get_axes(graphite, &origin, &xaxis, &yaxis, &zaxis, &R);
+               i0 = -(mview->showGraphite / 2) - 1;
+               i1 = i0 + mview->showGraphite + 1;
+               j0 = -(mview->showGraphite / 2);
+               j1 = j0 + mview->showGraphite;
+               dx = 0.5 * R;
+               dy = 0.866025403784439 * R;
+               glDisable(GL_LIGHTING); 
+               glColor3fv(sDarkCyanColor);
+               for (i = i0; i <= i1; i++) {
+                       for (j = j0; j <= j1; j++) {
+                               Byte f1, f2, f3;
+                               ir = (i % 2 == 0 ? 0 : 1);
+                               x = 3 * i * dx;
+                               y = (2 * j + ir) * dy;
+                               yy = y - dy;
+                               xx = x - 2 * dx;
+                               p[0] = xaxis.x * xx + yaxis.x * y + origin.x;
+                               p[1] = xaxis.y * xx + yaxis.y * y + origin.y;
+                               p[2] = xaxis.z * xx + yaxis.z * y + origin.z;
+                               xx += dx;
+                               p[3] = xaxis.x * xx + yaxis.x * yy + origin.x;
+                               p[4] = xaxis.y * xx + yaxis.y * yy + origin.y;
+                               p[5] = xaxis.z * xx + yaxis.z * yy + origin.z;
+                               xx += 2 * dx;
+                               p[6] = xaxis.x * xx + yaxis.x * yy + origin.x;
+                               p[7] = xaxis.y * xx + yaxis.y * yy + origin.y;
+                               p[8] = xaxis.z * xx + yaxis.z * yy + origin.z;
+                               xx += dx;
+                               p[9] = xaxis.x * xx + yaxis.x * y + origin.x;
+                               p[10] = xaxis.y * xx + yaxis.y * y + origin.y;
+                               p[11] = xaxis.z * xx + yaxis.z * y + origin.z;
+                               f1 = f2 = f3 = 1;
+                               if (i == i0) {
+                                       f1 = f2 = 0;
+                                       if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
+                                               continue;
+                               } else if (i == i1) {
+                                       f2 = f3 = 0;
+                                       if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
+                                               continue;
+                               } else if (j == j1) {
+                                       if (ir == 1) {
+                                               f1 = f3 = 0;
+                                       } else if (i == i0 + 1) {
+                                               f1 = 0;
+                                       } else if (i == i1 - 1) {
+                                               f3 = 0;
+                                       }
+                               }
+                               glBegin(GL_LINES);
+                               if (f1) { 
+                                       glVertex3fv(p);
+                                       glVertex3fv(p + 3);
+                               }
+                               if (f2) {
+                                       glVertex3fv(p + 3);
+                                       glVertex3fv(p + 6);
+                               }
+                               if (f3) {
+                                       glVertex3fv(p + 6);
+                                       glVertex3fv(p + 9);
+                               }
+                               glEnd();
+                       }
+               }
+               glEnable(GL_LIGHTING);  
+       }
+}
+
+static GLfloat sRedColor[] = {1, 0, 0, 1};
+
+static void
+drawAtom(MainView *mview, int i1, int selected, const Vector *dragOffset, const Vector *periodicOffset)
+{
+       const Atom *ap;
+       const AtomPar *dp;
+       int an1;
+       int expanded = 0;
+       Vector r1;
+       GLfloat p[6];
+       char label[16];
+       GLfloat rgba[4];
+       Transform *trp = NULL;
+       int natoms = mview->mol->natoms;
+       if (i1 >= natoms) {
+               /*  Extra 2 atoms for the bond being newly created  */
+               if (mview->draggingMode != kMainViewCreatingBond)
+                       return;
+       /*      printf("mview->tempAtoms[%d] = %d\n", i - natoms, mview->tempAtoms[i - natoms]); */
+               if (mview->tempAtoms[i1 - natoms] >= 0)
+                       return;  /*  Already drawn  */
+               ap = NULL;
+               an1 = 6;
+               r1 = mview->tempAtomPos[i1 - natoms];
+               label[0] = 0;
+       } else if (i1 < 0) {
+               ExAtom *ep = mview->mol->exatoms + (- i1 - 1);
+               ap = ATOM_AT_INDEX(mview->mol->atoms, ep->index);
+               an1 = ap->atomicNumber;
+               r1 = ap->r;
+               trp = &(mview->mol->syms[ep->symop]);
+               if (/* !mview->mol->is_xtal_coord && */ mview->mol->cell != NULL) {
+                       TransformVec(&r1, mview->mol->cell->rtr, &r1);
+                       TransformVec(&r1, *trp, &r1);
+                       TransformVec(&r1, mview->mol->cell->tr, &r1);
+               } else TransformVec(&r1, *trp, &r1);
+               VecInc(r1, ep->dr);
+               label[0] = 0;
+       } else {
+               ap = ATOM_AT_INDEX(mview->mol->atoms, i1);
+               if (ap == NULL)
+                       return;
+               an1 = ap->atomicNumber;
+               r1 = ap->r;
+               strncpy(label, ap->aname, 4);
+               label[4] = 0;
+               if (SYMOP_ALIVE(ap->symop))
+                       expanded = 1;
+       }
+       if (!mview->showHydrogens && an1 == 1)
+               return;
+       if (!mview->showDummyAtoms && an1 == 0)
+               return;
+       if (!mview->showExpandedAtoms && expanded)
+               return;
+       if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
+               return;
+       dp = &(gDispAtomParameters[an1]);
+       if (dp == NULL)
+               return;
+       if (selected) {
+               memcpy(rgba, sRedColor, sizeof(rgba));
+       } else {
+               rgba[0] = dp->r; rgba[1] = dp->g; rgba[2] = dp->b; rgba[3] = 1.0;
+       }
+       if (expanded || periodicOffset != NULL) {
+               rgba[0] *= 0.5;
+               rgba[1] *= 0.5;
+               rgba[2] *= 0.5;
+       }
+       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
+       if (periodicOffset != NULL)
+               VecInc(r1, *periodicOffset);
+/*     if (mview->mol->is_xtal_coord)
+               TransformVec(&r1, mview->mol->cell->tr, &r1); */
+       p[0] = r1.x; p[1] = r1.y; p[2] = r1.z;
+       if (mview->draggingMode == kMainViewDraggingSelectedAtoms && selected) {
+               p[0] += dragOffset->x;
+               p[1] += dragOffset->y;
+               p[2] += dragOffset->z;
+       }
+       if (mview->showEllipsoids) {
+               if (ap != NULL && ap->aniso != NULL) {
+               /*      Double xp[3][3]; */
+               /*      int i, j; */
+                       GLfloat elip[9];
+                       Mat33 pmat2;
+                       int i;
+                       if (trp != NULL) {
+                               MatrixMul(pmat2, mview->mol->cell->rtr, ap->aniso->pmat);
+                               MatrixMul(pmat2, *((Mat33 *)trp), pmat2);
+                               MatrixMul(pmat2, mview->mol->cell->tr, pmat2);
+                               MatrixTranspose(pmat2, pmat2);
+                       } else {
+                               MatrixTranspose(pmat2, ap->aniso->pmat);
+                       }
+                       for (i = 0; i < 9; i++)
+                               elip[i] = pmat2[i] * mview->probabilityScale;
+               /*      for (i = 0; i < 3; i++) {
+                               Double w = ap->aniso->val[i];
+                               Vector vv = ap->aniso->axis[i];
+                               if (w <= 0.0)
+                                       w = 0.001;
+                               if (trp != NULL) {
+                                       TransformVec(&vv, mview->mol->cell->rtr, &vv);
+                                       MatrixVec(&vv, *((Mat33 *)trp), &vv);
+                                       TransformVec(&vv, mview->mol->cell->tr, &vv);
+                               }
+                               w *= mview->probabilityScale;
+                               xp[i][0] = w * vv.x;
+                               xp[i][1] = w * vv.y;
+                               xp[i][2] = w * vv.z;
+                       } */
+                       
+                       drawEllipsoid(p, elip, elip+3, elip+6, 15);
+               } else {
+                       Double rad;
+                       rad = biso2radius(ap->tempFactor);
+                       rad *= mview->probabilityScale;
+                       drawSphere(p, rad, 8);
+               }
+       } else {
+               drawSphere(p, dp->radius * mview->atomRadius, 8);
+       }
+       if (MainView_convertObjectPositionToScreenPosition(mview, p, p + 3)) {
+       /*      fprintf(stderr, "atom %d: {%f, %f, %f}\n", i1, p[3], p[4], p[5]); */
+               float fp[3];
+               fp[0] = p[3]; fp[1] = p[4]; fp[2] = p[5];
+               MainViewCallback_drawLabel(mview, fp, label);
+       }
+}
+
+static void
+drawBond(MainView *mview, int i1, int i2, int selected, int selected2, int draft, const Vector *dragOffset, const Vector *periodicOffset)
+{
+       const AtomPar *dp;
+       int i, in;
+       int an[2];
+       int expanded[2];
+       Vector r[2];
+       GLfloat p[6];
+       GLfloat rgba[4];
+       int natoms = mview->mol->natoms;
+       expanded[0] = expanded[1] = 0;
+
+       for (i = 0; i < 2; i++) {
+               const Atom *ap;
+               in = (i == 0 ? i1 : i2);
+               if (in >= natoms && in < natoms + 2) {
+                       if (mview->tempAtoms[in - natoms] >= 0) {
+                               ap = ATOM_AT_INDEX(mview->mol->atoms, mview->tempAtoms[in - natoms]);
+                               an[i] = ap->atomicNumber;
+                               r[i] = ap->r;
+                       } else {
+                               ap = NULL;
+                               r[i] = mview->tempAtomPos[in - natoms];
+                               an[i] = 6;
+                       }
+               } else if (in < 0) {
+                       ExAtom *ep = mview->mol->exatoms + (- in - 1);
+                       if (!mview->showExpandedAtoms)
+                               return;
+                       ap = ATOM_AT_INDEX(mview->mol->atoms, ep->index);
+                       an[i] = ap->atomicNumber;
+                       r[i] = ap->r;
+                       TransformVec(&r[i], mview->mol->syms[ep->symop], &r[i]);
+                       VecInc(r[i], ep->dr);
+               } else {
+                       ap = ATOM_AT_INDEX(mview->mol->atoms, in);
+                       an[i] = ap->atomicNumber;
+                       r[i] = ap->r;
+                       if (SYMOP_ALIVE(ap->symop))
+                               expanded[i] = 1;
+               }
+               if (!mview->showHydrogens && an[i] == 1)
+                       return;
+               if (!mview->showDummyAtoms && an[i] == 0)
+                       return;
+               if (!mview->showExpandedAtoms && expanded[i])
+                       return;
+               if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
+                       return;
+       }
+
+       if (periodicOffset != NULL) {
+               VecInc(r[0], *periodicOffset);
+               VecInc(r[1], *periodicOffset);
+       }
+/*     if (mview->mol->is_xtal_coord) {
+               TransformVec(&r[0], mview->mol->cell->tr, &r[0]);
+               TransformVec(&r[1], mview->mol->cell->tr, &r[1]);
+       } */
+
+       dp = &(gDispAtomParameters[an[0]]);
+       if (dp == NULL)
+               return;
+       if (selected && selected2) {
+               memcpy(rgba, sRedColor, sizeof(rgba));
+       } else {
+               rgba[0] = dp->r; rgba[1] = dp->g; rgba[2] = dp->b; rgba[3] = 1.0;
+       }
+       if (expanded[0] || periodicOffset != NULL) {
+               rgba[0] *= 0.5;
+               rgba[1] *= 0.5;
+               rgba[2] *= 0.5;
+       }               
+       if (mview->draggingMode == kMainViewDraggingSelectedAtoms) {
+               if (selected)
+                       VecInc(r[0], *dragOffset);
+               if (selected2)
+                       VecInc(r[1], *dragOffset);
+       }
+       p[0] = r[0].x; p[1] = r[0].y; p[2] = r[0].z;
+       p[3] = (r[1].x + p[0]) * 0.5;
+       p[4] = (r[1].y + p[1]) * 0.5;
+       p[5] = (r[1].z + p[2]) * 0.5;
+       if (draft) {
+               glColor3f(rgba[0], rgba[1], rgba[2]);
+               glBegin(GL_LINES);
+               glVertex3f(p[0], p[1], p[2]);
+               glVertex3f(p[3], p[4], p[5]);
+               glEnd();
+       } else {
+               glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
+               drawCylinder(p, p + 3, mview->bondRadius, 6);
+       }
+       dp = &(gDispAtomParameters[an[1]]);
+       if (dp == NULL)
+               return;
+       if (!selected || !selected2) {
+               rgba[0] = dp->r; rgba[1] = dp->g; rgba[2] = dp->b; rgba[3] = 1.0;
+       }
+       if (expanded[1] || periodicOffset != NULL) {
+               rgba[0] *= 0.5;
+               rgba[1] *= 0.5;
+               rgba[2] *= 0.5;
+       }               
+       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
+       p[0] = r[1].x; p[1] = r[1].y; p[2] = r[1].z;
+       if (draft) {
+               glColor3f(rgba[0], rgba[1], rgba[2]);
+               glBegin(GL_LINES);
+               glVertex3f(p[0], p[1], p[2]);
+               glVertex3f(p[3], p[4], p[5]);
+               glEnd();
+       } else {
+               glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
+               drawCylinder(p, p + 3, mview->bondRadius, 6);
+       }
+}
+
+/*  Calculate drag offset during moving the selection.  */
+static void
+calcDragOffset(MainView *mview, Vector *outVector)
+{
+       GLfloat p[6];
+       if (mview->draggingMode == kMainViewDraggingSelectedAtoms
+       && MainView_convertScreenPositionToObjectPosition(mview, mview->dragStartPos, p)
+       && MainView_convertScreenPositionToObjectPosition(mview, mview->dragEndPos, p + 3)) {
+               outVector->x = p[3] - p[0];
+               outVector->y = p[4] - p[1];
+               outVector->z = p[5] - p[2];
+       } else {
+               outVector->x = outVector->y = outVector->z = 0;
+       }
+
+}
+
+static void
+drawModel(MainView *mview)
+{
+       Molecule *mol;
+    int i, natoms, nbonds;
+       int amin, amax, bmin, bmax, cmin, cmax, da, db, dc;
+       Byte original;
+       Double atomRadius, bondRadius;
+       Vector dragOffset;
+       Vector periodicOffset;
+       Vector *axes;
+       int selected, selected2;
+       char *selectFlags;
+       int draft = mview->lineMode;
+/*    static Double gray[] = {0.8, 0.8, 0.8, 1}; */
+       
+       atomRadius = mview->atomRadius;
+       bondRadius = mview->bondRadius;
+       mol = mview->mol;
+       natoms = mol->natoms;
+/*    if (natoms == 0) {
+        return;
+    }
+*/
+       if (mview->draggingMode == kMainViewSelectingRegion)
+               selectFlags = temporarySelection(mview, mview->modifierFlags, 0, 1);
+       else selectFlags = NULL;
+       
+       if (mview->draggingMode == kMainViewDraggingSelectedAtoms)
+               calcDragOffset(mview, &dragOffset);
+       else dragOffset.x = dragOffset.y = dragOffset.z = 0;
+       
+       if (mview->showGraphite) {
+               drawGraphite(mview);
+       }
+       
+       amin = amax = bmin = bmax = cmin = cmax = 0;
+       if (mview->showExpandedAtoms && mol->cell != NULL) {
+               if (mol->cell->flags[0]) {
+                       amin = mview->showPeriodicImage[0];
+                       amax = mview->showPeriodicImage[1];
+               }
+               if (mol->cell->flags[1]) {
+                       bmin = mview->showPeriodicImage[2];
+                       bmax = mview->showPeriodicImage[3];
+               }
+               if (mol->cell->flags[2]) {
+                       cmin = mview->showPeriodicImage[4];
+                       cmax = mview->showPeriodicImage[5];
+               }
+               axes = mol->cell->axes;
+       } else {
+               axes = NULL;
+       }
+       
+       if (draft == 0) {
+               for (da = amin; da <= amax; da++) {
+                       for (db = bmin; db <= bmax; db++) {
+                               for (dc = cmin; dc <= cmax; dc++) {
+                                       original = (da == 0 && db == 0 && dc == 0);
+                                       if (!original) {
+                                               VecScale(periodicOffset, axes[0], da);
+                                               VecScaleInc(periodicOffset, axes[1], db);
+                                               VecScaleInc(periodicOffset, axes[2], dc);
+                                       }
+                                       for (i = 0; i < natoms; i++) {
+                                               if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
+                                                       /*  Mouse event is detected  */
+                                                       draft = 1;
+                                                       goto skip;
+                                               }
+                                               if (mview->draggingMode == kMainViewCreatingBond && (i == mview->tempAtoms[0] || i == mview->tempAtoms[1] || i >= natoms))
+                                                       selected = 1;  /*  extra atoms  */
+                                               else if (selectFlags != NULL)
+                                                       selected = selectFlags[i];
+                                               else
+                                                       selected = MoleculeIsAtomSelected(mview->mol, i);
+                                               drawAtom(mview, i, selected, &dragOffset, (original ? NULL : &periodicOffset));
+                                       }
+                               }
+       
+                               if (draft == 0) {
+                                       if (original) {
+                                               /*  Extra atoms  */
+                                               drawAtom(mview, natoms, 1, &dragOffset, NULL);
+                                               drawAtom(mview, natoms + 1, 1, &dragOffset, NULL);
+                                       }
+                                       /*  Expanded atoms  */
+                                       if (mview->showExpandedAtoms) {
+                                               for (i = 0; i < mview->mol->nexatoms; i++) {
+                                                       if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
+                                                               /*  Mouse event is detected  */
+                                                               draft = 1;
+                                                               break;
+                                                       /*      goto cleanup;  */
+                                                       }
+                                                       drawAtom(mview, -i-1, (selectFlags == NULL ? 0 : selectFlags[i]), &dragOffset, (original ? NULL : &periodicOffset));
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       
+skip:
+       nbonds = mol->nbonds;
+       if (draft)
+               glDisable(GL_LIGHTING); 
+       for (da = amin; da <= amax; da++) {
+               for (db = bmin; db <= bmax; db++) {
+                       for (dc = cmin; dc <= cmax; dc++) {
+                               original = (da == 0 && db == 0 && dc == 0);
+                               if (!original) {
+                                       VecScale(periodicOffset, axes[0], da);
+                                       VecScaleInc(periodicOffset, axes[1], db);
+                                       VecScaleInc(periodicOffset, axes[2], dc);
+                               }
+                               
+                               for (i = 0; i < nbonds; i++) {
+                                       int n1, n2;
+                                       if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
+                                               /*  Mouse event is detected  */
+                                               draft = 1;
+                                               glDisable(GL_LIGHTING);
+                                       /*      goto cleanup;  */
+                                       }
+                                       n1 = mview->mol->bonds[i * 2];
+                                       n2 = mview->mol->bonds[i * 2 + 1];
+                                       if (selectFlags == NULL) {
+                                               selected = MoleculeIsAtomSelected(mview->mol, n1);
+                                               selected2 = MoleculeIsAtomSelected(mview->mol, n2);
+                                       } else {
+                                               selected = selectFlags[n1];
+                                               selected2 = selectFlags[n2];
+                                       }
+                                       drawBond(mview, n1, n2, selected, selected2, draft, &dragOffset, (original ? NULL : &periodicOffset));
+                               }
+                               
+                               /*  Extra bond  */
+                               if (original && mview->draggingMode == kMainViewCreatingBond) {
+                                       drawBond(mview, natoms, natoms + 1, 1, 1, draft, &dragOffset, NULL);
+                               }
+                               
+                               /*  Expanded bonds  */
+                               for (i = 0; i < mview->mol->nexbonds; i++) {
+                                       int n1, n2;
+                                       if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
+                                               /*  Mouse event is detected  */
+                                               draft = 1;
+                                               glDisable(GL_LIGHTING);
+                                       /*      goto cleanup;  */
+                                       }
+                                       n1 = mview->mol->exbonds[i * 2];
+                                       n2 = mview->mol->exbonds[i * 2 + 1];
+                                       if (n1 < 0)
+                                               n1 = -n1 - 1;
+                                       if (n2 < 0)
+                                               n2 = -n2 - 1;
+                                       if (selectFlags == NULL) {
+                                               selected = MoleculeIsAtomSelected(mview->mol, n1);
+                                               selected2 = MoleculeIsAtomSelected(mview->mol, n2);
+                                       } else {
+                                               selected = selectFlags[n1];
+                                               selected2 = selectFlags[n2];
+                                       }
+                                       drawBond(mview, mview->mol->exbonds[i * 2], mview->mol->exbonds[i * 2 + 1], selected, selected2, draft, &dragOffset, (original ? NULL : &periodicOffset));
+                               }
+                       }
+               }
+       }
+       
+/*  cleanup: */
+       if (draft)
+               glEnable(GL_LIGHTING);
+       if (selectFlags != NULL)
+               free(selectFlags);
+}
+
+static void
+drawUnitCell(MainView *mview)
+{
+       GLfloat a[3], b[3], c[3], ab[3], bc[3], ca[3], abc[3];
+       XtalCell *cp;
+       static GLfloat sGrayColor[] = {0.5, 0.5, 0.5, 1};
+       GLfloat origin[3];
+       int i;
+       glDisable(GL_LIGHTING);
+       for (i = 0; i < 1; i++) {
+               if (i == 0) {
+                       if (!mview->showUnitCell || (cp = mview->mol->cell) == NULL)
+                               continue;
+                       a[0] = cp->tr[0];
+                       a[1] = cp->tr[3];
+                       a[2] = cp->tr[6];
+                       b[0] = cp->tr[1];
+                       b[1] = cp->tr[4];
+                       b[2] = cp->tr[7];
+                       c[0] = cp->tr[2];
+                       c[1] = cp->tr[5];
+                       c[2] = cp->tr[8];
+                       origin[0] = cp->origin.x;
+                       origin[1] = cp->origin.y;
+                       origin[2] = cp->origin.z;
+                       glColor3f(0.75, 0.2, 0.0);
+/*             } else {
+                       if (!mview->showPeriodicBox || (bp = mview->mol->box) == NULL)
+                               continue;
+                       origin[0] = bp->origin.x;
+                       origin[1] = bp->origin.y;
+                       origin[2] = bp->origin.z;
+                       a[0] = bp->axes[0].x + origin[0];
+                       a[1] = bp->axes[0].y + origin[1];
+                       a[2] = bp->axes[0].z + origin[2];
+                       b[0] = bp->axes[1].x + origin[0];
+                       b[1] = bp->axes[1].y + origin[1];
+                       b[2] = bp->axes[1].z + origin[2];
+                       c[0] = bp->axes[2].x + origin[0];
+                       c[1] = bp->axes[2].y + origin[1];
+                       c[2] = bp->axes[2].z + origin[2];
+                       glColor3f(0.0, 0.75, 0.2); */
+               }
+       
+               ab[0] = a[0] + b[0]; ab[1] = a[1] + b[1]; ab[2] = a[2] + b[2];
+               bc[0] = b[0] + c[0]; bc[1] = b[1] + c[1]; bc[2] = b[2] + c[2];
+               ca[0] = c[0] + a[0]; ca[1] = c[1] + a[1]; ca[2] = c[2] + a[2];
+               abc[0] = a[0] + bc[0]; abc[1] = a[1] + bc[1]; abc[2] = a[2] + bc[2];
+
+       /*      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, sGrayColor);
+               drawCylinder(sOrigin, a, 0.05, 4);
+               drawCylinder(sOrigin, b, 0.05, 4);
+               drawCylinder(sOrigin, c, 0.05, 4);
+               drawCylinder(a, ab, 0.05, 4);
+               drawCylinder(a, ca, 0.05, 4);
+               drawCylinder(b, ab, 0.05, 4);
+               drawCylinder(b, bc, 0.05, 4);
+               drawCylinder(c, ca, 0.05, 4);
+               drawCylinder(c, bc, 0.05, 4);
+               drawCylinder(ab, abc, 0.05, 4);
+               drawCylinder(bc, abc, 0.05, 4);
+               drawCylinder(ca, abc, 0.05, 4); */
+
+               glBegin(GL_LINES);
+               glVertex3fv(origin);
+               glVertex3fv(a);
+               glVertex3fv(origin);
+               glVertex3fv(b);
+               glVertex3fv(origin);
+               glVertex3fv(c);
+               glVertex3fv(a);
+               glVertex3fv(ab);
+               glVertex3fv(a);
+               glVertex3fv(ca);
+               glVertex3fv(b);
+               glVertex3fv(ab);
+               glVertex3fv(b);
+               glVertex3fv(bc);
+               glVertex3fv(c);
+               glVertex3fv(ca);
+               glVertex3fv(c);
+               glVertex3fv(bc);
+               glVertex3fv(ab);
+               glVertex3fv(abc);
+               glVertex3fv(bc);
+               glVertex3fv(abc);
+               glVertex3fv(ca);
+               glVertex3fv(abc);
+               glEnd();
+       }
+       glEnable(GL_LIGHTING);
+}
+
+static void
+drawRotationCenter(MainView *mview)
+{
+       GLfloat ps[3], pe[3], col[4];
+       float fd[2];  /*  Fovy and distance  */
+       float tr[3];  /*  Translation  */
+       float r, rr;
+       if (mview == NULL || !mview->showRotationCenter)
+               return;
+       TrackballGetTranslate(mview->track, tr);
+       TrackballGetPerspective(mview->track, fd);
+       tr[0] *= -mview->dimension;
+       tr[1] *= -mview->dimension;
+       tr[2] *= -mview->dimension;
+       r = fd[1] * mview->dimension * tan(fd[0] * 0.5 * kDeg2Rad) * 0.1;
+       rr = r * 0.1;
+       ps[0] = tr[0];
+       ps[1] = tr[1];
+       ps[2] = tr[2];
+       col[0] = col[1] = col[2] = 0.5; col[3] = 1.0;
+       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
+       drawSphere(ps, rr, 8);
+       ps[0] = tr[0] - r;
+       pe[0] = tr[0] + r;
+       pe[1] = tr[1];
+       pe[2] = tr[2];
+       col[0] = 1.0; col[1] = col[2] = 0.0;
+       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
+       drawSphere(ps, rr, 8);
+       drawSphere(pe, rr, 8);
+       drawCylinder(ps, pe, rr, 8);
+       ps[0] = tr[0];
+       ps[1] = tr[1] - r;
+       pe[0] = tr[0];
+       pe[1] = tr[1] + r;
+       col[1] = 1.0; col[0] = col[2] = 0.0;
+       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
+       drawSphere(ps, rr, 8);
+       drawSphere(pe, rr, 8);
+       drawCylinder(ps, pe, rr, 8);
+       ps[1] = tr[1];
+       ps[2] = tr[2] - r;
+       pe[1] = tr[1];
+       pe[2] = tr[2] + r;
+       col[2] = 1.0; col[0] = col[1] = 0.0;
+       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
+       drawSphere(ps, rr, 8);
+       drawSphere(pe, rr, 8);
+       drawCylinder(ps, pe, rr, 8);
+}
+
+static int
+compareLabelByDepth(const void *ap, const void *bp)
+{
+       Double dz = ((const LabelRecord *)bp)->pos.z - ((const LabelRecord *)ap)->pos.z;
+       if (dz > 0)
+               return 1;
+       else if (dz < 0)
+               return -1;
+       else return 0;
+}
+
+static void
+drawLabels(MainView *mview)
+{
+       Transform *trp;
+       Atom *ap;
+       LabelRecord *lp;
+       int i, nlabels;
+
+       if (mview->nlabels == 0)
+               return;
+       
+/*     mview->sortedLabels = (LabelRecord **)calloc(sizeof(LabelRecord *), mview->nlabels);
+       if (mview->sortedLabels == NULL)
+               return; */
+       
+/*     if (mview->mol->is_xtal_coord)
+               trp = &(mview->mol->cell->tr);
+       else trp = NULL; */
+
+       /*  Get the screen coordinates of the labels  */
+       nlabels = 0;
+       for (i = 0; i < mview->mol->natoms; i++) {
+               float scrp[3], f[3];
+               ap = ATOM_AT_INDEX(mview->mol->atoms, i);
+               if (ap->exflags & kAtomHiddenFlag)
+                       continue;
+               if (!mview->showHydrogens && ap->atomicNumber == 1)
+                       continue;
+               if (!mview->showDummyAtoms && ap->atomicNumber == 0)
+                       continue;
+               if (ap->labelid <= 0 || ap->labelid > mview->nlabels)
+                       continue;
+               lp = mview->labels + (ap->labelid - 1);
+               MainView_screenCenterPointOfAtom(mview, lp->idx1, scrp);
+       /*      if (lp->idx2 >= 0) {
+                       Atom *bp;
+                       r1 = ap->r;
+                       if (trp != NULL)
+                               TransformVec(&r1, *trp, &r1);
+                       bp = ATOM_AT_INDEX(mview->mol->atoms, lp->idx2);
+                       if (bp->exflags & kAtomHiddenFlag)
+                               continue;
+                       r2 = bp->r;
+                       if (trp != NULL)
+                               TransformVec(&r2, *trp, &r2);
+                       r1.x = (r1.x + r2.x) * 0.5;
+                       r1.y = (r1.y + r2.y) * 0.5;
+                       r1.z = (r1.z + r2.z) * 0.5;
+                       objp[0] = r1.x;
+                       objp[1] = r1.y;
+                       objp[2] = r1.z;
+                       MainView_convertObjectPositionToScreenPosition(mview, objp, scrp);
+               } */
+               lp->pos.x = scrp[0];
+               lp->pos.y = scrp[1];
+               lp->pos.z = scrp[2];
+               MainViewCallback_labelSize(lp->label, f);
+               f[0] = floor(lp->pos.x - f[0] * 0.5);
+               f[1] = -floor(lp->pos.y + f[1] * 0.5);
+               f[2] = lp->pos.z;
+       /*      fprintf(stderr, "label position (%d) = {%f, %f, %f}\n", i, f[0], f[1], f[2]); */
+               MainViewCallback_drawLabelAtPoint(lp->label, f);
+       /*      mview->sortedLabels[nlabels++] = lp;
+               if (nlabels >= mview->nlabels)
+                       break; */
+       }
+       
+/*     //  Sort labels by z coordinates (descending order) 
+       qsort(mview->sortedLabels, nlabels, sizeof(LabelRecord *), compareLabelByDepth);
+       
+       //  Draw labels 
+       for (i = 0; i < nlabels; i++) {
+               LabelRecord *lp = mview->sortedLabels[i];
+               Double f[3];
+               MainViewCallback_labelSize(lp->label, f);
+               f[0] = floor(lp->pos.x - f[0] * 0.5);
+               f[1] = -floor(lp->pos.y + f[1] * 0.5);
+               f[2] = lp->pos.z;
+               MainViewCallback_drawLabelAtPoint(lp->label, f);
+       }
+*/     
+}
+
+void
+MainView_drawModel(MainView *mview)
+{
+    float w[4], dimension, distance;
+       float frame[4], width, height;
+       GLdouble *pp;
+       Transform mtr;
+       
+/*    int i;  */
+       
+       if (mview == NULL)
+               return;
+
+    if (!mview->isInitialized) {
+        MainView_initializeOpenGLView(mview);
+    }
+    
+    /*  Clear the buffer  */
+    glClearColor (0, 0, 0, 0);
+    glClear(GL_COLOR_BUFFER_BIT |
+            GL_DEPTH_BUFFER_BIT);
+
+       if (mview->mol == NULL)
+               return;
+
+       dimension = mview->dimension;
+/*    dimension = [model dimension];  */
+
+       MainViewCallback_frame(mview, frame);
+       width = frame[2] - frame[0];
+       height = frame[3] - frame[1];
+
+    glViewport(0, 0, width, height);
+    
+    /*  Set up the projection  */
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+       TrackballGetPerspective(mview->track, w);
+    distance = w[1] * dimension;
+    mview->perspective_vector[0] = w[0];
+    mview->perspective_vector[1] = width / height;
+    mview->perspective_vector[2] = dimension;
+    mview->perspective_vector[3] = distance + 200.0 * dimension;
+    gluPerspective(mview->perspective_vector[0], mview->perspective_vector[1], mview->perspective_vector[2], mview->perspective_vector[3]);
+
+    /*  Set up the model view  */
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glTranslatef(0.0, 0.0, -distance);
+       TrackballGetRotate(mview->track, w);
+    glRotatef(w[0], w[1], w[2], w[3]);
+       TrackballGetTranslate(mview->track, w);
+       w[0] *= dimension;
+       w[1] *= dimension;
+       w[2] *= dimension;
+    glTranslatef(w[0], w[1], w[2]);
+       mview->lookat.x = -w[0];
+       mview->lookat.y = -w[1];
+       mview->lookat.z = -w[2];
+       
+       MainViewCallback_clearLabels(mview);
+    drawModel(mview);
+       drawUnitCell(mview);
+       drawRotationCenter(mview);
+
+       /*  Get important matrices and vectors  */
+    glGetDoublev(GL_MODELVIEW_MATRIX, mview->modelview_matrix);
+    glGetDoublev(GL_PROJECTION_MATRIX, mview->projection_matrix);
+       pp = mview->modelview_matrix;
+       mtr[0] = pp[0]; mtr[1] = pp[4]; mtr[2] = pp[8];
+       mtr[3] = pp[1]; mtr[4] = pp[5]; mtr[5] = pp[9];
+       mtr[6] = pp[2]; mtr[7] = pp[6]; mtr[8] = pp[10];
+       mtr[9] = pp[12]; mtr[10] = pp[13]; mtr[11] = pp[14];
+       TransformInvert(mtr, mtr);
+       mview->camera.x = mtr[9]; mview->camera.y = mtr[10]; mview->camera.z = mtr[11];
+       mview->lookto.x = mtr[2]; mview->lookto.y = mtr[5]; mview->lookto.z = mtr[8];
+       mview->up.x = mtr[1]; mview->up.y = mtr[4]; mview->up.z = mtr[7];
+
+       /*  Draw labels  */
+       glDisable(GL_LIGHTING);
+//     glDisable (GL_DEPTH_TEST);
+//     glEnable (GL_BLEND);
+//     glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+#if __WXMAC__
+       glEnable (GL_TEXTURE_RECTANGLE_EXT);
+#endif
+       glMatrixMode (GL_PROJECTION);
+       glLoadIdentity ();
+       glMatrixMode (GL_MODELVIEW);
+       glLoadIdentity ();
+       glOrtho(0, width, 0, -height, 0.0,-1.0);  /*  non-flipped view  */
+       drawLabels(mview);
+#if __WXMAC__
+       glDisable (GL_TEXTURE_RECTANGLE_EXT);
+#endif
+//     glDisable(GL_BLEND);
+//     glEnable(GL_DEPTH_TEST);
+       glEnable(GL_LIGHTING);
+
+    if (mview->draggingMode == kMainViewSelectingRegion) {
+               /*  Draw selection rectangle  */
+               glDisable(GL_LIGHTING);
+               glDisable(GL_DEPTH_TEST);
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+        glMatrixMode(GL_PROJECTION);
+        glLoadIdentity();
+        glOrtho(0, width, 0, height, -1.0, 1.0);
+        glColor3f(1.0, 1.0, 0.0);
+        glBegin(GL_LINE_STRIP);
+               glVertex2f(mview->dragStartPos[0], mview->dragStartPos[1]);
+               glVertex2f(mview->dragStartPos[0], mview->dragEndPos[1]);
+               glVertex2f(mview->dragEndPos[0], mview->dragEndPos[1]);
+               glVertex2f(mview->dragEndPos[0], mview->dragStartPos[1]);
+               glVertex2f(mview->dragStartPos[0], mview->dragStartPos[1]);
+        glEnd();
+               glEnable(GL_DEPTH_TEST);
+               glEnable(GL_LIGHTING);
+    }
+
+    glFinish();
+        
+}
+
+#pragma mark ====== Labels ======
+
+void
+MainView_attachLabelToAtom(MainView *mview, int index)
+{
+       LabelRecord rec;
+       char buf[24], *p;
+       Atom *ap;
+/*     const AtomPar *dp; */
+       static float foreColor[] = {1, 1, 0, 1};
+       static float backColor[] = {1, 1, 1, 0};
+       ap = ATOM_AT_INDEX(mview->mol->atoms, index);
+       if (ap->resSeq == 0)
+               snprintf(buf, sizeof buf, "%-.4s", ap->aname);
+       else
+               snprintf(buf, sizeof buf, "%d:%-.4s", ap->resSeq, ap->aname);
+       for (p = buf; *p; p++) {
+               if (isspace(*p)) {
+                       *p = 0;
+                       break;
+               }
+       }
+/*     dp = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
+       foreColor[0] = 1.0 - dp->r;
+       foreColor[1] = 1.0 - dp->g;
+       foreColor[2] = 1.0 - dp->b; */
+       rec.label = MainViewCallback_newLabel(mview, buf, 14, foreColor, backColor);
+       rec.idx1 = index;
+       rec.idx2 = kInvalidIndex;
+       rec.labelid = mview->nlabels + 1;
+       ap->labelid = rec.labelid;
+       AssignArray(&mview->labels, &mview->nlabels, sizeof(LabelRecord), mview->nlabels, &rec);
+}
+
+void
+MainView_detachLabelFromAtom(MainView *mview, int index)
+{
+       Atom *ap;
+       if (index >= 0 && index < mview->mol->natoms) {
+               ap = ATOM_AT_INDEX(mview->mol->atoms, index);
+               if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
+                       mview->labels[ap->labelid - 1].idx1 = kInvalidIndex;
+               }
+               ap->labelid = 0;
+       }
+}
+
+void
+MainView_purgeUnusedLabels(MainView *mview)
+{
+       int i, n;
+       Atom *ap;
+       Int *tempid;
+       if (mview == NULL || mview->nlabels == 0 || mview->mol->natoms == 0)
+               return;
+       tempid = (Int *)calloc(sizeof(Int), mview->nlabels);
+       if (tempid == NULL)
+               return;
+
+       /*  Mark the labels in use  */
+       for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (ap->labelid > 0 && ap->labelid <= mview->nlabels)
+                       tempid[ap->labelid - 1] = 1;
+       }
+
+       /*  Release the labels not in use, and assign new IDs  */
+       n = 1;
+       for (i = 0; i < mview->nlabels; i++) {
+               if (tempid[i] == 0) {
+                       MainViewCallback_releaseLabel(mview->labels[i].label);
+                       mview->labels[i].label = NULL;
+               } else {
+                       mview->labels[i].labelid = tempid[i] = n++;
+               }
+       }
+       
+       /*  Purge the unused entries  */
+       for (i = mview->nlabels - 1; i >= 0; i--) {
+               if (tempid[i] == 0) {
+                       memmove(mview->labels + i + 1, mview->labels + i, (mview->nlabels - 1 - i) * sizeof(LabelRecord));
+                       mview->nlabels--;
+               }
+       }
+       if (mview->nlabels == 0) {
+               free(mview->labels);
+               mview->labels = NULL;
+       }
+       
+       /*  Renumber  */
+       for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
+                       ap->labelid = tempid[ap->labelid - 1];
+               } else {
+                       ap->labelid = 0;
+               }
+       }
+}
+
+#pragma mark ====== Mode ======
+
+void
+MainView_setMode(MainView *mview, int mode)
+{
+       if (mview != NULL)
+               mview->mode = mode;
+}
+
+int
+MainView_getMode(const MainView *mview)
+{
+       if (mview != NULL)
+               return mview->mode;
+       else return 0;
+}
+
+#pragma mark ====== Mouse operations ======
+
+static void
+mousePosToTrackballPos(MainView *mview, const float *mousePos, float *pos)
+{
+       float frame[4], width, height, radius;
+       NULL_CHECK(mview, "mousePosToTrackballPos");
+       MainViewCallback_frame(mview, frame);
+       width = frame[2] - frame[0];
+       height = frame[3] - frame[1];
+       radius = (width > height ? height * 0.5 : width * 0.5);
+       pos[0] = (mousePos[0] - frame[0] - width * 0.5) / radius;
+       pos[1] = (mousePos[1] - frame[1] - height * 0.5) / radius;
+}
+
+static void
+showAtomsInInfoText(MainView *mview, int n1, int n2)
+{
+       char buf[64];
+       if (n1 >= 0) {
+               MoleculeGetAtomName(mview->mol, n1, buf, sizeof buf - 1);
+               if (n2 >= 0) {
+                       int nn = strlen(buf);
+                       buf[nn++] = '-';
+                       MoleculeGetAtomName(mview->mol, n2, buf + nn, sizeof buf - nn);
+               }
+               MainViewCallback_drawInfoText(mview, buf);
+       } else MainViewCallback_drawInfoText(mview, NULL);
+}
+
+void
+MainView_mouseDown(MainView *mview, const float *mousePos, int flags)
+{
+       float p[3];
+       float screenPos[3], objectPos[3];
+       int n1, n2, found;
+       Atom *ap;
+       mview->dragStartPos[0] = mousePos[0];
+       mview->dragStartPos[1] = mousePos[1];
+       mview->dragStartPos[2] = 0.5;
+       found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
+       mview->clickedAtoms[0] = n1;
+       mview->clickedAtoms[1] = n2;
+       if (found) {
+               /*  Estimate the screen z-coordinate of the mouse position  */
+               Vector r1, r2;
+               ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
+               r1 = ap->r;
+       /*      if (mview->mol->is_xtal_coord)
+                               TransformVec(&r1, mview->mol->cell->tr, &r1); */
+               objectPos[0] = r1.x;
+               objectPos[1] = r1.y;
+               objectPos[2] = r1.z;
+               if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos)) {
+                       if (n2 >= 0) {
+                               ap = ATOM_AT_INDEX(mview->mol->atoms, n2);
+                               r2 = ap->r;
+                       /*      if (mview->mol->is_xtal_coord)
+                                               TransformVec(&r2, mview->mol->cell->tr, &r2); */
+                               objectPos[0] = r2.x;
+                               objectPos[1] = r2.y;
+                               objectPos[2] = r2.z;
+                               if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, p)) {
+                                       Double w;
+                                       r1.x = p[0] - screenPos[0];
+                                       r1.y = p[1] - screenPos[1];
+                                       r1.z = p[2] - screenPos[2];
+                                       NormalizeVec(&r1, &r1);
+                                       r2.x = mousePos[0] - screenPos[0];
+                                       r2.y = mousePos[1] - screenPos[1];
+                                       r2.z = 0.5;
+                                       w = VecDot(r1, r2);
+                                       VecScale(r2, r1, w);
+                                       screenPos[2] += r2.z;
+                               }
+                       }
+               }
+               mview->dragStartPos[2] = screenPos[2];
+       } else {
+               /*  Set the 'depth' value of the molecule center (i.e. trackball->translate * dimension)
+                   as the z-coordinate of the drag start position  */
+               TrackballGetTranslate(mview->track, p);
+               objectPos[0] = -mview->dimension * p[0];
+               objectPos[1] = -mview->dimension * p[1];
+               objectPos[2] = -mview->dimension * p[2];
+               if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos))
+                       mview->dragStartPos[2] = screenPos[2];
+       }
+
+       mview->isDragging = 0;
+
+       switch (mview->mode) {
+               case kTrackballRotateMode:
+               case kTrackballTranslateMode:
+               case kTrackballScaleMode:
+                       if (!found) {
+                               mousePosToTrackballPos(mview, mousePos, p);
+                               TrackballStartDragging(mview->track, p, mview->mode);
+                               mview->draggingMode = kMainViewMovingTrackball;
+                               break;
+                       } else {
+                               /*  No 'break' intentional; drop to next case  */
+                       }
+               case kTrackballSelectionMode: {
+                       /*  Select or move around */
+                       if (found) {
+                               if (flags & kShiftKeyMask) {
+                                       /*  Shift-key pressed: toggle selection  */
+                                       MoleculeToggleSelectionOfAtom(mview->mol, n1);
+                                       if (n2 >= 0)
+                                               MoleculeToggleSelectionOfAtom(mview->mol, n2);
+                               } else {
+                                       if (n2 < 0) {
+                                               if (!MoleculeIsAtomSelected(mview->mol, n1)) {
+                                                       /*  If this atom is not selected, select this atom and unselect others  */
+                                                       MoleculeSelectAtom(mview->mol, n1, 0);
+                                               }
+                                       } else {
+                                               if (!MoleculeIsBondSelected(mview->mol, n1, n2)) {
+                                                       /*  If this bond is not selected, select this bond and unselect others */
+                                                       MoleculeSelectAtom(mview->mol, n1, 0);
+                                                       MoleculeSelectAtom(mview->mol, n2, 1);
+                                               }
+                                       }
+                               }
+                               if (MoleculeIsAtomSelected(mview->mol, n1))
+                                       mview->draggingMode = kMainViewDraggingSelectedAtoms;
+                               else mview->draggingMode = 0;
+                       } else mview->draggingMode = kMainViewSelectingRegion;
+                       mview->dragEndPos[0] = mousePos[0];
+                       mview->dragEndPos[1] = mousePos[1];
+                       mview->dragEndPos[2] = mview->dragStartPos[2];
+                       mview->modifierFlags = flags;
+                       break;
+               }
+               case kTrackballCreateMode: {
+                       if (md_is_running(mview->mol->arena)) {
+                               MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
+                               mview->draggingMode = 0;
+                               break;
+                       }
+                       /*  Draw a new bond  */
+                       MoleculeSetSelection(mview->mol, NULL);
+                       if (found && n2 < 0) {
+                               /*  An atom under mouse: create a new atom and a bond  */
+                               ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
+                               mview->tempAtoms[0] = n1;
+                               mview->tempAtomPos[0] = ap->r;
+                       } else {
+                               /*  No atom under mouse: create two new atoms and a bond  */
+                               mview->tempAtoms[0] = -1;
+                               screenPos[0] = mousePos[0];
+                               screenPos[1] = mousePos[1];
+                               screenPos[2] = mview->dragStartPos[2];
+                               if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0) {
+                                       mview->tempAtoms[0] = -1;  /*  Cannot create  */
+                                       mview->draggingMode = 0;
+                               } else {
+                                       mview->tempAtomPos[0].x = objectPos[0];
+                                       mview->tempAtomPos[0].y = objectPos[1];
+                                       mview->tempAtomPos[0].z = objectPos[2];
+                               /*      if (mview->mol->is_xtal_coord)
+                                               TransformVec(&mview->tempAtomPos[0], mview->mol->cell->rtr, &mview->tempAtomPos[0]); */
+                               }
+                       }
+                       mview->tempAtoms[1] = -1;
+                       mview->tempAtomPos[1] = mview->tempAtomPos[0];
+                       mview->draggingMode = kMainViewCreatingBond;
+                       break;
+               }
+               default:
+                       mview->draggingMode = 0;
+                       break;
+       }
+       if (found)
+               showAtomsInInfoText(mview, n1, n2);
+       else
+               showAtomsInInfoText(mview, -1, -1);
+/*     MainViewCallback_setNeedsDisplay(mview, 1); */
+}
+
+void
+MainView_mouseDragged(MainView *mview, const float *mousePos, int flags)
+{
+       float p[2];
+       if (mview->isDragging == 0) {
+               if (abs(mousePos[0] - mview->dragStartPos[0]) >= 3 || abs(mousePos[1] - mview->dragStartPos[1]) >= 3)
+                       mview->isDragging = 1;
+               else return;
+       }
+       mousePosToTrackballPos(mview, mousePos, p);
+       switch (mview->draggingMode) {
+               case kMainViewMovingTrackball:
+                       TrackballDrag(mview->track, p);
+                       break;
+               case kMainViewSelectingRegion:
+               case kMainViewDraggingSelectedAtoms:
+                       mview->dragEndPos[0] = mousePos[0];
+                       mview->dragEndPos[1] = mousePos[1];
+                       mview->dragEndPos[2] = mview->dragStartPos[2];
+                       mview->modifierFlags = flags;
+                       MainViewCallback_display(mview);
+                       break;
+               case kMainViewCreatingBond: {
+                       int n1, n2;
+                       if (MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0) && n2 < 0) {
+                               /*  An atom under mouse  */
+                               Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
+                       /*      printf("n1=%d, n2=%d\n", n1, n2); */
+                               mview->tempAtoms[1] = n1;
+                               mview->tempAtomPos[1] = ap->r;
+                       } else {
+                               float screenPos[3], objectPos[3];
+                               Vector r1;
+                               mview->tempAtoms[1] = -1;
+                               /*  Convert the position of temporary atom 0 to screen position */
+                               r1 = mview->tempAtomPos[0];
+                       /*      if (mview->mol->is_xtal_coord)
+                                       TransformVec(&r1, mview->mol->cell->tr, &r1); */
+                               objectPos[0] = r1.x;
+                               objectPos[1] = r1.y;
+                               objectPos[2] = r1.z;
+                               if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) == 0)
+                                       break;  /*  Do nothing  */
+                               /*  Convert the mouse position to object position, while using the same Z depth calculated from the temporary atom 0 position  */
+                               screenPos[0] = mousePos[0];
+                               screenPos[1] = mousePos[1];
+                               if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0)
+                                       break;  /*  Do nothing  */
+                               r1.x = objectPos[0];
+                               r1.y = objectPos[1];
+                               r1.z = objectPos[2];
+                       /*      if (mview->mol->is_xtal_coord)
+                                       TransformVec(&r1, mview->mol->cell->rtr, &r1); */
+                               mview->tempAtomPos[1] = r1;
+                       }
+                       if (mview->tempAtoms[0] < 0)
+                               showAtomsInInfoText(mview, mview->tempAtoms[1], -1);
+                       else
+                               showAtomsInInfoText(mview, mview->tempAtoms[0], mview->tempAtoms[1]);
+                       MainViewCallback_display(mview);
+                       break;
+               }
+               default:
+                       return;
+       }
+       MainViewCallback_display(mview);
+}
+
+void
+MainView_mouseMoved(MainView *mview, const float *mousePos, int flags)
+{
+       int n1, n2, found;
+       if (mview->isDragging)
+               return;
+       found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 1, 0);
+       if (found)
+               showAtomsInInfoText(mview, n1, n2);
+       else
+               showAtomsInInfoText(mview, -1, -1);
+}
+
+static int
+sCreateNewAtom(Molecule *mol, Vector pos)
+{
+       int i, j;
+       Atom a;
+       char name[6];
+       memset(&a, 0, sizeof(Atom));
+       a.occupancy = 1.0;
+       for (i = 0; i < 1000; i++) {
+               snprintf(name, sizeof name, "C%03d", i);
+               for (j = 0; j < mol->natoms; j++) {
+                       if (strncmp(ATOM_AT_INDEX(mol->atoms, j)->aname, name, 4) == 0)
+                               break;
+               }
+               if (j == mol->natoms)
+                       break;
+       }
+       strncpy(a.aname, name, 4);
+       a.atomicNumber = 6;
+       strcpy(a.element, "C");
+       a.type = AtomTypeEncodeToUInt("c3");
+       a.weight = WeightForAtomicNumber(a.atomicNumber);
+       a.r = pos;
+       if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, -1) == 0)
+               return mol->natoms - 1;
+       else return -1;
+/*     return MoleculeAddAtom(mol, &a); */
+}
+
+void
+MainView_mouseUp(MainView *mview, const float *mousePos, int flags, int clickCount)
+{
+       float p[2];
+       char buf[1024];
+
+       mousePosToTrackballPos(mview, mousePos, p);
+
+       if (clickCount == 2 && mview->isDragging == 0) {
+               /*  Create a new molecular fragment  */
+               buf[0] = 0;
+               if (MyAppCallback_getTextWithPrompt("Enter formula (e.g. CH2OCH3)", buf, sizeof buf) == 0)
+                       return;
+               MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("s"), "dock_formula", buf);
+               goto exit;
+       }
+
+       if (mview->mode == kTrackballEraseMode) {
+               int n1, n2, found;
+               found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
+               if (found && n1 == mview->clickedAtoms[0] && n2 == mview->clickedAtoms[1]) {
+                       if (n2 < 0) {
+                       /*      IntGroup *sel, *newsel;
+                               sel = MoleculeGetSelection(mview->mol);
+                               if (sel != NULL) {
+                                       newsel = MoleculeModifySelectionByRemovingAtoms(mview->mol, sel, IntGroupNewWithPoints(n1, 1, -1));
+                                       if (!IntGroupIsEqual(sel, newsel))
+                                               MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, newsel);
+                                       if (newsel != NULL)
+                                               IntGroupRelease(newsel);
+                               } */
+                               MolActionCreateAndPerform(mview->mol, gMolActionDeleteAnAtom, (Int)n1);
+                               
+                       } else {
+                               Int nn[2];
+                               nn[0] = n1;
+                               nn[1] = n2;
+                               MolActionCreateAndPerform(mview->mol, gMolActionDeleteBonds, 2, nn);
+                       }
+               }
+               goto exit;
+       }
+       
+       switch (mview->draggingMode) {
+               case kMainViewMovingTrackball:
+                       TrackballEndDragging(mview->track, p);
+                       break;
+               case kMainViewDraggingSelectedAtoms: {
+                       Vector offset;
+                       if (mview->isDragging) {
+                               calcDragOffset(mview, &offset);
+                       /*      if (mview->mol->is_xtal_coord)
+                                       TransformVec(&offset, mview->mol->cell->rtr, &offset); */
+                               MolActionCreateAndPerform(mview->mol, gMolActionTranslateAtoms, &offset, mview->mol->selection);
+               /*      } else if (clickCount == 2) {
+                               buf[0] = 0;
+                               if (MyAppCallback_getTextWithPrompt("Enter formula (e.g. CH2OCH3)", buf, sizeof buf) == 0)
+                                       return;
+                               MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("s"), "dock_formula", buf);
+               */
+                       }
+                       break;
+               }
+               case kMainViewSelectingRegion: {
+                       char *selectFlags = temporarySelection(mview, mview->modifierFlags, (mview->isDragging ? 0 : 1), 1);
+                       if (selectFlags != NULL) {
+                               IntGroup *ig = IntGroupNew();
+                               if (ig != NULL) {
+                                       int i, natoms;
+                                       natoms = mview->mol->natoms;
+                                       for (i = 0; i < natoms; i++) {
+                                               if (selectFlags[i])
+                                                       IntGroupAdd(ig, i, 1);
+                                       }
+                                       MoleculeSetSelection(mview->mol, ig);
+                               /*      printf("current selection = {");
+                                       for (i = j = 0; i < natoms; i++) {
+                                               if (selectFlags[i]) {
+                                                       printf("%s%.4s", (j == 0 ? "" : " "), mview->mol->atoms[i].name);
+                                                       j++;
+                                               }
+                                       }
+                                       printf("}\n"); */
+                                       IntGroupRelease(ig);
+                               }
+                               free(selectFlags);
+                       }
+                       break;
+               }
+               case kMainViewCreatingBond: {
+                       Int b[3];
+                       int n1, n2;
+                       if (mview->tempAtoms[0] >= 0 && mview->tempAtoms[1] >= 0) {
+                               n1 = mview->tempAtoms[0];
+                               n2 = mview->tempAtoms[1];
+                       } else {
+                               if (mview->tempAtoms[0] < 0) {
+                                       /*  Create the first atom  */
+                                       n1 = sCreateNewAtom(mview->mol, mview->tempAtomPos[0]);
+                               } else n1 = mview->tempAtoms[0];
+                               if (mview->tempAtoms[1] < 0) {
+                                       /*  Create the second atom, if not too close  */
+                                       Vector dr = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
+                                       VecDec(dr, mview->tempAtomPos[1]);
+                               /*      if (mview->mol->is_xtal_coord)
+                                               TransformVec(&dr, mview->mol->cell->tr, &dr); */
+                                       if (VecLength2(dr) > 0.01)
+                                               n2 = sCreateNewAtom(mview->mol, mview->tempAtomPos[1]);
+                                       else n2 = -1;
+                               } else n2 = mview->tempAtoms[1];
+                       }
+                       if (n1 >= 0 && n2 >= 0 && n1 != n2) {
+                               b[0] = n1;
+                               b[1] = n2;
+                               b[2] = kInvalidIndex;
+                               MolActionCreateAndPerform(mview->mol, gMolActionAddBonds, 2, b);
+                       /*      MoleculeAddBonds(mview->mol, b, NULL); */
+                       }
+                       break;
+               }
+       }
+  exit:
+       mview->draggingMode = 0;
+       mview->isDragging = 0;
+       MainViewCallback_setNeedsDisplay(mview, 1);
+       MainViewCallback_setKeyboardFocus(mview);
+}
+
+void
+MainView_rotateBySlider(MainView *mview, float angle, int mode, int mouseStatus, int modifierFlags)
+{
+       int i, n1, n2;
+
+       if (mouseStatus == 1) {  /*  mouseDown  */
+       
+               if (mode == kSliderRotateBondMode) {
+                       if (MoleculeIsFragmentRotatable(mview->mol, mview->mol->selection, &n1, &n2, &(mview->rotateFragment))) {
+                               mview->rotateCenter = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
+                               mview->rotateAxis = ATOM_AT_INDEX(mview->mol->atoms, n2)->r;
+                               VecDec(mview->rotateAxis, mview->rotateCenter);
+                               if (VecLength2(mview->rotateAxis) < 1e-10)
+                                       return;
+                               NormalizeVec(&(mview->rotateAxis), &(mview->rotateAxis));
+                               if (modifierFlags & kAltKeyMask) {
+                                       /*  Option key: reverse the fragment  */
+                                       IntGroupRelease(mview->rotateFragment);
+                                       mview->rotateFragment = MoleculeFragmentExcludingAtoms(mview->mol, n2, 1, &n1);
+                                       VecScaleSelf(mview->rotateAxis, -1);
+                               }
+                       } else return;
+               } else if ((modifierFlags & kAltKeyMask) != 0) {
+                       /*  Rotate selection  */
+                       float tr[3];
+                       if (mview->mol->selection == NULL)
+                               return;
+                       mview->rotateFragment = IntGroupNewFromIntGroup(mview->mol->selection);
+                       TrackballGetTranslate(mview->track, tr);
+                       mview->rotateCenter.x = -mview->dimension * tr[0];
+                       mview->rotateCenter.y = -mview->dimension * tr[1];
+                       mview->rotateCenter.z = -mview->dimension * tr[2];
+                       if (mode == kSliderRotateXMode) {
+                               VecCross(mview->rotateAxis, mview->up, mview->lookto);
+                       } else {
+                               mview->rotateAxis = mview->up;
+                               VecScaleSelf(mview->rotateAxis, -1);
+                       }
+               } else {
+                       /*  Rotate along x/y axis (no coordinate transform)  */
+                       TrackballStartDragging(mview->track, NULL, kTrackballRotateMode);
+                       mview->rotateFragment = NULL;  /*  This is probably not necessary  */
+               }
+               if (mview->rotateFragment != NULL) {
+                       /*  Save the original position  */
+                       n1 = IntGroupGetCount(mview->rotateFragment);
+                       mview->rotateFragmentOldPos = (Vector *)calloc(sizeof(Vector), n1);
+                       if (mview->rotateFragmentOldPos == NULL) {
+                               IntGroupRelease(mview->rotateFragment);
+                               mview->rotateFragment = NULL;
+                               return;
+                       }
+                       for (i = 0; i < n1; i++) {
+                               n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
+                               mview->rotateFragmentOldPos[i] = (ATOM_AT_INDEX(mview->mol->atoms, n2))->r;
+                       }                       
+               }
+       
+       } else {  /*  mouseDragged or mouseUp */
+               
+               if (mview->rotateFragment != NULL) {
+               
+                       /*  Restore original positions  */
+                       n1 = IntGroupGetCount(mview->rotateFragment);
+                       for (i = 0; i < n1; i++) {
+                               n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
+                               (ATOM_AT_INDEX(mview->mol->atoms, n2))->r = mview->rotateFragmentOldPos[i];
+                       }
+                       /*  Rotate  */
+                       if (mouseStatus == 2) { /* dragged */
+                               MoleculeRotate(mview->mol, &(mview->rotateAxis), angle, &(mview->rotateCenter), mview->rotateFragment);
+                       } else {  /* mouse up */
+                               IntGroup *ig = IntGroupNewFromIntGroup(mview->rotateFragment);
+                               MolActionCreateAndPerform(mview->mol, gMolActionRotateAtoms, &(mview->rotateAxis), (double)angle, &(mview->rotateCenter), ig);
+                               IntGroupRelease(ig);
+                               IntGroupRelease(mview->rotateFragment);
+                               mview->rotateFragment = NULL;
+                               free(mview->rotateFragmentOldPos);
+                               mview->rotateFragmentOldPos = NULL;
+                       }
+                       
+               } else {
+                       float quat[4];
+                       float cs = cos(angle / 2);
+                       float sn = sin(angle / 2);
+                       if (mouseStatus == 0) { /* mouseUp */
+                               TrackballEndDragging(mview->track, NULL);
+                       } else { /* mouseDragged */
+                               quat[0] = cs;
+                               if (mode == kSliderRotateXMode) {
+                                       /*  Rotation along x axis  */
+                                       quat[1] = -sn;
+                                       quat[2] = quat[3] = 0;
+                               } else {
+                                       quat[2] = sn;
+                                       quat[1] = quat[3] = 0;
+                               }
+                               TrackballSetTemporaryRotation(mview->track, quat);
+                       }
+               }
+       }       
+       
+       MainViewCallback_setNeedsDisplay(mview, 1);
+       MainViewCallback_setKeyboardFocus(mview);
+}
+
+#pragma mark ====== Menu Commands ======
+
+void
+MainView_selectAll(MainView *mview)
+{
+       IntGroup *ig;
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       ig = IntGroupNew();
+       if (ig == NULL)
+               return;
+       IntGroupAdd(ig, 0, mview->mol->natoms);
+       MoleculeSetSelection(mview->mol, ig);
+       IntGroupRelease(ig);
+       MainViewCallback_setNeedsDisplay(mview, 1);
+}
+
+void
+MainView_selectFragment(MainView *mview)
+{
+       IntGroup *ig;
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       ig = MoleculeFragmentWithAtomGroups(mview->mol, MoleculeGetSelection(mview->mol), NULL);
+       if (ig == NULL)
+               return;
+       MoleculeSetSelection(mview->mol, ig);
+       IntGroupRelease(ig);
+       MainViewCallback_setNeedsDisplay(mview, 1);
+}
+
+void
+MainView_selectReverse(MainView *mview)
+{
+       IntGroup *ig;
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       ig = IntGroupNewFromIntGroup(MoleculeGetSelection(mview->mol));
+       IntGroupReverse(ig, 0, mview->mol->natoms);
+       MoleculeSetSelection(mview->mol, ig);
+       IntGroupRelease(ig);
+       MainViewCallback_setNeedsDisplay(mview, 1);
+}
+
+void
+MainView_centerSelection(MainView *mview)
+{
+       IntGroup *ig;
+       Vector c;
+       float tr[3];
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       ig = MoleculeGetSelection(mview->mol);
+       MoleculeCenterOfMass(mview->mol, &c, ig);
+       tr[0] = -c.x / mview->dimension;
+       tr[1] = -c.y / mview->dimension;
+       tr[2] = -c.z / mview->dimension;
+       TrackballSetTranslate(mview->track, tr);
+       MainViewCallback_setNeedsDisplay(mview, 1);
+}
+
+#pragma mark ====== Pasteboard Support ======
+
+int
+MainView_copy(MainView *mview)
+{
+       Molecule *mol, *mol2;
+       IntGroup *sel;
+       Int len, result, time;
+       char *p;
+       if (mview == NULL || (mol = mview->mol) == NULL || (sel = MoleculeGetSelection(mol)) == NULL)
+               return 1;
+       if (MoleculeExtract(mol, &mol2, MoleculeGetSelection(mol), 1) != 0
+       || mol2 == NULL
+       || (p = MoleculeSerialize(mol2, &len, &time)) == NULL)
+               return 2;
+       result = MoleculeCallback_writeToPasteboard(kMoleculePasteboardType, p, len);
+       if (result != 0)
+               return result;
+       if (mol2 != NULL)
+               MoleculeRelease(mol2);
+       mview->pasteTimeStamp = time;
+       mview->pasteCount = 0;
+       return 0;
+}
+
+int
+MainView_delete(MainView *mview)
+{
+       int result;
+       Molecule *mol = mview->mol;
+       if (md_is_running(mview->mol->arena)) {
+               MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
+               return -1;
+       }
+       result = MolActionCreateAndPerform(mol, gMolActionUnmergeMolecule, MoleculeGetSelection(mol));
+       if (result == 0)
+               result = MolActionCreateAndPerform(mol, gMolActionSetSelection, NULL);
+       return result;
+}
+
+int
+MainView_cut(MainView *mview)
+{
+       int result;
+       if (md_is_running(mview->mol->arena)) {
+               MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
+               return -1;
+       }
+       result = MainView_copy(mview);
+       if (result == 0)
+               result = MainView_delete(mview);
+       return result;
+}
+
+int
+MainView_isPastable(MainView *mview)
+{
+       if (MoleculeCallback_isDataInPasteboard(kMoleculePasteboardType))
+               return 1;
+       else return 0;
+}
+
+int
+MainView_paste(MainView *mview)
+{
+       void *p;
+       Int len, result, time;
+       Molecule *mol2;
+       IntGroup *sel;
+
+       if (md_is_running(mview->mol->arena)) {
+               MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
+               return -1;
+       }       
+       if (!MainView_isPastable(mview))
+               return 1;
+       if (MoleculeCallback_readFromPasteboard(kMoleculePasteboardType, &p, &len) != 0)
+               return 2;
+       if ((mol2 = MoleculeDeserialize(p, len, &time)) == NULL) {
+               free(p);
+               return 3;
+       }
+       free(p);
+
+       if (time == mview->pasteTimeStamp) {
+               /*  Offset the pasted fragment by something  */
+               Vector v;
+               mview->pasteCount++;
+               v.x = v.y = v.z = mview->pasteCount * 0.5 + sin((double)mview->pasteCount) * 0.1; /* sin(..) is to avoid accidental coincidence  */
+               MoleculeTranslate(mol2, &v, NULL);
+       } else {
+               mview->pasteTimeStamp = time;
+               mview->pasteCount = 0;
+       }
+       
+       sel = MoleculeGetSelection(mview->mol);
+       if (sel == NULL || IntGroupGetCount(sel) == 0) {
+               /*  Remove dummy atoms from mol2  */
+               Atom *ap;
+               int i;
+               sel = IntGroupNew();
+               for (i = 0, ap = mol2->atoms; i < mol2->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       if (ap->atomicNumber == 0 && ap->aname[0] == '_') {
+                               /*  Dummy atom  */
+                               IntGroupAdd(sel, i, 1);
+                       }
+               }
+               if (IntGroupGetCount(sel) > 0)
+                       MoleculeUnmerge(mol2, NULL, sel, 0);
+               IntGroupRelease(sel);
+       }
+       
+       result = MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("M"), "dock_fragment", mol2);
+       MoleculeRelease(mol2);
+       return result;
+}
+
+static int
+sMainView_AppendParameterToSerialBuffer(void **pb_ptr, Int *pb_len, Int parType, Int count, const UnionPar *up)
+{
+       int len = sizeof(Int) * 2 + sizeof(UnionPar) * count;
+       char *p;
+       if (*pb_len == 0) {
+               *pb_ptr = malloc(len + 1);
+               if (*pb_ptr == NULL)
+                       return -1;
+               p = (char *)(*pb_ptr);
+               *pb_len = len + 1;
+       } else {
+               *pb_ptr = realloc(*pb_ptr, *pb_len + len);
+               if (*pb_ptr == NULL)
+                       return -1;
+               p = (char *)(*pb_ptr) + *pb_len - 1;  /*  *pb_len includes the end mark  */
+               *pb_len += len;
+       }
+       *((Int *)p) = 0;
+       *p = parType;
+       p += sizeof(Int);
+       *((Int *)p) = count;
+       p += sizeof(Int);
+       memmove(p, up, sizeof(UnionPar) * count);
+       p += sizeof(UnionPar) * count;
+       *p = 0;  /*  End mark  */
+       return 0;
+}
+
+int
+MainView_pasteParameters(MainView *mview)
+{
+       char *p, *pp;
+       Int len, i;
+       if (mview == NULL || mview->mol == NULL || mview->mol->par == NULL)
+               return -1;
+       if (!MoleculeCallback_isDataInPasteboard(kParameterPasteboardType))
+               return 1;
+       if (MoleculeCallback_readFromPasteboard(kParameterPasteboardType, (void **)&p, &len) != 0)
+               return 2;
+       pp = p;
+       while (*p != 0) {
+               int count;
+               UnionPar *up;
+               IntGroup *ig;
+               int parType = *p;
+               if (parType < kFirstParType || parType > kLastParType)
+                       break;
+               p += sizeof(Int);
+               count = *((Int *)p);
+               p += sizeof(Int);
+               up = (UnionPar *)calloc(sizeof(UnionPar), count);
+               memmove(up, p, sizeof(UnionPar) * count);
+
+               /*  The global parameters become local when pasted  */
+               for (i = 0; i < count; i++) {
+                       if (up[i].bond.src > 0)
+                               up[i].bond.src = 0;
+               }
+               
+               ig = IntGroupNewWithPoints(ParameterGetCountForType(mview->mol->par, parType), count, -1);
+               MolActionCreateAndPerform(mview->mol, gMolActionAddParameters, parType, ig, count, up);
+               free(up);
+               IntGroupRelease(ig);
+               p += sizeof(UnionPar) * count;
+       }
+       free(pp);
+       MoleculeCallback_notifyModification(mview->mol, 0);
+       return 0;
+}
+
+/*  flags: 1, delete; 2, copy; 3, cut  */
+int
+MainView_copyOrCutParameters(MainView *mview, int flags)
+{
+       IntGroup *ig = MainViewCallback_getTableSelection(mview);
+       IntGroup *ig2;
+       int i, n, idx, type = -1, t1;
+       Parameter *par = mview->mol->par;
+       void *pb_ptr = NULL;
+       Int pb_len = 0;
+       i = 0;
+       ig2 = NULL;
+       while (1) {
+               n = IntGroupGetNthPoint(ig, i);
+               if (n >= 0)
+                       idx = ParameterTableGetItemIndex(par, n, &t1);
+               else {
+                       idx = -1;
+                       t1 = -2;
+               }
+               if (t1 != type) {
+                       /*  Process Parameters for the last group  */
+                       if (type >= kFirstParType && ig2 != NULL && (n = IntGroupGetCount(ig2)) > 0) {
+                               UnionPar *up = (UnionPar *)calloc(sizeof(UnionPar), n);
+                               if (flags & 1) {
+                                       MolAction *act;
+                                       if (ParameterDelete(par, type, up, ig2) < 0)
+                                               return -1;
+                                       act = MolActionNew(gMolActionAddParameters, type, ig2, n, up);
+                                       MolActionCallback_registerUndo(mview->mol, act);
+                                       MolActionRelease(act);
+                               } else {
+                                       if (ParameterCopy(par, type, up, ig2) < 0)
+                                               return -1;
+                               }
+                               if (flags & 2) {
+                                       if (sMainView_AppendParameterToSerialBuffer(&pb_ptr, &pb_len, type, n, up) != 0)
+                                               return -1;
+                               }
+                               free(up);
+                               IntGroupRelease(ig2);
+                               ig2 = NULL;
+                       }
+                       if (t1 == -2)
+                               break;
+                       type = t1;
+               }
+               if (idx >= 0) {
+                       if (ig2 == NULL)
+                               ig2 = IntGroupNew();
+                       IntGroupAdd(ig2, idx, 1);
+               }
+               i++;
+       }
+       if (ig2 != NULL)
+               IntGroupRelease(ig2);
+       IntGroupRelease(ig);
+       
+       if (flags & 2) {
+               n = MoleculeCallback_writeToPasteboard(kParameterPasteboardType, pb_ptr, pb_len);
+               if (n != 0)
+                       return n;
+       }
+       
+       MoleculeCallback_notifyModification(mview->mol, 0);
+       return 0;
+}
+
+#pragma mark ====== Table View (also for gBuiltinParameters) ======
+
+/*  As a special case, the table view functions will handle gBuiltinParameters when mview == NULL.  */
+
+typedef struct ColumnInfoRecord {
+       char *name;
+       int width;
+       int editable;
+} ColumnInfoRecord;
+
+static ColumnInfoRecord sAtomColumns[] = {
+{"atom", 4, 0}, {"name", 4, 1}, {"type", 4, 1}, {"element", 4, 1}, {"residue", 6, 1},
+{"x", 6, 1}, {"y", 6, 1}, {"z", 6, 1}, {"charge", 6, 1}, {NULL}
+};
+static ColumnInfoRecord sBondColumns[] = {
+{"atoms", 9, 0}, {"names", 9, 0}, {"type", 9, 0}, {"length", 8, 0}, {"r0", 8, 0}, {"force", 8, 0}, {NULL}
+};
+static ColumnInfoRecord sAngleColumns[] = {
+{"atoms", 12, 0}, {"names", 12, 0}, {"type", 12, 0}, {"angle", 8, 0}, {"a0", 8, 0}, {"force", 8, 0}, {NULL}
+};
+static ColumnInfoRecord sDihedralColumns[] = {
+{"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"dihedral", 8, 0}, {"force", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
+};
+static ColumnInfoRecord sImproperColumns[] = {
+{"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"improper", 8, 0}, {"force", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
+};
+static ColumnInfoRecord sParameterColumns[] = {
+{"class", 5, 0}, {"type", 9, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"src", 8, 0}, {"comment", 25, 0}, {NULL}
+};
+static ColumnInfoRecord sMOInfoColumns[] = {
+{"MO", 10, 0}, {"energy", 20, 0}, {NULL}
+};
+static ColumnInfoRecord *sColumnInfo[] = {
+sAtomColumns, sBondColumns, sAngleColumns, sDihedralColumns, sImproperColumns, sParameterColumns, sMOInfoColumns
+};
+static char *sTableTitles[] = {
+       "atom", "bond", "angle", "dihedral", "improper", "parameter", "MO info"
+};
+
+void
+MainView_tableTitleForIndex(MainView *mview, int idx, char *buf, int bufsize)
+{
+       if (mview == NULL)
+               idx = kMainViewParameterTableIndex;
+       if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0])) {
+               buf[0] = 0;
+               return;
+       }
+       snprintf(buf, bufsize, "%s", sTableTitles[idx]);
+}
+
+void
+MainView_createColumnsForTableAtIndex(MainView *mview, int idx)
+{
+       int i;
+
+       if (mview == NULL)
+               idx = kMainViewParameterTableIndex;
+       if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
+               return;
+
+       /*  Remove all existing columns  */
+       while (MainViewCallback_removeTableColumnAtIndex(mview, 0) > 0);
+       
+       if (mview != NULL)
+               mview->tableIndex = idx;
+       
+       /*  Create columns  */
+       for (i = 0; ; i++) {
+               int width;
+               ColumnInfoRecord *recp = &(sColumnInfo[idx][i]);
+               if (recp->name == NULL)
+                       break;
+               width = recp->width;
+               if (mview == 0 && strcmp(recp->name, "comment") == 0)
+                       width = 80;
+               MainViewCallback_addTableColumn(mview, recp->name, width, recp->editable);
+       }
+}
+
+void
+MainView_refreshTable(MainView *mview)
+{
+       /*  Reload data  */
+       if (mview != NULL && mview->mol != NULL)
+               MainView_refreshCachedInfo(mview);
+       
+       MainViewCallback_reloadTableData(mview);
+
+       if (mview != NULL && mview->mol != NULL)
+               MainViewCallback_setTableSelection(mview, mview->tableSelection);
+}
+
+int
+MainView_numberOfRowsInTable(MainView *mview)
+{
+       if (mview == NULL)
+               return ParameterTableNumberOfRows(gBuiltinParameters);
+       if (mview->mol == NULL)
+               return 0;
+       if (mview->tableIndex == kMainViewParameterTableIndex)
+               return ParameterTableNumberOfRows(mview->mol->par);
+       if (mview->tableCache == NULL)
+               MainView_refreshCachedInfo(mview);
+       return IntGroupGetCount(mview->tableCache);
+}
+
+static char *
+sAtomDescription(Atom *ap, char *buf, int bufsize)
+{
+       snprintf(buf, bufsize, "%d:%.4s", ap->resSeq, ap->aname);
+       return buf;
+}
+
+static UnionPar *
+sParameterOfTypeAtIndex(Molecule *mol, int parType, int idx)
+{
+       int i;
+       if (mol->arena == NULL || mol->needsMDRebuild || mol->arena->par == NULL)
+               return NULL;
+       switch (parType) {
+               case kVdwParType:
+                       i = mol->arena->vdw_par_i[idx];
+                       if (i >= 0 && i < mol->arena->par->nvdwPars)
+                               return (UnionPar *)(mol->arena->par->vdwPars + i);
+                       else return NULL;
+               case kBondParType:
+                       i = mol->arena->bond_par_i[idx];
+                       if (i >= 0 && i < mol->arena->par->nbondPars)
+                               return (UnionPar *)(mol->arena->par->bondPars + i);
+                       else return NULL;
+               case kAngleParType:
+                       i = mol->arena->angle_par_i[idx];
+                       if (i >= 0 && i < mol->arena->par->nanglePars)
+                               return (UnionPar *)(mol->arena->par->anglePars + i);
+                       else return NULL;
+               case kDihedralParType:
+                       i = mol->arena->dihedral_par_i[idx];
+                       if (i >= 0 && i < mol->arena->par->ndihedralPars)
+                               return (UnionPar *)(mol->arena->par->dihedralPars + i);
+                       else return NULL;
+               case kImproperParType:
+                       i = mol->arena->improper_par_i[idx];
+                       if (i >= 0 && i < mol->arena->par->nimproperPars)
+                               return (UnionPar *)(mol->arena->par->improperPars + i);
+                       else return NULL;
+       }
+       return NULL;
+}
+
+void
+MainView_valueForTable(MainView *mview, int column, int row, char *buf, int bufsize)
+{
+       int idx;
+       Int *ip;
+       Atom *ap[4];
+       char descbuf[4][20], typebuf[4][8];
+       Molecule *mol;
+
+       if (mview == NULL)
+               return ParameterTableGetItemText(gBuiltinParameters, column, row, buf, bufsize);
+
+       if (mview == NULL || (mol = mview->mol) == NULL) {
+               buf[0] = 0;
+               return;
+       }
+       
+       if (mview->tableIndex == kMainViewParameterTableIndex)
+               return ParameterTableGetItemText(mview->mol->par, column, row, buf, bufsize);
+
+       if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
+               idx = IntGroupGetNthPoint(mview->tableCache, row);
+               ap[0] = ATOM_AT_INDEX(mol->atoms, idx);
+               switch (column) {
+                       case 0: snprintf(buf, bufsize, "%d", idx); break;
+                       case 1: snprintf(buf, bufsize, "%.4s", ap[0]->aname); break;
+                       case 2: snprintf(buf, bufsize, "%.6s", AtomTypeDecodeToString(ap[0]->type, NULL)); break;
+                       case 3: snprintf(buf, bufsize, "%.2s", ap[0]->element); break;
+                       case 4: 
+                               if (ap[0]->resSeq == 0)
+                                       buf[0] = 0;
+                               else
+                                       snprintf(buf, bufsize, "%.4s.%d", ap[0]->resName, ap[0]->resSeq);
+                               break;
+                       case 5: snprintf(buf, bufsize, "%.3f", ap[0]->r.x); break;
+                       case 6: snprintf(buf, bufsize, "%.3f", ap[0]->r.y); break;
+                       case 7: snprintf(buf, bufsize, "%.3f", ap[0]->r.z); break;
+                       case 8: snprintf(buf, bufsize, "%.3f", ap[0]->charge); break;
+                       default: buf[0] = 0; break;
+               }
+       } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
+               idx = IntGroupGetNthPoint(mview->tableCache, row);
+               ip = mol->bonds + idx * 2;
+               ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
+               ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
+               switch (column) {
+                       case 0: snprintf(buf, bufsize, "%d-%d", ip[0], ip[1]); break;
+                       case 1: snprintf(buf, bufsize, "%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20)); break;
+                       case 2: snprintf(buf, bufsize, "%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1])); break;
+                       case 3:
+                               snprintf(buf, bufsize, "%.3f", MoleculeMeasureBond(mview->mol, &(ap[0]->r), &(ap[1]->r)));
+                               break;
+                       case 4:
+                       case 5: {
+                               BondPar *bp = (BondPar *)sParameterOfTypeAtIndex(mol, kBondParType, idx);
+                               if (bp == NULL)
+                                       buf[0] = 0;
+                               else snprintf(buf, bufsize, "%.3f", (column == 4 ? bp->r0 : bp->k * INTERNAL2KCAL));
+                               break;
+                       }
+                       default: buf[0] = 0; break;
+               }
+       } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
+               idx = IntGroupGetNthPoint(mview->tableCache, row);
+               ip = mol->angles + idx * 3;
+               ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
+               ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
+               ap[2] = ATOM_AT_INDEX(mol->atoms, ip[2]);
+               switch (column) {
+                       case 0: snprintf(buf, bufsize, "%d-%d-%d", ip[0], ip[1], ip[2]); break;
+                       case 1: snprintf(buf, bufsize, "%s-%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20), sAtomDescription(ap[2], descbuf[2], 20)); break;
+                       case 2: snprintf(buf, bufsize, "%.6s-%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1]), AtomTypeDecodeToString(ap[2]->type, typebuf[2])); break;
+                       case 3:
+                               snprintf(buf, bufsize, "%.3f", MoleculeMeasureAngle(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r)));
+                               break;
+                       case 4:
+                       case 5: {
+                               AnglePar *anp = (AnglePar *)sParameterOfTypeAtIndex(mol, kAngleParType, idx);
+                               if (anp == NULL)
+                                       buf[0] = 0;
+                               else snprintf(buf, bufsize, "%.3f", (column == 4 ? anp->a0 * kRad2Deg : anp->k * INTERNAL2KCAL));
+                               break;
+                       }
+                       default: buf[0] = 0; break;
+               }               
+       } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) { /* Dihedrals, Impropers */
+               int f = (mview->tableIndex == kMainViewDihedralTableIndex);
+               idx = IntGroupGetNthPoint(mview->tableCache, row);
+               ip = (f ? mview->mol->dihedrals : mview->mol->impropers) + idx * 4;
+               ap[0] = ATOM_AT_INDEX(mview->mol->atoms, ip[0]);
+               ap[1] = ATOM_AT_INDEX(mview->mol->atoms, ip[1]);
+               ap[2] = ATOM_AT_INDEX(mview->mol->atoms, ip[2]);
+               ap[3] = ATOM_AT_INDEX(mview->mol->atoms, ip[3]);
+               switch (column) {
+                       case 0: snprintf(buf, bufsize, "%d-%d-%d-%d", ip[0], ip[1], ip[2], ip[3]); break;
+                       case 1: snprintf(buf, bufsize, "%s-%s-%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20), sAtomDescription(ap[2], descbuf[2], 20), sAtomDescription(ap[3], descbuf[3], 20)); break;
+                       case 2: snprintf(buf, bufsize, "%.6s-%.6s-%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1]), AtomTypeDecodeToString(ap[2]->type, typebuf[2]), AtomTypeDecodeToString(ap[3]->type, typebuf[3])); break;
+                       case 3:
+                               snprintf(buf, bufsize, "%.3f", MoleculeMeasureDihedral(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r), &(ap[3]->r)));
+                               break;
+                       case 4:
+                       case 5:
+                       case 6: {
+                               TorsionPar *tp = (TorsionPar *)sParameterOfTypeAtIndex(mol, (f ? kDihedralParType : kImproperParType), idx);
+                               if (tp == NULL)
+                                       buf[0] = 0;
+                               else if (column == 5)
+                                       snprintf(buf, bufsize, "%d", tp->period[0]);
+                               else snprintf(buf, bufsize, "%.3f", (column == 4 ? tp->k[0] * INTERNAL2KCAL : tp->phi0[0] * kRad2Deg));
+                               break;
+                       }
+                       default: buf[0] = 0; break;
+               }               
+       } else if (mview->tableIndex == kMainViewMOTableIndex) { /* MO info */
+               BasisSet *bset = mview->mol->bset;
+               idx = row;
+               buf[0] = 0;
+               if (bset != NULL && idx >= 0 && idx < bset->nmos) {
+                       char *s = "";
+                       if (idx * 2 + 2 == bset->nelectrons)
+                               s = " (HOMO)";
+                       else if (idx * 2 + 1 == bset->nelectrons)
+                               s = " (SOMO)";
+                       else if ((bset->nelectrons + 1) / 2 == idx)
+                               s = " (LUMO)";
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "%d%s", idx + 1, s); break;
+                               case 1: snprintf(buf, bufsize, "%.8f", bset->moenergies[idx]); break;
+                       }
+               }
+       }
+}
+
+/*  Set color for the locally defined or undefined MM parameters  */
+int
+MainView_setColorForTable(MainView *mview, int column, int row, float *fg, float *bg)
+{
+       int parType = -1;
+       int idx;
+       UnionPar *up;
+       switch (mview->tableIndex) {
+               case kMainViewBondTableIndex:
+                       parType = kBondParType;
+                       break;
+               case kMainViewAngleTableIndex:
+                       parType = kAngleParType;
+                       break;
+               case kMainViewDihedralTableIndex:
+                       parType = kDihedralParType;
+                       break;
+               case kMainViewImproperTableIndex:
+                       parType = kImproperParType;
+                       break;
+               default:
+                       return 0;
+       }
+       if (column < 3)
+               return 0;
+
+       idx = IntGroupGetNthPoint(mview->tableCache, row);
+       up = sParameterOfTypeAtIndex(mview->mol, parType, idx);
+       if (up == NULL)
+               return 0;
+
+       if (column == 3) {
+               /*  Value column; warn if the value is "abnormal"  */
+               int f;
+               switch (parType) {
+                       case kBondParType: f = md_check_abnormal_bond(mview->mol->arena, mview->mol, idx); break;
+                       case kAngleParType: f = md_check_abnormal_angle(mview->mol->arena, mview->mol, idx); break;
+                       case kDihedralParType: f = md_check_abnormal_dihedral(mview->mol->arena, mview->mol, idx); break;
+                       case kImproperParType: f = md_check_abnormal_improper(mview->mol->arena, mview->mol, idx); break;
+                       default: return 0;
+               }
+               if (f == 1) {
+                       bg[0] = 1.0;
+                       bg[1] = bg[2] = 0.5;
+                       return 2;
+               } else return 0;
+       } else {
+               if (up->bond.src == 0) {
+                       bg[0] = bg[1] = 1.0;
+                       bg[2] = 0.6;
+                       return 2;
+               } else if (up->bond.src == -1) {
+                       bg[0] = 1.0;
+                       bg[1] = bg[2] = 0.2;
+                       return 2;
+               }
+               return 0;
+       }
+}
+
+void
+MainView_setSelectionFromTable(MainView *mview)
+{
+       IntGroup *ig, *sel;
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       if (mview->tableIndex == kMainViewMOTableIndex || mview->tableIndex == kMainViewParameterTableIndex)
+               return;  /*  Do nothing  */
+       ig = MainViewCallback_getTableSelection(mview);
+       sel = IntGroupNew();
+       if (ig != NULL) {
+               int i, i1, i2;
+               Int *ip;
+               for (i = 0; (i1 = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
+                       i2 = IntGroupGetNthPoint(mview->tableCache, i1);
+                       if (i2 < 0)
+                               continue;
+                       if (mview->tableIndex == kMainViewAtomTableIndex) {  /* Atoms */
+                               IntGroupAdd(sel, i2, 1);
+                       } else if (mview->tableIndex == kMainViewBondTableIndex) {  /* Bonds */
+                               ip = mview->mol->bonds + i2 * 2;
+                               IntGroupAdd(sel, ip[0], 1);
+                               IntGroupAdd(sel, ip[1], 1);
+                       } else if (mview->tableIndex == kMainViewAngleTableIndex) {  /* Angles */
+                               ip = mview->mol->angles + i2 * 3;
+                               IntGroupAdd(sel, ip[0], 1);
+                               IntGroupAdd(sel, ip[1], 1);
+                               IntGroupAdd(sel, ip[2], 1);
+                       } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) {  /* Dihedrals, impropers */
+                               ip = (mview->tableIndex == kMainViewDihedralTableIndex ? mview->mol->dihedrals : mview->mol->impropers) + i2 * 4;
+                               IntGroupAdd(sel, ip[0], 1);
+                               IntGroupAdd(sel, ip[1], 1);
+                               IntGroupAdd(sel, ip[2], 1);
+                               IntGroupAdd(sel, ip[3], 1);
+                       }
+               }
+               IntGroupRelease(ig);
+       }
+       MoleculeSetSelection(mview->mol, sel);
+       IntGroupRelease(sel);
+}
+
+static void
+sMainView_ParameterTableSetItemText(Molecule *mol, int column, int row, const char *s)
+{
+       Parameter *par;
+       int type, idx;
+       const char *kstr = NULL;
+       UnionPar *up = NULL;
+       if (mol == NULL || (par = mol->par) == NULL || row < 0)
+               return;
+       up = ParameterTableGetItemPtr(par, row, &type);
+       if (up == NULL)
+               return;
+       switch (type) {
+               case kVdwParType:
+                       switch (column) {
+                               case 1: kstr = "atom_type"; break;
+                               case 2: kstr = "eps"; break;
+                               case 3: kstr = "r_eq"; break;
+                               case 4: kstr = "eps14"; break;
+                               case 5: kstr = "r_eq14"; break;
+                               case 6: kstr = "atomic_number"; break;
+                               case 7: kstr = "weight"; break;
+                       }
+                       break;
+               case kBondParType:
+                       switch (column) {
+                               case 1: kstr = "atom_types"; break;
+                               case 2: kstr = "k"; break;
+                               case 3: kstr = "r0"; break;
+                       }
+                       break;
+               case kAngleParType:
+                       switch (column) {
+                               case 1: kstr = "atom_types"; break;
+                               case 2: kstr = "k"; break;
+                               case 3: kstr = "a0"; break;
+                       }
+                       break;
+               case kDihedralParType:
+               case kImproperParType:
+                       switch (column) {
+                               case 1: kstr = "atom_types"; break;
+                               case 2: kstr = "k"; break;
+                               case 3: kstr = "period"; break;
+                               case 4: kstr = "phi0"; break;
+                       }
+                       break;
+               case kVdwPairParType:
+                       switch (column) {
+                               case 1: kstr = "atom_types"; break;
+                               case 2: kstr = "eps"; break;
+                               case 3: kstr = "r_eq"; break;
+                               case 4: kstr = "eps14"; break;
+                               case 5: kstr = "r_eq14"; break;
+                       }
+                       break;
+       }
+       if (column == 9)
+               kstr = "comment";
+       if (kstr == NULL)
+               return;
+       idx = ParameterTableGetItemIndex(par, row, &type);
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION("iissi"), "set_parameter_attr", type, idx, kstr, s, 0);
+}
+
+void
+MainView_setValueForTable(MainView *mview, int column, int row, const char *buf)
+{
+       int idx;
+       char *key;
+       Molecule *mol;
+       char temp[256];
+       if (mview == NULL || (mol = mview->mol) == NULL)
+               return;
+       MainView_valueForTable(mview, column, row, temp, sizeof temp);
+       if (strcmp(buf, temp) == 0 || buf[0] == 0)
+               return;  /*  No change  */
+       idx = IntGroupGetNthPoint(mview->tableCache, row);
+       if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
+               switch (column) {
+                       case 1: key = "name"; break;
+                       case 2: key = "atom_type"; break;
+                       case 3: key = "element"; break;
+                       case 4: {  /*  Residue  */
+                               IntGroup *ig = IntGroupNewWithPoints(idx, 1, -1);
+                               MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "assign_residue", ig, buf);
+                               IntGroupRelease(ig);
+                               return;
+                       }
+                       case 5: key = "x"; break;
+                       case 6: key = "y"; break;
+                       case 7: key = "z"; break;
+                       case 8: key = "charge"; break;
+                       default: return;
+               }
+               MolActionCreateAndPerform(mol, SCRIPT_ACTION("iss"), "set_atom_attr", idx, key, buf);
+       } else if (mview->tableIndex == kMainViewParameterTableIndex) { /* Parameters */
+               sMainView_ParameterTableSetItemText(mview->mol, column, row, buf);
+       }
+}
+
+int
+MainView_isTableItemEditable(MainView *mview, int column, int row)
+{
+       if (mview == NULL)
+               return ParameterTableIsItemEditable(gBuiltinParameters, column, row);
+       if (mview->mol == NULL)
+               return 0;
+       if (mview->tableIndex == kMainViewParameterTableIndex)
+               return ParameterTableIsItemEditable(mview->mol->par, column, row);
+       if (mview->tableIndex >= 0 && mview->tableIndex < sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
+               return sColumnInfo[mview->tableIndex][column].editable != 0;
+       else return 0;
+}
+
+int
+MainView_tableType(MainView *mview)
+{
+       if (mview == NULL)
+               return kMainViewParameterTableIndex;
+       if (mview->mol == NULL)
+               return -1;
+       return mview->tableIndex;
+}
+
+void
+MainView_dragTableSelectionToRow(MainView *mview, int row)
+{
+       Int *new2old, i, n, count, natoms, start_row;
+       IntGroup *sel;
+       if (mview == NULL || mview->mol == NULL || mview->tableIndex != 0 || row < 0 || row > (natoms = mview->mol->natoms))
+               return;
+       if (md_is_running(mview->mol->arena)) {
+               MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
+               return;
+       }       
+       sel = MoleculeGetSelection(mview->mol);
+       if (sel == NULL || (count = IntGroupGetCount(sel)) == 0)
+               return;
+       new2old = (Int *)calloc(sizeof(Int), natoms);
+       if (new2old == NULL)
+               return;
+
+       //  Place the atoms above the target position
+       for (i = n = 0; i < row; i++) {
+               if (IntGroupLookupPoint(sel, i) < 0)
+                       new2old[n++] = i;
+       }
+       start_row = n;
+       //  Place the atoms within the selection
+       for (i = 0; i < count; i++) {
+               new2old[n++] = IntGroupGetNthPoint(sel, i);
+       }
+       //  Place the remaining atoms
+       for (i = row; i < natoms; i++) {
+               if (IntGroupLookupPoint(sel, i) < 0)
+                       new2old[n++] = i;
+       }
+       MolActionCreateAndPerform(mview->mol, gMolActionReorderAtoms, n, new2old);
+       
+       //  Change selection
+       sel = IntGroupNewWithPoints(start_row, count, -1);
+       MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, sel);
+       IntGroupRelease(sel);
+}
+
+IntGroup *
+MainView_selectedMO(MainView *mview)
+{
+       if (mview == NULL || mview->mol == NULL || mview->tableIndex != kMainViewMOTableIndex)
+               return NULL;
+       return MainViewCallback_getTableSelection(mview);  /*  Note: the indices are 0 based  */
+}
+
+       
\ No newline at end of file
diff --git a/MolLib/MainView.h b/MolLib/MainView.h
new file mode 100755 (executable)
index 0000000..a2d32d6
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *  MainView.h
+ *
+ *  Created by Toshi Nagata on 06/07/30.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __mainview_h__
+#define __mainview_h__
+
+#include "MolLib.h"
+
+/*  The OpenGL header location may be different for different platform  */
+#ifdef __WXMAC__
+#include <OpenGL/gl.h>
+#include <OpenGL/glu.h>
+#include <GLUT/vvector.h>
+#else
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glext.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+enum {
+       kShiftKeyMask = 1,
+       kAltKeyMask = 2
+};
+
+struct Molecule;
+struct Trackball;
+struct IntGroup;
+
+struct Label;  /*  A customized data record for drawing text in OpenGL views. Should be defined somewhere in one of the machine-dependent source files. */
+
+typedef struct LabelRecord {
+       Int labelid;      /*  LabelID is 1-based! (0 means "no label")  */
+       struct Label *label;
+       Int idx1, idx2;
+       Vector pos;  /*  Screen position with depth  */
+} LabelRecord;
+
+enum MainViewDraggingMode {
+       kMainViewMovingTrackball = 1,  /* Rotate or scale by trackball */
+       kMainViewSelectingRegion = 2,  /* Selecting a region */
+       kMainViewDraggingSelectedAtoms = 3, /* Dragging selection */
+       kMainViewCreatingBond = 4,     /* Creating a bond */
+};
+
+enum MainViewSliderMode {
+       kSliderRotateBondMode = 1,
+       kSliderRotateXMode = 2,
+       kSliderRotateYMode = 3
+};
+
+enum {
+       kMainViewAtomTableIndex = 0,
+       kMainViewBondTableIndex = 1,
+       kMainViewAngleTableIndex = 2,
+       kMainViewDihedralTableIndex = 3,
+       kMainViewImproperTableIndex = 4,
+       kMainViewParameterTableIndex = 5,
+       kMainViewMOTableIndex = 6
+};
+       
+/*  The custom python classes for MyMolView GUI  */
+typedef struct MainView {
+//     PyObject_HEAD
+       struct Molecule *mol;
+       void *ref;  /*  A platform-dependent pointer to the main view object (or the main window **controller** object)  */
+       void *tableRef;  /*  The table view object  */
+
+       unsigned char isInitialized;
+       int mode;
+       struct Trackball *track;
+    GLdouble modelview_matrix[16];
+    GLdouble projection_matrix[16];
+    GLdouble perspective_vector[4];
+       
+       /*  Camera position and direction in object (Cartesian) coordinate  */
+       Vector camera;  /*  The camera position  */
+       Vector lookat;  /*  The center of the screen  */
+       Vector lookto;  /*  The direction from the camera to the screen center; this is easily derived by normalizing (lookat - camera), but provided for convenience  */
+       Vector up;      /*  The direction up in the screen  */
+
+       float atomRadius; /* Scale the vdW radius by this value */
+       float bondRadius; /* in angstrom */
+       float probabilityScale;
+       float dimension;
+       Byte showUnitCell;
+       Byte showPeriodicBox;
+       Byte showExpandedAtoms;
+       Byte showEllipsoids;
+       Byte showHydrogens;
+       Byte showDummyAtoms;
+       Byte showRotationCenter;
+
+       Int  showGraphite;
+       Int  showPeriodicImage[6];  /* amin, amax, bmin, bmax, cmin, cmax  */
+
+       Byte *visibleFlags;     /*  This is used only as internal cache;
+                                   The attribute of "hidden" atom is set as (ap->exflags & kAtomHiddenFlag).  */
+       Int countHidden;
+       Byte freezeScreen;
+       
+       unsigned char lineMode;     /*  Draw the model with lines  */
+       unsigned char draggingMode; /*  MainViewDraggingMode  */
+       unsigned char isDragging;   /*  Becomes true if mouse moved by a certain amount  */
+       int clickedAtoms[2]; /*  The object under the mouse on mouseDown event. [-1,-1]: nothing, [n,-1]: atom n, [n,m]: bond between atoms n and m  */
+       int modifierFlags;  /* The modifier flags during the dragging operation */
+       float dragStartPos[3];  /* If starting position is on some object, then dragStartPos[2] is the screen z-coordinates of that object. Otherwise, dragStartPos[2] = 0.5.  */
+       float dragEndPos[3];    /* dragEndPos[2] is always == dragStartpos[2]  */
+       Vector tempAtomPos[2];  /* The positions of the atoms forming the temporary bond */
+       int tempAtoms[2];       /* The atoms forming the temporary bond */
+
+       Int pasteCount;         /* Used to offset the pasted fragment when the same fragment is pasted multiple times */
+       Int pasteTimeStamp;     /* A time stamp for the last cut/copied/pasted fragment */
+
+       /*  Rotate fragment  */
+//     int rotateBond[2];      /* The bond along which the fragment is to be rotated  */
+       struct IntGroup *rotateFragment;  /*  The fragment to rotate  */
+       Vector rotateCenter;
+       Vector rotateAxis;
+       Vector *rotateFragmentOldPos; /*  The original positions (malloc'ed pointer)  */
+       
+       /*  Labels  */
+       Int nlabels;
+       LabelRecord *labels;
+       LabelRecord **sortedLabels; /* (LabelRecord *)[nlabels], internally used in drawLabels(). Should be updated when nlabels changes  */
+
+       /*  Table view  */
+       int tableIndex;  /* kMainViewAtomTableIndex, etc.  */
+
+       /*  Caches for the table view; recalculated in MainView_refreshCachedInfo  */
+       struct IntGroup *tableCache;     /* Indices of atoms etc. that are shown in the table */
+       struct IntGroup *tableSelection; /* Selected rows in the table  */
+
+} MainView;
+//extern PyTypeObject MainViewType;
+
+/*  Public functions  */
+MainView *MainView_newMainView(void *ref);
+void MainView_initializeOpenGLView(MainView *mview);
+void MainView_release(MainView *mview);
+void MainView_setMolecule(MainView *mview, struct Molecule *mol);
+void MainView_refreshCachedInfo(MainView *mview);
+int MainView_isAtomHidden(MainView *mview, int index);
+void MainView_getCamera(MainView *mview, Vector *outCamera, Vector *outLookAt, Vector *outUp);
+void MainView_resizeToFit(MainView *mview);
+void MainView_drawModel(MainView *view);
+void MainView_invalidateLabels(MainView *mview);
+void MainView_mouseDown(MainView *view, const float *p, int eventMask);
+void MainView_mouseUp(MainView *view, const float *p, int eventMask, int clickCount);
+void MainView_mouseDragged(MainView *view, const float *p, int eventMask);
+void MainView_mouseMoved(MainView *view, const float *p, int eventMask);
+void MainView_setMode(MainView *mview, int mode);
+int MainView_getMode(const MainView *mview);
+void MainView_attachLabelToAtom(MainView *mview, int index);
+void MainView_detachLabelFromAtom(MainView *mview, int index);
+void MainView_purgeUnusedLabels(MainView *mview);
+void MainView_rotateBySlider(MainView *mview, float angle, int mode, int mouseStatus, int modifierFlags);
+void MainView_selectAll(MainView *mview);
+void MainView_selectFragment(MainView *mview);
+void MainView_selectReverse(MainView *mview);
+void MainView_centerSelection(MainView *mview);
+int MainView_copy(MainView *mview);
+int MainView_cut(MainView *mview);
+int MainView_delete(MainView *mview);
+int MainView_paste(MainView *mview);
+int MainView_pasteParameters(MainView *mview);
+int MainView_copyOrCutParameters(MainView *mview, int flags);
+
+/*  Table view  */
+void MainView_tableTitleForIndex(MainView *mview, int idx, char *buf, int bufsize);
+void MainView_createColumnsForTableAtIndex(MainView *mview, int idx);
+void MainView_refreshTable(MainView *mview);
+int MainView_numberOfRowsInTable(MainView *mview);
+void MainView_valueForTable(MainView *mview, int column, int row, char *buf, int bufsize);
+void MainView_setValueForTable(MainView *mview, int column, int row, const char *buf);
+int MainView_setColorForTable(MainView *mview, int column, int row, float *fg, float *bg);
+void MainView_setSelectionFromTable(MainView *mview);
+int MainView_isTableItemEditable(MainView *mview, int column, int row);
+int MainView_tableType(MainView *mview);
+void MainView_dragTableSelectionToRow(MainView *mview, int row);
+IntGroup *MainView_selectedMO(MainView *mview);
+
+/*  Stubs  */
+/*  These operations should work for the main *view* object  */
+STUB int MainViewCallback_modifierFlags(void *eventRef);
+STUB int MainViewCallback_clickCount(void *eventRef);
+STUB void MainViewCallback_lockFocus(MainView *mview);
+STUB void MainViewCallback_unlockFocus(MainView *mview);
+STUB void MainViewCallback_frame(MainView *mview, float *rect);
+STUB void MainViewCallback_display(MainView *mview);
+STUB void MainViewCallback_setNeedsDisplay(MainView *mview, int flag);
+STUB void MainViewCallback_setKeyboardFocus(MainView *mview);
+STUB int MainViewCallback_mouseCheck(MainView *mview);
+STUB void MainViewCallback_clearLabels(MainView *mview);
+STUB void MainViewCallback_drawLabel(MainView *mview, const float *pos, const char *label);
+STUB void MainViewCallback_drawInfoText(MainView *mview, const char *label);
+STUB void MainViewCallback_selectMatrixCellForMode(MainView *mview, int mode);
+//STUB int MainViewCallback_getTag(MainView *mview);
+STUB MainView *MainViewCallback_viewWithTag(int tag);
+STUB MainView *MainViewCallback_activeView(void);
+STUB MainView *MainViewCallback_newFromFile(const char *fname);
+STUB int MainViewCallback_importFromFile(MainView *mview, const char *fname);
+STUB void MainViewCallback_getFilename(MainView *mview, char *buf, int bufsize);
+STUB void MainViewCallback_moleculeReplaced(MainView *mview, struct Molecule *mol);
+
+STUB struct Label *MainViewCallback_newLabel(MainView *mview, const char *message, float fontsize, const float *forecolor, const float *backcolor); /* colors are rgba */
+STUB void MainViewCallback_releaseLabel(struct Label *label);
+STUB void MainViewCallback_drawLabelAtPoint(struct Label *label, const float *pos);
+STUB void MainViewCallback_labelSize(struct Label *label, float *outSize);
+STUB int MainViewCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize);
+
+/*  Table view  */
+STUB void MainViewCallback_selectTable(MainView *mview, int idx);
+STUB int MainViewCallback_numberOfTableColumns(MainView *mview);
+STUB int MainViewCallback_addTableColumn(MainView *mview, const char *name, int width, int editable);
+STUB int MainViewCallback_removeTableColumnAtIndex(MainView *mview, int idx);
+STUB void MainViewCallback_reloadTableData(MainView *mview);
+STUB void MainViewCallback_setTableSelection(MainView *mview, IntGroup *selection);
+STUB IntGroup *MainViewCallback_getTableSelection(MainView *mview);
+STUB void MainViewCallback_showTable(MainView *mview);
+STUB void MainViewCallback_hideTable(MainView *mview);
+
+/*  Register the type definition  */
+//extern void MainView_register(PyObject *module);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __mainview_h__ */
diff --git a/MolLib/Makefile b/MolLib/Makefile
new file mode 100755 (executable)
index 0000000..5e9994c
--- /dev/null
@@ -0,0 +1,27 @@
+ifdef DESTDIR
+OBJDIR = $(DESTDIR)/MolLib
+else
+DESTDIR = .
+OBJDIR = .
+endif
+
+OBJECTS = IntGroup.o MainView.o MolAction.o Molecule.o \
+  Object.o Dcd.o Parameter.o Trackball.o Types.o Missing.o
+MD_OBJECTS = MDCore.o MDForce.o MDGraphite.o MDPressure.o MDSurface.o
+
+ALL_OBJECTS = $(addprefix $(OBJDIR)/,$(OBJECTS)) $(addprefix $(OBJDIR)/MD/,$(MD_OBJECTS))
+
+$(OBJDIR)/%.o: %.c
+       $(CC) -c $< -o $@ $(CFLAGS)
+
+$(OBJDIR)/MD/%.o: %.c
+       $(CC) -c $< -o $@ $(CFLAGS)
+
+$(DESTDIR)/MolLib.a : $(ALL_OBJECTS)
+       $(AR) $(ARFLAGS) $(DESTDIR)/MolLib.a $(ALL_OBJECTS)
+       ranlib $(DESTDIR)/MolLib.a
+
+clean:
+       rm -f $(OBJDIR)/*.o
+       rm -f $(OBJDIR)/MD/*.o
+       rm -f $(DESTDIR)/*.a
diff --git a/MolLib/Missing.c b/MolLib/Missing.c
new file mode 100755 (executable)
index 0000000..3e5f275
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ *  Missing.c
+ *  Molby
+ */
+
+#pragma mark ====== Below codes are distributed under GPL ======
+
+/*
+ *  Created by Toshi Nagata on 08/11/06.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "Missing.h"
+
+/*
+ *  Utility function
+ *  Filename handling
+ */
+#if __WXMSW__
+#include <windows.h>
+void
+translate_char(char *p, int from, int to)
+{
+       while (*p != 0) {
+               if ((unsigned char)*p == from)
+                       *p = to;
+               p = CharNext(p);
+       }
+}
+#else
+void
+translate_char(char *p, int from, int to)
+{
+}
+#endif
+
+
+#if MISSING_STRSEP
+
+char *
+strpbrk(const char *cs, const char *ct)
+{
+       const char *sc1,*sc2;
+       
+       for (sc1 = cs; *sc1 != '\0'; sc1++) {
+               for (sc2 = ct; *sc2 != '\0'; sc2++) {
+                       if (*sc1 == *sc2)
+                               return (char *)sc1;
+               }
+       }
+       return NULL;
+}
+
+char *
+strsep(char **stringp, const char *delim)
+{
+       char *start = *stringp;
+       char *ptr;
+       
+       if (!start)
+               return NULL;
+       
+       if (!*delim)
+               ptr = start + strlen(start);
+       else {
+               ptr = strpbrk(start, delim);
+               if (!ptr) {
+                       *stringp = NULL;
+                       return start;
+        }
+    }
+       
+       *ptr = '\0';
+       *stringp = ptr + 1;
+       
+       return start;
+}
+
+#endif
+
+#if MISSING_ASPRINTF
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+int
+vasprintf(char **ret, const char *fmt, va_list ap)
+{
+       int size = 128;
+       char *buf;
+       buf = (char *)malloc(size);
+       if (buf == NULL) {
+               *ret = NULL;
+               errno = ENOMEM;
+               return -1;
+       }
+       while (1) {
+               int n = vsnprintf(buf, size, fmt, ap);
+               if (n < 0)
+                       break;
+               if (n >= size) {
+                       size *= 2;
+                       buf = (char *)realloc(buf, size);
+                       if (buf == NULL)
+                               break;
+                       continue;
+               } else {
+                       *ret = buf;
+                       return n;
+               }
+       }
+       *ret = NULL;
+       errno = ENOMEM;
+       return -1;
+}
+
+int
+asprintf(char **ret, const char *fmt, ...)
+{
+       int retval;
+       va_list ap;
+       va_start(ap, fmt);
+       retval = vasprintf(ret, fmt, ap);
+       va_end(ap);
+       return (retval);
+}
+
+#endif /* MISSING_ASPRINTF */
+
+#pragma mark ====== Below codes are distributed under the BSD License =======
+
+#if MISSING_STRTOK_R
+#include <string.h>
+/*-
+ * Copyright (c) 1998 Softweyr LLC.  All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters <wes@softweyr.com>
+ *
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notices, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notices, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by Softweyr LLC, the
+ *      University of California, Berkeley, and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+char *
+strtok_r(char *s, const char *delim, char **last)
+{
+       char *spanp, *tok;
+       int c, sc;
+       
+       if (s == NULL && (s = *last) == NULL)
+               return (NULL);
+       
+       /*
+        * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+        */
+cont:
+       c = *s++;
+       for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
+               if (c == sc)
+                       goto cont;
+       }
+       
+       if (c == 0) {           /* no non-delimiter characters */
+               *last = NULL;
+               return (NULL);
+       }
+       tok = s - 1;
+       
+       /*
+        * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+        * Note that delim must have one NUL; we stop if we see that, too.
+        */
+       for (;;) {
+               c = *s++;
+               spanp = (char *)delim;
+               do {
+                       if ((sc = *spanp++) == c) {
+                               if (c == 0)
+                                       s = NULL;
+                               else
+                                       s[-1] = '\0';
+                               *last = s;
+                               return (tok);
+                       }
+               } while (sc != 0);
+       }
+       /* NOTREACHED */
+}
+#endif /* MISSING_STRTOK_R */
+
+#if MISSING_MERGESORT
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Peter McIlroy.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)merge.c    8.2 (Berkeley) 2/14/94";
+#endif /* LIBC_SCCS and not lint */
+//#include <sys/cdefs.h>
+//__FBSDID("$FreeBSD: src/lib/libc/stdlib/merge.c,v 1.6 2002/03/21 22:48:42 obrien Exp $");
+
+/*
+ * Hybrid exponential search/linear search merge sort with hybrid
+ * natural/pairwise first pass.  Requires about .3% more comparisons
+ * for random data than LSMS with pairwise first pass alone.
+ * It works for objects as small as two bytes.
+ */
+
+#define NATURAL
+#define THRESHOLD 16   /* Best choice for natural merge cut-off. */
+
+/* #define NATURAL to get hybrid natural merge.
+ * (The default is pairwise merging.)
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef unsigned char u_char;
+
+static void setup(u_char *, u_char *, size_t, size_t, int (*)());
+static void insertionsort(u_char *, size_t, size_t, int (*)());
+
+#define ISIZE sizeof(int)
+#define PSIZE sizeof(u_char *)
+#define ICOPY_LIST(src, dst, last)                             \
+do                                                     \
+*(int*)dst = *(int*)src, src += ISIZE, dst += ISIZE;   \
+while(src < last)
+#define ICOPY_ELT(src, dst, i)                                 \
+do                                                     \
+*(int*) dst = *(int*) src, src += ISIZE, dst += ISIZE; \
+while (i -= ISIZE)
+
+#define CCOPY_LIST(src, dst, last)             \
+do                                     \
+*dst++ = *src++;               \
+while (src < last)
+#define CCOPY_ELT(src, dst, i)                 \
+do                                     \
+*dst++ = *src++;               \
+while (i -= 1)
+
+/*
+ * Find the next possible pointer head.  (Trickery for forcing an array
+ * to do double duty as a linked list when objects do not align with word
+ * boundaries.
+ */
+/* Assumption: PSIZE is a power of 2. */
+#define EVAL(p) (u_char **)                                            \
+((u_char *)0 +                                                 \
+(((u_char *)p + PSIZE - 1 - (u_char *) 0) & ~(PSIZE - 1)))
+
+/*
+ * Arguments are as for qsort.
+ */
+int
+mergesort(base, nmemb, size, cmp)
+void *base;
+size_t nmemb;
+size_t size;
+int (*cmp)(const void *, const void *);
+{
+       int i, sense;
+       int big, iflag;
+       u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2;
+       u_char *list2, *list1, *p2, *p, *last, **p1;
+       
+       if (size < PSIZE / 2) {         /* Pointers must fit into 2 * size. */
+               errno = EINVAL;
+               return (-1);
+       }
+       
+       if (nmemb == 0)
+               return (0);
+       
+       /*
+        * XXX
+        * Stupid subtraction for the Cray.
+        */
+       iflag = 0;
+       if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE))
+               iflag = 1;
+       
+       if ((list2 = malloc(nmemb * size + PSIZE)) == NULL)
+               return (-1);
+       
+       list1 = base;
+       setup(list1, list2, nmemb, size, cmp);
+       last = list2 + nmemb * size;
+       i = big = 0;
+       while (*EVAL(list2) != last) {
+           l2 = list1;
+           p1 = EVAL(list1);
+           for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) {
+               p2 = *EVAL(p2);
+               f1 = l2;
+               f2 = l1 = list1 + (p2 - list2);
+               if (p2 != last)
+                       p2 = *EVAL(p2);
+               l2 = list1 + (p2 - list2);
+               while (f1 < l1 && f2 < l2) {
+                       if ((*cmp)(f1, f2) <= 0) {
+                               q = f2;
+                               b = f1, t = l1;
+                               sense = -1;
+                       } else {
+                               q = f1;
+                               b = f2, t = l2;
+                               sense = 0;
+                       }
+                       if (!big) {     /* here i = 0 */
+                                       while ((b += size) < t && cmp(q, b) >sense)
+                                       if (++i == 6) {
+                                               big = 1;
+                                               goto EXPONENTIAL;
+                                       }
+                       } else {
+                               EXPONENTIAL:                    for (i = size; ; i <<= 1)
+                                       if ((p = (b + i)) >= t) {
+                                               if ((p = t - size) > b &&
+                                                   (*cmp)(q, p) <= sense)
+                                                       t = p;
+                                               else
+                                                       b = p;
+                                               break;
+                                       } else if ((*cmp)(q, p) <= sense) {
+                                               t = p;
+                                               if (i == size)
+                                                       big = 0;
+                                               goto FASTCASE;
+                                       } else
+                                               b = p;
+                                       while (t > b+size) {
+                                       i = (((t - b) / size) >> 1) * size;
+                                       if ((*cmp)(q, p = b + i) <= sense)
+                                               t = p;
+                                       else
+                                               b = p;
+                               }
+                               goto COPY;
+                               FASTCASE:                       while (i > size)
+                                       if ((*cmp)(q,
+                                                          p = b + (i >>= 1)) <= sense)
+                                               t = p;
+                                       else
+                                               b = p;
+                               COPY:                           b = t;
+                       }
+                       i = size;
+                       if (q == f1) {
+                               if (iflag) {
+                                       ICOPY_LIST(f2, tp2, b);
+                                       ICOPY_ELT(f1, tp2, i);
+                               } else {
+                                       CCOPY_LIST(f2, tp2, b);
+                                       CCOPY_ELT(f1, tp2, i);
+                               }
+                       } else {
+                               if (iflag) {
+                                       ICOPY_LIST(f1, tp2, b);
+                                       ICOPY_ELT(f2, tp2, i);
+                               } else {
+                                       CCOPY_LIST(f1, tp2, b);
+                                       CCOPY_ELT(f2, tp2, i);
+                               }
+                       }
+               }
+               if (f2 < l2) {
+                       if (iflag)
+                               ICOPY_LIST(f2, tp2, l2);
+                       else
+                               CCOPY_LIST(f2, tp2, l2);
+               } else if (f1 < l1) {
+                       if (iflag)
+                               ICOPY_LIST(f1, tp2, l1);
+                       else
+                               CCOPY_LIST(f1, tp2, l1);
+               }
+               *p1 = l2;
+           }
+           tp2 = list1;        /* swap list1, list2 */
+           list1 = list2;
+           list2 = tp2;
+           last = list2 + nmemb*size;
+       }
+       if (base == list2) {
+               memmove(list2, list1, nmemb*size);
+               list2 = list1;
+       }
+       free(list2);
+       return (0);
+}
+
+#define        swap(a, b) {                                    \
+s = b;                                 \
+i = size;                              \
+do {                                   \
+tmp = *a; *a++ = *s; *s++ = tmp; \
+} while (--i);                         \
+a -= size;                             \
+}
+#define reverse(bot, top) {                            \
+s = top;                                       \
+do {                                           \
+i = size;                              \
+do {                                   \
+tmp = *bot; *bot++ = *s; *s++ = tmp; \
+} while (--i);                         \
+s -= size2;                            \
+} while(bot < s);                              \
+}
+
+/*
+ * Optional hybrid natural/pairwise first pass.  Eats up list1 in runs of
+ * increasing order, list2 in a corresponding linked list.  Checks for runs
+ * when THRESHOLD/2 pairs compare with same sense.  (Only used when NATURAL
+ * is defined.  Otherwise simple pairwise merging is used.)
+ */
+void
+setup(list1, list2, n, size, cmp)
+size_t n, size;
+int (*cmp)(const void *, const void *);
+u_char *list1, *list2;
+{
+       int i, length, size2, tmp, sense;
+       u_char *f1, *f2, *s, *l2, *last, *p2;
+       
+       size2 = size*2;
+       if (n <= 5) {
+               insertionsort(list1, n, size, cmp);
+               *EVAL(list2) = (u_char*) list2 + n*size;
+               return;
+       }
+       /*
+        * Avoid running pointers out of bounds; limit n to evens
+        * for simplicity.
+        */
+       i = 4 + (n & 1);
+       insertionsort(list1 + (n - i) * size, i, size, cmp);
+       last = list1 + size * (n - i);
+       *EVAL(list2 + (last - list1)) = list2 + n * size;
+       
+#ifdef NATURAL
+       p2 = list2;
+       f1 = list1;
+       sense = (cmp(f1, f1 + size) > 0);
+       for (; f1 < last; sense = !sense) {
+               length = 2;
+               /* Find pairs with same sense. */
+               for (f2 = f1 + size2; f2 < last; f2 += size2) {
+                       if ((cmp(f2, f2+ size) > 0) != sense)
+                               break;
+                       length += 2;
+               }
+               if (length < THRESHOLD) {               /* Pairwise merge */
+                       do {
+                               p2 = *EVAL(p2) = f1 + size2 - list1 + list2;
+                               if (sense > 0)
+                                       swap (f1, f1 + size);
+                       } while ((f1 += size2) < f2);
+               } else {                                /* Natural merge */
+                       l2 = f2;
+                       for (f2 = f1 + size2; f2 < l2; f2 += size2) {
+                               if ((cmp(f2-size, f2) > 0) != sense) {
+                                       p2 = *EVAL(p2) = f2 - list1 + list2;
+                                       if (sense > 0)
+                                               reverse(f1, f2-size);
+                                       f1 = f2;
+                               }
+                       }
+                       if (sense > 0)
+                               reverse (f1, f2-size);
+                       f1 = f2;
+                       if (f2 < last || cmp(f2 - size, f2) > 0)
+                               p2 = *EVAL(p2) = f2 - list1 + list2;
+                       else
+                               p2 = *EVAL(p2) = list2 + n*size;
+               }
+       }
+#else          /* pairwise merge only. */
+       for (f1 = list1, p2 = list2; f1 < last; f1 += size2) {
+               p2 = *EVAL(p2) = p2 + size2;
+               if (cmp (f1, f1 + size) > 0)
+                       swap(f1, f1 + size);
+       }
+#endif /* NATURAL */
+}
+
+/*
+ * This is to avoid out-of-bounds addresses in sorting the
+ * last 4 elements.
+ */
+static void
+insertionsort(a, n, size, cmp)
+u_char *a;
+size_t n, size;
+int (*cmp)(const void *, const void *);
+{
+       u_char *ai, *s, *t, *u, tmp;
+       int i;
+       
+       for (ai = a+size; --n >= 1; ai += size)
+               for (t = ai; t > a; t -= size) {
+                       u = t - size;
+                       if (cmp(u, t) <= 0)
+                               break;
+                       swap(u, t);
+               }
+}
+#endif /* MISSING_MERGESORT */
diff --git a/MolLib/Missing.h b/MolLib/Missing.h
new file mode 100755 (executable)
index 0000000..87817b1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  Missing.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/11/06.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MISSING_H__
+#define __MISSING_H__
+
+#ifdef __WXMSW__
+#define MISSING_STRSEP 1
+#define MISSING_ASPRINTF 1
+#define MISSING_STRTOK_R 1
+#define MISSING_MERGESORT 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void translate_char(char *p, int from, int to);
+
+#if MISSING_STRSEP
+char *strpbrk(const char *cs, const char *ct);
+char *strsep(char **stringp, const char *delim);
+#endif
+
+#if MISSING_ASPRINTF
+#include <stdarg.h>
+int asprintf(char **ret, const char *format, ...);
+int vasprintf(char **ret, const char *format, va_list ap);
+#endif /* MISSING_ASPRINTF */
+
+#if MISSING_STRTOK_R
+char *strtok_r(char *str, const char *sep, char **lasts);
+#endif /* MISSING_STRTOK_R */
+
+#if MISSING_MERGESORT
+#include <stdlib.h>
+int mergesort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
+#endif /* MISSING_MERGESORT */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MISSING_H__ */
diff --git a/MolLib/MolAction.c b/MolLib/MolAction.c
new file mode 100644 (file)
index 0000000..1c861cb
--- /dev/null
@@ -0,0 +1,1270 @@
+/*
+ *  MolAction.c
+ *
+ *  Created by Toshi Nagata on 07/06/23.
+ *  Copyright 2007-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "Ruby_bind/Molby.h"  /*  Support Ruby binding  */
+
+#include "IntGroup.h"
+#include "Molecule.h"
+#include "MolAction.h"
+#include "MD/MDCore.h"
+
+#include <stdarg.h>
+#include <ctype.h>
+
+const char *gMolActionNone            = "none";
+const char *gMolActionAddAnAtom       = "addAtom:Ai;i";
+const char *gMolActionDeleteAnAtom    = "deleteAtom:i";
+const char *gMolActionMergeMolecule   = "mergeMol:MG";
+const char *gMolActionUnmergeMolecule = "unmergeMol:G";
+const char *gMolActionAddBonds        = "addBonds:I";
+const char *gMolActionDeleteBonds     = "deleteBonds:I";
+const char *gMolActionAddAngles       = "addAngles:IG";
+const char *gMolActionDeleteAngles    = "deleteAngles:G";
+const char *gMolActionAddDihedrals    = "addDihedrals:IG";
+const char *gMolActionDeleteDihedrals = "deleteDihedrals:G";
+const char *gMolActionAddImpropers    = "addImpropers:IG";
+const char *gMolActionDeleteImpropers = "deleteImpropers:G";
+/* const char *gMolActionReplaceTables   = "replaceTables:III"; */
+const char *gMolActionTranslateAtoms  = "translateAtoms:vG";
+const char *gMolActionRotateAtoms     = "rotate:vdvG";
+const char *gMolActionTransformAtoms  = "transform:tG";
+const char *gMolActionSetAtomPositions = "atomPosition:GV";
+const char *gMolActionInsertFrames    = "insertFrames:GV";
+const char *gMolActionRemoveFrames    = "removeFrames:G";
+const char *gMolActionSetSelection    = "selection:G";
+const char *gMolActionChangeResidueNumber = "changeResSeq:Gi";
+const char *gMolActionChangeResidueNumberForUndo = "changeResSeqForUndo:GIi";
+const char *gMolActionChangeResidueNames = "changeResNames:IC";
+const char *gMolActionOffsetResidueNumbers = "offsetResSeq:Gii";
+const char *gMolActionChangeNumberOfResidues = "changeNres:i";
+const char *gMolActionReorderAtoms    = "reorderAtoms:I";
+const char *gMolActionExpandBySymmetry = "expandSym:Giiii";
+const char *gMolActionDeleteSymmetryOperation = "deleteSymop";
+const char *gMolActionAddSymmetryOperation = "addSymop:t";
+const char *gMolActionSetCell         = "setCell:Di";
+const char *gMolActionSetBox          = "setBox:vvvvi";
+const char *gMolActionClearBox        = "clearBox";
+/*const char *gMolActionSetParameterAttribute = "setParameterAttr:iirri"; */
+const char *gMolActionAddParameters   = "addParameters:iGU";
+const char *gMolActionDeleteParameters = "deleteParameters:iG";
+const char *gMolActionCartesianToXtal  = "cartesianToXtal";
+const char *gMolActionXtalToCartesian  = "xtalToCartesian";
+
+/*  A Ruby array to retain objects used in MolActionArg  */
+static VALUE sMolActionArgValues = Qfalse;
+
+/*  Action arguments  */
+/*  (Simple types)  i: Int, d: double, s: string, v: Vector, t: Transform, u: UnionPar
+ (Array types)   I: array of Int, D: array of double, V: array of Vector, C: array of char, T: array of Transform, U: array of UnionPars
+ (Complex types) M: Molecule, G: IntGroup, A: Atom
+ (Ruby value)    b: Ruby boolean, r: Ruby object, R: an array of Ruby object (a Ruby array)
+ (Return value from Ruby script) i, d, s, v, t + 0x80 */
+typedef struct MolActionArg {
+       Byte type;
+       union {
+               Int ival;
+               double dval;
+               struct {  /*  Array type: the size of the element is defined by the argument type  */
+                       Int nitems; /* Number of items */
+                       void *ptr;
+               } arval;
+               struct IntGroup *igval; /* The record is retained but not duplicated */
+               struct Molecule *mval; /* The record is retained but not duplicated */
+               struct Atom *aval; /* The value is duplicated, so any pointer can be passed */
+               VALUE vval;        /* The value is retained in sMolActionArgValues  */
+               void *pval;        /* Store address for the return value; only meaningful in performing Ruby script  */
+       } u;
+} MolActionArg;
+
+/*  For debug  */
+/* static int sMolActionCount = 0; */
+
+/*  Create a new MolAction with the given arguments.
+ *  Note: If the argument is an array type, it is represented by an integer (number
+ *  of items) followed by the pointer.  */
+MolAction *
+MolActionNewArgv(const char *name, va_list ap)
+{
+       MolAction *action;
+       const char *p;
+
+       action = (MolAction *)malloc(sizeof(MolAction));
+       if (action == NULL)
+               goto low_memory;
+       memset(action, 0, sizeof(MolAction));
+       action->refCount = 1;
+       action->frame = -1;
+       action->name = strdup(name);
+
+       /*  For debug  */
+/*     fprintf(stderr, "MolAction %p created, count = %d\n", action, ++sMolActionCount); */
+       
+       /*  Handle arguments  */
+       p = strchr(name, ':');
+       if (p == NULL)
+               return action;
+       p++;
+
+       while (*p) {
+               MolActionArg arg;
+               memset(&arg, 0, sizeof(MolActionArg));
+               arg.type = *p++;
+               switch (arg.type) {
+                       case 'i':
+                               arg.u.ival = va_arg(ap, Int);
+                               break;
+                       case 'd':
+                               arg.u.dval = va_arg(ap, double);
+                               break;
+                       case 'I':
+                       case 'D':
+                       case 'C':
+                       case 's':
+                       case 'V':
+                       case 'v':
+                       case 'T':
+                       case 't':
+                       case 'U':
+                       case 'u': {
+                               /*  Array type of some kind  */
+                               void *ptr;
+                               Int nitems, itemsize, allocsize;
+                               if (arg.type != 's' && arg.type != 'v' && arg.type != 't' && arg.type != 'u')
+                                       nitems = va_arg(ap, Int);
+                               ptr = va_arg(ap, void *);
+                               switch (arg.type) {
+                                       case 'I': itemsize = sizeof(Int); break;
+                                       case 'D': itemsize = sizeof(double); break;
+                                       case 'C':
+                                       case 's': itemsize = sizeof(char); break;
+                                       case 'V':
+                                       case 'v': itemsize = sizeof(Vector); break;
+                                       case 'T':
+                                       case 't': itemsize = sizeof(Transform); break;
+                                       case 'U':
+                                       case 'u': itemsize = sizeof(UnionPar); break;
+                               }
+                               if (arg.type == 's')
+                                       nitems = (ptr == NULL ? 0 : strlen((char *)ptr));
+                               else if (arg.type == 'v' || arg.type == 't' || arg.type == 'u')
+                                       nitems = 1;
+                               arg.u.arval.nitems = nitems;
+                               allocsize = itemsize * nitems + (arg.type == 's'); /* +1 for string type */
+                               if (allocsize == 0)
+                                       arg.u.arval.ptr == NULL;
+                               else {
+                                       arg.u.arval.ptr = calloc(1, allocsize);
+                                       if (arg.u.arval.ptr == NULL)
+                                               goto low_memory;
+                                       memmove(arg.u.arval.ptr, ptr, itemsize * nitems);
+                               }
+                               break;
+                       }
+/*                     case 's':
+                               arg.u.sval = va_arg(ap, char *);
+                               arg.u.sval = strdup(arg.u.sval);
+                               break; */
+/*                     case 'v': {
+                               Vector *vp = va_arg(ap, Vector *);
+                               arg.u.vval = (Vector *)malloc(sizeof(Vector));
+                               if (arg.u.vval == NULL)
+                                       goto low_memory;
+                               *(arg.u.vval) = *vp;
+                               break;
+                       } */
+                       case 'G':
+                               arg.u.igval = va_arg(ap, IntGroup *);
+                               if (arg.u.igval != NULL)
+                                       IntGroupRetain(arg.u.igval);
+                               break;
+                       case 'M':
+                               arg.u.mval = va_arg(ap, Molecule *);
+                               MoleculeRetain(arg.u.mval);
+                               break;
+                       case 'A': {
+                               Atom *aval = va_arg(ap, Atom *);
+                               arg.u.aval = (Atom *)malloc(gSizeOfAtomRecord);
+                               if (arg.u.aval == NULL)
+                                       goto low_memory;
+                               AtomDuplicate(arg.u.aval, aval);
+                               break;
+                       }
+                       case 'r':
+                       case 'R': {
+                               int rtype;
+                               arg.u.vval = va_arg(ap, VALUE);
+                               rtype = TYPE(arg.u.vval);
+                               if (rtype != T_NIL && rtype != T_FIXNUM && rtype != T_SYMBOL && rtype != T_TRUE && rtype != T_FALSE) {
+                                       if (sMolActionArgValues == Qfalse) {
+                                               sMolActionArgValues = rb_ary_new();
+                                               rb_global_variable(&sMolActionArgValues);
+                                       }
+                                       rb_ary_push(sMolActionArgValues, arg.u.vval);
+                               }
+                               break;
+                       }
+                       case 'b':
+                               arg.u.vval = (va_arg(ap, Int) ? Qtrue : Qfalse);
+                               break;
+                       case ';':
+                               if (*p == 'i' || *p == 'd' || *p == 's' || *p == 'v' || *p == 't' || *p == 'r') {
+                                       arg.type = (*p | 0x80);
+                                       p++;
+                                       arg.u.pval = va_arg(ap, void *);
+                               } else {
+                                       fprintf(stderr, "Internal error: unknown return-type \'%c\' in NewMolAction()", *p);
+                               }
+                               break;
+                       default:
+                               fprintf(stderr, "Internal error: unknown argument type \'%c\' in NewMolAction()", arg.type);
+                               break;
+               }
+               if (AssignArray(&action->args, &action->nargs, sizeof(arg), action->nargs, &arg) == NULL)
+                       goto low_memory;
+       }
+       return action;
+                               
+  low_memory:
+       if (action != NULL)
+               free(action);
+       Panic("Low memory during creation of action record");
+       return NULL;  /* Not reached */
+}
+
+MolAction *
+MolActionNew(const char *name, ...)
+{
+       va_list ap;
+       MolAction *retval;
+       va_start(ap, name);
+       retval = MolActionNewArgv(name, ap);
+       va_end(ap);
+       return retval;
+}
+
+MolAction *
+MolActionRetain(MolAction *action)
+{
+       if (action != NULL)
+               action->refCount++;
+       return action;
+}
+
+void
+MolActionRelease(MolAction *action)
+{
+       int i;
+       if (action == NULL)
+               return;
+       if (--action->refCount > 0)
+               return;
+       if (action->name != NULL)
+               free((void *)action->name);
+       for (i = 0; i < action->nargs; i++) {
+               MolActionArg *argp = action->args + i;
+               switch (argp->type) {
+                       case 'i':
+                       case 'd':
+                       case 'b':
+                               break;
+                       case 'I':
+                       case 'D':
+                       case 'C':
+                       case 's':
+                       case 'V':
+                       case 'v':
+                       case 'T':
+                       case 't':
+                       case 'U':
+                       case 'u':
+                               if (argp->u.arval.ptr != NULL)
+                                       free(argp->u.arval.ptr);
+                               break;
+                       
+               /*      case 'a':
+                               if (argp->u.iaval != NULL)
+                                       free(argp->u.iaval);
+                               break;
+                       case 's':
+                               if (argp->u.sval != NULL)
+                                       free(argp->u.sval);
+                               break;
+                       case 'v':
+                               if (argp->u.vval != NULL)
+                                       free(argp->u.vval);
+                               break; */
+                       case 'G':
+                               if (argp->u.igval != NULL)
+                                       IntGroupRelease(argp->u.igval);
+                               break;
+                       case 'M':
+                               MoleculeRelease(argp->u.mval);
+                               break;
+                       case 'A':
+                               if (argp->u.aval != NULL) {
+                                       AtomClean(argp->u.aval);
+                                       free(argp->u.aval);
+                               }
+                               break;
+                       case 'r':
+                       case 'R': {
+                               int rtype = TYPE(argp->u.vval);
+                               if (rtype != T_NIL && rtype != T_FIXNUM && rtype != T_SYMBOL && rtype != T_TRUE && rtype != T_FALSE) {
+                                       /*  Look for the same value in sMolActionArgValues and remove it  */
+                                       if (sMolActionArgValues != Qfalse) {
+                                               VALUE *ptr = RARRAY_PTR(sMolActionArgValues);
+                                               int n = RARRAY_LEN(sMolActionArgValues);
+                                               while (--n >= 0) {
+                                                       if (ptr[n] == argp->u.vval) {
+                                                               rb_ary_delete_at(sMolActionArgValues, n);
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+                               break;
+                       }
+                       default:
+                               break;
+               }
+       }
+       if (action->args != NULL)
+               free(action->args);
+       free(action);
+
+       /*  For debug  */
+/*     fprintf(stderr, "MolAction %p disposed, count = %d\n", action, --sMolActionCount);      */
+}
+
+void
+MolActionSetFrame(MolAction *action, int frame)
+{
+       if (action != NULL)
+               action->frame = frame;
+}
+
+typedef struct MolRubyActionInfo {
+       Molecule *mol;
+       MolAction *action;
+       VALUE receiver;
+       ID method_id;
+       VALUE ary;
+       char enable_interrupt;
+       char return_type;
+       void *return_ptr;
+} MolRubyActionInfo;
+
+static VALUE
+s_MolActionToRubyArguments(VALUE vinfo)
+{
+       MolRubyActionInfo *info = (MolRubyActionInfo *)vinfo;
+       VALUE val;
+       int i, argc;
+       char *s, *p;
+
+       argc = info->action->nargs - 1;
+       info->return_type = 0;
+       info->return_ptr = NULL;
+       info->ary = rb_ary_new2(argc);
+       for (i = 1; i <= argc; i++) {
+               MolActionArg *argp = &(info->action->args[i]);
+               if (argp->type & 0x80) {
+                       /*  Return value specified  */
+                       info->return_type = (argp->type & 0x7f);
+                       info->return_ptr = argp->u.pval;
+                       break;  /*  This should be the last argument  */
+               }
+               switch (argp->type) {
+                       case 'i':
+                               val = INT2NUM(argp->u.ival);
+                               break;
+                       case 'd':
+                               val = rb_float_new(argp->u.dval);
+                               break;
+                       case 's':
+                       case 'C':
+                               val = rb_str_new((char *)argp->u.arval.ptr, argp->u.arval.nitems);
+                               break;
+                       case 'v':
+                               val = ValueFromVector((Vector *)argp->u.arval.ptr);
+                               break;
+                       case 't':
+                               val = ValueFromTransform((Transform *)argp->u.arval.ptr);
+                               break;
+                       case 'I':
+                       case 'D':
+                       case 'V':
+                       case 'T': {
+                               int j;
+                               val = rb_ary_new();
+                               for (j = 0; j < argp->u.arval.nitems; j++) {
+                                       VALUE val1;
+                                       void *ptr = argp->u.arval.ptr;
+                                       switch (argp->type) {
+                                               case 'I': val1 = INT2NUM(((Int *)ptr)[j]); break;
+                                               case 'D': val1 = rb_float_new(((double *)ptr)[j]); break;
+                                               case 'V': val1 = ValueFromVector(&((Vector *)ptr)[j]); break;
+                                               case 'T': val1 = ValueFromTransform(&((Transform *)ptr)[j]); break;
+                                       }
+                                       rb_ary_push(val, val1);
+                               }
+                               break;
+                       }
+                       case 'G':
+                               val = ValueFromIntGroup(info->action->args[i].u.igval);
+                               break;
+                       case 'M':
+                               val = ValueFromMolecule(info->action->args[i].u.mval);
+                               break;
+                       case 'r':
+                       case 'b':
+                               val = (VALUE)info->action->args[i].u.vval;
+                               break;
+                       case 'R':
+                               rb_ary_concat(info->ary, (VALUE)info->action->args[i].u.vval);
+                               continue;  /*  Skip rb_ary_push()  */
+                       default:
+                               rb_raise(rb_eArgError, "Unsupported argument type '%c'", info->action->args[i].type);
+               }
+               rb_ary_push(info->ary, val);
+       }
+
+       if (info->mol != NULL) {
+               info->receiver = ValueFromMolecule(info->mol);
+       } else {
+               info->receiver = rb_cMolecule;  /*  Assume class method  */
+       }
+       
+       s = info->action->args[0].u.arval.ptr;
+       for (p = s; *p != 0; p++) {
+               if (isalpha(*p) || *p == '_' || (p > s && isdigit(*p)) || (p[1] == 0 && (*p == '?' || *p == '!')))
+                       continue;
+               break;
+       }
+       
+       if (*p == 0) {
+               /*  Can be a method name  */
+               info->method_id = rb_intern(s);
+       } else {
+               /*  Cannot be a method name: try to create a proc object and call it  */
+               VALUE procstr = rb_str_new2(s);
+               VALUE proc = rb_obj_instance_eval(1, &procstr, info->receiver);
+               info->receiver = proc;
+               info->method_id = rb_intern("call");
+       }
+
+       return Qnil;
+}
+
+static void
+s_MolActionStoreReturnValue(MolRubyActionInfo *info, VALUE val)
+{
+       if (info->return_type != 0 && info->return_ptr != NULL) {
+               switch (info->return_type) {
+                       case 'i': *((Int *)(info->return_ptr)) = NUM2INT(rb_Integer(val)); break;
+                       case 'd': *((Double *)(info->return_ptr)) = NUM2DBL(rb_Float(val)); break;
+                       case 's': *((char **)(info->return_ptr)) = strdup(StringValuePtr(val)); break;
+                       case 'v': VectorFromValue(val, (Vector *)(info->return_ptr)); break;
+                       case 't': TransformFromValue(val, (Transform *)(info->return_ptr)); break;
+                       case 'r': *((RubyValue *)(info->return_ptr)) = (RubyValue)val; break;
+               }
+       }
+}
+
+static VALUE
+s_MolActionPerformRubyScript(VALUE vinfo)
+{
+/*     VALUE save_interrupt_flag; */
+       VALUE val;
+       MolRubyActionInfo *info = (MolRubyActionInfo *)vinfo;
+/*     if (info->enable_interrupt)
+               save_interrupt_flag = Ruby_SetInterruptFlag(Qtrue); */
+       val = rb_funcall2(info->receiver, info->method_id, RARRAY_LEN(info->ary), RARRAY_PTR(info->ary));
+       s_MolActionStoreReturnValue(info, val);
+/*     if (info->enable_interrupt)
+               Ruby_SetInterruptFlag(save_interrupt_flag); */
+       return val;
+}
+
+static void
+sMolActionUpdateSelectionAndParameterNumbering(Molecule *mol, const IntGroup *ig, int is_insert)
+{
+       int i, j, n, old_natoms;
+       IntGroup *sel, *orig_atoms, *ig3;
+       UnionPar *up;
+       MolAction *act;
+
+       if (ig == NULL || (n = IntGroupGetCount(ig)) == 0)
+               return;
+       
+       /*  Insert: orig_atoms = set of original atoms in new indices  */
+       /*  Delete: orig_atoms = set of remaining atoms in old indices  */
+       orig_atoms = IntGroupNew();
+       old_natoms = (is_insert ? mol->natoms - n : mol->natoms + n);
+       ig3 = IntGroupNewWithPoints(0, (is_insert ? mol->natoms : old_natoms), -1);
+       IntGroupXor(ig, ig3, orig_atoms);
+       IntGroupRelease(ig3);
+
+       /*  Update selection  */
+       sel = MoleculeGetSelection(mol);
+       if (sel != NULL && IntGroupGetCount(sel) > 0) {
+               IntGroup *sel2 = IntGroupNew();
+               if (is_insert)
+                       IntGroupConvolute(sel, orig_atoms, sel2);  /*  Renumber  */
+               else {
+                       IntGroup *selx = IntGroupNew();
+                       IntGroupIntersect(sel, orig_atoms, selx);
+                       IntGroupDeconvolute(selx, orig_atoms, sel2);
+                       IntGroupRelease(selx);
+               }
+               IntGroupRetain(sel);  /*  To avoid being released during MoleculeSetSelection()  */
+               MoleculeSetSelection(mol, sel2);
+               if (IntGroupGetCount(sel) != IntGroupGetCount(sel2)) {
+                       /*  Register undo action  */
+                       act = MolActionNew(gMolActionSetSelection, sel);
+                       MolActionCallback_registerUndo(mol, act);
+                       MolActionRelease(act);
+               }
+               IntGroupRelease(sel2);
+               IntGroupRelease(sel);
+       }
+       
+       /*  Update parameters  */
+       if (mol->par != NULL) {
+               Int *ip = (Int *)malloc(sizeof(Int) * old_natoms);
+               if (is_insert) {
+                       for (i = 0; i < old_natoms; i++)
+                               ip[i] = IntGroupGetNthPoint(orig_atoms, i);
+               } else {
+                       for (i = 0; i < old_natoms; i++)
+                               ip[i] = IntGroupLookupPoint(orig_atoms, i);
+               }
+               for (i = kFirstParType; i <= kLastParType; i++) {
+                       UnionPar usave;
+                       UnionPar *upary = NULL;
+                       Int count_upary = 0;
+                       if (!is_insert)
+                               ig3 = IntGroupNew();
+                       for (j = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, i, j)) != NULL; j++) {
+                               usave = *up;
+                               if (ParameterRenumberAtoms(i, up, old_natoms, ip) && !is_insert) {
+                                       IntGroupAdd(ig3, j, 1);  /*  This parameter is to be restored on undo  */
+                                       AssignArray(&upary, &count_upary, sizeof(UnionPar), count_upary, &usave);
+                               }
+                       }
+                       if (count_upary > 0) {
+                               /*  Register undo for modifying parameters  */
+                               act = MolActionNew(gMolActionAddParameters, i, ig3, count_upary, upary);
+                               MolActionCallback_registerUndo(mol, act);
+                               MolActionRelease(act);
+                               act = MolActionNew(gMolActionDeleteParameters, i, ig3);
+                               MolActionCallback_registerUndo(mol, act);
+                               MolActionRelease(act);
+                               free(upary);
+                       }
+                       if (!is_insert)
+                               IntGroupRelease(ig3);
+               }
+               free(ip);
+       }
+       IntGroupRelease(orig_atoms);
+}
+
+int
+MolActionPerform(Molecule *mol, MolAction *action)
+{
+       int n1, result, natoms;
+       Molecule *mol2;
+       IntGroup *ig;
+       MolAction *act2;
+       int needsSymmetryAmendment = 0;
+       int needsRebuildMDArena = 0;
+       Int *ip;
+       Vector *vp, v;
+       n1 = 0;
+       
+       if (action == NULL)
+               return -1;
+       
+       if (action->frame >= 0 && mol->cframe != action->frame)
+               MoleculeSelectFrame(mol, action->frame, 1);
+
+       /*  Ruby script execution  */
+       if (strncmp(action->name, kMolActionPerformScript, strlen(kMolActionPerformScript)) == 0) {
+               MolRubyActionInfo info;
+               memset(&info, 0, sizeof(info));
+               if (gMolbyIsCheckingInterrupt) {
+                       MolActionAlertRubyIsRunning();
+                       return -1;
+               }
+       /*      gMolbyBacktrace = Qnil; */
+               info.mol = mol; /*  May be NULL  */
+               info.action = action;
+               gMolbyRunLevel++;
+               rb_protect(s_MolActionToRubyArguments, (VALUE)&info, &result);
+               gMolbyRunLevel--;
+               if (result == 0) {
+                       VALUE save_interrupt;
+                       MyAppCallback_beginUndoGrouping();
+               /*      info.enable_interrupt = 1; */
+                       save_interrupt = Ruby_SetInterruptFlag(Qtrue);
+                       gMolbyRunLevel++;
+                       rb_protect(s_MolActionPerformRubyScript, (VALUE)&info, &result);
+                       gMolbyRunLevel--;
+                       Ruby_SetInterruptFlag(save_interrupt);
+                       MyAppCallback_endUndoGrouping();
+               }
+               if (result != 0) {
+                       Molby_showError(result);
+               }
+               MyAppCallback_hideProgressPanel();  /*  In case when the progress panel is still onscreen */\v
+               return (result == 0 ? 0 : -1);
+       }
+       
+       if (mol == NULL)
+               return -1;
+       natoms = mol->natoms;
+       
+       if (strcmp(action->name, gMolActionAddAnAtom) == 0) {
+               n1 = MoleculeCreateAnAtom(mol, action->args[0].u.aval, action->args[1].u.ival);
+               if ((ip = action->args[2].u.pval) != NULL)
+                       *ip = n1;
+               if (n1 < 0)
+                       return -1;
+               ig = IntGroupNewWithPoints(n1, 1, -1);
+
+               /*  Update selection  */
+               sMolActionUpdateSelectionAndParameterNumbering(mol, ig, 1);
+
+               act2 = MolActionNew(gMolActionDeleteAnAtom, n1);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionMergeMolecule) == 0) {
+               IntGroup *ig2;
+               Molecule *mol2;
+               ig = action->args[1].u.igval;
+               mol2 = action->args[0].u.mval;
+               if (ig != NULL) {
+                       ig2 = ig;
+                       IntGroupRetain(ig2);
+               } else {
+                       ig2 = IntGroupNew();
+                       IntGroupAdd(ig2, mol->natoms, mol2->natoms);
+               }
+               /*  Calculate the offset for the residue number  */
+               {
+                       int minreg, maxreg;
+                       Atom *ap;
+                       maxreg = -1;
+                       for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
+                               if (ap->resSeq > maxreg)
+                                       maxreg = ap->resSeq;
+                       }
+                       minreg = ATOMS_MAX_NUMBER;
+                       for (n1 = 0, ap = mol2->atoms; n1 < mol2->natoms; n1++, ap = ATOM_NEXT(ap)) {
+                               if (ap->resSeq < minreg)
+                                       minreg = ap->resSeq;
+                       }
+                       if (maxreg < 0)
+                               maxreg = 0;
+                       if (minreg == ATOMS_MAX_NUMBER)
+                               minreg = 0;
+                       if (maxreg >= minreg)
+                               n1 = maxreg - minreg + 1;
+                       else n1 = 0;
+               }
+               if ((result = MoleculeMerge(mol, mol2, ig, n1)) != 0)
+                       return result;
+
+               sMolActionUpdateSelectionAndParameterNumbering(mol, ig, 1);
+
+               act2 = MolActionNew(gMolActionUnmergeMolecule, ig2);
+               IntGroupRelease(ig2);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionDeleteAnAtom) == 0 || (strcmp(action->name, gMolActionUnmergeMolecule) == 0 && (n1 = 1))) {
+               IntGroup *bg;
+               if (n1 == 0) {
+                       ig = IntGroupNew();
+                       if (ig == NULL || IntGroupAdd(ig, action->args[0].u.ival, 1) != 0)
+                               return -1;
+               } else {
+                       ig = action->args[0].u.igval;
+                       IntGroupRetain(ig);
+               }
+               /*  Search bonds crossing the molecule border  */
+               bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
+               ip = NULL;
+               if (bg != NULL) {
+                       n1 = IntGroupGetCount(bg);
+                       if (n1 > 0) {
+                               IntGroupIterator iter;
+                               Int i, idx;
+                               ip = (Int *)calloc(sizeof(Int), n1 * 2 + 1);
+                               if (ip == NULL)
+                                       return -1;
+                               IntGroupIteratorInit(bg, &iter);
+                               idx = 0;
+                               while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+                                       /*  The atoms at the border  */
+                                       ip[idx++] = mol->bonds[i * 2];
+                                       ip[idx++] = mol->bonds[i * 2 + 1];
+                               }
+                               IntGroupIteratorRelease(&iter);
+                       }
+                       IntGroupRelease(bg);
+               }
+               /*  Unmerge molecule  */
+               if ((result = MoleculeUnmerge(mol, &mol2, ig, 0)) != 0) {
+                       if (ip != NULL)
+                               free(ip);
+                       return result;
+               }
+               
+               sMolActionUpdateSelectionAndParameterNumbering(mol, ig, 0);
+
+#if 0
+               /*  Update selection  */
+               {
+                       IntGroup *sel = MoleculeGetSelection(mol);
+                       if (sel != NULL && IntGroupGetCount(sel) > 0) {
+                               IntGroup *remain_atoms = IntGroupNew();  /*  Remaining atoms (with original indices)  */
+                               IntGroup *sel2 = IntGroupNew();
+                               IntGroup *sel3 = IntGroupNew();
+                               IntGroupXor(ig, IntGroupNewWithPoints(0, mol->natoms + IntGroupGetCount(ig), -1), remain_atoms);
+                               IntGroupIntersect(sel, remain_atoms, sel2);  /*  Atoms to select after deletion  */
+                               IntGroupDeconvolute(sel2, remain_atoms, sel3);  /*  Renumber  */
+                               if (!IntGroupIsEqual(sel, sel3)) {
+                                       MoleculeSetSelection(mol, sel3);
+                                       act2 = MolActionNew(gMolActionSetSelection, sel);
+                                       MolActionCallback_registerUndo(mol, act2);
+                                       act2 = NULL;
+                               }
+                               IntGroupRelease(sel3);
+                               IntGroupRelease(sel2);
+                               IntGroupRelease(remain_atoms);
+                       }
+               }
+#endif
+               if (mol2 == NULL)
+                       act2 = NULL;
+               else {
+                       /*  If there exist bonds crossing the molecule border, then register
+                           an action to restore them  */
+                       if (ip != NULL) {
+                               act2 = MolActionNew(gMolActionAddBonds, n1 * 2, ip);
+                               MolActionCallback_registerUndo(mol, act2);
+                               free(ip);
+                       }
+                       act2 = MolActionNew(gMolActionMergeMolecule, mol2, ig);
+                       MoleculeRelease(mol2);
+               }
+               IntGroupRelease(ig);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionAddBonds) == 0) {
+               ip = (Int *)action->args[0].u.arval.ptr;
+               n1 = action->args[0].u.arval.nitems / 2;
+               if ((result = MoleculeAddBonds(mol, n1, ip)) < 0)
+                       return result;
+               act2 = MolActionNew(gMolActionDeleteBonds, n1 * 2, ip);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionDeleteBonds) == 0) {
+               ip = (Int *)action->args[0].u.arval.ptr;
+               n1 = action->args[0].u.arval.nitems / 2;
+               if ((result = MoleculeDeleteBonds(mol, n1, ip)) < 0)
+                       return result;
+               act2 = MolActionNew(gMolActionAddBonds, n1 * 2, ip);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionAddAngles) == 0) {
+               ip = (Int *)action->args[0].u.arval.ptr;
+               n1 = action->args[0].u.arval.nitems / 3;
+               ig = action->args[1].u.igval;
+               if (ig == NULL)
+                       ig = IntGroupNewWithPoints(mol->nangles, n1, -1);
+               else
+                       IntGroupRetain(ig);
+               if ((result = MoleculeAddAngles(mol, ip, ig)) < 0) {
+                       IntGroupRelease(ig);
+                       return result;
+               }
+               act2 = MolActionNew(gMolActionDeleteAngles, ig);
+               IntGroupRelease(ig);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionDeleteAngles) == 0) {
+               ig = action->args[0].u.igval;
+               n1 = IntGroupGetCount(ig) * 3;
+               ip = (Int *)malloc(sizeof(Int) * n1);
+               if ((result = MoleculeDeleteAngles(mol, ip, ig)) < 0) {
+                       free(ip);
+                       return result;
+               }
+               act2 = MolActionNew(gMolActionAddAngles, n1, ip, ig);
+               free(ip);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionAddDihedrals) == 0) {
+               ip = (Int *)action->args[0].u.arval.ptr;
+               n1 = action->args[0].u.arval.nitems / 4;
+               ig = action->args[1].u.igval;
+               if (ig == NULL)
+                       ig = IntGroupNewWithPoints(mol->ndihedrals, n1, -1);
+               else
+                       IntGroupRetain(ig);
+               if ((result = MoleculeAddDihedrals(mol, ip, ig)) < 0) {
+                       IntGroupRelease(ig);
+                       return result;
+               }
+               act2 = MolActionNew(gMolActionDeleteDihedrals, ig);
+               IntGroupRelease(ig);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionDeleteDihedrals) == 0) {
+               ig = action->args[0].u.igval;
+               n1 = IntGroupGetCount(ig) * 4;
+               ip = (Int *)malloc(sizeof(Int) * n1);
+               if ((result = MoleculeDeleteDihedrals(mol, ip, ig)) < 0) {
+                       free(ip);
+                       return result;
+               }
+               act2 = MolActionNew(gMolActionAddDihedrals, n1, ip, ig);
+               free(ip);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionAddImpropers) == 0) {
+               ip = (Int *)action->args[0].u.arval.ptr;
+               n1 = action->args[0].u.arval.nitems / 4;
+               ig = action->args[1].u.igval;
+               if (ig == NULL)
+                       ig = IntGroupNewWithPoints(mol->nimpropers, n1, -1);
+               else
+                       IntGroupRetain(ig);
+               if ((result = MoleculeAddImpropers(mol, ip, ig)) < 0) {
+                       IntGroupRelease(ig);
+                       return result;
+               }
+               act2 = MolActionNew(gMolActionDeleteImpropers, ig);
+               IntGroupRelease(ig);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionDeleteImpropers) == 0) {
+               ig = action->args[0].u.igval;
+               n1 = IntGroupGetCount(ig) * 4;
+               ip = (Int *)malloc(sizeof(Int) * n1);
+               if ((result = MoleculeDeleteImpropers(mol, ip, ig)) < 0) {
+                       free(ip);
+                       return result;
+               }
+               act2 = MolActionNew(gMolActionAddImpropers, n1, ip, ig);
+               free(ip);
+               needsRebuildMDArena = 1;
+/*     } else if (strcmp(action->name, gMolActionReplaceTables) == 0) {
+               Int i1, i2, i3;
+               Int *ip1, *ip2, *ip3;
+               if ((i1 = MoleculeReplaceAllAngles(mol, action->args[0].u.arval.nitems / 3, (Int *)action->args[0].u.arval.ptr, &ip1)) < 0)
+                       return -1;
+               if ((i2 = MoleculeReplaceAllDihedrals(mol, action->args[1].u.arval.nitems / 4, (Int *)action->args[1].u.arval.ptr, &ip2)) < 0)
+                       return -1;
+               if ((i3 = MoleculeReplaceAllImpropers(mol, action->args[2].u.arval.nitems / 4, (Int *)action->args[2].u.arval.ptr, &ip3)) < 0)
+                       return -1;
+               act2 = MolActionNew(gMolActionReplaceTables, i1, ip1, i2, ip2, i3, ip3);
+               free(ip1);
+               free(ip2);
+               free(ip3);
+               needsRebuildMDArena = 1; */
+       } else if (strcmp(action->name, gMolActionTranslateAtoms) == 0) {
+               vp = (Vector *)action->args[0].u.arval.ptr;
+               ig = action->args[1].u.igval;
+               if (vp == NULL)
+                       return -1;
+               MoleculeTranslate(mol, vp, ig);
+               VecScale(v, *vp, -1);
+               act2 = MolActionNew(gMolActionTranslateAtoms, &v, ig);
+               act2->frame = mol->cframe;
+               needsSymmetryAmendment = 1;
+       } else if (strcmp(action->name, gMolActionRotateAtoms) == 0) {
+               Vector *vp2;
+               Double ang;
+               vp = (Vector *)action->args[0].u.arval.ptr;
+               ang = action->args[1].u.dval;
+               vp2 = (Vector *)action->args[2].u.arval.ptr;
+               ig = action->args[3].u.igval;
+               MoleculeRotate(mol, vp, ang, vp2, ig);
+               act2 = MolActionNew(gMolActionRotateAtoms, vp, -ang, vp2, ig);
+               act2->frame = mol->cframe;
+               needsSymmetryAmendment = 1;
+       } else if (strcmp(action->name, gMolActionTransformAtoms) == 0) {
+               Transform *trp, tr_inv;
+               int atomPositions = 0; /* Save atom positions for undo? */
+               trp = (Transform *)action->args[0].u.arval.ptr;
+               ig = action->args[1].u.igval;
+               if (TransformInvert(tr_inv, *trp) != 0)
+                       atomPositions = 1;
+               else {
+                       /*  Check if inverse transform is reliable enough  */
+                       Transform temp;
+                       int j;
+                       TransformMul(temp, *trp, tr_inv);
+                       temp[0] -= 1.0;
+                       temp[4] -= 1.0;
+                       temp[8] -= 1.0;
+                       for (j = 0; j < 12; j++) {
+                               if (temp[j] < -1e-4 || temp[j] > 1e-4)
+                                       break;
+                       }
+                       if (j != 12)
+                               atomPositions = 1;
+               }
+               if (atomPositions) {
+                       IntGroupIterator iter;
+                       int j, k;
+                       n1 = IntGroupGetCount(ig);
+                       vp = (Vector *)malloc(sizeof(Vector) * n1);
+                       if (vp == NULL)
+                               return -1;
+                       IntGroupIteratorInit(ig, &iter);
+                       k = 0;
+                       while ((j = IntGroupIteratorNext(&iter)) >= 0) {
+                               vp[k++] = (ATOM_AT_INDEX(mol->atoms, j))->r;
+                       }
+                       act2 = MolActionNew(gMolActionSetAtomPositions, ig, n1, vp);
+                       free(vp);
+                       IntGroupIteratorRelease(&iter);
+               } else {
+                       act2 = MolActionNew(gMolActionTransformAtoms, &tr_inv, ig);
+               }
+               MoleculeTransform(mol, *trp, ig);
+               act2->frame = mol->cframe;
+               needsSymmetryAmendment = 1;
+       } else if (strcmp(action->name, gMolActionSetAtomPositions) == 0) {
+               IntGroupIterator iter;
+               int j, k;
+               ig = action->args[0].u.igval;
+               n1 = IntGroupGetCount(ig);
+               vp = (Vector *)malloc(sizeof(Vector) * n1);
+               if (vp == NULL)
+                       return -1;
+               IntGroupIteratorInit(ig, &iter);
+               k = 0;
+               while ((j = IntGroupIteratorNext(&iter)) >= 0) {
+                       vp[k++] = (ATOM_AT_INDEX(mol->atoms, j))->r;
+               }
+               act2 = MolActionNew(gMolActionSetAtomPositions, ig, n1, vp);
+               free(vp);
+               vp = (Vector *)action->args[1].u.arval.ptr;
+               IntGroupIteratorReset(&iter);
+               k = 0;
+               while ((j = IntGroupIteratorNext(&iter)) >= 0) {
+                       (ATOM_AT_INDEX(mol->atoms, j))->r = vp[k++];
+               }
+               IntGroupIteratorRelease(&iter);
+               act2->frame = mol->cframe;
+               needsSymmetryAmendment = 1;
+       } else if (strcmp(action->name, gMolActionInsertFrames) == 0) {
+               int old_nframes, new_nframes;
+               ig = action->args[0].u.igval;
+               vp = (Vector *)action->args[1].u.arval.ptr;
+               n1 = IntGroupGetCount(ig);
+               if (n1 == 0)
+                       return 0;  /*  Do nothing  */
+               if (vp != NULL && action->args[1].u.arval.nitems != n1 * mol->natoms)
+                       return -1;  /*  Internal inconsistency  */
+               old_nframes = MoleculeGetNumberOfFrames(mol);
+               if (MoleculeInsertFrames(mol, ig, vp) < 0)
+                       return -1;  /*  Error  */
+               new_nframes = MoleculeGetNumberOfFrames(mol);
+               if (old_nframes + n1 < new_nframes) {
+                       /*  "Extra" frames were automatically inserted because large frame indices were specified  */
+                       /*  Register undo operation to remove these extra frames  */
+                       IntGroup *ig2 = IntGroupNewWithPoints(old_nframes, new_nframes - (old_nframes + n1), -1);
+                       act2 = MolActionNew(gMolActionRemoveFrames, ig2);
+                       IntGroupRelease(ig2);
+                       MolActionCallback_registerUndo(mol, act2);
+                       MolActionRelease(act2);
+               }                       
+               act2 = MolActionNew(gMolActionRemoveFrames, ig);
+               act2->frame = mol->cframe;
+       } else if (strcmp(action->name, gMolActionRemoveFrames) == 0) {
+               ig = action->args[0].u.igval;
+               n1 = IntGroupGetCount(ig);
+               if (n1 == 0)
+                       return 0;  /*  Do nothing  */
+               vp = (Vector *)calloc(sizeof(Vector), n1 * mol->natoms);
+               if (MoleculeRemoveFrames(mol, ig, vp) < 0)
+                       return -1;  /*  Error  */
+               act2 = MolActionNew(gMolActionInsertFrames, ig, n1 * mol->natoms, vp);
+               act2->frame = mol->cframe;
+               free(vp);
+       } else if (strcmp(action->name, gMolActionSetSelection) == 0) {
+               IntGroup *ig2;
+               ig2 = MoleculeGetSelection(mol);
+               if (ig2 != NULL)
+                       IntGroupRetain(ig2);  /*  To avoid releasing during MoleculeSetSelection() */
+               ig = action->args[0].u.igval;
+               MoleculeSetSelection(mol, ig);
+               act2 = MolActionNew(gMolActionSetSelection, ig2);
+               if (ig2 != NULL)
+                       IntGroupRelease(ig2);
+       } else if (strcmp(action->name, gMolActionReorderAtoms) == 0) {
+               Int *ip2 = (Int *)malloc(sizeof(Int) * (mol->natoms + 1));
+               if (ip2 == NULL)
+                       return -1;
+               ip = (Int *)action->args[0].u.arval.ptr;
+               n1 = action->args[0].u.arval.nitems;
+       /*      for (n1 = 0; n1 < mol->natoms; n1++) {
+                       if (ip[n1] < 0)
+                               break;
+               } */
+               result = MoleculeReorderAtoms(mol, ip, ip2, n1);
+               if (result != 0) {
+                       free(ip2);
+                       return result;
+               }
+               act2 = MolActionNew(gMolActionReorderAtoms, mol->natoms, ip2);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionChangeResidueNumber) == 0 || (strcmp(action->name, gMolActionChangeResidueNumberForUndo) == 0 && (n1 = 1))) {
+               IntGroupIterator iter;
+               int i, nresidues;
+               int forUndo;
+               forUndo = n1;
+               ig = action->args[0].u.igval;
+               n1 = IntGroupGetCount(ig);
+               ip = (Int *)calloc(sizeof(Int), n1 + 1);
+               IntGroupIteratorInit(ig, &iter);
+               i = 0;
+               while ((n1 = IntGroupIteratorNext(&iter)) >= 0) {
+                       ip[i++] = ATOM_AT_INDEX(mol->atoms, n1)->resSeq;
+               }
+               n1 = i;
+               ip[i] = kInvalidIndex;
+               nresidues = mol->nresidues;
+               if (forUndo) {
+                       MoleculeChangeResidueNumberWithArray(mol, ig, (Int *)action->args[1].u.arval.ptr);
+                       MoleculeChangeNumberOfResidues(mol, action->args[2].u.ival);
+               } else {
+                       MoleculeChangeResidueNumber(mol, ig, action->args[1].u.ival);
+               }
+               act2 = MolActionNew(gMolActionChangeResidueNumberForUndo, ig, n1, ip, nresidues);
+       } else if (strcmp(action->name, gMolActionChangeResidueNames) == 0) {
+               char *new_names, *old_names;
+               int i, argc;
+               ip = (Int *)action->args[0].u.arval.ptr;
+               argc = action->args[0].u.arval.nitems;
+               new_names = action->args[1].u.arval.ptr;
+               old_names = (char *)malloc(argc * 4 + 1);
+               for (i = 0; i < argc; i++) {
+                       if (ip[i] >= mol->nresidues)
+                               old_names[i * 4] = 0;
+                       else
+                               strncpy(old_names + i * 4, mol->residues[ip[i]], 4);
+               }
+               MoleculeChangeResidueNames(mol, argc, ip, new_names);
+               act2 = MolActionNew(gMolActionChangeResidueNames, argc, ip, argc * 4, old_names);
+               free(old_names);
+       } else if (strcmp(action->name, gMolActionOffsetResidueNumbers) == 0) {
+               ig = action->args[0].u.igval;
+               n1 = mol->nresidues;
+               result = MoleculeOffsetResidueNumbers(mol, ig, action->args[1].u.ival, action->args[2].u.ival);
+               if (result != 0)
+                       return result;  /*  The molecule is not modified  */
+               act2 = MolActionNew(gMolActionOffsetResidueNumbers, ig, -(action->args[1].u.ival), n1);
+       } else if (strcmp(action->name, gMolActionChangeNumberOfResidues) == 0) {
+               int nresidues = mol->nresidues;
+               n1 = action->args[0].u.ival;
+               if (n1 < nresidues) {
+                       /*  The residue names will be lost, so undo must be registered to restore the names  */
+                       int argc = nresidues - n1;
+                       char *names = (char *)malloc(4 * argc + 1);
+                       Int *ip = (Int *)malloc(sizeof(Int) * (argc + 1));
+                       int i;
+                       for (i = 0; i < argc; i++) {
+                               strncpy(names + i * 4, mol->residues[i + n1], 4);
+                               ip[i] = i + n1;
+                       }
+                       ip[i] = kInvalidIndex;
+                       act2 = MolActionNew(gMolActionChangeResidueNames, argc, ip, argc * 4, names);
+                       MolActionCallback_registerUndo(mol, act2);
+                       free(ip);
+                       free(names);
+               }
+               MoleculeChangeNumberOfResidues(mol, n1);
+               act2 = MolActionNew(gMolActionChangeNumberOfResidues, nresidues);
+       } else if (strcmp(action->name, gMolActionExpandBySymmetry) == 0) {
+               Symop symop;
+               ig = action->args[0].u.igval;
+               symop.dx = action->args[1].u.ival;
+               symop.dy = action->args[2].u.ival;
+               symop.dz = action->args[3].u.ival;
+               symop.sym = action->args[4].u.ival;
+               symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
+               n1 = MoleculeAddExpandedAtoms(mol, symop, ig);
+               if (n1 > 0) {
+                       ig = IntGroupNew();
+                       IntGroupAdd(ig, mol->natoms - n1, n1);
+                       act2 = MolActionNew(gMolActionUnmergeMolecule, ig);
+                       IntGroupRelease(ig);
+               } else return n1;
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionAddSymmetryOperation) == 0) {
+               Transform *trp;
+               trp = (Transform *)action->args[0].u.arval.ptr;
+               if (mol->nsyms == 0) {
+                       Transform itr = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
+                       if (AssignArray(&mol->syms, &mol->nsyms, sizeof(Transform), mol->nsyms, &itr) == 0)
+                               return -1;
+               }
+               if (AssignArray(&mol->syms, &mol->nsyms, sizeof(Transform), mol->nsyms, trp) == 0)
+                               return -1;
+               act2 = MolActionNew(gMolActionDeleteSymmetryOperation);
+       } else if (strcmp(action->name, gMolActionDeleteSymmetryOperation) == 0) {
+               if (mol->nsyms == 0)
+                       return -1;
+               act2 = MolActionNew(gMolActionAddSymmetryOperation, &(mol->syms[mol->nsyms - 1]));
+               mol->nsyms--;
+               if (mol->nsyms == 1)
+                       mol->nsyms--;  /*  Remove the identity operation  */
+               if (mol->nsyms == 0) {
+                       free(mol->syms);
+                       mol->syms = NULL;
+               }
+       } else if (strcmp(action->name, gMolActionSetCell) == 0) {
+               double *dp, d[6];
+               if (mol->cell == NULL)
+                       d[0] = 0.0;
+               else {
+                       for (n1 = 0; n1 < 6; n1++)
+                               d[n1] = mol->cell->cell[n1];
+               }
+               n1 = action->args[1].u.ival;
+               dp = action->args[0].u.arval.ptr;
+               if (action->args[0].u.arval.nitems == 0)
+                       MoleculeSetCell(mol, 0, 0, 0, 0, 0, 0, n1);
+               else
+                       MoleculeSetCell(mol, dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], n1);
+               act2 = MolActionNew(gMolActionSetCell, (d[0] == 0.0 ? 0 : 6), d, n1);
+       } else if (strcmp(action->name, gMolActionSetBox) == 0) {
+               Vector v[4];
+               char flags[3];
+               if (mol->cell == NULL)
+                       act2 = MolActionNew(gMolActionClearBox);
+               else {
+                       n1 = ((mol->cell->flags[0] != 0) * 4 + (mol->cell->flags[1] != 0) * 2 + (mol->cell->flags[2] != 0));
+                       act2 = MolActionNew(gMolActionSetBox, &(mol->cell->axes[0]), &(mol->cell->axes[1]), &(mol->cell->axes[2]), &(mol->cell->origin), n1);
+               }
+               for (n1 = 0; n1 < 4; n1++)
+                       v[n1] = *((Vector *)(action->args[n1].u.arval.ptr));
+               for (n1 = 0; n1 < 3; n1++)
+                       flags[n1] = ((action->args[4].u.ival >> (2 - n1)) & 1);
+               MoleculeSetPeriodicBox(mol, &v[0], &v[1], &v[2], &v[3], flags);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionClearBox) == 0) {
+               if (mol->cell == NULL)
+                       return 0;  /*  Do nothing  */
+               n1 = ((mol->cell->flags[0] != 0) * 4 + (mol->cell->flags[1] != 0) * 2 + (mol->cell->flags[2] != 0));
+               act2 = MolActionNew(gMolActionSetBox, &(mol->cell->axes[0]), &(mol->cell->axes[1]), &(mol->cell->axes[2]), &(mol->cell->origin), n1);
+               MoleculeSetPeriodicBox(mol, NULL, NULL, NULL, NULL, NULL);
+               needsRebuildMDArena = 1;
+       } else if (strcmp(action->name, gMolActionAddParameters) == 0) {
+               UnionPar *up;
+               Int parType;
+               if (mol->par == NULL)
+                       return -1;
+               parType = action->args[0].u.ival;
+               ig = action->args[1].u.igval;
+               up = action->args[2].u.arval.ptr;
+               ParameterInsert(mol->par, parType, up, ig);
+               act2 = MolActionNew(gMolActionDeleteParameters, parType, ig);
+       } else if (strcmp(action->name, gMolActionDeleteParameters) == 0) {
+               UnionPar *up;
+               Int parType;
+               if (mol->par == NULL)
+                       return -1;
+               parType = action->args[0].u.ival;
+               ig = action->args[1].u.igval;
+               n1 = IntGroupGetCount(ig);
+               up = (UnionPar *)calloc(sizeof(UnionPar), n1);
+               ParameterDelete(mol->par, parType, up, ig);
+               act2 = MolActionNew(gMolActionAddParameters, parType, ig, n1, up);
+               free(up);
+/*     } else if (strcmp(action->name, gMolActionCartesianToXtal) == 0 || (strcmp(action->name, gMolActionXtalToCartesian) == 0 && (n1 = 1))) {
+               Int i, j;
+               Atom *ap;
+               if (mol->cell == NULL || (n1 == 0 && mol->is_xtal_coord) || (n1 == 1 && !mol->is_xtal_coord))
+                       return 0;
+               if (n1 == 0) {
+                       for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
+                               TransformVec(&(ap->r), mol->cell->tr, &(ap->r));
+                               if (ap->nframes > 0) {
+                                       for (j = 0; j < ap->nframes; j++)
+                                               TransformVec(&(ap->frames[j]), mol->cell->tr, &(ap->frames[j]));
+                               }
+                       }
+                       mol->is_xtal_coord = 1;
+                       act2 = MolActionNew(gMolActionXtalToCartesian);
+               } else {
+                       for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap))
+                               TransformVec(&(ap->r), mol->cell->rtr, &(ap->r));
+                       if (ap->nframes > 0) {
+                               for (j = 0; j < ap->nframes; j++)
+                                       TransformVec(&(ap->frames[j]), mol->cell->rtr, &(ap->frames[j]));
+                       }
+                       mol->is_xtal_coord = 0;
+                       act2 = MolActionNew(gMolActionCartesianToXtal);
+               } */
+       } else {
+               fprintf(stderr, "Internal error: unknown action name %s\n", action->name);
+               return -1;
+       }
+       if (act2 != NULL) {
+               MolActionCallback_registerUndo(mol, act2);
+               MolActionRelease(act2);
+       }
+       if (needsSymmetryAmendment) {
+               //  ig should be valid
+               IntGroup *ig2;
+               Vector *vp2;
+               n1 = MoleculeAmendBySymmetry(mol, ig, &ig2, &vp2);
+               if (n1 > 0) {
+                       act2 = MolActionNew(gMolActionSetAtomPositions, ig2, n1, vp2);
+                       act2->frame = mol->cframe;
+                       MolActionCallback_registerUndo(mol, act2);
+                       MolActionRelease(act2);
+                       free(vp2);
+                       IntGroupRelease(ig2);
+               }
+       }
+       
+       if (needsRebuildMDArena) {
+               mol->needsMDRebuild = 1;
+       }
+       
+       MoleculeCallback_notifyModification(mol, 0);
+       return 0;
+}
+
+int
+MolActionCreateAndPerform(Molecule *mol, const char *name, ...)
+{
+       va_list ap;
+       MolAction *action;
+       va_start(ap, name);
+       action = MolActionNewArgv(name, ap);
+       va_end(ap);
+       if (action != NULL) {
+               int result = MolActionPerform(mol, action);
+               MolActionRelease(action);
+               return result;
+       } else return -1;
+}
+
+void
+MolActionAlertRubyIsRunning(void)
+{
+       MyAppCallback_errorMessageBox("Cannot perform operation (Ruby script is running background)");
+}
diff --git a/MolLib/MolAction.h b/MolLib/MolAction.h
new file mode 100644 (file)
index 0000000..fb673d4
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *  MolAction.h
+ *
+ *  Created by Toshi Nagata on 07/06/23.
+ *  Copyright 2007-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MolAction_h__
+#define __MolAction_h__
+
+#include "Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+/*  Action names with signatures; definitions are in MolAction.c  */
+
+extern const char *gMolActionNone;
+extern const char *gMolActionAddAnAtom;
+extern const char *gMolActionDeleteAnAtom;
+extern const char *gMolActionMergeMolecule;
+extern const char *gMolActionUnmergeMolecule;
+extern const char *gMolActionAddBonds;
+extern const char *gMolActionDeleteBonds;
+extern const char *gMolActionAddAngles;
+extern const char *gMolActionDeleteAngles;
+extern const char *gMolActionAddDihedrals;
+extern const char *gMolActionDeleteDihedrals;
+extern const char *gMolActionAddImpropers;
+extern const char *gMolActionDeleteImpropers;
+/* extern const char *gMolActionReplaceTables; */
+extern const char *gMolActionTranslateAtoms;
+extern const char *gMolActionRotateAtoms;
+extern const char *gMolActionTransformAtoms;
+extern const char *gMolActionSetAtomPositions;
+extern const char *gMolActionInsertFrames;
+extern const char *gMolActionRemoveFrames;
+extern const char *gMolActionSetSelection;
+extern const char *gMolActionChangeResidueNumber;
+extern const char *gMolActionChangeResidueNumberForUndo;
+extern const char *gMolActionChangeResidueNames;
+extern const char *gMolActionOffsetResidueNumbers;
+extern const char *gMolActionChangeNumberOfResidues;
+extern const char *gMolActionReorderAtoms;
+extern const char *gMolActionExpandBySymmetry;
+extern const char *gMolActionDeleteSymmetryOperation;
+extern const char *gMolActionAddSymmetryOperation;
+extern const char *gMolActionSetCell;
+extern const char *gMolActionSetBox;
+extern const char *gMolActionClearBox;
+/*extern const char *gMolActionSetParameterAttributeForUndo; */
+extern const char *gMolActionAddParameters;
+extern const char *gMolActionDeleteParameters;
+extern const char *gMolActionCartesianToXtal;
+extern const char *gMolActionXtalToCartesian;
+       
+/*  Special action signatures to invoke the Ruby script. Used as follows:
+ *  MolActionCreateAndPerform(mol, SCRIPT_ACTION("vd"), "rotate", vec, angle);
+ *    (Will perform 'mol.rotate(vec, angle)')
+ *  or:
+ *  MolActionCreateAndPerform(mol, SCRIPT_ACTION("vd"), "proc {|v,d| rotate(v,d*3.1415927/180)}", vec, deg)
+ *    (Will perform '(mol.instance_eval "proc {...}").call(vec, deg)')
+ */
+#define kMolActionPerformScript "script:s"
+#define SCRIPT_ACTION(sig) (kMolActionPerformScript sig)
+
+/*  Action record for reversible editing  */
+typedef struct MolAction {
+       int refCount;
+       const char *name;
+       int frame;  /*  The frame number which the action should be performed on.
+                       Usually -1 = no specific frame, and set by MolActionSetFrame() if necessary. */
+       int nargs;
+       struct MolActionArg *args;
+} MolAction;
+
+MolAction *MolActionNew(const char *name, ...);
+MolAction *MolActionRetain(MolAction *action);
+void MolActionRelease(MolAction *action);
+void MolActionSetFrame(MolAction *action, int frame);
+
+/*  Perform a MolAction, and register undo action through MolActionCallback_registerUndo(). */
+int MolActionPerform(Molecule *mol, MolAction *action);
+
+/*  A convenient function, which creates a MolAction, perform it, and release it.  */
+int MolActionCreateAndPerform(Molecule *mol, const char *name, ...);
+
+/*  Show an error dialog saying Ruby is already running  */
+void MolActionAlertRubyIsRunning(void);
+       
+STUB void MolActionCallback_registerUndo(Molecule *mol, MolAction *action);
+STUB int MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag);
+STUB int MolActionCallback_isUndoRegistrationEnabled(Molecule *mol);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __MolAction_h__ */
diff --git a/MolLib/MolLib.h b/MolLib/MolLib.h
new file mode 100755 (executable)
index 0000000..5797e1c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  mollib.h
+ *
+ *  Created by Toshi Nagata on 2005/06/03.
+ *  Copyright 2005-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MolLib_h__
+#define __MolLib_h__
+
+#include "Types.h"
+#include "Parameter.h"
+#include "Molecule.h"
+#include "MainView.h"
+#include "Trackball.h"
+#include "MolAction.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+typedef struct MolArena {
+       Molecule *mp;       /*  Current molecule  */
+       struct MainView *mview;    /*  Active view  */
+       Int npars;          /*  Number of parameters in pars[] */
+       Parameter **pars;   /*  Parameters. pars[npars - 1] is always the 'custom' parameters, that are defined individually from TCL command. Other parameters are those from files, and they are registered by the order they are read from files.  */
+       
+       /*  These two fields are used by T_MolNew, T_MolRelease  */
+       Int nmollist;
+       Molecule **mollist;
+
+} MolArena;
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __MolLib_h__ */
diff --git a/MolLib/Molecule.c b/MolLib/Molecule.c
new file mode 100755 (executable)
index 0000000..02d6fd6
--- /dev/null
@@ -0,0 +1,8313 @@
+/*
+ *  Molecule.c
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MolLib.h"
+#include <string.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "Dcd.h"
+#include "MD/MDCore.h"
+#include "MD/MDPressure.h"
+
+static Molecule *sMoleculeRoot = NULL;
+static int sMoleculeUntitledCount = 0;
+
+Int gSizeOfAtomRecord = sizeof(Atom);
+
+#pragma mark ====== Utility function ======
+
+int
+strlen_limit(const char *s, int limit)
+{
+       int len;
+       for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
+       return len;
+}
+
+#pragma mark ======  Atom handling  ======
+
+Atom *
+AtomDuplicate(Atom *dst, const Atom *src)
+{
+       if (dst == NULL) {
+               dst = (Atom *)malloc(gSizeOfAtomRecord);
+               if (dst == NULL)
+                       return NULL;
+       }
+       memmove(dst, src, gSizeOfAtomRecord);
+       if (src->aniso != NULL) {
+               dst->aniso = (Aniso *)malloc(sizeof(Aniso));
+               if (dst->aniso != NULL)
+                       memmove(dst->aniso, src->aniso, sizeof(Aniso));
+       }
+       if (src->frames != NULL) {
+               dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
+               if (dst->frames != NULL) {
+                       memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
+                       dst->nframes = src->nframes;
+               } else {
+                       dst->nframes = 0;
+               }
+       }
+       return dst;
+}
+
+void
+AtomClean(Atom *ap)
+{
+       if (ap->aniso != NULL) {
+               free(ap->aniso);
+               ap->aniso = NULL;
+       }
+       if (ap->frames != NULL) {
+               free(ap->frames);
+               ap->frames = NULL;
+               ap->nframes = 0;
+       }
+}
+
+void
+CubeRelease(Cube *cp)
+{
+       if (cp != NULL) {
+               if (cp->dp != NULL)
+                       free(cp->dp);
+               free(cp);
+       }
+}
+
+void
+BasisSetRelease(BasisSet *bset)
+{
+       int i;
+       if (bset == NULL)
+               return;
+       if (bset->shells != NULL)
+               free(bset->shells);
+       if (bset->priminfos != NULL)
+               free(bset->priminfos);
+       if (bset->mo != NULL)
+               free(bset->mo);
+       if (bset->cns != NULL)
+               free(bset->cns);
+       if (bset->moenergies != NULL)
+               free(bset->moenergies);
+       if (bset->scfdensities != NULL)
+               free(bset->scfdensities);
+       if (bset->pos != NULL)
+               free(bset->pos);
+       if (bset->nuccharges != NULL)
+               free(bset->nuccharges);
+       if (bset->cubes != NULL) {
+               for (i = 0; i < bset->ncubes; i++) {
+                       CubeRelease(bset->cubes[i]);
+               }
+               free(bset->cubes);
+       }
+       free(bset);
+}
+
+#pragma mark ====== Accessor types ======
+
+MolEnumerable *
+MolEnumerableNew(Molecule *mol, int kind)
+{
+       MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
+       if (mseq != NULL) {
+               mseq->mol = MoleculeRetain(mol);
+               mseq->kind = kind;
+       }
+       return mseq;
+}
+
+void
+MolEnumerableRelease(MolEnumerable *mseq)
+{
+       if (mseq != NULL) {
+               MoleculeRelease(mseq->mol);
+               free(mseq);
+       }
+}
+
+AtomRef *
+AtomRefNew(Molecule *mol, int idx)
+{
+       AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
+       if (aref != NULL) {
+               aref->mol = MoleculeRetain(mol);
+               aref->idx = idx;
+       }
+       return aref;
+}
+
+void
+AtomRefRelease(AtomRef *aref)
+{
+       if (aref != NULL) {
+               MoleculeRelease(aref->mol);
+               free(aref);
+       }
+}
+
+#pragma mark ====== Creation of molecules ======
+
+Molecule *
+MoleculeNew(void)
+{
+       char name[40];
+       Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
+       if (mp == NULL)
+               Panic("Cannot allocate new molecule record");
+       snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
+       ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
+       return mp;
+}
+
+Molecule *
+MoleculeNewWithName(const char *name)
+{
+       Molecule *mp = MoleculeNew();
+       MoleculeSetName(mp, name);
+       return mp;
+}
+
+Molecule *
+MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
+{
+       int i;
+       if (mp == NULL)
+               mp = MoleculeNew();
+       if (natoms == 0)
+               return mp;
+       if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
+               Panic("Cannot allocate memory for atoms");
+       for (i = 0; i < natoms; i++)
+               AtomDuplicate(mp->atoms + i, atoms + i);
+       mp->nframes = -1;  /*  Should be recalculated later  */
+       return mp;
+}
+
+Molecule *
+MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp)
+{
+       MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
+       if (mp->nbonds > 0) {
+               if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
+                       goto error;
+               memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
+       }
+       if (mp->nangles > 0) {
+               if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
+                       goto error;
+               memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
+       }
+       if (mp->ndihedrals > 0) {
+               if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
+                       goto error;
+               memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
+       }
+       if (mp->nimpropers > 0) {
+               if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
+                       goto error;
+               memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
+       }
+       if (mp->nresidues > 0) {
+               if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
+                       goto error;
+               memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
+       }
+       return mp2;
+  error:
+       Panic("Cannot allocate memory for duplicate molecule");
+       return NULL;  /*  Not reached  */
+}
+
+/*  Assign a unique name to this parameter record  */
+void
+MoleculeSetName(Molecule *mp, const char *name)
+{
+       ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
+}
+
+const char *
+MoleculeGetName(Molecule *mp)
+{
+       return ObjectGetName((Object *)mp);
+}
+
+Molecule *
+MoleculeWithName(const char *name)
+{
+       return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
+}
+
+Molecule *
+MoleculeRetain(Molecule *mp)
+{
+       ObjectIncrRefCount((Object *)mp);
+       return mp;
+}
+
+void
+MoleculeRelease(Molecule *mp)
+{
+       if (mp == NULL)
+               return;
+       if (ObjectDecrRefCount((Object *)mp) == 0) {
+               if (mp->atoms != NULL) {
+                       int i;
+                       for (i = 0; i < mp->natoms; i++)
+                               AtomClean(mp->atoms + i);
+                       free(mp->atoms);
+               }
+               if (mp->bonds != NULL)
+                       free(mp->bonds);
+               if (mp->angles != NULL)
+                       free(mp->angles);
+               if (mp->dihedrals != NULL)
+                       free(mp->dihedrals);
+               if (mp->impropers != NULL)
+                       free(mp->impropers);
+               if (mp->residues != NULL)
+                       free(mp->residues);
+               if (mp->bset != NULL)
+                       BasisSetRelease(mp->bset);
+               if (mp->par != NULL)
+                       ParameterRelease(mp->par);
+               if (mp->elpots != NULL)
+                       free(mp->elpots);
+               ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
+       }
+}
+
+void
+MoleculeExchange(Molecule *mp1, Molecule *mp2)
+{
+       Molecule mp_temp;
+       /*  'natoms' is the first member to be copied  */
+       int ofs = offsetof(Molecule, natoms);
+       memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
+       memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
+       memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
+       if (mp1->arena != NULL && mp1->arena->mol == mp2)
+               mp1->arena->mol = mp1;
+       if (mp1->arena != NULL && mp1->arena->xmol == mp2)
+               mp1->arena->xmol = mp1;
+       if (mp2->arena != NULL && mp2->arena->mol == mp1)
+               mp2->arena->mol = mp2;
+       if (mp2->arena != NULL && mp2->arena->xmol == mp1)
+               mp2->arena->xmol = mp2;
+}
+
+#pragma mark ====== Mutex ======
+
+void
+MoleculeLock(Molecule *mol)
+{
+       if (mol == NULL || mol->mutex == NULL)
+               return;
+       MoleculeCallback_lockMutex(mol->mutex);
+}
+
+void
+MoleculeUnlock(Molecule *mol)
+{
+       if (mol == NULL || mol->mutex == NULL)
+               return;
+       MoleculeCallback_unlockMutex(mol->mutex);
+}
+
+#pragma mark ====== Modify count ======
+
+void
+MoleculeIncrementModifyCount(Molecule *mp)
+{
+       if (mp != NULL) {
+               if (++(mp->modifyCount) == 1)
+                       MoleculeCallback_notifyModification(mp, 0);
+       /*      fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
+       }
+}
+
+void
+MoleculeClearModifyCount(Molecule *mp)
+{
+       if (mp != NULL) {
+               mp->modifyCount = 0;
+       /*      fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
+       }
+}
+
+#pragma mark ====== File handling functions ======
+
+static const char *
+guessMoleculeType(const char *fname)
+{
+       char buf[1024], *p;
+       FILE *fp;
+       const char *retval = NULL;
+       fp = fopen(fname, "rb");
+       if (fp != NULL) {
+               memset(buf, 0, sizeof buf);
+               if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
+                       if (strncmp(buf, "PSF", 3) == 0)
+                               retval = "psf";
+                       else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
+                       || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
+                               retval = "pdb";
+                       else
+                               retval = "???";  /*  unknown  */
+               }
+               fclose(fp);
+       }
+       return retval;
+}
+
+static int
+guessElement(Atom *ap)
+{
+       int atomicNumber = -1;
+       if (ap->atomicNumber > 0)
+               atomicNumber = ap->atomicNumber;
+       else {
+               atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
+               if (atomicNumber <= 0 && ap->aname[0] != 0)
+                       atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
+       }
+       if (atomicNumber >= 0) {
+               ap->atomicNumber = atomicNumber;
+               if (ap->weight <= 0)
+                       ap->weight = WeightForAtomicNumber(atomicNumber);
+               if (ap->element[0] == 0)
+                       ElementToString(atomicNumber, ap->element);
+       }
+       return atomicNumber;
+}
+
+int
+MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
+{
+       int retval;
+       if (ftype == NULL || *ftype == 0) {
+               const char *cp;
+               cp = strrchr(fname, '.');
+               if (cp != NULL)
+                       ftype = cp + 1;
+               else {
+                       cp = guessMoleculeType(fname);
+                       if (strcmp(cp, "???") != 0)
+                               ftype = cp;
+               }
+       }
+       if (strcasecmp(ftype, "psf") == 0) {
+               retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
+       } else if (strcasecmp(ftype, "pdb") == 0) {
+               retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
+       } else if (strcasecmp(ftype, "tep") == 0) {
+               retval = MoleculeLoadTepFile(mp, fname, errbuf, errbufsize);
+       } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
+               retval = MoleculeLoadShelxFile(mp, fname, errbuf, errbufsize);
+       } else if (strcasecmp(ftype, "fchk") == 0) {
+               retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf, errbufsize);
+       } else {
+               snprintf(errbuf, errbufsize, "Unknown format %s", ftype);
+               return 1;
+       }
+/*     if (retval != 0) {
+               retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
+       } */
+       return retval;
+}
+
+int
+MoleculeLoadMbsfFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       Molecule *mp;
+       char buf[1024];
+       int i, j, k, n, err, fn, nframes;
+       int lineNumber;
+       int ibuf[12];
+       Int iibuf[4];
+       double dbuf[12];
+       char cbuf[12][6];
+       const char **pp;
+       char *bufp, *valp, *comp;
+       Int *ip;
+       Double *dp;
+       Vector v;
+       Atom *ap;
+       err = 0;
+       if (errbuf == NULL) {
+               errbuf = buf;
+               errbufsize = 1024;
+       }
+       errbuf[0] = 0;
+       mp = MoleculeNew();
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return 1;
+       }
+       /*      flockfile(fp); */
+       lineNumber = 0;
+       fn = 0;
+       nframes = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               if (strstr(buf, "!:atoms") == buf) {
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
+                               if (sscanf(buf, "%d %4s %d %4s %4s %4s %lf %lf %4s %d %lf %lf %d", &ibuf[0], cbuf[0], &ibuf[1], cbuf[1], cbuf[2], cbuf[3], &dbuf[0], &dbuf[1], cbuf[4], &ibuf[2], &dbuf[2], &dbuf[3], &ibuf[3]) < 13) {
+                                       snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
+                                       goto exit;
+                               }
+                               ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
+                               strncpy(ap->segName, cbuf[0], 4);
+                               ap->resSeq = ibuf[1];
+                               strncpy(ap->resName, cbuf[1], 4);
+                               strncpy(ap->aname, cbuf[2], 4);
+                               ap->type = AtomTypeEncodeToUInt(cbuf[3]);
+                               ap->charge = dbuf[0];
+                               ap->weight = dbuf[1];
+                               strncpy(ap->element, cbuf[4], 2);
+                               ap->atomicNumber = ibuf[2];
+                               ap->occupancy = dbuf[2];
+                               ap->tempFactor = dbuf[3];
+                               ap->intCharge = ibuf[3];
+                       }
+                       continue;
+               } else if (strstr(buf, "!:atoms_symop") == buf) {
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* idx symop symbase */
+                               if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
+                                       snprintf(errbuf, errbufsize, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
+                                       goto exit;
+                               }
+                               if (i >= mp->natoms) {
+                                       snprintf(errbuf, errbufsize, "line %d: too many atomic symmetry info\n", lineNumber);
+                                       goto exit;
+                               }
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               ap->symop.sym = ibuf[1] / 1000000;
+                               ap->symop.dx = (ibuf[1] % 1000000) / 10000;
+                               ap->symop.dy = (ibuf[1] % 10000) / 100;
+                               ap->symop.dz = ibuf[1] % 100;
+                               ap->symbase = ibuf[2];
+                               i++;
+                       }
+                       continue;
+               } else if (strstr(buf, "!:atoms_fix") == buf) {
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* idx fix_force fix_pos */
+                               if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
+                                       snprintf(errbuf, errbufsize, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
+                                       goto exit;
+                               }
+                               if (i >= mp->natoms) {
+                                       snprintf(errbuf, errbufsize, "line %d: too many fix atom info\n", lineNumber);
+                                       goto exit;
+                               }
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               ap->fix_force = dbuf[0];
+                               ap->fix_pos.x = dbuf[1];
+                               ap->fix_pos.y = dbuf[2];
+                               ap->fix_pos.z = dbuf[3];
+                               i++;
+                       }
+                       continue;
+               } else if (strstr(buf, "!:positions") == buf) {
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* idx x y z */
+                               if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
+                                       snprintf(errbuf, errbufsize, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
+                                       goto exit;
+                               }
+                               if (i >= mp->natoms) {
+                                       snprintf(errbuf, errbufsize, "line %d: too many atom position records\n", lineNumber);
+                                       goto exit;
+                               }
+                               v.x = dbuf[0];
+                               v.y = dbuf[1];
+                               v.z = dbuf[2];
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               if (nframes > 0) {
+                                       AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
+                                       if (nframes == 1)
+                                               ap->frames[0] = ap->r;
+                               }
+                               ap->r = v;
+                               i++;
+                       }
+                       nframes++;
+                       if (nframes >= 2) {
+                               mp->nframes = nframes;
+                               mp->cframe = nframes - 1;
+                       } else {
+                               mp->nframes = mp->cframe = 0;
+                       }
+                       continue;
+               } else if (strstr(buf, "!:bonds") == buf) {
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
+                               i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
+                               if (i < 2 || i % 2 != 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
+                                       goto exit;
+                               }
+                               for (j = 0; j < i; j += 2) {
+                                       iibuf[0] = ibuf[j];
+                                       iibuf[1] = ibuf[j + 1];
+                                       if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
+                                               snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
+                                               goto exit;
+                                       }
+                                       AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
+                                       for (k = 0; k < 2; k++) {
+                                               ap = ATOM_AT_INDEX(mp->atoms, iibuf[k]);
+                                               for (n = 0; n < ap->nconnects; n++) {
+                                                       if (ap->connects[n] == iibuf[1 - k])
+                                                               break;
+                                               }
+                                               if (n >= ap->nconnects) {
+                                                       if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1) {
+                                                               snprintf(errbuf, errbufsize, "line %d: too many bonds on atom %d", lineNumber, iibuf[k]);
+                                                               goto exit;
+                                                       }
+                                                       ap->connects[ap->nconnects++] = iibuf[1 - k];
+                                               }
+                                       }
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:angles") == buf) {
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
+                               i = sscanf(buf, "%d %d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7], &ibuf[8]);
+                               if (i == 0 || i % 3 != 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
+                                       goto exit;
+                               }
+                               for (j = 0; j < i; j += 3) {
+                                       iibuf[0] = ibuf[j];
+                                       iibuf[1] = ibuf[j + 1];
+                                       iibuf[2] = ibuf[j + 2];
+                                       if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2]) {
+                                               snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
+                                               goto exit;
+                                       }
+                                       AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:dihedrals") == buf) {
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
+                               i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
+                               if (i == 0 || i % 4 != 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
+                                       goto exit;
+                               }
+                               for (j = 0; j < i; j += 4) {
+                                       iibuf[0] = ibuf[j];
+                                       iibuf[1] = ibuf[j + 1];
+                                       iibuf[2] = ibuf[j + 2];
+                                       iibuf[3] = ibuf[j + 3];
+                                       if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
+                                               snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
+                                               goto exit;
+                                       }
+                                       AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:impropers") == buf) {
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
+                               i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
+                               if (i == 0 || i % 4 != 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
+                                       goto exit;
+                               }
+                               for (j = 0; j < i; j += 4) {
+                                       iibuf[0] = ibuf[j];
+                                       iibuf[1] = ibuf[j + 1];
+                                       iibuf[2] = ibuf[j + 2];
+                                       iibuf[3] = ibuf[j + 3];
+                                       if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
+                                               snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
+                                               goto exit;
+                                       }
+                                       AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:xtalcell") == buf && mp->cell == NULL) {
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* a b c alpha beta gamma */ 
+                               if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad xtalcell format", lineNumber);
+                                       goto exit;
+                               }
+                               MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
+                       }
+                       continue;
+               } else if (strstr(buf, "!:symmetry_operations") == buf) {
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               Transform tr;
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
+                               if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
+                                       goto exit;
+                               }
+                               tr[i * 3] = dbuf[0];
+                               tr[i * 3 + 1] = dbuf[1];
+                               tr[i * 3 + 2] = dbuf[2];
+                               i++;
+                               if (i == 4) {
+                                       AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
+                                       i = 0;
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:periodic_box") == buf) {
+                       Vector vs[5];
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
+                               if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
+                                       goto exit;
+                               }
+                               vs[i].x = dbuf[0];
+                               vs[i].y = dbuf[1];
+                               vs[i].z = dbuf[2];
+                               i++;
+                               if (i == 5) {
+                               /*      j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3]); */
+                                       cbuf[0][0] = dbuf[0];
+                                       cbuf[0][1] = dbuf[1];
+                                       cbuf[0][2] = dbuf[2];
+                                       MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0]);
+                               /*      if (j == 4)
+                                               mp->is_xtal_coord = (ibuf[3] != 0); */
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:md_parameters") == buf) {
+                       MDArena *arena;
+                       if (mp->arena == NULL)
+                               mp->arena = md_arena_new(NULL);
+                       arena = mp->arena;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               bufp = buf;
+                               comp = strsep(&bufp, " \t");
+                               if (bufp != NULL) {
+                                       while (*bufp == ' ' || *bufp == '\t')
+                                               bufp++;
+                                       valp = strsep(&bufp, "\n");
+                               } else valp = NULL;
+                               /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
+                               if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
+                                       || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
+                                       || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
+                                       || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
+                                       || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
+                                       if (*valp == 0 || strstr(valp, "(null)") == valp)
+                                               *pp = NULL;
+                                       else {
+                                               valp = strdup(valp);
+                                               if (valp != NULL) {
+                                                       char *valp1 = strchr(valp, '\n');
+                                                       if (valp1 != NULL)
+                                                               *valp1 = 0;
+                                               }
+                                               *pp = valp;
+                                       }
+                               } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
+                                                  || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
+                                                  || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
+                                                  || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
+                                                  || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
+                                                  || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
+                                                  || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
+                                                  || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
+                                                  || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
+                                                  || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
+                                       *ip = (valp == NULL ? 0 : atoi(valp));
+                               } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
+                                                  || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
+                                                  || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
+                                                  || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
+                                                  || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
+                                                  || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
+                                                  || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
+                                                  || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
+                                                  || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
+                                                  || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
+                                                  || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
+                                                  || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
+                                                  || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)) {
+                                       *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:pressure_control_parameters") == buf) {
+                       MDPressureArena *pressure;
+                       if (mp->arena == NULL)
+                               mp->arena = md_arena_new(mp);
+                       if (mp->arena->pressure == NULL)
+                               mp->arena->pressure = pressure_new();
+                       pressure = mp->arena->pressure;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               bufp = buf;
+                               comp = strsep(&bufp, " \t");
+                               if (bufp != NULL) {
+                                       while (*bufp == ' ' || *bufp == '\t')
+                                               bufp++;
+                                       valp = strsep(&bufp, "\n");
+                               } else valp = NULL;
+                               if (strcmp(comp, "pressure") == 0) {
+                                       if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7], &dbuf[8]) < 9) {
+                                               snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
+                                               goto exit;
+                                       }
+                                       for (i = 0; i < 9; i++)
+                                               pressure->apply[i] = dbuf[i];
+                               } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
+                                       if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7]) < 8) {
+                                               snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
+                                               goto exit;
+                                       }
+                                       for (i = 0; i < 8; i++)
+                                               pressure->cell_flexibility[i] = dbuf[i];
+                               } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
+                                       *ip = (valp == NULL ? 0 : atoi(valp));
+                               } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
+                                                  || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
+                                                  || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
+                                       *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
+                               }
+                       }
+                       continue;
+               } else if (strstr(buf, "!:velocity") == buf) {
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* idx vx vy vz */
+                               if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
+                                       snprintf(errbuf, errbufsize, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
+                                       goto exit;
+                               }
+                               if (i >= mp->natoms) {
+                                       snprintf(errbuf, errbufsize, "line %d: too many atom velocity records\n", lineNumber);
+                                       goto exit;
+                               }
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               ap->v.x = dbuf[0];
+                               ap->v.y = dbuf[1];
+                               ap->v.z = dbuf[2];
+                               i++;
+                       }
+                       continue;
+               } else if (strstr(buf, "!:force") == buf) {
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               /* idx fx fy fz */
+                               if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
+                                       snprintf(errbuf, errbufsize, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
+                                       goto exit;
+                               }
+                               if (i >= mp->natoms) {
+                                       snprintf(errbuf, errbufsize, "line %d: too many atom force records\n", lineNumber);
+                                       goto exit;
+                               }
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               ap->f.x = dbuf[0];
+                               ap->f.y = dbuf[1];
+                               ap->f.z = dbuf[2];
+                               i++;
+                       }
+                       continue;
+               } else if (strstr(buf, "!:parameter") == buf) {
+                       Parameter *par = mp->par;
+                       if (par == NULL) {
+                               mp->par = ParameterNew();
+                               par = mp->par;
+                       }
+                       bufp = NULL;
+                       i = 0;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
+                               if (j < 0) {
+                                       snprintf(errbuf, errbufsize, "%s", bufp);
+                                       goto exit;
+                               }
+                               i += j;
+                       }
+                       if (bufp != NULL) {
+                               MyAppCallback_setConsoleColor(1);
+                               MyAppCallback_showScriptMessage("%s", bufp);
+                               MyAppCallback_setConsoleColor(0);
+                               free(bufp);
+                       }
+                       continue;
+               }
+               /*  Unknown sections are silently ignored  */
+       }
+
+       MoleculeCleanUpResidueTable(mp);
+       if (mp->arena != NULL)
+               md_arena_set_molecule(mp->arena, mp);
+
+exit:
+       fclose(fp);
+       if (errbuf[0] != 0) {
+               free(mp);
+               return -1;
+       } else {
+               MoleculeExchange(mp, mol);
+               MoleculeRelease(mp);
+       }
+       return 0;
+       
+}
+
+int
+MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       char buf[1024];
+       char *p;
+       int section = -1;
+       int i, j, err, fn;
+       int lineNumber;
+       Int ibuf[12];
+       Vector *frames = NULL;
+       Atom *ap;
+       err = 0;
+       if (errbuf == NULL) {
+               errbuf = buf;
+               errbufsize = 1024;
+       }
+       errbuf[0] = 0;
+       if (mp == NULL)
+               mp = MoleculeNew();
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return 1;
+       }
+/*     flockfile(fp); */
+       lineNumber = 0;
+       fn = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               if (strncmp(buf, "PSF", 3) == 0) {
+                       section = 0;
+                       continue;
+               } else {
+                       for (p = buf; *p != 0 && isspace(*p); p++) {}
+                       if (*p == 0) {
+                               section++;
+                               continue;
+                       }
+               }
+               if (strstr(buf, "!COORD") != NULL) {
+                       /*  Extended psf file with coordinates  */
+                       if (fn > 0) {
+                               /*  Allocate a temporary storage for frames  */
+                               size_t size = sizeof(Vector) * mp->natoms * fn;
+                               if (frames == NULL)
+                                       frames = (Vector *)malloc(size);
+                               else
+                                       frames = (Vector *)realloc(frames, size);
+                               if (frames == NULL)
+                                       goto panic;
+                       #if 0
+                               if (fn == 1) {
+                                       /*  Copy the coordinates of the first frame  */
+                                       for (i = 0; i < mp->natoms; i++) {
+                                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                                               frames[i] = ap->r;
+                                       }
+                               }
+                               /*  Copy the coordinates of the last frame to the newly created frame  */
+                               memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
+                       #endif
+                       }
+                       /*  Read coordinates  */
+                       for (i = 0; i < mp->natoms; i++) {
+                               double dval[3];
+                               Vector r;
+                               if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                       err = 1;
+                                       snprintf(errbuf, errbufsize, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
+                                       goto exit;
+                               }
+                               if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
+                                       err = 1;
+                                       snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
+                                       goto exit;
+                               }
+                               r.x = dval[0];
+                               r.y = dval[1];
+                               r.z = dval[2];
+                               if (fn == 0)
+                                       ATOM_AT_INDEX(mp->atoms, i)->r = r;
+                               else
+                                       frames[mp->natoms * (fn - 1) + i] = r;
+                       }
+                       fn++;
+                       continue;
+               }
+               
+               if (section == 2) {
+                       /*  Atoms  */
+                       Int natoms;
+                       ReadFormat(buf, "I8", &natoms);
+                       if (mp->atoms != NULL)
+                               free(mp->atoms);
+                       if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
+                               goto panic;
+                       mp->nresidues = 0;
+                       for (i = 0; i < natoms; i++) {
+                               struct {
+                                       char segName[5], resName[4], atomName[5], atomType[3], element[3];
+                                       Int serial;
+                               } w;
+                               memset(&w, 0, sizeof(w));
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                       err = 1;
+                                       snprintf(errbuf, errbufsize, "line %d: premature end of file while reading atoms", lineNumber);
+                                       goto exit;
+                               }
+                               ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
+                                       &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
+                                       w.atomType, &ap->charge, &ap->weight);
+                               strncpy(ap->segName, w.segName, 4);
+                               strncpy(ap->resName, w.resName, 3);
+                               strncpy(ap->aname, w.atomName, 4);
+                               ap->type = AtomTypeEncodeToUInt(w.atomType);
+                               /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
+                               ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
+                               ElementToString(ap->atomicNumber, w.element);
+                               strncpy(ap->element, w.element, 2);
+                       /*      w.element[0] = 0;
+                               for (p = w.atomName; *p != 0; p++) {
+                                       if (isalpha(*p) && *p != '_') {
+                                               w.element[0] = toupper(*p);
+                                               if (isalpha(p[1]) && p[1] != '_') {
+                                                       w.element[1] = toupper(p[1]);
+                                                       w.element[2] = 0;
+                                               } else {
+                                                       w.element[1] = 0;
+                                               }
+                                               break;
+                                       }
+                               }
+                               strncpy(ap->element, w.element, 2);
+                               ap->atomicNumber = ElementToInt(w.element); */
+                               if (w.resName[0] == 0)
+                                       strncpy(ap->resName, "XXX", 3);
+                               if (ap->resSeq > mp->nresidues)
+                                       mp->nresidues = ap->resSeq;
+                       }
+                       if (mp->residues != NULL)
+                               free(mp->residues);
+                       if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
+                               goto panic;
+                       for (i = 0; i < mp->natoms; i++) {
+                               j = mp->atoms[i].resSeq;
+                               if (mp->residues[j][0] == 0)
+                                       strncpy(mp->residues[j], mp->atoms[i].resName, 4);
+                       }
+                       continue;
+               } else if (section == 3) {
+                       /*  Bonds  */
+                       Int nbonds;
+                       Int *bp;
+                       ReadFormat(buf, "I8", &nbonds);
+                       if (mp->bonds != NULL)
+                               free(mp->bonds);
+                       if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
+                               goto panic;
+                       bp = mp->bonds;
+                       for (i = 0; i < nbonds; i += 4) {
+                               if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: premature end of file while reading bonds", lineNumber);
+                                       err = 1;
+                                       goto exit;
+                               }
+                               ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
+                                       ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
+                               for (j = 0; j < 4 && i + j < nbonds; j++) {
+                                       Int b1, b2;
+                                       Atom *ap;
+                                       b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
+                                       b2 = ibuf[j * 2 + 1] - 1;
+                                       if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
+                                               snprintf(errbuf, errbufsize, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
+                                               err = 1;
+                                               goto exit;
+                                       }
+                                       *bp++ = b1;
+                                       *bp++ = b2;
+                                       ap = ATOM_AT_INDEX(mp->atoms, b1);
+                                       if (ap->nconnects < ATOMS_MAX_CONNECTS)
+                                               ap->connects[ap->nconnects++] = b2;
+                                       else {
+                                               snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b1+1, ATOMS_MAX_CONNECTS);
+                                               err = 1;
+                                               goto exit;
+                                       }
+                                       ap = ATOM_AT_INDEX(mp->atoms, b2);
+                                       if (ap->nconnects < ATOMS_MAX_CONNECTS)
+                                               ap->connects[ap->nconnects++] = b1;
+                                       else {
+                                               snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b2+1, ATOMS_MAX_CONNECTS);
+                                               err = 1;
+                                               goto exit;
+                                       }
+                               }
+                       }
+                       continue;
+               } else if (section == 4) {
+                       /*  Angles  */
+                       Int nangles;
+                       Int *gp;
+                       ReadFormat(buf, "I8", &nangles);
+                       if (mp->angles != NULL)
+                               free(mp->angles);
+                       if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
+                               goto panic;
+                       gp = mp->angles;
+                       for (i = 0; i < nangles; i += 3) {
+                               if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: premature end of file while reading angles", lineNumber);
+                                       err = 1;
+                                       goto exit;
+                               }
+                               ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
+                                       ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
+                               for (j = 0; j < 3 && i + j < nangles; j++) {
+                                       Int a1, a2, a3;
+                                       a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
+                                       a2 = ibuf[j * 3 + 1] - 1;
+                                       a3 = ibuf[j * 3 + 2] - 1;
+                                       if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
+                                               snprintf(errbuf, errbufsize, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
+                                               err = 1;
+                                               goto exit;
+                                       }
+                                       *gp++ = a1;
+                                       *gp++ = a2;
+                                       *gp++ = a3;
+                               }
+                       }
+                       continue;
+               } else if (section == 5 || section == 6) {
+                       /*  Dihedrals and Impropers  */
+                       Int ndihedrals;
+                       Int *dp;
+                       ReadFormat(buf, "I8", &ndihedrals);
+                       if (section == 5) {
+                               if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
+                                       goto panic;
+                               dp = mp->dihedrals;
+                       } else {
+                               if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
+                                       goto panic;
+                               dp = mp->impropers;
+                       }
+                       for (i = 0; i < ndihedrals; i += 2) {
+                               if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                       fclose(fp);
+                                       snprintf(errbuf, errbufsize, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
+                                       err = 1;
+                                       goto exit;
+                               }
+                               ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
+                               for (j = 0; j < 2 && i + j < ndihedrals; j++) {
+                                       Int d1, d2, d3, d4;
+                                       d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
+                                       d2 = ibuf[j * 4 + 1] - 1;
+                                       d3 = ibuf[j * 4 + 2] - 1;
+                                       d4 = ibuf[j * 4 + 3] - 1;
+                                       if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
+                                               snprintf(errbuf, errbufsize, "line %d: The %s %d-%d-%d-%d angle includes non-existent atom", lineNumber, (section == 5 ? "dihedral" : "improper"), d1+1, d2+1, d3+1, d4+1);
+                                               err = 1;
+                                               goto exit;
+                                       }
+                                       *dp++ = d1;
+                                       *dp++ = d2;
+                                       *dp++ = d3;
+                                       *dp++ = d4;
+                               }
+                       }
+                       continue;
+               }
+       }
+       
+       /*  Create frames for each atom if necessary  */
+       if (fn > 1) {
+               for (i = 0; i < mp->natoms; i++) {
+                       ap = ATOM_AT_INDEX(mp->atoms, i);
+                       ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
+                       if (ap->frames == NULL)
+                               goto panic;
+                       ap->nframes = fn;
+                       for (j = 0; j < fn; j++)
+                               ap->frames[j] = frames[mp->natoms * j + i];
+               }
+               free(frames);
+               frames = NULL;
+       }
+
+  exit:
+/*     funlockfile(fp); */
+       fclose(fp);
+       mp->nframes = -1;  /*  Should be recalculated later  */
+       if (err)
+               return 1;
+       else if (section == -1)
+               return -1;
+       return 0;
+  panic:
+       Panic("low memory while reading structure file %s", fname);
+       return 1; /* not reached */
+}
+
+/* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
+static int
+sMoleculeSymopStringsToTransform(char **symops, Transform tr)
+{
+       int i;
+       char *symop;
+       memset(tr, 0, sizeof(Transform));
+       for (i = 0; i < 3; i++) {
+               symop = symops[i];
+               if (symop == NULL)
+                       return 1;
+               while (*symop != 0) {
+                       int sn = 1;
+                       while (isspace(*symop))
+                               symop++;
+                       if (*symop == 0 || *symop == '\r' || *symop == 'n')
+                               break;
+                       if (*symop == '-') {
+                               sn = -1;
+                               symop++;
+                       } else if (*symop == '+') {
+                               sn = 1;
+                               symop++;
+                       }
+                       while (isspace(*symop))
+                               symop++;
+                       if (*symop == '.' || isdigit(*symop)) {
+                               /*  Numerical offset  */
+                               double d = strtod(symop, &symop);
+                               if (*symop == '/') {
+                                       double dd = strtod(symop + 1, &symop);
+                                       if (dd > 0)
+                                               d /= dd;
+                                       else
+                                               return 1;  /*  Bad format  */
+                               }
+                               tr[9 + i] = d * sn;
+                       } else if (*symop == 'x' || *symop == 'X') {
+                               tr[i * 3] = sn;
+                               symop++;
+                       } else if (*symop == 'y' || *symop == 'Y') {
+                               tr[i * 3 + 1] = sn;
+                               symop++;
+                       } else if (*symop == 'z' || *symop == 'Z') {
+                               tr[i * 3 + 2] = sn;
+                               symop++;
+                       } else return 1;  /*  Bad format  */
+               } /* end while (*symop != 0) */
+       }
+       return 0;
+}
+
+static void
+sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
+{
+       int i, j;
+       Transform tr;
+       if (num <= 0)
+               num = mp->nsyms;
+       for (i = 0; i < num; i++) {
+               memmove(tr, mp->syms[i], sizeof(Transform));
+               TransformMul(tr, gtr, tr);
+               for (j = 9; j < 12; j++) {
+                       if (tr[j] >= 1.0)
+                               tr[j] -= 1.0;
+                       else if (tr[j] <= 0.0)
+                               tr[j] += 1.0;
+               }
+               AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
+       }
+}
+
+static char *
+sChomp(char *buf)
+{
+       char *p = buf + strlen(buf) - 1;
+       if (p >= buf && (*p == '\n' || *p == '\r')) {
+               *p = 0;
+               if (--p >= buf && (*p == '\n' || *p == '\r')) {
+                       *p = 0;
+               }
+       }
+       return buf;
+}
+
+int
+MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       char buf[1024];
+       int section = -1;
+       int lineNumber;
+       int cellType;
+       Int ibuf[12];
+       Double fbuf[12];
+       Int *bonds, nbonds;
+       if (errbuf == NULL) {
+               errbuf = buf;
+               errbufsize = 1024;
+       }
+       errbuf[0] = 0;
+       if (mp == NULL)
+               mp = MoleculeNew();
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return 1;
+       }
+       lineNumber = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               if (section == -1) {
+                       /*  Title  */
+                       section = 0;
+                       continue;
+               }
+               if (section == 0) {
+                       /*  XtalCell  */
+                       ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
+                       cellType = ibuf[0];
+                       MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
+                       section = 1;
+                       continue;
+               }
+               if (section == 1) {
+                       /*  Symmetry  */
+                       Transform tr;
+                       if (cellType == 0) {
+                               ReadFormat(buf, "I1F14F3F3F3F15F3F3F3F15F3F3F3", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6, fbuf+7, fbuf+8, fbuf+9, fbuf+10, fbuf+11);
+                               tr[0] = fbuf[1];
+                               tr[1] = fbuf[2];
+                               tr[2] = fbuf[3];
+                               tr[3] = fbuf[5];
+                               tr[4] = fbuf[6];
+                               tr[5] = fbuf[7];
+                               tr[6] = fbuf[9];
+                               tr[7] = fbuf[10];
+                               tr[8] = fbuf[11];
+                               tr[9] = fbuf[0];
+                               tr[10] = fbuf[4];
+                               tr[11] = fbuf[8];
+                       } else {
+                               char *symops[3], *brks;
+                               sChomp(buf);
+                               memset(tr, 0, sizeof(Transform));
+                               ReadFormat(buf, "I1", ibuf);
+                               symops[0] = strtok_r(buf + 1, ", ", &brks);
+                               symops[1] = strtok_r(NULL, ", ", &brks);
+                               symops[2] = strtok_r(NULL, ", ", &brks);
+                               if (sMoleculeSymopStringsToTransform(symops, tr)) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
+                                       return 1;
+                               }
+                       }
+                       if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
+                               goto panic;
+                       if (ibuf[0] != 0)
+                               section = 2;
+                       continue;
+               }
+               if (section == 2) {      /*  Atoms  */
+                       char name[8];
+                       Atom *ap;
+                       int atomType;
+                       int atomIndex = mp->natoms;
+                       ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
+                       memset(ap, 0, gSizeOfAtomRecord);
+                       ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
+                       strncpy(ap->aname, name, 4);
+                       ap->r.x = fbuf[0];
+                       ap->r.y = fbuf[1];
+                       ap->r.z = fbuf[2];
+                       MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
+               /*      ap->atomicNumber = AtomNameToElement(ap->name);
+                       ElementToString(ap->atomicNumber, ap->element); */
+               /*      sAtomSetElement(ap, -1, ap->name); */
+                       guessElement(ap);
+                       atomType = fbuf[3];
+                       if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                               snprintf(errbuf, errbufsize, "unexpected end of file");
+                               return 1;
+                       }
+                       ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
+                       atomType = fbuf[6];
+                       if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
+                               /*  Anisotropic thermal parameters  */
+                               MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4]);
+                       }
+                       if (ibuf[0] != 0)
+                               section = 3;
+                       continue;
+               }
+       }
+       fclose(fp);
+       MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
+       if (nbonds > 0) {
+               MoleculeAddBonds(mp, nbonds, bonds);
+               free(bonds);
+       }
+       mp->nframes = -1;  /*  Should be recalculated later  */
+       return 0;
+  panic:
+       Panic("low memory while reading structure file %s", fname);
+       return -1; /* not reached */
+}
+
+int
+MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       char buf[1024];
+       char *p1, *p2;
+       int n;
+       int lineNumber;
+       int latticeType;
+       int currentResSeq = 0;
+       char currentResName[6];
+       Transform tr;
+       int ibuf[12];
+       float fbuf[12];
+       Int nsfacs = 0;
+       Int nbonds, *bonds;
+       char (*sfacs)[4] = NULL;
+
+       if (errbuf == NULL) {
+               errbuf = buf;
+               errbufsize = 1024;
+       }
+       errbuf[0] = 0;
+       if (mp == NULL)
+               mp = MoleculeNew();
+       currentResName[0] = 0;
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return 1;
+       }
+       lineNumber = 0;
+       tr[0] = tr[4] = tr[8] = 1;
+       tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
+       if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
+               goto panic;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               if (strncmp(buf, "CELL", 4) == 0) {
+                       /*  XtalCell  */
+                       sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
+                       MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
+                       continue;
+               } else if (strncmp(buf, "SFAC", 4) == 0) {
+                       sChomp(buf);
+                       for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
+                               char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
+                               if (pp == NULL)
+                                       goto panic;
+                               strncpy(pp, p1, 3);
+                               pp[3] = 0;
+                       }
+                       continue;
+               } else if (strncmp(buf, "LATT", 4) == 0) {
+                       sscanf(buf + 4, " %d", &latticeType);
+                       continue;
+               } else if (strncmp(buf, "SYMM", 4) == 0) {
+                       char *symops[3], *brks;
+                       memset(tr, 0, sizeof(Transform));
+               //      ReadFormat(buf + 4, "I1", ibuf);
+                       sChomp(buf);
+                       symops[0] = strtok_r(buf + 4, ",", &brks);
+                       symops[1] = strtok_r(NULL, ",", &brks);
+                       symops[2] = strtok_r(NULL, ",", &brks);
+                       if (sMoleculeSymopStringsToTransform(symops, tr)) {
+                               snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
+                               return 1;
+                       }
+                       if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
+                               goto panic;
+                       continue;
+               } else if (strncmp(buf, "RESI", 4) == 0) {
+                       for (p1 = buf + 4; isspace(*p1); p1++);
+                       if (isalpha(*p1)) {
+                               for (p2 = p1 + 1; isalnum(*p2); p2++);
+                               *p2 = 0;
+                               strncpy(currentResName, p1, 4);
+                               currentResName[4] = 0;
+                               p1 = p2 + 1;
+                       } else currentResName[0] = 0;
+                       sscanf(buf + 4, " %d", &currentResSeq);
+                       continue;
+               } else {
+                       /* Atom name: [A-Za-z]{1,2}[0-9]*  */
+                       for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
+                       if (p1 > buf) {
+                               while (isdigit(*p1))
+                                       p1++;
+                       }
+                       if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
+                               /*  Atom  */
+                               Atom *ap;
+                               char cont[4];
+                               int atomIndex = mp->natoms;
+                               ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
+                               memset(ap, 0, gSizeOfAtomRecord);
+                               strncpy(ap->aname, buf, 4);
+                               n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
+                               if (n == 8 && strcmp(cont, "=") == 0) {
+                                       if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                               snprintf(errbuf, errbufsize, "line %d: unexpected end of file within the atom cards", lineNumber);
+                                               return 1;
+                                       }
+                                       sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
+                                       n = 10;   /*  Aniso  */
+                               } else n = 5; /*  Iso  */
+                               ap->r.x = fbuf[0];
+                               ap->r.y = fbuf[1];
+                               ap->r.z = fbuf[2];
+                               MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
+                               ap->occupancy = fbuf[3];
+                               if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
+                                       strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
+                                       ap->element[2] = 0;
+                               /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
+                               /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
+                                       ap->atomicNumber = ElementToInt(ap->element); */
+                       /*      } else {
+                                       sAtomSetElement(ap, -1, ap->name); */
+                               /*      ap->atomicNumber = AtomNameToElement(ap->name);
+                                       ElementToString(ap->atomicNumber, ap->element); */
+                               }
+                               guessElement(ap);
+                               if (n == 5)
+                                       ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
+                               else
+                                       MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8]);
+                               ap->resSeq = currentResSeq;
+                               strncpy(ap->resName, currentResName, 4);
+                       }
+                       continue;
+               }
+       }
+       fclose(fp);
+
+       /*  Add symmetry operations according to the lattice type  */
+       switch (latticeType < 0 ? -latticeType : latticeType) {
+               static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
+               static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
+               static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
+               static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
+               static Transform tr_r1 = {0, -1, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0};
+               static Transform tr_r2 = {-1, 1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0};
+               case 1:  /* P */
+                       break;
+               case 2:  /* I */
+                       sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
+                       break;
+               case 3:  /* Rhombohedral obverse on hexagonal axes  */
+                       n = mp->nsyms;
+                       sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
+                       sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
+                       break;
+               case 4:  /* F */
+                       n = mp->nsyms;
+                       sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
+                       sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
+                       sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
+                       break;
+               case 5:  /* A */
+                       sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
+                       break;
+               case 6:  /* B */
+                       sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
+                       break;
+               case 7:  /* C */
+                       sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
+                       break;
+       }
+               
+       if (latticeType > 0) {
+               static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
+               sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
+       }
+       
+       MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
+       if (nbonds > 0) {
+               MoleculeAddBonds(mp, nbonds, bonds);
+               free(bonds);
+       }
+       mp->nframes = -1;  /*  Should be recalculated later  */
+       return 0;
+  panic:
+       Panic("low memory while reading structure file %s", fname);
+       return -1; /* not reached */
+}
+
+static void
+sSeparateTokens(char *inString, char **outPtr, int size)
+{
+       char *p;
+       int i;
+       for (i = 0; i < size; i++) {
+               p = strtok((i == 0 ? inString : NULL), " \n");
+               if (p == NULL)
+                       break;
+               outPtr[i] = p;
+       }
+       while (i < size) {
+               outPtr[i++] = NULL;
+       }
+}
+
+static int
+sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
+{
+       char buf[256];
+       Int i, n;
+       *((void **)basep) = NULL;
+       *countp = 0;
+       if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
+               return 4;  /*  Out of memory  */
+       n = 0;
+       while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
+               char *tokens[16], *p;
+               sSeparateTokens(buf, tokens, 16);
+               for (i = 0; i < 16; i++) {
+                       if (tokens[i] == NULL)
+                               break;
+                       if (size == sizeof(Int)) {
+                               (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
+                       } else if (size == sizeof(Double)) {
+                               (*((Double **)basep))[n] = strtod(tokens[i], &p);
+                       } else return -1;  /*  Internal error  */
+                       if (tokens[i] == p || *p != 0)
+                               return 1;  /*  Non-digit character  */
+                       if (++n == num) {
+                               if (i < 15 && tokens[i + 1] != NULL)
+                                       return 2;  /*  Too many data  */
+                               return 0;  /*  All data are successfully read  */
+                       }
+               }
+       }
+       return 3;  /*  Unexpected EOF  */                       
+}
+
+static int
+sSetupGaussianCoefficients(BasisSet *bset)
+{
+       ShellInfo *sp;
+       PrimInfo *pp;
+       int i, j, k;
+       Double *dp, d;
+       
+       /*  Cache the contraction coefficients for efficient calculation  */
+       /*  Sum up the number of components for all primitives  */
+       for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
+               sp->cn_idx = k;
+               k += sp->nprim * sp->ncomp;
+       }
+       /*  Allocate memory for the cached values  */
+       if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
+               return 1;
+       /*  Iterate over all primitives  */
+       dp = bset->cns;
+       for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
+               for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
+                       switch (sp->sym) {
+                               case kGTOType_S:
+                                       // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
+                                       *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
+                                       break;
+                               case kGTOType_P:
+                                       // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
+                                       d = pp->C * pow(pp->A, 1.25) * 1.425410941;
+                                       *dp++ = d;
+                                       *dp++ = d;
+                                       *dp++ = d;
+                                       break;
+                               case kGTOType_SP:
+                                       *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
+                                       d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
+                                       *dp++ = d;
+                                       *dp++ = d;
+                                       *dp++ = d;
+                                       break;
+                               case kGTOType_D:
+                                       //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
+                                       //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
+                                       d = pp->C * pow(pp->A, 1.75);
+                                       dp[0] = dp[1] = dp[2] = d * 1.645922781;
+                                       dp[3] = dp[4] = dp[5] = d * 2.850821881;
+                                       dp += 6;
+                                       break;
+                               case kGTOType_D5:
+                                       //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
+                                       //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
+                                       //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
+                                       d = pp->C * pow(pp->A, 1.75);
+                                       dp[0] = d * 0.822961390;
+                                       dp[1] = dp[2] = dp[4] = d * 2.850821881;
+                                       dp[3] = d * 1.425410941;
+                                       dp += 5;
+                                       break;
+                       }
+               }
+       }
+       return 0;
+}
+
+int
+MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       char buf[1024];
+       int lineNumber;
+       int natoms, nbasis, i, j, k, n, mxbond, retval, nmos, nprims;
+       BasisSet *bset;
+       ShellInfo *sp;
+       PrimInfo *pp;
+       Int nary;
+       Int *iary;
+       Double *dary;
+       Atom *ap;
+       Vector *vp;
+       Double w;
+
+       if (errbuf == NULL) {
+               errbuf = buf;
+               errbufsize = 1024;
+       }
+       errbuf[0] = 0;
+       if (mp == NULL)
+               mp = MoleculeNew();
+       bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
+       if (bset == NULL)
+               goto panic;
+       mp->bset = bset;
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return 1;
+       }
+       lineNumber = 0;
+       natoms = nbasis = -1;
+       mxbond = 0;
+       nmos = 0;
+       nprims = 0;
+       nary = 0;
+       iary = NULL;
+       dary = NULL;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               char *tokens[16];
+               char *p = buf + 41;
+               while (p > buf && *p == ' ')
+                       p--;
+               p[1] = 0;
+               sSeparateTokens(buf + 42, tokens, 16);
+               if (strcmp(buf, "Number of atoms") == 0) {
+                       if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       /*  Allocate atom records (all are empty for now)  */
+                       AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
+                       /*  Also allocate atom position array for MO calculations  */
+                       AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
+                       /*  Also allocate nuclear charge array  */
+                       bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
+               } else if (strcmp(buf, "Number of electrons") == 0) {
+                       if (tokens[1] == NULL || (bset->nelectrons = atoi(tokens[1])) <= 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+               } else if (strcmp(buf, "Number of basis functions") == 0) {
+                       if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+               } else if (strcmp(buf, "Atomic numbers") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read atomic numbers", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
+                               ap->atomicNumber = iary[i];
+                               bset->nuccharges[i] = iary[i];
+                               ElementToString(ap->atomicNumber, ap->element);
+                               memmove(ap->aname, ap->element, 4);
+                               if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
+                                       ap->weight = w;
+                       }
+                       free(iary);
+                       iary = NULL;
+               } else if (strcmp(buf, "Nuclear charges") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read nuclear charges", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
+                               bset->nuccharges[i] = dary[i];
+                       }
+                       free(iary);
+                       iary = NULL;
+               } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read cartesian coordinates", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
+                               vp->x = dary[i * 3];
+                               vp->y = dary[i * 3 + 1];
+                               vp->z = dary[i * 3 + 2];
+                               ap->r.x = vp->x * kBohr2Angstrom;
+                               ap->r.y = vp->y * kBohr2Angstrom;
+                               ap->r.z = vp->z * kBohr2Angstrom;
+                       }
+                       free(dary);
+                       dary = NULL;
+               } else if (strcmp(buf, "MxBond") == 0) {
+                       if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+               } else if (strcmp(buf, "IBond") == 0) {
+                       Int bonds[ATOMS_MAX_CONNECTS * 2 + 1], lim;
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read bond information", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       lim = (mxbond > ATOMS_MAX_CONNECTS ? ATOMS_MAX_CONNECTS : mxbond);
+                       for (i = 0; i < natoms; i++) {
+                               for (j = k = 0; j < lim; j++) {
+                                       n = iary[i * mxbond + j] - 1;
+                                       if (n > i) {
+                                               /*  Connect atom i and atom n  */
+                                               bonds[k++] = i;
+                                               bonds[k++] = n;
+                                       }
+                               }
+                               if (k > 0) {
+                                       bonds[k] = kInvalidIndex;
+                                       MoleculeAddBonds(mp, k / 2, bonds);
+                               }
+                       }
+                       free(iary);
+                       iary = NULL;
+               } else if (strcmp(buf, "Shell types") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read shell types", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       /*  Allocate ShellInfo table and store shell type information  */
+                       AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
+                       for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
+                               switch (iary[i]) {
+                                       case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
+                                       case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
+                                       case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
+                                       case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
+                                       case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
+                                               /*  TODO: Support F/F7 type orbitals  */
+                                               /*      case 3: sp->sym = kGTOtype_F;  sp->ncomp = 10; break;
+                                                case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
+                                       default:
+                                               snprintf(errbuf, errbufsize, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
+                                               retval = 2;
+                                               goto cleanup;
+                               }
+                               sp->m_idx = n;
+                               n += sp->ncomp;
+                       }
+                       nmos = n;
+                       free(iary);
+                       iary = NULL;
+               } else if (strcmp(buf, "Number of primitives per shell") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read primitive table", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
+                               sp->nprim = iary[i];
+                               sp->p_idx = n;
+                               n += sp->nprim;
+                       }
+                       nprims = n;
+                       free(iary);
+                       iary = NULL;
+               } else if (strcmp(buf, "Shell to atom map") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read shell-to-atom table", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
+                               sp->a_idx = iary[i] - 1;
+                       }
+                       free(iary);
+                       iary = NULL;
+               } else if (strcmp(buf, "Primitive exponents") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read primitive exponents", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       /*  Allocate PrimInfo table  */
+                       AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
+                       for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
+                               pp->A = dary[i];
+                       }
+                       free(dary);
+                       dary = NULL;
+               } else if (strcmp(buf, "Contraction coefficients") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read contraction coefficients", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
+                               pp->C = dary[i];
+                       }
+                       free(dary);
+                       dary = NULL;
+               } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
+                               pp->Csp = dary[i];
+                       }
+                       free(dary);
+                       dary = NULL;
+               } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nmos) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read alpha orbital energies", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+               } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nmos * bset->nmos) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of MO coefficients: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read MO coefficients", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+               } else if (strcmp(buf, "Total SCF Density") == 0) {
+                       if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nmos * (bset->nmos + 1) / 2) {
+                               snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
+                               retval = 2;
+                               goto cleanup;
+                       }
+                       if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
+                               snprintf(errbuf, errbufsize, "Line %d: cannot read SCF densities", lineNumber);
+                               retval = 2;
+                               goto cleanup;
+                       }
+               }
+       }
+       if (mp->natoms == 0) {
+               snprintf(errbuf, errbufsize, "Atom information is missing");
+               retval = 2;
+               goto cleanup;
+       }
+       if (bset->shells == NULL || bset->priminfos == NULL) {
+               snprintf(errbuf, errbufsize, "Gaussian primitive information is missing");
+               retval = 2;
+               goto cleanup;
+       }
+       if (bset->mo == NULL) {
+               snprintf(errbuf, errbufsize, "MO coefficients were not found");
+               retval = 2;
+               goto cleanup;
+       }
+       if (sSetupGaussianCoefficients(bset) != 0) {
+               snprintf(errbuf, errbufsize, "Internal error during setup MO calculation");
+               retval = 2;
+               goto cleanup;
+       }
+       mp->nframes = -1;
+       retval = 0;
+cleanup:
+       fclose(fp);
+       if (iary != NULL)
+               free(iary);
+       if (dary != NULL)
+               free(dary);
+       if (retval != 0) {
+               if (mp->bset != NULL) {
+                       BasisSetRelease(mp->bset);
+                       mp->bset = NULL;
+               }
+       }
+       return retval;
+panic:
+       Panic("low memory while reading fchk file %s", fname);
+       return -1; /* not reached */    
+}
+
+int
+MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       int newmol = 0;
+       char buf[1024];
+       int lineNumber, i, natoms = 0;
+       int nframes = 0;
+       int n1;
+       int ival[8];
+       double dval[8];
+       char sval[16];
+       Vector *vbuf = NULL;
+       IntGroup *ig;
+       if (errbuf == NULL) {
+               errbuf = buf;
+               errbufsize = 1024;
+       }
+       errbuf[0] = 0;
+       if (mol == NULL) {
+               mol = MoleculeNew();
+       }
+       if (mol->natoms == 0)
+               newmol = 1;
+
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return 1;
+       }
+       
+       /*  ESP is cleared (not undoable!)  */
+       if (mol->elpots != NULL) {
+               free(mol->elpots);
+               mol->elpots = NULL;
+               mol->nelpots = 0;
+       }
+       
+       lineNumber = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+       redo:
+               n1 = 0;
+               if (strncmp(buf, " $DATA", 6) == 0) {
+                       /*  Initial geometry  */
+                       if (!newmol) {
+                               vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
+                       }
+                       i = 0;
+                       ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
+                       ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               if (strncmp(buf, " $END", 5) == 0)
+                                       break;
+                               if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
+                                       snprintf(errbuf, errbufsize, "Line %d: bad format in $DATA section", lineNumber);
+                                       return 2;
+                               }
+                               if (newmol) {
+                                       Atom a;
+                                       memset(&a, 0, sizeof(a));
+                                       strncpy(a.aname, sval, 4);
+                                       a.r.x = dval[1];
+                                       a.r.y = dval[2];
+                                       a.r.z = dval[3];
+                                       a.atomicNumber = (Int)dval[0];
+                                       strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
+                                       a.type = AtomTypeEncodeToUInt(a.element);
+                                       a.weight = WeightForAtomicNumber(a.atomicNumber);
+                                       MoleculeCreateAnAtom(mol, &a, mol->natoms);
+                               } else {
+                                       Atom *ap;
+                                       if (i >= mol->natoms) {
+                                               snprintf(errbuf, errbufsize, "Line %d: too many atoms", lineNumber);
+                                               return 3;
+                                       }
+                                       if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
+                                               snprintf(errbuf, errbufsize, "Line %d: atomic number does not match", lineNumber);
+                                               return 4;
+                                       }
+                                       vbuf[i].x = dval[1];
+                                       vbuf[i].y = dval[2];
+                                       vbuf[i].z = dval[3];
+                               }
+                               /*  Skip until a blank line is found  */
+                               while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                                       int j;
+                                       for (j = 0; buf[j] == ' '; j++);
+                                       if (buf[j] == '\n')
+                                               break;
+                               }
+                               i++;
+                       }
+                       natoms = i;
+                       if (!newmol) {
+                               /*  Set atom positions  */
+                               IntGroup *ig;
+                               if (natoms < mol->natoms) {
+                                       snprintf(errbuf, errbufsize, "Line %d: too few atoms", lineNumber);
+                                       return 5;
+                               }
+                               ig = IntGroupNewWithPoints(0, natoms, -1);
+                               MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
+                               IntGroupRelease(ig);
+                       }
+                       if (vbuf == NULL)
+                               vbuf = (Vector *)calloc(sizeof(Vector), natoms);
+                       nframes = MoleculeGetNumberOfFrames(mol);
+                       continue;
+               } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
+                       /*  Skip three lines  */
+                       ReadLine(buf, sizeof buf, fp, &lineNumber);
+                       ReadLine(buf, sizeof buf, fp, &lineNumber);
+                       ReadLine(buf, sizeof buf, fp, &lineNumber);
+                       for (i = 0; i < natoms; i++) {
+                               if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                       snprintf(errbuf, errbufsize, "Unexpected end of file in reading NSERCH data");
+                                       return 6;
+                               }
+                               if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
+                                       snprintf(errbuf, errbufsize, "Line %d: bad format in NSERCH coordinate data", lineNumber);
+                                       return 7;
+                               }
+                               vbuf[i].x = dval[1];
+                               vbuf[i].y = dval[2];
+                               vbuf[i].z = dval[3];
+                       }
+                       ig = IntGroupNewWithPoints(nframes, 1, -1);
+                       MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf);
+                       IntGroupRelease(ig);
+                       nframes++;
+                       if (n1) {
+                               /*  TODO: read the VEC group  */
+                       }
+                       continue;
+               } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
+                       i = 0;
+                       while ((n1 = ReadLine(buf, sizeof buf, fp, &lineNumber)) > 0) {
+                               Elpot *ep;
+                               if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
+                                       continue;
+                               if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
+                                       break;
+                               ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
+                               ep->pos.x = dval[0];
+                               ep->pos.y = dval[1];
+                               ep->pos.z = dval[2];
+                               ep->esp = dval[3];
+                               i++;
+                       }
+                       if (n1 > 0)
+                               goto redo;  /*  This section has no end line, so the last line should be processed again  */
+                       else break;    /*  End of file encountered  */
+               }  /*  TODO: read MOLPLT info if present  */
+       }
+       if (vbuf != NULL)
+               free(vbuf);
+       if (newmol && mol->nbonds == 0) {
+               /*  Guess bonds  */
+               Int nbonds, *bonds;
+               MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
+               if (nbonds > 0) {
+                       MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
+                       free(bonds);
+               }
+       }
+       return 0;
+}
+
+int
+MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
+{
+       int retval;
+       if (ftype == NULL || *ftype == 0) {
+               const char *cp;
+               cp = strrchr(fname, '.');
+               if (cp != NULL)
+                       ftype = cp + 1;
+               else {
+                       cp = guessMoleculeType(fname);
+                       if (strcmp(cp, "???") != 0)
+                               ftype = cp;
+               }
+       }
+       if (strcasecmp(ftype, "pdb") == 0) {
+               retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
+       }
+       if (retval != 0) {
+               /*  Try all formats once again  */
+               retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
+       }
+       return retval;
+}
+
+int
+MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       char buf[1024];
+       char *p;
+       int lineNumber;
+       int i, j, new_unit, retval;
+       Atom *ap;
+       Int ibuf[12];
+       Int entries = 0;
+       retval = 0;
+       if (errbuf == NULL) {
+               errbuf = buf;
+               errbufsize = 1024;
+       }
+       errbuf[0] = 0;
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return -1;
+       }
+/*     flockfile(fp); */
+       if (mp->natoms == 0)
+               new_unit = 1;
+       else new_unit = 0;
+       lineNumber = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               if (strncmp(buf, "END", 3) == 0)
+                       break;
+               if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
+                       struct {
+                               Int serial, intCharge, resSeq;
+                               Vector r;
+                               Double occ, temp;
+                               char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
+                       } w;
+                       memset(&w, 0, sizeof(w));
+                       ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
+                               &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
+                               w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
+                       if (w.atomName[0] == 0) {
+                               continue;  /*  Atom name is empty  */
+                       }
+                       /*  A workaround for residue number >= 10000 (XPLOR style)  */
+                       if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
+                               w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
+                       } else {
+                               w.resSeq = atoi(w.resSeqStr);
+                       }
+                       if (w.element[0] == 0) {
+                               /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
+                               for (p = w.atomName; *p != 0; p++) {
+                                       if (isalpha(*p) && *p != '_') {
+                                               w.element[0] = toupper(*p);
+                                               if (isalpha(p[1]) && p[1] != '_') {
+                                                       w.element[1] = toupper(p[1]);
+                                                       w.element[2] = 0;
+                                               } else {
+                                                       w.element[1] = 0;
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+                       if (w.occStr[0] == 0)
+                               w.occ = 1.0;
+                       else
+                               w.occ = atof(w.occStr);
+                       if (w.serial <= 0) {
+                               snprintf(errbuf, errbufsize, "line %d: non-positive atom number %d", lineNumber, w.serial);
+                               retval = 1;
+                               goto abort;
+                       }
+                       w.serial--;  /*  The internal atom number is 0-based  */
+                       if (w.serial >= mp->natoms) {
+                               if (new_unit) {
+                                       ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
+                               } else {
+                                       snprintf(errbuf, errbufsize, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
+                                       retval = 1;
+                                       goto abort;
+                               }
+                       }
+                       ap = ATOM_AT_INDEX(mp->atoms, w.serial);
+                       ap->r = w.r;
+                       ap->occupancy = w.occ;
+                       ap->tempFactor = w.temp;
+                       if (new_unit) {
+                               if (w.segName[0] == 0)
+                                       strncpy(w.segName, "MAIN", 4);
+                               strncpy(ap->segName, w.segName, 4);
+                               ap->resSeq = w.resSeq;
+                               strncpy(ap->resName, w.resName, 4);
+                               strncpy(ap->aname, w.atomName, 4);
+                               strncpy(ap->element, w.element, 2);
+                               ap->intCharge = w.intCharge;
+                               if (ap->resSeq > 0) {
+                                       if (ap->resSeq < mp->nresidues) {
+                                               /*  Update the resName according to residues[]  */
+                                               strncpy(ap->resName, mp->residues[ap->resSeq], 4);
+                                       } else {
+                                               /*  Register the resName to residues[]  */
+                                               AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
+                                       }
+                               } else {
+                                       ap->resSeq = 0;
+                                       strcpy(ap->resName, "XXX");
+                                       if (mp->nresidues == 0)
+                                               AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
+                               }
+                               i = ElementToInt(ap->element);
+                               if (i >= 0)
+                                       ap->weight = gDispAtomParameters[i].weight;
+                       }
+                       entries++;
+               } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
+                       i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
+                               ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
+                               ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
+                               ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
+                       if (i >= 2) {
+                               Int bbuf[25];
+                               int bi;
+                               for (j = 0; j < i; j++) {
+                                       if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
+                                               snprintf(errbuf, errbufsize, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
+                                               retval = 1;
+                                               goto abort;
+                                       } else if (ibuf[j] == 0)
+                                               break;
+                               }
+                               i = j;
+                               if (i < 2)
+                                       continue;
+                               for (j = 1, bi = 0; j < i; j++) {
+                                       if (ibuf[0] < ibuf[j]) {
+                                               bbuf[bi * 2] = ibuf[0] - 1;
+                                               bbuf[bi * 2 + 1] = ibuf[j] - 1;
+                                               bi++;
+                                       }
+                               }
+                               if (bi == 0)
+                                       continue;
+                               bbuf[bi * 2] = -1;
+                               retval = MoleculeAddBonds(mp, bi, bbuf);
+                               if (retval < 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: bad bond specification", lineNumber);
+                                       retval = 1;
+                                       goto abort;
+                               }
+                       }
+               }
+       }
+/*     funlockfile(fp); */
+       fclose(fp);
+       if (new_unit) {
+               /*  Renumber atoms if some atom number is unoccupied  */
+               int *old2new, oldidx, newidx;
+               old2new = (int *)calloc(sizeof(int), mp->natoms);
+               if (old2new == NULL) {
+                       snprintf(errbuf, errbufsize, "Out of memory");
+                       retval = 1;
+                       goto abort;
+               }
+               for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
+                       ap = ATOM_AT_INDEX(mp->atoms, oldidx);
+                       if (ap->aname[0] != 0) {
+                               old2new[oldidx] = newidx;
+                               if (oldidx > newidx)
+                                       memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
+                               newidx++;
+                       }
+               }
+               mp->natoms = newidx;
+               if (oldidx > newidx) {
+                       /*  Renumber the connects and bonds  */
+                       for (i = 0; i < mp->natoms; i++) {
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               for (j = 0; j < ap->nconnects; j++) {
+                                       ap->connects[j] = old2new[ap->connects[j]];
+                               }
+                       }
+                       for (i = 0; i < mp->nbonds * 2; i++) {
+                               mp->bonds[i] = old2new[mp->bonds[i]];
+                       }
+               }
+               retval = MoleculeRebuildTablesFromConnects(mp);
+               if (retval != 0) {
+                       /*  This error may not happen  */
+                       snprintf(errbuf, errbufsize, "Cannot build angle/dihedral/improper tables");
+                       retval = 1;
+                       goto abort;
+               }
+
+       }
+       mp->nframes = -1;  /*  Should be recalculated later  */
+       if (entries == 0)
+               return 1;  /*  No atoms  */
+       return 0;
+       abort:
+       if (fp != NULL) {
+       /*      funlockfile(fp); */
+               fclose(fp);
+       }
+       if (entries == 0)
+               return 1;  /*  Maybe different format?  */
+       return retval;
+}
+
+int
+MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       DcdRecord dcd;
+       SFloat32 *xp, *yp, *zp;
+       Vector *vp;
+       IntGroup *ig;
+       int n;
+       errbuf[0] = 0;
+       if (mp == NULL || mp->natoms == 0) {
+               snprintf(errbuf, errbufsize, "Molecule is empty");
+               return 1;
+       }
+       n = DcdOpen(fname, &dcd);
+       if (n != 0) {
+               switch (n) {
+                       case -2: snprintf(errbuf, errbufsize, "Cannot open file"); break;
+                       case 1:  snprintf(errbuf, errbufsize, "Premature EOF encountered"); break;
+                       case 2:  snprintf(errbuf, errbufsize, "Bad block length of the first section"); break;
+                       case 3:  snprintf(errbuf, errbufsize, "\"CORD\" signature is missing"); break;
+                       case 4:  snprintf(errbuf, errbufsize, "Bad termination of the first section"); break;
+                       case 5:  snprintf(errbuf, errbufsize, "The title section is not correct"); break;
+                       case 6:  snprintf(errbuf, errbufsize, "The atom number section is not correct"); break;
+                       default: snprintf(errbuf, errbufsize, "Read error in dcd file"); break;
+               }
+       } else {
+               if (dcd.natoms == 0)
+                       snprintf(errbuf, errbufsize, "No atoms were found in the dcd file");
+               else if (dcd.nframes == 0)
+                       snprintf(errbuf, errbufsize, "No frames were found in the dcd file");
+       }
+       if (errbuf[0] != 0) {
+               if (n == 0)
+                       DcdClose(&dcd);
+               return 1;
+       }
+
+       vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
+       xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
+       yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
+       zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
+       ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
+       if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot allocate memory");
+               if (vp) free(vp);
+               if (xp) free(xp);
+               if (yp) free(yp);
+               if (zp) free(zp);
+               if (ig) IntGroupRelease(ig);
+               return 1;
+       }
+       for (n = 0; n < dcd.nframes; n++) {
+               int i;
+               Vector *vpp;
+               if (DcdReadFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
+                       snprintf(errbuf, errbufsize, "Read error in dcd file");
+                       goto exit;
+               }
+               for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
+                       vpp->x = xp[i];
+                       vpp->y = yp[i];
+                       vpp->z = zp[i];
+               }
+       }
+       if (MoleculeInsertFrames(mp, ig, vp) < 0)
+               snprintf(errbuf, errbufsize, "Cannot insert frames");
+       if (dcd.with_unitcell) {
+               Vector ax, ay, az, orig;
+               char flags[3] = {1, 1, 1};
+               ax.x = dcd.globalcell[0]; ax.y = ax.z = 0;
+               ay.y = dcd.globalcell[1]; ay.x = ay.z = 0;
+               az.z = dcd.globalcell[2]; az.x = az.y = 0;
+               orig.x = dcd.globalcell[3];
+               orig.y = dcd.globalcell[4];
+               orig.z = dcd.globalcell[5];
+               if (MoleculeSetPeriodicBox(mp, &ax, &ay, &az, &orig, flags) != 0) {
+                       snprintf(errbuf, errbufsize, "Cannot set unit cell");
+                       goto exit;
+               }
+       }
+       mp->startStep = dcd.nstart;
+       mp->stepsPerFrame = dcd.ninterval;
+       mp->psPerStep = dcd.delta;
+exit:
+       DcdClose(&dcd);
+       free(vp);
+       free(xp);
+       free(yp);
+       free(zp);
+       IntGroupRelease(ig);
+       if (errbuf[0] == 0)
+               return 0;
+       else return 1;
+}
+
+int
+MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       char buf[1024];
+       int lineNumber;
+       int i, retval;
+       Vector v[3], vv;
+       double d[3];
+       int n, flag;
+       char flags[3];
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot open file");
+               return -1;
+       }
+       errbuf[0] = 0;
+       lineNumber = 0;
+       retval = 0;
+       flags[0] = flags[1] = flags[2] = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               if (strncmp(buf, "Bounding box:", 13) == 0) {
+                       for (i = 0; i < 3; i++) {
+                               if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
+                                       snprintf(errbuf, errbufsize, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
+                                       retval = 1;
+                                       goto abort;
+                               }
+                               n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
+                               if (n < 3) {
+                                       vv.x = vv.y = vv.z = 0.0;
+                                       switch (i) {
+                                               case 0: vv.x = d[0]; break;
+                                               case 1: vv.y = d[0]; break;
+                                               case 2: vv.z = d[0]; break;
+                                       }
+                                       if (n == 1 || (n == 2 && d[1] != 0.0))
+                                               flags[i] = 1;
+                               } else {
+                                       vv.x = d[0];
+                                       vv.y = d[1];
+                                       vv.z = d[2];
+                                       if (n == 4)
+                                               flags[i] = (flag != 0);
+                                       else
+                                               flags[i] = (VecLength2(vv) != 0);
+                               }
+                               v[i] = vv;
+                       }
+                       if (mp->cell != NULL)
+                               vv = mp->cell->origin;
+                       else
+                               vv.x = vv.y = vv.z = 0.0;
+                       MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
+               } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
+                       if (mp->cell != NULL) {
+                               v[0] = mp->cell->axes[0];
+                               v[1] = mp->cell->axes[1];
+                               v[2] = mp->cell->axes[2];
+                               memmove(flags, mp->cell->flags, 3);
+                       } else {
+                               v[0].x = 1.0; v[0].y = v[0].z = 0.0;
+                               v[1].y = 1.0; v[1].x = v[1].z = 0.0;
+                               v[2].z = 1.0; v[2].x = v[2].y = 0.0;
+                               flags[0] = flags[1] = flags[2] = 1.0;
+                       }
+                       if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
+                               snprintf(errbuf, errbufsize, "line %d: wrong format for the bounding box origin", lineNumber);
+                               retval = 1;
+                               goto abort;
+                       }
+                       vv.x = d[0];
+                       vv.y = d[1];
+                       vv.z = d[2];
+                       MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
+               }
+       }
+       fclose(fp);
+       return 0;
+abort:
+       if (fp != NULL)
+               fclose(fp);
+       return retval;
+}
+                       
+int
+MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
+{
+       int retval;
+       if (ftype == NULL || *ftype == 0) {
+               const char *cp;
+               cp = strrchr(fname, '.');
+               if (cp != NULL)
+                       ftype = cp + 1;
+               else {
+                       cp = guessMoleculeType(fname);
+                       if (strcmp(cp, "???") != 0)
+                               ftype = cp;
+               }
+       }
+       if (strcasecmp(ftype, "psf") == 0) {
+               retval = MoleculeWriteToPsfFile(mp, fname, errbuf, errbufsize);
+       } else if (strcasecmp(ftype, "pdb") == 0) {
+               retval = MoleculeWriteToPdbFile(mp, fname, errbuf, errbufsize);
+       } else if (strcasecmp(ftype, "tep") == 0) {
+               retval = MoleculeWriteToTepFile(mp, fname, errbuf, errbufsize);
+       } else {
+               snprintf(errbuf, errbufsize, "The file format should be specified");
+               retval = 1;
+       }
+
+       return retval;
+}
+
+int
+MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       int i, j, k, n1, n2;
+       Atom *ap;
+       char bufs[6][8];
+
+       fp = fopen(fname, "wb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
+               return 1;
+       }
+       errbuf[0] = 0;
+
+       fprintf(fp, "!:atoms\n");
+       fprintf(fp, "! idx seg_name res_seq res_name name type weight element atomic_number occupancy temp_factor int_charge\n");
+       n1 = n2 = 0;
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               strncpy(bufs[0], ap->segName, 4);
+               strncpy(bufs[1], ap->resName, 4);
+               strncpy(bufs[2], ap->aname, 4);
+               AtomTypeDecodeToString(ap->type, bufs[3]);
+               strncpy(bufs[4], ap->element, 4);
+               for (j = 0; j < 5; j++) {
+                       bufs[j][4] = 0;
+                       if (bufs[j][0] == 0) {
+                               bufs[j][0] = '_';
+                               bufs[j][1] = 0;
+                       }
+                       for (k = 0; k < 4; k++) {
+                               if (bufs[j][k] > 0 && bufs[j][k] < ' ')
+                                       bufs[j][k] = '_';
+                       }
+               }
+               if (SYMOP_ALIVE(ap->symop))
+                       n1++;
+               if (ap->fix_force != 0)
+                       n2++;
+               fprintf(fp, "%d %s %d %s %s %s %.5f %.5f %s %d %f %f %d\n", i, bufs[0], ap->resSeq, bufs[1], bufs[2], bufs[3], ap->charge, ap->weight, bufs[4], ap->atomicNumber, ap->occupancy, ap->tempFactor, ap->intCharge);
+       }
+       fprintf(fp, "\n");
+       
+       if (n1 > 0) {
+               fprintf(fp, "!:atoms_symop\n");
+               fprintf(fp, "! idx symop symbase\n");
+               for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       int n;
+                       n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
+                       fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if (n2 > 0) {
+               fprintf(fp, "!:atoms_fix\n");
+               fprintf(fp, "! idx fix_force fix_pos\n");
+               for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if ((n1 = MoleculeGetNumberOfFrames(mp)) > 0)
+               n2 = mp->cframe;
+       else
+               n2 = 0;
+       for (i = 0; (i == n2 || i < n1); i++) {
+               fprintf(fp, "!:positions ; frame %d\n", i);
+               fprintf(fp, "! idx x y z\n");
+               for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
+                       Vector *vp;
+                       if (i != n2 && i < ap->nframes)
+                               vp = ap->frames + i;
+                       else
+                               vp = &(ap->r);
+                       fprintf(fp, "%d %.8f %.8f %.8f\n", j, vp->x, vp->y, vp->z);
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if (mp->nbonds > 0) {
+               fprintf(fp, "!:bonds\n");
+               fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
+               for (i = 0; i < mp->nbonds; i++) {
+                       fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if (mp->nangles > 0) {
+               fprintf(fp, "!:angles\n");
+               fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
+               for (i = 0; i < mp->nangles; i++) {
+                       fprintf(fp, "%d %d %d%c", mp->angles[i * 3], mp->angles[i * 3 + 1], mp->angles[i * 3 + 2], (i % 3 == 2 || i == mp->nangles - 1 ? '\n' : ' '));
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if (mp->ndihedrals > 0) {
+               fprintf(fp, "!:dihedrals\n");
+               fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
+               for (i = 0; i < mp->ndihedrals; i++) {
+                       fprintf(fp, "%d %d %d %d%c", mp->dihedrals[i * 4], mp->dihedrals[i * 4 + 1], mp->dihedrals[i * 4 + 2], mp->dihedrals[i * 4 + 3], (i % 2 == 1 || i == mp->ndihedrals - 1 ? '\n' : ' '));
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if (mp->nimpropers > 0) {
+               fprintf(fp, "!:impropers\n");
+               fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
+               for (i = 0; i < mp->nimpropers; i++) {
+                       fprintf(fp, "%d %d %d %d%c", mp->impropers[i * 4], mp->impropers[i * 4 + 1], mp->impropers[i * 4 + 2], mp->impropers[i * 4 + 3], (i % 2 == 1 || i == mp->nimpropers - 1 ? '\n' : ' '));
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if (mp->cell != NULL) {
+               fprintf(fp, "!:periodic_box\n");
+               fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz; fa fb fc\n");
+               for (i = 0; i < 3; i++)
+                       fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
+               fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
+               fprintf(fp, "%d %d %d\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2]);
+               fprintf(fp, "\n");
+
+               fprintf(fp, "!:xtalcell\n");
+               fprintf(fp, "! a b c alpha beta gamma; this info is redundant, periodic_box is used instead\n");
+               fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cell[0], mp->cell->cell[1], mp->cell->cell[2], mp->cell->cell[3], mp->cell->cell[4], mp->cell->cell[5]);
+               fprintf(fp, "\n");
+       }
+       
+       if (mp->nsyms > 0) {
+               fprintf(fp, "!:symmetry_operations\n");
+               fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
+               for (i = 0; i < mp->nsyms; i++) {
+                       Transform *tp = mp->syms + i;
+                       for (j = 0; j < 12; j++)
+                               fprintf(fp, "%11.6f%c", (*tp)[j], (j % 3 == 2 ? '\n' : ' '));
+               }
+               fprintf(fp, "\n");
+       }
+       
+       if (mp->arena != NULL) {
+               MDArena *arena = mp->arena;
+               fprintf(fp, "!:md_parameters\n");
+               fprintf(fp, "log_file %s\n", arena->log_result_name);
+               fprintf(fp, "coord_file %s\n", arena->coord_result_name);
+               fprintf(fp, "vel_file %s\n", arena->vel_result_name);
+               fprintf(fp, "force_file %s\n", arena->force_result_name);
+               fprintf(fp, "debug_file %s\n", arena->debug_result_name);
+               fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
+               fprintf(fp, "step %d\n", arena->step);
+               fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
+               fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
+               fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
+               fprintf(fp, "timestep %g\n", arena->timestep);
+               fprintf(fp, "cutoff %g\n", arena->cutoff);
+               fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
+               fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
+               fprintf(fp, "temperature %g\n", arena->temperature);
+               fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
+               fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
+               fprintf(fp, "random_seed %d\n", arena->random_seed);
+               fprintf(fp, "dielectric %g\n", arena->dielectric);
+               fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
+               fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
+               fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
+               fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
+               fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
+               fprintf(fp, "relocate_center %d\n", arena->relocate_center);
+               fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
+               fprintf(fp, "surface_tension %g\n", arena->surface_tension);
+               fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
+               fprintf(fp, "use_graphite %d\n", arena->use_graphite);
+               if (arena->pressure != NULL) {
+                       Double *dp;
+                       fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
+                       fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
+                       dp = arena->pressure->apply;
+                       fprintf(fp, "pressure %g %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7], dp[8]);
+                       dp = arena->pressure->cell_flexibility;
+                       fprintf(fp, "pressure_cell_flexibility %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7]);
+                       fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
+                       fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
+               }
+               fprintf(fp, "\n");
+
+               if (mp->par != NULL) {
+                       Parameter *par = mp->par;
+                       fprintf(fp, "!:parameters\n");
+                       ParameterAppendToFile(par, fp);
+                       fprintf(fp, "\n");
+               }
+               
+               fprintf(fp, "!:velocity\n");
+               fprintf(fp, "! idx vx vy vz\n");
+               for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
+               }
+               fprintf(fp, "\n");
+
+               fprintf(fp, "!:force\n");
+               fprintf(fp, "! idx fx fy fz\n");
+               for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
+               }
+               fprintf(fp, "\n");
+               
+       }
+
+       fclose(fp);
+       return 0;
+}
+
+int
+MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       int i;
+       Atom *ap;
+       fp = fopen(fname, "wb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
+               return 1;
+       }
+       errbuf[0] = 0;
+       fprintf(fp, "PSF\n\n");
+       fprintf(fp, "       1 !NTITLE\n");
+       fprintf(fp, " REMARKS FILENAME=\n");
+       fprintf(fp, "\n");
+       
+       /*  Atoms  */
+       fprintf(fp, "%8d !NATOM\n", mp->natoms);
+       for (i = 0; i < mp->natoms; i++) {
+               const char *fmt;
+               ap = ATOM_AT_INDEX(mp->atoms, i);
+               fprintf(fp, "%8d ", i + 1);
+               if (ap->resSeq >= 10000) {
+                       fmt = "%-3.3s %-5d ";
+               } else {
+                       fmt = "%-4.4s %-4d ";
+               }
+               fprintf(fp, fmt, ap->segName, ap->resSeq);
+               fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
+                       ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
+       }
+       fprintf(fp, "\n");
+       
+       /*  Bonds  */
+       fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
+       for (i = 0; i < mp->nbonds * 2; i++) {
+               fprintf(fp, "%8d", mp->bonds[i] + 1);
+               if (i % 8 == 7)
+                       fprintf(fp, "\n");
+       }
+       if (i % 8 != 0)
+               fprintf(fp, "\n");
+       fprintf(fp, "\n");
+       
+       /*  Angles  */
+       fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
+       for (i = 0; i < mp->nangles * 3; i++) {
+               fprintf(fp, "%8d", mp->angles[i] + 1);
+               if (i % 9 == 8)
+                       fprintf(fp, "\n");
+       }
+       if (i % 9 != 0)
+               fprintf(fp, "\n");
+       fprintf(fp, "\n");
+       
+       /*  Dihedrals  */
+       fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
+       for (i = 0; i < mp->ndihedrals * 4; i++) {
+               fprintf(fp, "%8d", mp->dihedrals[i] + 1);
+               if (i % 8 == 7)
+                       fprintf(fp, "\n");
+       }
+       if (i % 8 != 0)
+               fprintf(fp, "\n");
+       fprintf(fp, "\n");
+       
+       /*  Dihedrals  */
+       fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
+       for (i = 0; i < mp->nimpropers * 4; i++) {
+               fprintf(fp, "%8d", mp->impropers[i] + 1);
+               if (i % 8 == 7)
+                       fprintf(fp, "\n");
+       }
+       if (i % 8 != 0)
+               fprintf(fp, "\n");
+       fprintf(fp, "\n");
+       
+       fprintf(fp, "%8d !NDON: donors\n\n", 0);
+       fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
+       fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
+       for (i = 0; i < mp->natoms; i++) {
+               fprintf(fp, "%8d", 0);
+               if (i % 8 == 7)
+                       fprintf(fp, "\n");
+       }
+       if (i % 8 != 0)
+               fprintf(fp, "\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "%8d !NGRP: groups\n", 1);
+       fprintf(fp, "       0       0       0\n");
+       fprintf(fp, "\n");
+       
+       i = strlen(fname);
+       if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
+               /*  Extended psf (with coordinates and other info)  */
+               fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
+               for (i = 0; i < mp->natoms; i++) {
+                       Vector r;
+                       ap = ATOM_AT_INDEX(mp->atoms, i);
+                       r = ap->r;
+                       fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
+               }
+               fprintf(fp, "\n");
+#if 0
+               if (mp->nframes > 0) {
+                       int fn;  /*  Frame number  */
+                       for (fn = 0; fn < ap->nframes; fn++) {
+                               fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
+                               for (i = 0; i < mp->natoms; i++) {
+                                       Vector r;
+                                       ap = ATOM_AT_INDEX(mp->atoms, i);
+                                       if (ap->frames == NULL || fn >= ap->nframes)
+                                               r = ap->r;
+                                       else
+                                               r = ap->frames[fn];
+                                       fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
+                               }
+                               fprintf(fp, "\n");
+                       }
+               }
+#endif
+       }
+               
+       fclose(fp);
+       return 0;
+}
+
+int
+MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       int i, j;
+       Atom *ap;
+       fp = fopen(fname, "wb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
+               return 1;
+       }
+       errbuf[0] = 0;
+       for (i = 0; i < mp->natoms; i++) {
+               char buf[6];
+               ap = ATOM_AT_INDEX(mp->atoms, i);
+               if (ap->resSeq >= 10000) {
+                       snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
+               } else {
+                       snprintf(buf, sizeof buf, "%4d", ap->resSeq);
+               }
+               fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
+                                       "%8.3f%8.3f%8.3f %5.2f %5.2f      "
+                                       "%-4.4s%-2.2s%-2d\n",
+                       i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
+                       ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
+                       ap->segName, ap->element, ap->intCharge);
+       }
+       for (i = 0; i < mp->natoms; i++) {
+               ap = ATOM_AT_INDEX(mp->atoms, i);
+               for (j = 0; j < ap->nconnects; j++) {
+                       if (j % 4 == 0) {
+                               if (j > 0)
+                                       fprintf(fp, "\n");
+                               fprintf(fp, "CONECT%5d", i + 1);
+                       }
+                       fprintf(fp, "%5d", ap->connects[j] + 1);
+               }
+               if (j > 0)
+                       fprintf(fp, "\n");
+       }
+       fprintf(fp, "END\n");
+       fclose(fp);
+       return 0;
+}
+
+int
+MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       DcdRecord dcd;
+       SFloat32 *xp, *yp, *zp;
+       int n;
+       errbuf[0] = 0;
+       if (mp == NULL || mp->natoms == 0) {
+               snprintf(errbuf, errbufsize, "Molecule is empty");
+               return 1;
+       }
+       memset(&dcd, 0, sizeof(dcd));
+       dcd.natoms = mp->natoms;
+       dcd.nframes = MoleculeGetNumberOfFrames(mp);
+       if (dcd.nframes == 0) {
+               snprintf(errbuf, errbufsize, "no frame is present");
+               return 1;
+       }
+       dcd.nstart = mp->startStep;
+       dcd.ninterval = mp->stepsPerFrame;
+       if (dcd.ninterval == 0)
+               dcd.ninterval = 1;
+       dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
+       if (mp->cell != NULL) {
+               dcd.with_unitcell = 1;
+               dcd.globalcell[0] = VecLength(mp->cell->axes[0]);
+               dcd.globalcell[1] = VecLength(mp->cell->axes[1]);
+               dcd.globalcell[2] = VecLength(mp->cell->axes[2]);
+               dcd.globalcell[3] = mp->cell->origin.x;
+               dcd.globalcell[4] = mp->cell->origin.y;
+               dcd.globalcell[5] = mp->cell->origin.z;
+       }
+       dcd.delta = mp->psPerStep;
+       if (dcd.delta == 0.0)
+               dcd.delta = 1.0;
+       n = DcdCreate(fname, &dcd);
+       if (n != 0) {
+               if (n < 0)
+                       snprintf(errbuf, errbufsize, "Cannot create dcd file");
+               else
+                       snprintf(errbuf, errbufsize, "Cannot write dcd header");
+               DcdClose(&dcd);
+               return 1;
+       }
+       
+       xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
+       yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
+       zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
+       if (xp == NULL || yp == NULL || zp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot allocate memory");
+               if (xp) free(xp);
+               if (yp) free(yp);
+               if (zp) free(zp);
+               DcdClose(&dcd);
+               return 1;
+       }
+       for (n = 0; n < dcd.nframes; n++) {
+               int i;
+               Atom *ap;
+               for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       Vector r;
+                       if (ap->frames == NULL || n >= ap->nframes)
+                               r = ap->r;
+                       else
+                               r = ap->frames[n];
+                       xp[i] = r.x;
+                       yp[i] = r.y;
+                       zp[i] = r.z;
+               }
+               if (i < dcd.natoms) {
+                       size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
+                       memset(xp + i, 0, sz);
+                       memset(yp + i, 0, sz);
+                       memset(zp + i, 0, sz);
+               }
+               if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
+                       snprintf(errbuf, errbufsize, "Write error in dcd file");
+                       goto exit;
+               }
+       }
+       
+exit:
+       DcdClose(&dcd);
+       free(xp);
+       free(yp);
+       free(zp);
+       if (errbuf[0] == 0)
+               return 0;
+       else return 1;
+}
+
+int
+MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       int i;
+       Vector v;
+       errbuf[0] = 0;
+       fp = fopen(fname, "wb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
+               return 1;
+       }
+       if (mp->cell != NULL) {
+               fprintf(fp, "Bounding box:\n");
+               for (i = 0; i < 3; i++) {
+                       v = mp->cell->axes[i];
+                       fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
+               }
+               fprintf(fp, "Bounding box origin:\n");
+               v = mp->cell->origin;
+               fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
+       }
+       fclose(fp);
+       return 0;
+}
+               
+ static int
+sCompareByElement(const void *ap, const void *bp)
+{
+       return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
+}
+
+static int
+sMakeAdc(int n, int base, Symop symop)
+{
+       int an, sym;
+       if (SYMOP_ALIVE(symop)) {
+               an = base;
+               sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
+       } else {
+               an = n;
+               sym = 55501;
+       }
+       return (an + 1) * 100000 + sym;
+}
+
+static int
+sCompareAdc(const void *ap, const void *bp)
+{
+       int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
+       if (n == 0)
+               n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
+       return n;
+}
+
+static void
+sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
+{
+       int i, j, k, an, sym;
+       Atom *ap;
+       Int *adc;
+       adc = (Int *)malloc(sizeof(Int) * natoms);
+       if (adc == NULL)
+               return;
+       for (i = 0, ap = atoms; i < natoms; i++, ap++) {
+               if (ap->exflags & kAtomHiddenFlag)
+                       continue;
+               adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
+       }
+       mergesort(adc, natoms, sizeof(Int), sCompareAdc);
+       
+       /*  Create the atom list  */
+       an = sym = -1;
+       for (i = j = k = 0; i < natoms; i++) {
+               int an1 = adc[i] / 100000;
+               int sym1 = adc[i] % 100000;
+               if (sym == sym1 && an1 == an + 1) {
+                       /*  Continuous  */
+                       an = an1;
+                       k++;
+                       continue;
+               }
+               if (k > 0)
+                       /*  Output the last atom with a minus sign  */
+                       adc[j++] = -(an * 100000 + sym);
+               /*  Output this atom  */
+               adc[j++] = adc[i];
+               an = an1;
+               sym = sym1;
+               k = 0;
+       }
+       if (k > 0)
+               adc[j++] = -(an * 100000 + sym);
+       
+       /*  Create the instruction cards  */
+       for (i = k = 0; i < j; i++) {
+               if (k == 0)
+                       fprintf(fp, "      401");
+               fprintf(fp, "%9d", adc[i]);
+               k++;
+               if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
+                       fprintf(fp, "\n");
+                       k = 0;
+               }
+       }
+       free(adc);
+}
+
+static int
+sEllipsoidType(int an)
+{
+       return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
+}
+
+static void
+sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
+{
+       int i;
+       Atom *ap;
+       int etype, elast, istart, ilast, n1, n2;
+       elast = istart = ilast = -1;
+       for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
+               if (i < natoms) {
+                       if (SYMOP_ALIVE(ap->symop))
+                               continue;
+                       if (ap->exflags & kAtomHiddenFlag)
+                               continue;
+                       etype = sEllipsoidType(ap->atomicNumber);
+                       if (elast < 0) {
+                               istart = ilast = i;
+                               elast = etype;
+                               continue;
+                       } else if (elast == etype && ilast == i - 1) {
+                               ilast++;
+                               continue;
+                       }
+               }
+               /*  Output the instruction card for the 'last' block of atoms  */
+               switch (etype) {
+                       case 2:
+                               n1 = 4; n2 = 0; break;
+                       case 3:
+                               n1 = 4; n2 = 5; break;
+                       default:
+                               n1 = 1; n2 = 0; break;
+               }
+               fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
+               fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
+               elast = etype;
+               ilast = istart = i;
+       }
+}
+
+static int
+sCompareBondType(const void *ap, const void *bp)
+{
+       /*  Descending order  */
+       return *((int *)bp) - *((int *)ap);
+}
+
+static void
+sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
+{
+       Atom *ap, *ap2;
+       char buf[96];
+       int i, j, n[5], an, count, n1, n2, k;
+       Int nexbonds;
+       Int *exbonds;
+       static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
+       static const int sBondShade[4] = {5, 3, 1, 1};
+
+       n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
+       n[4] = natoms;
+       for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
+               an = ap->atomicNumber;
+               if (an < 2)
+                       n[3] = i;
+               if (an < 10)
+                       n[2] = i;
+               if (an < 18)
+                       n[1] = i;
+       }
+       nexbonds = 0;
+       exbonds = NULL;
+       count = 0;
+
+       if (overlap_correction)
+               strcpy(buf, "  2  1001    0.000\n");
+       else
+               strcpy(buf, "  2   812\n");
+       
+       for (i = 0; i < 4; i++) {
+               for (j = i; j < 4; j++) {
+                       /*  Examine bonds between "group i" and "group j"  */
+                       Vector dr;
+                       double d;
+                       double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
+                       double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
+                       double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
+                       int count_exbond = 0;          /*  Number of explicit bonds in this group  */
+                       for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
+                               for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
+                                       if (n1 == n2)
+                                               continue;
+                                       VecSub(dr, ap->r, ap2->r);
+                                       d = VecLength(dr);
+                                       for (k = ap->nconnects - 1; k >= 0; k--) {
+                                               if (ap->connects[k] == n2)
+                                                       break;
+                                       }
+                                       if (k >= 0) {
+                                               /*  n1 and n2 are bound  */
+                                               if (d < min_bond)
+                                                       min_bond = d;
+                                               if (d > max_bond)
+                                                       max_bond = d;
+                                       } else {
+                                               /*  n1 and n2 are not bound  */
+                                               if (d < min_nonbond)
+                                                       min_nonbond = d;
+                                       }
+                               }
+                       }
+                       if (min_bond == 10000.0)
+                               continue;  /*  No bonds between these groups  */
+                       min_bond *= 0.9;
+                       if (max_bond + 0.002 < min_nonbond)
+                               max_bond += 0.002;
+                       else {
+                               max_bond = min_nonbond - 0.002;
+                               /*  Some bonds may be omitted, so scan all bonds again  */
+                               for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
+                                       for (k = ap->nconnects - 1; k >= 0; k--) {
+                                               n2 = ap->connects[k];
+                                               if (n2 < n[j] || n2 >= n[j + 1])
+                                                       continue;
+                                               ap2 = atoms + n2;
+                                               VecSub(dr, ap->r, ap2->r);
+                                               d = VecLength(dr);
+                                               if (d > max_bond) {
+                                                       /*  This bond should be explicitly defined  */
+                                                       Int adc1, adc2;
+                                                       if (count_exbond == 0) {
+                                                               adc1 = -(i + 1);  /*  Bond type  */
+                                                               AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
+                                                       }
+                                                       adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
+                                                       adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
+                                                       AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
+                                                       AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
+                                                       count_exbond++;
+                                               }
+                                       }
+                               }
+                       }
+                       /*  Output the last instruction card  */
+                       fputs(buf, fp);
+                       /*  Make a new trailer card  */
+                       snprintf(buf, sizeof(buf), "  2      %3d%3d%3d%3d%3d%6.3f%6.3f%6.3f\n", n[i]+1, n[i+1], n[j]+1, n[j+1], sBondShade[i], min_bond, max_bond, sBondRad[i]);
+                       count++;
+               }
+       }
+       if (count > 0) {
+               /*  Output the last trailer card  */
+               buf[2] = ' ';
+               fputs(buf, fp);
+       }
+       if (nexbonds > 0) {
+               if (count == 0 && overlap_correction) {
+                       /*  1001 card is not yet written, so write it  */
+                       buf[2] = ' ';
+                       fputs(buf, fp);
+               }
+               snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
+               k = -exbonds[0] - 1;  /*  Bond type for the first block  */
+               i = 1;  /*  Index for exbonds[]  */
+               j = 0;  /*  Count in this block  */
+               while (i <= nexbonds) {
+                       if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
+                               /*  End of block  */
+                               buf[2] = '2';
+                               fputs(buf, fp);
+                               /*  The trailer card  */
+                               fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
+                               if (i == nexbonds)
+                                       break;
+                               if (exbonds[i] < 0)
+                                       k = -exbonds[i++] - 1;  /*  The new bond type  */
+                               j = 0;
+                       } else if (j > 0 && j % 3 == 0) {
+                               buf[2] = '1';
+                               fputs(buf, fp);
+                       }
+                       n1 = exbonds[i++];
+                       n2 = exbonds[i++];
+                       snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
+                       j++;
+               }
+               free(exbonds);
+       }
+}
+       
+#if 0
+{
+       /*  Explicit bond table, sorted by bond type  */
+       for (i = j = 0; i < mp->nbonds; i++) {
+               n1 = mp->bonds[i * 2];
+               n2 = mp->bonds[i * 2 + 1];
+               ap1 = ATOM_AT_INDEX(mp->atoms, n1);
+               ap2 = ATOM_AT_INDEX(mp->atoms, n2);
+               if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
+                       continue;
+               if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
+                       type = 3;
+               } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
+                       type = 2;
+               } else {
+                       type = 1;
+               }
+               ip[j * 3] = type;
+               ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
+               ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
+               j++;
+       }
+       mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
+       
+       /*  Output instruction cards  */
+       strcpy(buf, "  1   811");
+       for (i = n1 = 0; i < j; i++) {
+               n2 = (n1 % 3) * 18 + 9;
+               snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
+               if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
+                       /*  End of this instruction  */
+                       buf[2] = '2';
+                       fputs(buf, fp);
+                       switch (ip[i * 3]) {
+                               case 3: rad = 0.06; nshades = 5; break;
+                               case 2: rad = 0.06; nshades = 1; break;
+                               default: rad = 0.04; nshades = 1; break;
+                       }
+                       fprintf(fp, "                     %3d            %6.3f\n", nshades, rad);
+                       strcpy(buf, "  1   811");
+                       n1 = 0;
+                       continue;
+               } else if (n1 % 3 == 2) {
+                       fputs(buf, fp);
+                       strcpy(buf, "  1      ");
+               }
+               n1++;
+       }
+       free(ip);
+}
+#endif
+
+int
+MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
+{
+       FILE *fp;
+       int i, j, natoms, *ip;
+       Atom *ap, *atoms, **app;
+       Double *cp;
+       
+       errbuf[0] = 0;
+
+       /*  Create sorted array of atoms  */
+       natoms = mp->natoms;
+       atoms = (Atom *)calloc(sizeof(Atom), natoms);
+       app = (Atom **)calloc(sizeof(Atom *), natoms);
+       ip = (int *)calloc(sizeof(int), natoms);
+       if (atoms == NULL || app == NULL || ip == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot allocate memory");
+               return 1;
+       }
+       /*  Sort the atom pointer by atomic number  */
+       for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
+               app[i] = ap;
+       mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
+       for (i = 0; i < natoms; i++) {
+               /*  ip[old_index] is new_index  */
+               ip[app[i] - mp->atoms] = i;
+       }
+       /*  Move the atom record to atoms[]  */
+       /*  aniso and frames share the memory with the original  */
+       /*  The 'v' member contains crystallographic coordinates  */
+       /*  The connection table and symbase are renumbered  */
+       /*  Hidden flags are modified to reflect the visibility in the MainView  */
+       for (i = 0, ap = atoms; i < natoms; i++, ap++) {
+               memmove(ap, app[i], gSizeOfAtomRecord);
+               MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
+               for (j = ap->nconnects - 1; j >= 0; j--) {
+                       ap->connects[j] = ip[ap->connects[j]];
+               }
+               if (SYMOP_ALIVE(ap->symop))
+                       ap->symbase = ip[ap->symbase];
+               if (MainView_isAtomHidden(mp->mview, i)) {
+                       ap->exflags |= kAtomHiddenFlag;
+               } else {
+                       ap->exflags &= ~kAtomHiddenFlag;
+               }
+       }
+       free(ip);
+       free(app);
+       
+       fp = fopen(fname, "wb");
+       if (fp == NULL) {
+               snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
+               return 1;
+       }
+       errbuf[0] = 0;
+
+       /*  Title line  */
+       fprintf(fp, "Generated by MyMolView\n");
+       
+       /*  XtalCell  */
+       if (mp->cell != NULL) {
+               cp = mp->cell->cell;
+       } else {
+               static Double sUnit[] = {1, 1, 1, 90, 90, 90};
+               cp = sUnit;
+       }
+       fprintf(fp, "%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+       
+       /*  Symmetry operations  */
+       if (mp->nsyms > 0) {
+               for (i = 0; i < mp->nsyms; i++) {
+                       cp = mp->syms[i];
+                       fprintf(fp, "%c%14g%3g%3g%3g%15g%3g%3g%3g%15g%3g%3g%3g\n", (i == mp->nsyms - 1 ? '1' : ' '), cp[9], cp[0], cp[1], cp[2], cp[10], cp[3], cp[4], cp[5], cp[11], cp[6], cp[7], cp[8]);
+               }
+       } else {
+               fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
+       }
+       
+       /*  Atoms  */
+       for (i = 0, ap = atoms; i < natoms; i++, ap++) {
+               /*  The 'v' field contains crystallographic coordinates  */
+               fprintf(fp, " %4.4s%22s%9.4f%9.4f%9.4f%9d\n", ap->aname, "", ap->v.x, ap->v.y, ap->v.z, 0);
+               if (ap->aniso != NULL) {
+                       cp = ap->aniso->bij;
+                       fprintf(fp, " %8.5f%9.6f%9.6f%9.6f%9.6f%9.6f%9d\n", cp[0], cp[1], cp[2], cp[3], cp[5], cp[4], 0);
+               } else {
+                       Double temp = ap->tempFactor;
+                       if (temp <= 0)
+                               temp = 1.2;
+                       fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
+               }
+       }
+       /*  Special points  */
+       {
+               Vector camera, lookat, up, xvec, yvec, zvec;
+               MainView_getCamera(mp->mview, &camera, &lookat, &up);
+               VecSub(zvec, lookat, camera);
+               VecCross(xvec, zvec, up);
+               NormalizeVec(&xvec, &xvec);
+               NormalizeVec(&yvec, &up);
+               VecInc(xvec, lookat);
+               VecInc(yvec, lookat);
+               MoleculeCartesianToXtal(mp, &lookat, &lookat);
+               MoleculeCartesianToXtal(mp, &xvec, &xvec);
+               MoleculeCartesianToXtal(mp, &yvec, &yvec);
+               fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
+               fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
+               fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
+               fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
+               fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
+               fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
+               fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
+               fprintf(fp, "1%8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
+       }
+       
+       /*  Instructions  */
+       fprintf(fp, "      201\n");
+       fprintf(fp, "      205       12\n");
+       fprintf(fp, "      301      6.6      6.6        0      0.8\n");
+       sOutputAtomListInstructions(fp, natoms, atoms);
+       fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
+       fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
+       fprintf(fp, "      604                               1.538\n");
+
+       sOutputBondInstructions(fp, natoms, atoms, 1);
+       sOutputAtomTypeInstructions(fp, natoms, atoms);
+       sOutputBondInstructions(fp, natoms, atoms, 0);
+
+       fprintf(fp, "      202\n");
+       fprintf(fp, "  0    -1\n");
+       fclose(fp);
+       return 0;
+}
+
+void
+MoleculeDump(Molecule *mol)
+{
+       int i, j;
+       Atom *ap;
+       for (i = 0; i < mol->natoms; i++) {
+               char buf1[8];
+               ap = ATOM_AT_INDEX(mol->atoms, i);
+               snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
+               fprintf(stderr, "%4d %-7s %-4.6s %-4.6s %-2.2s %7.3f %7.3f %7.3f %6.3f [", i, buf1, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->element, ap->r.x, ap->r.y, ap->r.z, ap->charge);
+               for (j = 0; j < ap->nconnects; j++) {
+                       fprintf(stderr, "%s%d", (j > 0 ? "," : ""), ap->connects[j]);
+               }
+               fprintf(stderr, "]\n");
+       }
+}
+
+#pragma mark ====== Serialize ======
+
+Molecule *
+MoleculeDeserialize(const char *data, Int length, Int *timep)
+{
+       Molecule *mp;
+/*     int result; */
+
+       mp = MoleculeNew();
+       if (mp == NULL)
+               goto out_of_memory;
+       
+       while (length >= 12) {
+               const char *ptr = data + 8 + sizeof(Int);
+               int len = *((const Int *)(data + 8));
+               int i, j, n;
+               if (strcmp(data, "ATOM") == 0) {
+                       n = len / gSizeOfAtomRecord;
+                       NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
+                       memmove(mp->atoms, ptr, len);
+               } else if (strcmp(data, "ANISO") == 0) {
+                       Atom *ap;
+                       n = len / (sizeof(Int) + sizeof(Aniso));
+                       for (i = 0; i < n; i++) {
+                               j = *((const Int *)ptr);
+                               if (j < 0 || j >= mp->natoms)
+                                       goto bad_format;
+                               ap = ATOM_AT_INDEX(mp->atoms, j);
+                               ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
+                               if (ap->aniso == NULL)
+                                       goto out_of_memory;
+                               *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
+                               ptr += sizeof(Int) + sizeof(Aniso);
+                       }
+               } else if (strcmp(data, "FRAME") == 0) {
+                       Atom *ap;
+                       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                               if (ap->nframes == 0)
+                                       continue;
+                               ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
+                               if (ap->frames == NULL)
+                                       goto out_of_memory;
+                               memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
+                               ptr += sizeof(Vector) * ap->nframes;
+                       }
+               } else if (strcmp(data, "BOND") == 0) {
+                       n = len / (sizeof(Int) * 2);
+                       NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
+                       memmove(mp->bonds, ptr, len);
+               } else if (strcmp(data, "ANGLE") == 0) {
+                       n = len / (sizeof(Int) * 3);
+                       NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
+                       memmove(mp->angles, ptr, len);
+               } else if (strcmp(data, "DIHED") == 0) {
+                       n = len / (sizeof(Int) * 4);
+                       NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
+                       memmove(mp->dihedrals, ptr, len);
+               } else if (strcmp(data, "IMPROP") == 0) {
+                       n = len / (sizeof(Int) * 4);
+                       NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
+                       memmove(mp->impropers, ptr, len);
+               } else if (strcmp(data, "RESIDUE") == 0) {
+                       n = len / 4;
+                       NewArray(&mp->residues, &mp->nresidues, 4, n);
+                       memmove(mp->residues, ptr, len);
+               } else if (strcmp(data, "CELL") == 0) {
+                       mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
+                       if (mp->cell == NULL)
+                               goto out_of_memory;
+                       memmove(mp->cell, ptr, sizeof(XtalCell));
+               } else if (strcmp(data, "SYMOP") == 0) {
+                       n = len / sizeof(Transform);
+                       NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
+                       memmove(mp->syms, ptr, len);
+               } else if (strcmp(data, "EXATOM") == 0) {
+                       n = len / sizeof(ExAtom);
+                       NewArray(&mp->exatoms, &mp->nexatoms, sizeof(ExAtom), n);
+                       memmove(mp->exatoms, ptr, len);
+               } else if (strcmp(data, "EXBOND") == 0) {
+                       n = len / (sizeof(Int) * 2);
+                       NewArray(&mp->exbonds, &mp->nexbonds, sizeof(Int) * 2, n);
+                       memmove(mp->exbonds, ptr, len);
+               } else if (strcmp(data, "TIME") == 0) {
+                       if (timep != NULL)
+                               *timep = *((Int *)ptr);
+               }
+               len += 8 + sizeof(Int);
+               data += len;
+               length -= len;
+       }
+/*     result = MoleculeRebuildTablesFromConnects(mp);
+       if (result != 0)
+               goto bad_format; */
+       return mp;
+       
+  out_of_memory:
+       Panic("Low memory while deserializing molecule data");
+       return NULL; /* Not reached */
+
+  bad_format:
+       Panic("internal error: bad format during deserializing molecule data");
+       return NULL; /* Not reached */
+}
+
+char *
+MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
+{
+       char *ptr, *p;
+       int len, len_all, i, naniso, nframes;
+
+       /*  Array of atoms  */
+       len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
+       ptr = (char *)malloc(len);
+       if (ptr == NULL)
+               goto out_of_memory;
+       memmove(ptr, "ATOM\0\0\0\0", 8);
+       *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
+       p = ptr + 8 + sizeof(Int);
+       memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
+       naniso = nframes = 0;
+       for (i = 0; i < mp->natoms; i++) {
+               Atom *ap = ATOM_AT_INDEX(p, i);
+               if (ap->aniso != NULL) {
+                       naniso++;
+                       ap->aniso = NULL;
+               }
+               if (ap->frames != NULL) {
+                       nframes += ap->nframes;
+                       ap->frames = NULL;
+               }
+       }
+       len_all = len;
+
+       /*  Array of aniso  */
+       if (naniso > 0) {
+               len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "ANISO\0\0\0", 8);
+               *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
+               p += 8 + sizeof(Int);
+               for (i = 0; i < mp->natoms; i++) {
+                       Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
+                       if (ap->aniso != NULL) {
+                               *((Int *)p) = i;
+                               *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
+                               p += sizeof(Int) + sizeof(Aniso);
+                       }
+               }
+               len_all += len;
+       }
+       
+       /*  Array of frames  */
+       if (nframes > 0) {
+               len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "FRAME\0\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Vector) * nframes;
+               p += 8 + sizeof(Int);
+               for (i = 0; i < mp->natoms; i++) {
+                       Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
+                       if (ap->frames != NULL) {
+                               memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
+                               p += sizeof(Vector) * ap->nframes;
+                       }
+               }
+               len_all += len;
+       }
+       
+       /*  Bonds, angles, dihedrals, impropers  */
+       if (mp->nbonds > 0) {
+               len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "BOND\0\0\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
+               len_all += len;
+       }
+       if (mp->nangles > 0) {
+               len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "ANGLE\0\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
+               len_all += len;
+       }
+       if (mp->ndihedrals > 0) {
+               len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "DIHED\0\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
+               len_all += len;
+       }
+       if (mp->nimpropers > 0) {
+               len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "IMPROP\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
+               len_all += len;
+       }
+       
+       /*  Array of residues  */
+       if (mp->nresidues > 0) {
+               len = 8 + sizeof(Int) + 4 * mp->nresidues;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "RESIDUE\0", 8);
+               *((Int *)(p + 8)) = 4 * mp->nresidues;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->residues, 4 * mp->nresidues);
+               len_all += len;
+       }
+
+       /*  Unit cell  */
+       if (mp->cell != NULL) {
+               len = 8 + sizeof(Int) + sizeof(XtalCell);
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "CELL\0\0\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(XtalCell);
+               p += 8 + sizeof(Int);
+               memmove(p, mp->cell, sizeof(XtalCell));
+               len_all += len;
+       }
+       
+       /*  Symmetry operations  */
+       if (mp->nsyms > 0) {
+               len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "SYMOP\0\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
+               len_all += len;
+       }
+       
+       /*  Expanded atoms  */
+       if (mp->nexatoms > 0) {
+               len = 8 + sizeof(Int) + sizeof(ExAtom) * mp->nexatoms;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "EXATOM\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(ExAtom) * mp->nexatoms;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->exatoms, sizeof(ExAtom) * mp->nexatoms);
+               for (i = 0; i < mp->nexatoms; i++) {
+                       /*  Clear label id  */
+                       ((ExAtom *)p)[i].labelid = 0;
+               }
+               len_all += len;
+       }
+       
+       /*  Expanded bonds  */
+       if (mp->nexbonds > 0) {
+               len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nexbonds;
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "EXBOND\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nexbonds;
+               p += 8 + sizeof(Int);
+               memmove(p, mp->exbonds, sizeof(Int) * 2 * mp->nexbonds);
+               len_all += len;
+       }
+
+       /*  Time stamp  */
+       {
+               time_t tm = time(NULL);
+               len = 8 + sizeof(Int) + sizeof(Int);
+               ptr = (char *)realloc(ptr, len_all + len);
+               if (ptr == NULL)
+                       goto out_of_memory;
+               p = ptr + len_all;
+               memmove(p, "TIME\0\0\0\0", 8);
+               *((Int *)(p + 8)) = sizeof(Int);
+               p += 8 + sizeof(Int);
+               *((Int *)p) = (Int)tm;
+               len_all += len;
+               if (timep != NULL)
+                       *timep = (Int)tm;
+       }
+       
+       if (outLength != NULL)
+               *outLength = len_all;
+       return ptr;
+
+  out_of_memory:
+    Panic("Low memory while serializing a molecule data");
+       return NULL; /* Not reached */  
+}
+
+#pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
+
+static IntGroup *
+sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
+{
+       int i, j;
+       Int *ip;
+       IntGroup *gp = NULL;
+       if (atomgroup == NULL)
+               return NULL;
+       for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
+               for (j = 0; j < nsize; j++) {
+                       if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
+                               if (gp == NULL)
+                                       gp = IntGroupNew();
+                               if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
+                                       Panic("Low memory while searching %s", msg);
+                               break;
+                       }
+               }
+       }
+       return gp;
+}
+
+IntGroup *
+MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
+}
+
+IntGroup *
+MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
+}
+
+IntGroup *
+MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
+}
+
+IntGroup *
+MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
+}
+
+static IntGroup *
+sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
+{
+       int i, j;
+       Int *ip;
+       IntGroup *gp = NULL;
+       if (atomgroup == NULL)
+               return NULL;
+       for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
+               int k = -1;
+               for (j = 0; j < nsize; j++) {
+                       int kk;
+                       kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
+                       if (k < 0)
+                               k = kk;
+                       else if (k != kk) {
+                               /*  This bond etc. crosses the atom group border  */
+                               if (gp == NULL)
+                                       gp = IntGroupNew();
+                               if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
+                                       Panic("Low memory while searching %s", msg);
+                               break;
+                       }
+               }
+       }
+       return gp;
+}
+
+IntGroup *
+MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
+}
+
+IntGroup *
+MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
+}
+
+IntGroup *
+MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
+}
+
+IntGroup *
+MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
+{
+       if (mp == NULL)
+               return NULL;
+       return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
+}
+
+/*  Guess the bonds from the coordinates  */
+int
+MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
+{
+       int i, j, n1, n2;
+       Int nbonds, *bonds;
+       Atom *ap, *bp;
+       Vector r1, r2, dr;
+       Double a1, a2, alim;
+       Int newbond[2];
+       AtomPar *p = gDispAtomParameters;
+       nbonds = 0;
+       bonds = NULL;
+       for (i = 0; i < mp->natoms; i++) {
+               ap = ATOM_AT_INDEX(mp->atoms, i);
+               n1 = ap->atomicNumber;
+               if (n1 >= 0 && n1 < gCountDispAtomParameters)
+                       a1 = p[n1].radius;
+               else a1 = p[6].radius;
+               r1 = ap->r;
+       /*      if (mp->is_xtal_coord)
+                       TransformVec(&r1, mp->cell->tr, &r1); */
+               for (j = 0; j < i; j++) {
+                       bp = ATOM_AT_INDEX(mp->atoms, j);
+                       n2 = bp->atomicNumber;
+                       if (n2 >= 0 && n2 < gCountDispAtomParameters)
+                               a2 = p[n2].radius;
+                       else a2 = p[6].radius;
+                       r2 = bp->r;
+               /*      if (mp->is_xtal_coord)
+                               TransformVec(&r2, mp->cell->tr, &r2); */
+                       VecSub(dr, r1, r2);
+                       alim = limit * (a1 + a2);
+                       if (VecLength2(dr) < alim * alim) {
+                               newbond[0] = i;
+                               newbond[1] = j;
+                       /*      MoleculeAddBonds(mp, 1, newbonds); */
+                               AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
+                       }
+               }
+       }
+       if (nbonds > 0) {
+               newbond[0] = kInvalidIndex;
+               newbond[1] = 0;
+               AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
+               nbonds--;
+       }
+       if (outNbonds != NULL)
+               *outNbonds = nbonds;
+       if (outBonds != NULL)
+               *outBonds = bonds;
+       return 0;
+}
+
+/*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
+int
+MoleculeRebuildTablesFromConnects(Molecule *mp)
+{
+       int i, j, k, retval;
+       Atom *ap;
+       Int ibuf[6];
+       
+       __MoleculeLock(mp);
+
+       /*  Find bonds   */
+       if (mp->nbonds == 0) {
+               for (i = 0; i < mp->natoms; i++) {
+                       ap = ATOM_AT_INDEX(mp->atoms, i);
+                       for (j = 0; j < ap->nconnects; j++) {
+                               k = ap->connects[j];
+                               if (i >= k)
+                                       continue;
+                               ibuf[0] = i;
+                               ibuf[1] = k;
+                               /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
+                                   bonds are already in sync  */
+                               AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
+                       /*      retval = MoleculeAddBonds(mp, 1, ibuf);
+                               if (retval != 0)
+                                       goto abort; */
+                       }
+               }
+       }
+       
+       /*  Find angles  */
+       if (mp->nangles == 0) {
+               for (i = 0; i < mp->natoms; i++) {
+                       ap = ATOM_AT_INDEX(mp->atoms, i);
+                       for (j = 0; j < ap->nconnects; j++) {
+                               for (k = j + 1; k < ap->nconnects; k++) {
+                                       ibuf[0] = ap->connects[j];
+                                       ibuf[1] = i;
+                                       ibuf[2] = ap->connects[k];
+                                       ibuf[3] = -1;
+                                       retval = MoleculeAddAngles(mp, ibuf, NULL);
+                                       if (retval < 0)
+                                               goto abort;
+                               }
+                       }
+               }
+       }
+       
+       /*  Find dihedrals  */
+       if (mp->ndihedrals == 0) {
+               for (i = 0; i < mp->natoms; i++) {
+                       ap = ATOM_AT_INDEX(mp->atoms, i);
+                       for (j = 0; j < ap->nconnects; j++) {
+                               int jj, kk, mm, m;
+                               Atom *apjj;
+                               jj = ap->connects[j];
+                               if (i >= jj)
+                                       continue;
+                               apjj = ATOM_AT_INDEX(mp->atoms, jj);
+                               for (k = 0; k < ap->nconnects; k++) {
+                                       if (k == j)
+                                               continue;
+                                       kk = ap->connects[k];
+                                       for (m = 0; m < apjj->nconnects; m++) {
+                                               mm = apjj->connects[m];
+                                               if (mm == i || mm == kk)
+                                                       continue;
+                                               ibuf[0] = kk;
+                                               ibuf[1] = i;
+                                               ibuf[2] = jj;
+                                               ibuf[3] = mm;
+                                               ibuf[4] = -1;
+                                               retval = MoleculeAddDihedrals(mp, ibuf, NULL);
+                                               if (retval < 0)
+                                                       goto abort;
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       /*  Find impropers  */
+       if (mp->nimpropers == 0) {
+               for (i = 0; i < mp->natoms; i++) {
+                       int i1, i2, i4, n1, n2, n4;
+                       ap = ATOM_AT_INDEX(mp->atoms, i);
+                       for (i1 = 0; i1 < ap->nconnects; i1++) {
+                               n1 = ap->connects[i1];
+                               for (i2 = i1 + 1; i2 < ap->nconnects; i2++) {
+                                       n2 = ap->connects[i2];
+                                       for (i4 = i2 + 1; i4 < ap->nconnects; i4++) {
+                                               n4 = ap->connects[i4];
+                                               ibuf[0] = n1;
+                                               ibuf[1] = n2;
+                                               ibuf[2] = i;
+                                               ibuf[3] = n4;
+                                               ibuf[4] = -1;
+                                               retval = MoleculeAddImpropers(mp, ibuf, NULL);
+                                               if (retval < 0)
+                                                       goto abort;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return 0;
+
+  abort:
+       __MoleculeUnlock(mp);
+       return retval;
+}
+
+#pragma mark ====== Atom names ======
+
+/*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
+int
+MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
+{
+       int i, j, lasti;
+       Atom *ap;
+       if (mp == NULL || mp->natoms == 0)
+               return -1;
+       lasti = -1;
+       for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (ap->resSeq == resno) {
+                       lasti = i;
+                       if (j++ == n1)
+                               return i;
+               }
+       }
+       if (n1 == -1)
+               return lasti; /* max */
+       return -1;
+}
+
+int
+MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
+{
+    int n;
+    char *p;
+       n = strtol(s, &p, 0);
+       if (p > s) {
+               while (isspace(*p))
+                       p++;
+               if (*p == 0) {
+                 resName[0] = 0;
+                 *resSeq = -1;
+                 atomName[0] = 0;
+                 return n;
+               }
+       }
+
+       if ((p = strchr(s, ':')) != NULL) {
+               /*  Residue is specified  */
+               char *pp;
+               if ((pp = strchr(s, '.')) != NULL && pp < p) {
+                       /*  Residue number is also specified  */
+                       char *ppp;
+                       n = pp - s;
+                       *resSeq = strtol(pp + 1, &ppp, 0);
+                       if (ppp == pp + 1)
+                               return -2;  /*  Bad format  */
+                       while (isspace(*ppp))
+                               ppp++;
+                       if (ppp != p)
+                               return -2;  /*  Bad format  */
+               } else {
+                       *resSeq = -1;
+                       /*  Check whether the "residue name" is an integer  */
+                       n = strtol(s, &pp, 0);
+                       if (pp > s) {
+                               while (isspace(*pp))
+                                       pp++;
+                               if (*pp == 0 || *pp == ':') {
+                                       *resSeq = n;
+                                       if (*resSeq < 0)
+                                               return -2;  /*  Bad format  */
+                               }
+                       }
+                       if (*resSeq >= 0)
+                               n = 0;
+                       else
+                               n = p - s;
+               }
+               if (n >= sizeof(resName))
+                       n = sizeof(resName) - 1;
+               strncpy(resName, s, n);
+               resName[n] = 0;
+               p++;
+       } else {
+               resName[0] = 0;
+               *resSeq = -1;
+               p = (char *)s;
+       }
+       strncpy(atomName, p, 4);
+       atomName[4] = 0;
+       return 0;
+}
+
+/*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
+int
+MoleculeAtomIndexFromString(Molecule *mp, const char *s)
+{
+       char resName[6];
+       int resSeq, n;
+       char atomName[6];
+       /*      char *p; */
+
+       n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
+       if (atomName[0] == 0) {
+         if (n >= mp->natoms)
+           n = -1;  /* Out of range */
+         return n;
+       }
+       for (n = 0; n < mp->natoms; n++) {
+               Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
+               if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
+                       && (resSeq < 0 || ap->resSeq == resSeq)
+                       && strncmp(atomName, ap->aname, 4) == 0) {
+                       return n;
+               }
+       }
+       return -1;  /*  Not found  */
+}
+
+int
+MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2)
+{
+       Atom *ap;
+       int i;
+       if (mp == NULL || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
+               return 0;
+       ap = ATOM_AT_INDEX(mp->atoms, n1);
+       for (i = 0; i < ap->nconnects; i++)
+               if (ap->connects[i] == n2)
+                       return 1;
+       return 0;
+}
+
+
+void
+MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
+{
+       Atom *ap;
+       int n;
+       if (mp == NULL || index < 0 || index >= mp->natoms) {
+               buf[0] = 0;
+               return;
+       }
+       ap = mp->atoms + index;
+       if (ap->resSeq != 0) {
+               n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
+               buf += n;
+               bufsize -= n;
+       }
+       snprintf(buf, bufsize, "%.4s", ap->aname);
+}
+
+#pragma mark ====== Selection ======
+
+static void
+sMoleculeNotifyChangeSelection(Molecule *mp)
+{
+       /*  TODO: Finer control of notification types may be necessary  */
+       MoleculeCallback_notifyModification(mp, 0);
+}
+
+void
+MoleculeSetSelection(Molecule *mp, IntGroup *select)
+{
+       if (mp == NULL)
+               return;
+       if (select != NULL)
+               IntGroupRetain(select);
+       if (mp->selection != NULL)
+               IntGroupRelease(mp->selection);
+       mp->selection = select;
+       sMoleculeNotifyChangeSelection(mp);
+}
+
+IntGroup *
+MoleculeGetSelection(Molecule *mp)
+{
+       if (mp == NULL)
+               return NULL;
+       else return mp->selection;
+}
+
+void
+MoleculeSelectAtom(Molecule *mp, int n1, int extending)
+{
+       if (mp->selection == NULL)
+               mp->selection = IntGroupNew();
+       if (!extending)
+               IntGroupClear(mp->selection);
+       IntGroupAdd(mp->selection, n1, 1);
+       sMoleculeNotifyChangeSelection(mp);
+}
+
+void
+MoleculeUnselectAtom(Molecule *mp, int n1)
+{
+       if (mp->selection != NULL)
+               IntGroupRemove(mp->selection, n1, 1);
+       sMoleculeNotifyChangeSelection(mp);
+}
+
+void
+MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
+{
+       if (mp->selection == NULL)
+               mp->selection = IntGroupNew();
+       IntGroupReverse(mp->selection, n1, 1);
+       sMoleculeNotifyChangeSelection(mp);
+}
+
+int
+MoleculeIsAtomSelected(Molecule *mp, int n1)
+{
+       if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
+               return 1;
+       else return 0;
+}
+
+int
+MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
+{
+       if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
+               return 1;
+       else return 0;
+}
+
+IntGroup *
+MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
+{
+       int status;
+       IntGroup *remain, *ig1, *ig2;
+       ig1 = ig2 = NULL;
+       remain = IntGroupNewFromIntGroup(remove);
+       if (remain == NULL)
+               status = -1;
+       else
+               status = IntGroupReverse(remain, 0, mp->natoms);
+       if (status == 0) {
+               ig1 = IntGroupNew();
+               if (ig1 == NULL)
+                       status = -1;
+               else
+                       status = IntGroupDifference(selection, remove, ig1);
+       }
+       if (status == 0) {
+               ig2 = IntGroupNew();
+               if (ig2 == NULL)
+                       status = -1;
+               else
+                       status = IntGroupDeconvolute(ig1, remain, ig2);
+       }
+       if (remain != NULL)
+               IntGroupRelease(remain);
+       if (ig1 != NULL)
+               IntGroupRelease(ig1);
+       if (status == 0)
+               return ig2;
+       else {
+               if (ig2 != NULL)
+                       IntGroupRelease(ig2);
+               return NULL;
+       }
+}
+
+#pragma mark ====== Atom Equivalence ======
+
+struct sEqList {
+       int i[2];
+       struct sEqList *next;
+       struct sEqList *link;
+};
+
+static struct sEqList *sListBase = NULL;
+static struct sEqList *sListFree = NULL;
+
+static struct sEqList *
+sAllocEqList(void)
+{
+       struct sEqList *lp;
+       if (sListFree != NULL) {
+               lp = sListFree;
+               sListFree = lp->next;
+               lp->i[0] = lp->i[1] = 0;
+               lp->next = NULL;
+               return lp;
+       }
+       lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
+       lp->link = sListBase;
+       sListBase = lp;
+       return lp;
+}
+
+static void
+sFreeEqList(struct sEqList *list)
+{
+       list->next = sListFree;
+       sListFree = list;
+}
+
+static void
+sDeallocateEqLists(void)
+{
+       struct sEqList *lp, *lp_link;
+       for (lp = sListBase; lp != NULL; lp = lp_link) {
+               lp_link = lp->link;
+               free(lp);
+       }
+       sListBase = NULL;
+       sListFree = NULL;
+}
+
+static int
+sExistInEqList(int i, int idx, struct sEqList *list)
+{
+       while (list != NULL) {
+               if (list->i[idx] == i)
+                       return 1;
+               list = list->next;
+       }
+       return 0;
+}
+
+static struct sEqList *
+sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
+{
+       Atom *api, *apj;
+       struct sEqList *list1, *list2;
+       int ii, jj, ni, nj;
+       api = ATOM_AT_INDEX(mol->atoms, i);
+       apj = ATOM_AT_INDEX(mol->atoms, j);
+       if (api->atomicNumber != apj->atomicNumber)
+               return NULL;
+       list1 = sAllocEqList();
+       if (list1 == NULL)
+               return NULL;
+       list1->i[0] = i;
+       list1->i[1] = j;
+       list1->next = list;
+       if (i == j || (db[i] != NULL && db[i] == db[j]))
+               return list1;
+       for (ni = 0; ni < api->nconnects; ni++) {
+               ii = api->connects[ni];
+               if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
+                       continue;
+               if (sExistInEqList(ii, 0, list1))
+                       continue;
+               list2 = NULL;
+               for (nj = 0; nj < apj->nconnects; nj++) {
+                       jj = apj->connects[nj];
+                       if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
+                               continue;
+                       if (sExistInEqList(jj, 1, list1))
+                               continue;
+                       list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
+                       if (list2 != NULL)
+                               break;
+               }
+               if (list2 == NULL) {
+                       sFreeEqList(list1);
+                       return NULL;    /*  No equivalent to ii  */
+               }
+               list1 = list2;      /*  ii is OK, try next  */
+       }
+       return list1;
+}
+
+int
+sDBInclude(Int *ip, int i)
+{
+       int j;
+       if (ip == NULL)
+               return -1;
+       for (j = ip[0] - 1; j >= 0; j--) {
+               if (ip[j] == i)
+                       return j;
+       }
+       return -1;
+}
+
+Int *
+MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
+{
+       Int **db;  /*  List of equivalents for each atom  */
+       Int *ip, *result;
+       Atom *api, *apj, *apk;
+       int i, j, k, ii, jj, kk;
+       if (mol == NULL || mol->natoms == 0)
+               return NULL;
+       db = (Int **)calloc(sizeof(Int *), mol->natoms);
+
+       /*  Find the equivalent univalent atoms  */
+       for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
+               if (api->nconnects < 2)
+                       continue;
+               for (j = 0; j < api->nconnects; j++) {
+                       Int ibuf[ATOMS_MAX_CONNECTS], n;
+                       memset(ibuf, 0, sizeof(ibuf));
+                       n = 0;
+                       jj = api->connects[j];
+                       if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
+                               continue;
+                       ibuf[n++] = jj;
+                       apj = ATOM_AT_INDEX(mol->atoms, jj);
+                       if (apj->nconnects != 1 || db[jj] != NULL)
+                               continue;
+                       for (k = j + 1; k < api->nconnects; k++) {
+                               kk = api->connects[k];
+                               if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
+                                       continue;
+                               apk = ATOM_AT_INDEX(mol->atoms, kk);
+                               if (apk->nconnects != 1 || db[kk] != NULL)
+                                       continue;
+                               if (apj->atomicNumber == apk->atomicNumber)
+                                       ibuf[n++] = kk;
+                       }
+                       if (n > 1) {
+                               ip = (Int *)calloc(sizeof(Int), n + 1);
+                               if (ip == NULL)
+                                       return NULL;
+                               ip[0] = n;
+                               memmove(ip + 1, ibuf, sizeof(Int) * n);
+                               for (k = 0; k < n; k++)
+                                       db[ip[k + 1]] = ip;
+                       }
+               }
+       }
+       
+       /*  Try matching (i,j) pair  */
+       for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
+               if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
+                       continue;
+               for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
+                       struct sEqList *list;
+                       if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
+                               continue;
+                       if (api->atomicNumber != apj->atomicNumber)
+                               continue;  /*  Different elements do not match  */
+                       if (db[i] != NULL && db[i] == db[j])
+                               continue;  /*  Already equivalent  */
+                       list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
+                       if (list == NULL)
+                               continue;  /*  (i,j) do not match  */
+                       while (list != NULL) {
+                               ii = list->i[0];
+                               jj = list->i[1];
+                               if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
+                                       /*  Merge db[ii] and db[jj]  */
+                                       k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
+                                       ip = (Int *)calloc(sizeof(Int), k + 1);
+                                       if (ip == NULL)
+                                               return NULL;  /*  Out of memory  */
+                                       if (db[ii] == NULL) {
+                                               ip[1] = ii;
+                                               k = 2;
+                                       } else {
+                                               memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
+                                               k = db[ii][0] + 1;
+                                       }
+                                       if (db[jj] == NULL) {
+                                               ip[k++] = jj;
+                                       } else {
+                                               memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
+                                               k += db[jj][0];
+                                       }
+                                       ip[0] = k - 1;
+                                       /*  Free old ones  */
+                                       if (db[ii] != NULL)
+                                               free(db[ii]);
+                                       if (db[jj] != NULL)
+                                               free(db[jj]);
+                                       for (k = 0; k < ip[0]; k++)
+                                               db[ip[k + 1]] = ip;
+                                       if (0) {
+                                               /*  For debug  */
+                                               printf("(%d,%d) matched: ", ii, jj);
+                                               for (k = 0; k < ip[0]; k++) {
+                                                       printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
+                                               }
+                                               printf("]\n");
+                                       }
+                               }
+                               list = list->next;
+                       }
+               }
+       }
+       
+       /*  Record the equivalent atoms with the lowest index for each atom  */
+       result = (Int *)calloc(sizeof(Int), mol->natoms);
+       for (i = 0; i < mol->natoms; i++)
+               result[i] = -1;
+       for (i = 0; i < mol->natoms; i++) {
+               if (result[i] >= 0 || (ip = db[i]) == NULL)
+                       continue;
+               k = mol->natoms;
+               for (j = 0; j < ip[0]; j++) {
+                       kk = ip[j + 1];
+                       if (kk < k)
+                               k = kk;
+               }
+               for (j = 0; j < ip[0]; j++) {
+                       result[ip[j + 1]] = k;
+                       db[ip[j + 1]] = NULL;
+               }
+               free(ip);
+       }
+       sDeallocateEqLists();
+       return result;
+}
+
+#pragma mark ====== Symmetry expansion ======
+
+int
+MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
+{
+       if (mp == NULL)
+               return 1;
+       if (symop.sym >= mp->nsyms)
+               return 2;
+       if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
+               TransformVec(vpout, mp->cell->rtr, vpin);
+               TransformVec(vpout, mp->syms[symop.sym], vpout);
+               vpout->x += symop.dx;
+               vpout->y += symop.dy;
+               vpout->z += symop.dz;
+               TransformVec(vpout, mp->cell->tr, vpout);
+       } else {
+               TransformVec(vpout, mp->syms[symop.sym], vpin);
+               vpout->x += symop.dx;
+               vpout->y += symop.dy;
+               vpout->z += symop.dz;
+       }
+       return 0;
+}
+
+/*  TODO: Decide whether the symmetry operations are expressed in internal coordinates or cartesian coordinates. */
+int
+MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group)
+{
+       int i, n, n0, n1, count, *table;
+       Atom *ap;
+       IntGroupIterator iter;
+       int debug = 0;
+       if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
+               return -1;
+       if (symop.sym >= mp->nsyms)
+               return -2;
+       fprintf(stderr, "symop = {%d %d %d %d}\n", symop.sym, symop.dx, symop.dy, symop.dz);
+
+       /*  Create atoms, with avoiding duplicates  */
+       n0 = n1 = mp->natoms;
+       table = (int *)malloc(sizeof(int) * n0);
+       if (table == NULL)
+               return -3;
+       for (i = 0; i < n0; i++)
+               table[i] = -1;
+       IntGroupIteratorInit(group, &iter);
+       __MoleculeLock(mp);
+       for (i = 0; i < count; i++) {
+               int n2;
+               Atom *ap2;
+               Vector nr, dr;
+               n = IntGroupIteratorNext(&iter);
+               ap = ATOM_AT_INDEX(mp->atoms, n);
+               if (SYMOP_ALIVE(ap->symop)) {
+                       /*  Skip if the atom is expanded  */
+                       continue;
+               }
+               /*  Is this expansion already present?  */
+               for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
+                       if (ap2->symbase == n && SYMOP_EQUAL(symop, ap2->symop))
+                               break;
+               }
+               if (n2 < n0) {
+                       /*  If yes, then skip it  */
+                       continue;
+               }
+               /*  Is the expanded position coincides with itself?  */
+               MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
+               VecSub(dr, ap->r, nr);
+               if (VecLength2(dr) < 1e-6) {
+                       /*  If yes, then this atom is included but no new atom is created  */
+                       table[n] = n;
+               } else {
+                       /*  Create a new atom  */
+                       Atom newAtom;
+                       AtomDuplicate(&newAtom, ap);
+                       MoleculeCreateAnAtom(mp, &newAtom, -1);
+                       AtomClean(&newAtom);
+                       ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
+                       ap2->r = nr;
+                       ap2->symbase = n;
+                       ap2->symop = symop;
+                       ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
+                       table[n] = n1;  /*  The index of the new atom  */
+                       n1++;
+               }
+       }
+       IntGroupIteratorRelease(&iter);
+
+       /*  Create bonds  */
+       for (i = 0; i < n0; i++) {
+               int b[2];
+               b[0] = table[i];
+               if (b[0] < 0 || b[0] == i)
+                       continue;
+               ap = ATOM_AT_INDEX(mp->atoms, i);
+               for (n = 0; n < ap->nconnects; n++) {
+                       b[1] = table[ap->connects[n]];
+                       if (b[1] < 0)
+                               continue;
+                       if (b[1] > n0 && b[0] > b[1])
+                               continue;
+                       MoleculeAddBonds(mp, 1, b);
+               }
+       }
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       free(table);
+       return n1 - n0;  /*  The number of added atoms  */
+}
+
+/*  Recalculate the coordinates of symmetry expanded atoms.
+       Returns the number of affected atoms.
+    If group is non-NULL, only the expanded atoms whose base atoms are in the
+    given group are considered.
+       If groupout and vpout are non-NULL, the indices of the affected atoms
+       and the original positions are returned (for undo operation).
+       The pointers returned in *groupout and *vpout must be released and 
+       free()'ed by the caller  */
+int
+MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
+{
+       int i, count;
+       Atom *ap, *bp;
+       if (mp == NULL || mp->natoms == 0 || mp->nsyms == 0)
+               return 0;
+       if (groupout != NULL && vpout != NULL) {
+               *groupout = IntGroupNew();
+               if (*groupout == NULL)
+                       return -1;
+               *vpout = (Vector *)malloc(sizeof(Vector) * mp->natoms);
+               if (*vpout == NULL) {
+                       IntGroupRelease(*groupout);
+                       return -1;
+               }
+       } else groupout = NULL; /* To simplify test for validity of groupout/vpout */
+       
+       __MoleculeLock(mp);
+       count = 0;
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               Vector nr, dr;
+               if (!SYMOP_ALIVE(ap->symop))
+                       continue;
+               if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
+                       continue;
+               bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
+               MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
+               VecSub(dr, nr, ap->r);
+               if (VecLength2(dr) < 1e-20)
+                       continue;
+               if (groupout != NULL) {
+                       (*vpout)[count] = ap->r;
+                       IntGroupAdd(*groupout, i, 1);
+               }
+               ap->r = nr;
+               count++;
+       }
+       __MoleculeUnlock(mp);
+       if (groupout != NULL) {
+               if (count == 0) {
+                       free(*vpout);
+                       *vpout = NULL;
+                       IntGroupRelease(*groupout);
+                       *groupout = NULL;
+               } else {
+                       *vpout = (Vector *)realloc(*vpout, sizeof(Vector) * count);
+               }
+       }
+       return count;
+}
+
+#pragma mark ====== Show/hide atoms ======
+
+static void
+sMoleculeNotifyChangeAppearance(Molecule *mp)
+{
+       /*  TODO: Finer control of notification types may be necessary  */
+       MoleculeCallback_notifyModification(mp, 0);
+}
+
+
+static void
+sMoleculeUnselectHiddenAtoms(Molecule *mp)
+{
+       int i;
+       if (mp == NULL || mp->selection == NULL)
+               return;
+       for (i = 0; i < mp->natoms; i++) {
+               Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
+               if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
+                       IntGroupRemove(mp->selection, i, 1);
+       }
+       sMoleculeNotifyChangeAppearance(mp);
+}
+
+int
+MoleculeShowAllAtoms(Molecule *mp)
+{
+       int i;
+       if (mp == NULL)
+               return 0;
+       for (i = 0; i < mp->natoms; i++) {
+               Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
+               ap->exflags &= ~kAtomHiddenFlag;
+       }
+       sMoleculeNotifyChangeAppearance(mp);
+       return 1;
+}
+
+int
+MoleculeShowReverse(Molecule *mp)
+{
+       int i;
+       if (mp == NULL)
+               return 0;
+       for (i = 0; i < mp->natoms; i++) {
+               Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
+               ap->exflags ^= kAtomHiddenFlag;
+       }
+       sMoleculeUnselectHiddenAtoms(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+       return 1;
+}
+
+int
+MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
+{
+       int i;
+       if (mp == NULL || ig == NULL)
+               return 0;
+       for (i = 0; i < mp->natoms; i++) {
+               Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
+               if (ap->exflags & kAtomHiddenFlag)
+                       continue;  /*  Already hidden  */
+               if (IntGroupLookupPoint(ig, i) >= 0)
+                       ap->exflags |= kAtomHiddenFlag;
+       }
+       sMoleculeUnselectHiddenAtoms(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+       return 1;
+}
+
+#pragma mark ====== Reversible Editing ======
+
+/*
+static void
+sMoleculeNotifyModification(Molecule *mp)
+{
+       **  TODO: Finer control of notification types may be necessary  **
+       MoleculeCallback_notifyModification(mp, 0);
+}
+*/
+
+/*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
+int
+sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
+{
+       int n1, n2, n3, i;
+       if (where == NULL) {
+               /*  Append the new objects at the end  */
+               memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
+               return 0;
+       }
+       n1 = IntGroupGetCount(where);  /*  Position to get new object  */
+       n2 = nobjs;                    /*  Position to get old object  */
+       n3 = n1 + n2;                  /*  Position to place new/old object  */
+       for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
+               int start = IntGroupGetStartPoint(where, i);
+               int end = IntGroupGetEndPoint(where, i);
+               if (end < n3) {
+                       /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
+                       memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
+                       n2 = end - (n3 - n2);
+                       n3 = end;
+               }
+               /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
+               memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
+               n3 -= end - start;
+               n1 -= end - start;
+       }
+       return 0;
+}
+
+/*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
+int
+sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
+{
+       int n1, n2, n3, start, end, i;
+       if (objs == NULL || where == NULL)
+               return 1;  /*  Bad argument  */
+       n1 = 0;  /*  Position to move remaining elements to */
+       n2 = 0;  /*  Position to move remaining elements from  */
+       n3 = 0;  /*  Position to move removed elements to  */
+       for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
+               end = IntGroupGetEndPoint(where, i);
+               if (n2 < start) {
+                       /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
+                       if (n1 < n2)
+                               memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
+                       n1 += start - n2;
+                       n2 = start;
+               }
+               /*  Move (end - start) elements from objs[n2] to clip[n3]  */
+               if (clip != NULL)
+                       memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
+               n3 += (end - start);
+               n2 += (end - start);
+       }
+       /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
+       if (nobjs > n2)
+               memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
+       return 0;
+}
+
+/*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
+int
+sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
+{
+       int n1, start, end, i;
+       if (objs == NULL || where == NULL)
+               return 1;  /*  Bad argument  */
+       n1 = 0;  /*  Position to move removed elements to  */
+       for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
+               end = IntGroupGetEndPoint(where, i);
+               /*  Copy (end - start) elements from objs[start] to clip[n1]  */
+               if (clip != NULL)
+                       memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
+               n1 += (end - start);
+       }
+       return 0;
+}
+
+/*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
+   (Use AtomDuplicate() first) */
+int
+MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
+{
+    Atom *ap1, *api;
+       int i;
+       if (mp == NULL || ap == NULL || mp->noModifyTopology)
+               return -1;
+       __MoleculeLock(mp);
+       if (pos < 0 || pos >= mp->natoms)
+               pos = mp->natoms;
+       ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
+       if (ap1 == NULL)
+               goto error;  /*  Out of memory  */
+       ap1 = ATOM_AT_INDEX(mp->atoms, pos);
+       if (pos < mp->natoms - 1) {
+               memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
+       }
+       if (AtomDuplicate(ap1, ap) == NULL) {
+               /*  Cannot duplicate: restore the original state  */
+               memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
+               mp->natoms--;
+               goto error;
+       }
+       ap1->nconnects = 0;
+       if (ap1->resSeq >= mp->nresidues)
+               AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
+       if (ap1->resName[0] == 0)
+         strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
+       if (ap1->segName[0] == 0)
+         strncpy(ap1->segName, "MAIN", 4);
+       if (pos < mp->natoms - 1) {
+               /*  Renumber the connect table, bonds, angles, etc. */
+               for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
+                       int j;
+                       for (j = 0; j < api->nconnects; j++) {
+                               if (api->connects[j] >= pos)
+                                       api->connects[j]++;
+                       }
+               }
+               for (i = 0; i < mp->nbonds * 2; i++) {
+                       if (mp->bonds[i] >= pos)
+                               mp->bonds[i]++;
+               }
+               for (i = 0; i < mp->nangles * 3; i++) {
+                       if (mp->angles[i] >= pos)
+                               mp->angles[i]++;
+               }
+               for (i = 0; i < mp->ndihedrals * 4; i++) {
+                       if (mp->dihedrals[i] >= pos)
+                               mp->dihedrals[i]++;
+               }
+               for (i = 0; i < mp->nimpropers * 4; i++) {
+                       if (mp->impropers[i] >= pos)
+                               mp->impropers[i]++;
+               }
+       }
+       mp->nframes = -1;  /*  Should be recalculated later  */
+       MoleculeIncrementModifyCount(mp);
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return pos;
+error:
+       __MoleculeUnlock(mp);
+       return -1;
+}
+
+/*  Merge two molecules. We use this procedure for all add-atom operations.  */
+/*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
+int
+MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset)
+{
+       int nsrc, ndst;
+       int i, j, n1, n2, n3, n4;
+       Int *new2old, *old2new;
+       Atom *ap;
+       if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
+               return 0;  /*  Do nothing  */
+
+       if (dst->noModifyTopology)
+               return 1;  /*  Prohibited operation  */
+
+       if (where != NULL && IntGroupGetCount(where) != src->natoms)
+               return 1;  /*  Bad parameter  */
+
+       __MoleculeLock(dst);
+       nsrc = src->natoms;
+       ndst = dst->natoms;
+       if (resSeqOffset < 0)
+               resSeqOffset = 0;
+
+       /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
+           and ndst..ndst+nsrc-1 are for atoms in src.  */ 
+       new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
+       if (new2old == NULL)
+               goto panic;
+       old2new = new2old + ndst + nsrc;
+       n1 = 0;  /*  dst index  */
+       n2 = 0;  /*  src index  */
+       n3 = 0;  /*  "merged" index  */
+       i = 0;
+       while (n1 < ndst || n2 < nsrc) {
+               if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
+                       n4 = ndst - n1;
+               else n4 -= n3;
+               /*  n4 elements from dst[n1] will go to merged[n3]  */
+               for (j = 0; j < n4; j++) {
+                       old2new[n1 + j] = n3 + j;
+                       new2old[n3 + j] = n1 + j;
+               }
+               n3 += n4;
+               n1 += n4;
+               if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
+                       n4 = nsrc - n2;
+               /*  n4 elements from src[n2] will go to merged[n3]  */
+               for (j = 0; j < n4; j++) {
+                       old2new[ndst + n2 + j] = n3 + j;
+                       new2old[n3 + j] = ndst + n2 + j;
+               }
+               n3 += n4;
+               n2 += n4;
+               i++;
+       }
+
+       /*  Expand the destination array  */
+       if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
+               goto panic;
+
+       /*  Move the atoms  */
+       if (where == NULL) {
+               /*  Duplicate atoms to the end of the destination array  */
+               for (i = 0; i < nsrc; i++) {
+                       if (AtomDuplicate(ATOM_AT_INDEX(dst->atoms, ndst + i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
+                               goto panic;
+               }
+       //      memmove(ATOM_AT_INDEX(dst->atoms, ndst), src->atoms, gSizeOfAtomRecord * nsrc);
+       } else {
+               /*  Duplicate to a temporary storage and then insert  */
+               Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
+               if (tempatoms == NULL)
+                       goto panic;
+               for (i = 0; i < nsrc; i++) {
+                       if (AtomDuplicate(ATOM_AT_INDEX(tempatoms, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
+                               goto panic;
+               }
+               if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
+                       goto panic;
+               free(tempatoms);
+       }
+       dst->natoms = ndst + nsrc;
+
+       /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
+       for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (new2old[i] < ndst) {
+                       /*  This atom is from dst  */
+                       n1 = 0;
+               } else {
+                       /*  This atom is from src  */
+                       n1 = ndst;  /*  Offset to the internal number  */
+                       if (ap->resSeq != 0)
+                               ap->resSeq += resSeqOffset;  /*  Modify residue number  */
+               }
+               for (j = 0; j < ap->nconnects; j++)
+                       ap->connects[j] = old2new[ap->connects[j] + n1];
+               if (SYMOP_ALIVE(ap->symop))
+                       ap->symbase = old2new[ap->symbase + n1];
+       }
+       
+       /*  Move the bonds, angles, dihedrals, impropers  */
+       for (i = 0; i < 4; i++) {
+               Int *nitems, *nitems_src;
+               Int **items, **items_src;
+               Int nsize;  /*  Number of Ints in one element  */
+               switch (i) {
+                       case 0:
+                               nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
+                       case 1:
+                               nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
+                       case 2:
+                               nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
+                       case 3:
+                               nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
+               }
+               nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
+               items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
+               /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
+               n1 = *nitems;
+               /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
+               n2 = *nitems_src;
+               /*  Expand the array  */
+               if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
+                       goto panic;
+               /*  Copy the items  */
+               memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
+               /*  Renumber  */
+               for (j = 0; j < n1 * nsize; j++)
+                       (*items)[j] = old2new[(*items)[j]];
+               for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
+                       (*items)[j] = old2new[(*items)[j] + ndst];
+       }
+       
+       /*  Copy the residues if necessary  */
+       /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
+           However, 1+resSeqOffset should not overwrite the existing residue in dst;
+               i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
+       n1 = dst->nresidues;
+       if (1 + resSeqOffset < n1) {
+               n2 = n1;
+       } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
+       if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
+               if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
+                       goto panic;
+               memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
+       }
+
+       MoleculeCleanUpResidueTable(dst);
+       
+       free(new2old);
+       dst->nframes = -1;  /*  Should be recalculated later  */
+
+       MoleculeIncrementModifyCount(dst);
+       dst->needsMDRebuild = 1;
+       __MoleculeUnlock(dst);
+       return 0;
+
+  panic:
+       __MoleculeUnlock(dst);
+    Panic("Low memory while adding atoms");
+       return 1;  /*  Not reached  */
+}
+
+static int
+sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag)
+{
+       int nsrc, ndst, nsrcnew;
+       int i, j, n1, n2, n3, n4;
+       Int *new2old, *old2new;
+       IntGroup *move_g, *del_g;
+       Molecule *dst;
+       Atom *ap, *dst_ap;
+       
+       if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
+               /*  Do nothing  */
+               if (dstp != NULL)
+                       *dstp = NULL;
+               return 0;
+       }
+       
+       if (src->noModifyTopology && moveFlag)
+               return 1;  /*  Prohibit editing  */
+
+       if ((ndst = IntGroupGetCount(where)) > src->natoms)
+               return 1;  /*  Bad parameter  */
+
+       __MoleculeLock(src);
+       
+       nsrc = src->natoms;
+       nsrcnew = nsrc - ndst;
+       if (resSeqOffset < 0)
+               resSeqOffset = 0;
+
+       /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
+           and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
+       new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
+       if (new2old == NULL)
+               goto panic;
+       old2new = new2old + nsrc;
+       n1 = 0;  /*  src index  */
+       n2 = 0;  /*  dst index  */
+       n3 = 0;  /*  src index after "unmerge"  */
+       i = 0;
+       while (n1 < nsrc || n2 < ndst) {
+               if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
+                       n4 = nsrc - n1;
+               else n4 -= n1;
+               /*  n4 elements from src[n1] will go to unmerged[n3]  */
+               for (j = 0; j < n4; j++) {
+                       old2new[n1 + j] = n3 + j;
+                       new2old[n3 + j] = n1 + j;
+               }
+               n3 += n4;
+               n1 += n4;
+               if ((n4 = IntGroupGetInterval(where, i)) < 0)
+                       n4 = nsrc - n1;
+               /*  n4 elements from src[n1] will go to dst[n2]  */
+               for (j = 0; j < n4; j++) {
+                       old2new[n1 + j] = nsrcnew + n2 + j;
+                       new2old[nsrcnew + n2 + j] = n1 + j;
+               }
+               n1 += n4;
+               n2 += n4;
+               i++;
+       }
+
+       /*  Make sure that no bond between the two fragments exists  */
+/*     for (i = 0; i < src->nbonds; i++) {
+               n1 = old2new[src->bonds[i * 2]];
+               n2 = old2new[src->bonds[i * 2 + 1]];
+               if ((n1 < nsrcnew && n2 >= nsrcnew) || (n1 >= nsrcnew && n2 < nsrcnew)) {
+                       free(new2old);
+                       return 2;
+               }
+       } */
+       
+       /*  Make a new molecule  */
+       if (dstp != NULL) {
+               dst = MoleculeNew();
+               if (dst == NULL)
+                       goto panic;
+               /*  Expand the destination array  */
+               if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
+                       goto panic;
+               dst_ap = dst->atoms;
+       } else {
+               dst = NULL;
+               dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
+               if (dst_ap == NULL)
+                       goto panic;
+       }
+       
+       /*  Move the atoms  */
+       if (moveFlag) {
+               if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
+                       goto panic;
+               src->natoms = nsrcnew;
+               if (dst == NULL) {
+                       /*  The atom record must be deallocated correctly  */
+                       for (i = 0; i < ndst; i++)
+                               AtomClean(ATOM_AT_INDEX(dst_ap, i));
+               }
+       } else {
+               if (dst != NULL) {
+                       for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
+                               AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
+               }
+#if 0
+               if (sCopyElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
+                       goto panic;
+               if (dst != NULL) {
+                       /*  The atom record must be deep-copied correctly  */
+                       for (i = 0; i < ndst; i++) {
+                               if (AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
+                                       goto panic;
+                       }
+               }
+#endif
+       }
+       
+       if (dst == NULL) {
+               /*  The dummy destination array is no longer needed  */
+               free(dst_ap);
+               dst_ap = NULL;
+       }
+       
+       /*  Renumber the atom indices in connect[]  */
+       if (moveFlag) {
+               for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       for (j = n1 = 0; j < ap->nconnects; j++) {
+                               n2 = old2new[ap->connects[j]];
+                               if (n2 < nsrcnew)
+                                       ap->connects[n1++] = n2;
+                       }
+                       ap->nconnects = n1;
+               }
+       }
+       
+       /*  Renumber the atom indices in connect[] and the residue indices  */
+       if (dst != NULL) {
+               for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
+                               ap->resSeq -= resSeqOffset;
+                       else ap->resSeq = 0;
+                       for (j = n1 = 0; j < ap->nconnects; j++) {
+                               n2 = old2new[ap->connects[j]] - nsrcnew;
+                               if (n2 >= 0)
+                                       ap->connects[n1++] = n2;
+                       }
+                       ap->nconnects = n1;
+               }
+       }
+
+       /*  Separate the bonds, angles, dihedrals, impropers  */
+       move_g = IntGroupNew();
+       del_g = IntGroupNew();
+       if (move_g == NULL || del_g == NULL)
+               goto panic;
+       for (i = 0; i < 4; i++) {
+               Int *nitems, *nitems_dst;
+               Int **items, **items_dst;
+               Int nsize;  /*  Number of Ints in one element  */
+               unsigned char *counts;
+               switch (i) {
+                       case 0:
+                               nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
+                       case 1:
+                               nitems = &src->nangles; items = &src->angles; nsize = 3; break;
+                       case 2:
+                               nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
+                       case 3:
+                               nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
+                       default:
+                               nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
+               }
+               if (dst != NULL) {
+                       nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
+                       items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
+               } else {
+                       nitems_dst = NULL;
+                       items_dst = NULL;
+               }
+               counts = (unsigned char *)calloc(1, *nitems);
+               /*  Find the entries that should be moved to dst  */
+               n2 = 0;
+               for (j = 0; j < *nitems * nsize; j++) {
+                       n1 = old2new[(*items)[j]];
+                       if (n1 >= nsrcnew)
+                               counts[j / nsize]++; /* Count the atom belonging to dst */ 
+               /*      if (n1 >= nsrcnew) {
+                               n1 -= nsrcnew;
+                               if (j % nsize == 0) {
+                                       if (IntGroupAdd(sep, j / nsize, 1) != 0)
+                                               goto panic;
+                                       n2++;
+                               }
+                       }
+                       (*items)[j] = n1; */
+               }
+               for (j = n2 = n3 = 0; j < *nitems; j++) {
+                       if (counts[j] > 0) {
+                               /*  Remove from src  */
+                               n2++;
+                               if (IntGroupAdd(del_g, j, 1) != 0)
+                                       goto panic;
+                               if (counts[j] == nsize) {
+                                       /*  Move to dst  */
+                                       n3++;
+                                       if (IntGroupAdd(move_g, j, 1) != 0)
+                                               goto panic;
+                               }
+                       }
+               }
+               if (n2 > 0) {
+                       /*  Expand the destination array  */
+                       if (items_dst != NULL && n3 > 0) {
+                               if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
+                                       goto panic;
+                               if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
+                                       goto panic;
+                       }
+                       /*  Remove from src  */
+                       if (moveFlag) {
+                               if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
+                                       goto panic;
+                               (*nitems) -= n2;
+                       }
+               }
+               /*  Renumber the entries  */
+               if (moveFlag) {
+                       for (j = 0; j < *nitems * nsize; j++) {
+                               (*items)[j] = old2new[(*items)[j]];
+                       }
+               }
+               if (items_dst != NULL) {
+                       for (j = 0; j < *nitems_dst * nsize; j++) {
+                               (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
+                       }
+               }
+               free(counts);
+               IntGroupClear(move_g);
+               IntGroupClear(del_g);
+       }
+       IntGroupRelease(move_g);
+       IntGroupRelease(del_g);
+       
+       /*  Copy the residues  */
+       if (dst != NULL) {
+               /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
+               n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
+               if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
+                       goto panic;
+               if (n1 > 1) {
+                       memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
+               }
+       }
+
+#if 0
+       /*  Copy the parameters  */
+       if (dst != NULL && src->par != NULL) {
+               UnionPar *up;
+               for (i = 0; i < nsrc; i++) {
+                       old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
+               }
+               move_g = IntGroupNew();
+               for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
+                       n2 = ParameterGetCountForType(src->par, n1);
+                       if (n2 == 0)
+                               continue;
+                       /*  Find parameters to be copied to dst  */
+                       for (i = 0; i < n2; i++) {
+                               up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
+                               for (j = 0, ap = dst->atoms; j < dst->natoms; j++, ap = ATOM_NEXT(ap)) {
+                                       if (ParameterDoesContainAtom(n1, up, new2old[j + nsrcnew], kParameterLookupNoWildcard) || ParameterDoesContainAtom(n1, up, ap->type, kParameterLookupNoWildcard)) {
+                                               IntGroupAdd(move_g, i, 1);
+                                               break;
+                                       }
+                               }
+                       }
+                       n2 = IntGroupGetCount(move_g);
+                       if (n2 == 0)
+                               continue;
+                       up = (UnionPar *)calloc(sizeof(UnionPar), n2);
+                       if (up == NULL)
+                               goto panic;
+                       /*  Renumber indices if necessary  */
+                       for (i = 0; i < n2; i++)
+                               ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
+                       IntGroupClear(move_g);
+               }
+               for (i = 0; i < nsrc; i++) {
+                       old2new[i] += nsrcnew;  /*  Restore indices  */
+               }
+               IntGroupRelease(move_g);
+       }
+#endif
+       
+       /*  Clean up  */
+       MoleculeCleanUpResidueTable(src);
+       if (dst != NULL)
+               MoleculeCleanUpResidueTable(dst);
+       free(new2old);
+
+       src->nframes = -1;  /*  Should be recalculated later  */
+       if (dst != NULL)
+               dst->nframes = -1;  /*  Should be recalculated later  */
+
+       
+       if (dstp != NULL)
+               *dstp = dst;
+
+       MoleculeIncrementModifyCount(src);
+       src->needsMDRebuild = 1;
+       __MoleculeUnlock(src);
+       
+       return 0;
+
+  panic:
+       __MoleculeUnlock(src);
+/*    Panic("Low memory while removing atoms"); */
+       return -1;
+}
+
+/*  Separate molecule into two parts. The atoms specified by 'where' are moved
+    from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
+       in which case the moved atoms are discarded.  */
+int
+MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset)
+{
+       return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1);
+}
+
+/*  Extract atoms from a given molecule into two parts. The atoms specified by 
+       'where' are copied from src to a new molecule, which is returned as *dstp.
+    If dummyFlag is non-zero, then the atoms that are not included in the group 
+       but are connected to any atoms in the group are converted to "dummy" atoms 
+       (i.e. with element "Du" and names beginning with an underscore) and included 
+       in the new molecule object.  */
+int
+MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
+{
+       int retval;
+
+       /*  Extract the fragment  */
+       retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0);
+       if (retval != 0)
+               return retval;
+
+       if (dummyFlag) {
+
+               /*  Search bonds crossing the molecule border  */
+               IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
+               if (ig != NULL) {
+                       IntGroupIterator iter;
+                       Int i, idx;
+                       idx = 1;
+                       IntGroupIteratorInit(ig, &iter);
+                       while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+                               /*  The atoms at the border  */
+                               Int n1, n2, nn[3];
+                               Atom a, *ap;
+                               n1 = src->bonds[i*2];
+                               n2 = src->bonds[i*2+1];
+                               if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
+                                       int w = n1;
+                                       n1 = n2;
+                                       n2 = w;
+                                       if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
+                                               continue;  /*  Actually this is an internal error  */
+                               }
+                               /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
+                               /*  Create a new dummy atom with the same segment/residue info with n1
+                                   and the same position as n2  */
+                               ap = ATOM_AT_INDEX(src->atoms, n1);
+                               memset(&a, 0, gSizeOfAtomRecord);
+                               a.segSeq = ap->segSeq;
+                               memmove(a.segName, ap->segName, 4);
+                               a.resSeq = ap->resSeq;
+                               memmove(a.resName, ap->resName, 4);
+                               ElementToString(0, a.element);  /*  "Du"  */
+                               snprintf(a.aname, 4, "_%d", idx++);
+                               a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
+                               /*  Add the dummy atom to the new molecule; nn[1] is the index
+                                   of the new dummy atom in the new molecule  */
+                               nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
+                               /*  Connect nn1 and nn2  */
+                               nn[2] = kInvalidIndex;
+                               MoleculeAddBonds(*dstp, 1, nn);
+                       }
+                       IntGroupIteratorRelease(&iter);
+                       IntGroupRelease(ig);
+               }
+       }
+       
+       return 0;
+}
+
+int
+MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds)
+{
+       int i, j, n1, n2;
+       Atom *ap;
+       if (mp == NULL || bonds == NULL || nbonds <= 0)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+
+       /*  Check the bonds  */
+       for (i = 0; i < nbonds; i++) {
+               n1 = bonds[i * 2];
+               n2 = bonds[i * 2 + 1];
+               if (n1 == n2 || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
+                       return -1;  /*  Bad bond specification  */
+               ap = ATOM_AT_INDEX(mp->atoms, n1);
+               if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1 || ATOM_AT_INDEX(mp->atoms, n2)->nconnects >= ATOMS_MAX_CONNECTS - 1)
+                       return -2;  /*  Too many bonds  */
+               for (j = 0; j < ap->nconnects; j++) {
+                       if (ap->connects[j] == n2)
+                               return -3;  /*  Duplicate bond  */
+               }
+       }
+       
+       __MoleculeLock(mp);
+
+       /*  Add connects[]  */
+       for (i = 0; i < nbonds; i++) {
+               n1 = bonds[i * 2];
+               n2 = bonds[i * 2 + 1];
+               ap = ATOM_AT_INDEX(mp->atoms, n1);
+               ap->connects[ap->nconnects++] = n2;
+               ap = ATOM_AT_INDEX(mp->atoms, n2);
+               ap->connects[ap->nconnects++] = n1;
+       }
+       
+       /*  Expand the array and insert  */
+       n1 = mp->nbonds;
+/*     if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nb - 1, NULL) == NULL
+       || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nb, sizeof(Int) * 2, where) != 0) */
+       if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nbonds - 1, NULL) == NULL)
+               goto panic;
+       memmove(mp->bonds + n1 * 2, bonds, sizeof(Int) * 2 * nbonds);
+
+       /*  Add angles, dihedrals, impropers  */
+       {
+               Int nangles, ndihedrals, nimpropers;
+               Int *angles, *dihedrals, *impropers;
+               Int k, n3, n4;
+               Int *ip;
+               Int temp[4];
+               Atom *ap1, *ap2;
+
+               angles = dihedrals = impropers = NULL;
+               nangles = ndihedrals = nimpropers = 0;
+
+               for (i = 0; i < nbonds; i++) {
+                       n1 = bonds[i * 2];
+                       n2 = bonds[i * 2 + 1];
+                       ap1 = ATOM_AT_INDEX(mp->atoms, n1);
+                       ap2 = ATOM_AT_INDEX(mp->atoms, n2);
+                       /*  Angles X-n1-n2  */
+                       for (j = 0; j < ap1->nconnects; j++) {
+                               n3 = ap1->connects[j];
+                               if (n3 == n2)
+                                       continue;
+                               temp[0] = n3;
+                               temp[1] = n1;
+                               temp[2] = n2;
+                               for (k = 0; k < nangles; k++) {
+                                       ip = angles + k * 3;
+                                       if (ip[1] == n1 && ((ip[0] == n3 && ip[2] == n2) || (ip[0] == n2 && ip[2] == n3)))
+                                               break;
+                               }
+                               if (k == nangles) {
+                                       if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
+                                               goto panic;
+                               }
+                               /*  Dihedrals X-n1-n2-X  */
+                               for (k = 0; k < ap2->nconnects; k++) {
+                                       n4 = ap2->connects[k];
+                                       if (n4 == n1 || n4 == n3)
+                                               continue;
+                                       temp[3] = n4;
+                                       if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
+                                               goto panic;
+                               }
+                               /*  Impropers X-n2-n1-X  */
+                       /*      temp[1] = n2;
+                               temp[2] = n1;
+                               for (k = 0; k < ap1->nconnects; k++) {
+                                       n4 = ap1->connects[k];
+                                       if (n4 == n2 || n4 <= n3)
+                                               continue;
+                                       temp[3] = n4;
+                                       if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
+                                               goto panic;
+                               } */
+                       }
+                       /*  Angles X-n2-n1  */
+                       for (j = 0; j < ap2->nconnects; j++) {
+                               n3 = ap2->connects[j];
+                               if (n3 == n1)
+                                       continue;
+                               temp[0] = n1;
+                               temp[1] = n2;
+                               temp[2] = n3;
+                               for (k = 0; k < nangles; k++) {
+                                       ip = angles + k * 3;
+                                       if (ip[1] == n2 && ((ip[0] == n3 && ip[2] == n1) || (ip[0] == n1 && ip[2] == n3)))
+                                               break;
+                               }
+                               if (k == nangles) {
+                                       if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
+                                               goto panic;
+                               }
+                       }
+               }
+               temp[0] = kInvalidIndex;
+               if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
+                       goto panic;
+               if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
+                       goto panic;
+               if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
+                       goto panic;
+               MoleculeAddAngles(mp, angles, NULL);
+               MoleculeAddDihedrals(mp, dihedrals, NULL);
+               MoleculeAddImpropers(mp, impropers, NULL);
+               if (angles != NULL)
+                       free(angles);
+               if (dihedrals != NULL)
+                       free(dihedrals);
+               if (impropers != NULL)
+                       free(impropers);
+       }
+       
+       MoleculeIncrementModifyCount(mp);
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+
+       return nbonds;  
+
+  panic:
+       __MoleculeUnlock(mp);
+       Panic("Low memory while adding bonds");
+       return -1;  /*  Not reached  */
+}
+
+int
+MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds)
+{
+       int i, j, n1, n2;
+       Atom *ap;
+
+       if (mp == NULL || nbonds <= 0)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+
+       __MoleculeLock(mp);
+
+       /*  Update connects[]  */
+       for (i = 0; i < nbonds; i++) {
+               n1 = bonds[i * 2];
+               n2 = bonds[i * 2 + 1];
+               ap = ATOM_AT_INDEX(mp->atoms, n1);
+               for (j = 0; j < ap->nconnects; j++) {
+                       if (ap->connects[j] == n2) {
+                               memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
+                               ap->nconnects--;
+                               break;
+                       }
+               }
+               ap = ATOM_AT_INDEX(mp->atoms, n2);
+               for (j = 0; j < ap->nconnects; j++) {
+                       if (ap->connects[j] == n1) {
+                               memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
+                               ap->nconnects--;
+                               break;
+                       }
+               }
+       }
+
+       /*  Remove bonds, angles, dihedrals, impropers  */      
+       {
+               IntGroup *bg, *ag, *dg, *ig;
+               Int *ip;
+
+               bg = IntGroupNew();
+               ag = IntGroupNew();
+               dg = IntGroupNew();
+               ig = IntGroupNew();
+               if (bg == NULL || ag == NULL || dg == NULL || ig == NULL)
+                       goto panic;
+               for (i = 0; i < nbonds; i++) {
+                       n1 = bonds[i * 2];
+                       n2 = bonds[i * 2 + 1];
+                       for (j = 0; j < mp->nbonds; j++) {
+                               ip = mp->bonds + j * 2;
+                               if ((ip[0] == n1 && ip[1] == n2)
+                                || (ip[1] == n1 && ip[0] == n2)) {
+                                       if (IntGroupAdd(bg, j, 1) != 0)
+                                               goto panic;
+                               }
+                       }
+                       for (j = 0; j < mp->nangles; j++) {
+                               ip = mp->angles + j * 3;
+                               if ((ip[0] == n1 && ip[1] == n2)
+                                || (ip[1] == n1 && ip[0] == n2)
+                                || (ip[1] == n1 && ip[2] == n2)
+                                || (ip[2] == n1 && ip[1] == n2)) {
+                                       if (IntGroupAdd(ag, j, 1) != 0)
+                                               goto panic;
+                               }
+                       }
+                       for (j = 0; j < mp->ndihedrals; j++) {
+                               ip = mp->dihedrals + j * 4;
+                               if ((ip[1] == n1 && ip[2] == n2)
+                                || (ip[2] == n1 && ip[1] == n2)) {
+                                       if (IntGroupAdd(dg, j, 1) != 0)
+                                               goto panic;
+                               }
+                       }
+                       for (j = 0; j < mp->nimpropers; j++) {
+                               ip = mp->impropers + j * 4;
+                               if ((ip[0] == n1 && ip[2] == n2)
+                                || (ip[1] == n1 && ip[2] == n2)
+                                || (ip[3] == n1 && ip[2] == n2)
+                                || (ip[0] == n2 && ip[2] == n1)
+                                || (ip[1] == n2 && ip[2] == n1)
+                                || (ip[3] == n2 && ip[2] == n1)) {
+                                       if (IntGroupAdd(ig, j, 1) != 0)
+                                               goto panic;
+                               }
+                       }
+               }
+               if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, NULL, sizeof(Int) * 2, bg) != 0)
+                       goto panic;
+               mp->nbonds -= IntGroupGetCount(bg);
+               
+               if (IntGroupGetCount(ag) > 0)
+                       MoleculeDeleteAngles(mp, NULL, ag);
+               if (IntGroupGetCount(dg) > 0)
+                       MoleculeDeleteDihedrals(mp, NULL, dg);
+               if (IntGroupGetCount(ig) > 0)
+                       MoleculeDeleteImpropers(mp, NULL, ig);
+               IntGroupRelease(bg);
+               IntGroupRelease(ag);
+               IntGroupRelease(dg);
+               IntGroupRelease(ig);
+       }
+
+       MoleculeIncrementModifyCount(mp);
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+
+       return nbonds;
+
+  panic:
+       __MoleculeUnlock(mp);
+       Panic("Low memory while removing bonds");
+       return -1;  /*  Not reached  */
+}
+
+int
+MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
+{
+       int n1, nc;
+       if (mp == NULL || angles == NULL)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+
+       __MoleculeLock(mp);
+       if (where != NULL)
+               nc = IntGroupGetCount(where);
+       else {
+               for (n1 = 0; angles[n1 * 3] >= 0; n1++)
+                       ;
+               nc = n1;
+       }
+       if (nc > 0) {
+               n1 = mp->nangles;
+               if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
+                       || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
+                       __MoleculeUnlock(mp);
+                       Panic("Low memory while adding angles");
+               }
+       }
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return nc;
+}
+
+int
+MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
+{
+       int nc;
+       if (mp == NULL || where == NULL)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+       __MoleculeLock(mp);
+       if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
+               __MoleculeUnlock(mp);
+               Panic("Low memory while adding angles");
+       }
+       mp->nangles -= (nc = IntGroupGetCount(where));
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return nc;
+}
+
+int
+MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
+{
+       int n1, nc;
+       if (mp == NULL || dihedrals == NULL)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+       if (where != NULL)
+               nc = IntGroupGetCount(where);
+       else {
+               for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
+                       ;
+               nc = n1;
+       }
+       if (nc <= 0)
+               return 0;
+       n1 = mp->ndihedrals;
+       __MoleculeLock(mp);
+       if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
+       || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
+               __MoleculeUnlock(mp);
+               Panic("Low memory while adding dihedrals");
+       }
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return nc;
+}
+
+int
+MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
+{      
+       int nc;
+       if (mp == NULL || where == NULL)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+       __MoleculeLock(mp);
+       if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
+               __MoleculeUnlock(mp);
+               Panic("Low memory while adding dihedrals");
+       }
+       mp->ndihedrals -= (nc = IntGroupGetCount(where));
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return nc;
+}
+
+int
+MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
+{
+       int n1, nc;
+       if (mp == NULL || impropers == NULL)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+       if (where != NULL)
+               nc = IntGroupGetCount(where);
+       else {
+               for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
+                       ;
+               nc = n1;
+       }
+       if (nc <= 0)
+               return 0;
+       n1 = mp->nimpropers;
+       __MoleculeLock(mp);
+       if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
+       || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
+               __MoleculeUnlock(mp);
+               Panic("Low memory while adding impropers");
+       }
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return nc;
+}
+
+int
+MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
+{
+       int nc;
+       if (mp == NULL || where == NULL)
+               return 0;
+       if (mp->noModifyTopology)
+               return -4;  /*  Prohibited operation  */
+       __MoleculeLock(mp);
+       if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
+               __MoleculeUnlock(mp);
+               Panic("Low memory while adding impropers");
+       }
+       mp->nimpropers -= (nc = IntGroupGetCount(where));
+       __MoleculeUnlock(mp);
+       return nc;
+}
+
+int
+MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
+{
+       Int i, *ip;
+       if (mp == NULL || mp->bonds == NULL)
+               return -1;
+       for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
+               if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
+                       return i;
+       }
+       return -1;
+}
+
+int
+MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
+{
+       Int i, *ip;
+       if (mp == NULL || mp->angles == NULL)
+               return -1;
+       for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
+               if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
+                       (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
+                       return i;
+       }
+       return -1;
+}
+
+int
+MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
+{
+       Int i, *ip;
+       if (mp == NULL || mp->dihedrals == NULL)
+               return -1;
+       for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
+               if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
+                       (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
+                       return i;
+       }
+       return -1;
+}
+
+int
+MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
+{
+       Int i, *ip;
+       if (mp == NULL || mp->impropers == NULL)
+               return -1;
+       for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
+               if (n3 != ip[2])
+                       continue;
+               if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
+                       (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
+                       (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
+                       return i;
+       }
+       return -1;
+}
+
+/*  Remove the bond at bondIndex and create two dummy atoms instead.
+    The dummy atoms are placed at the end of atoms[], and the residue
+       numbers are the same as the root atoms (i.e. the atoms to which
+       the dummy atoms are connected). The indices are returned in
+       dummyIndices[0,1].  */
+int
+MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
+{
+       Int roots[3], newBonds[5];
+       Vector dr;
+       Atom *rootp[2];
+       Atom na[2], *nap;
+       int i, natoms;
+       if (mp == NULL || mp->noModifyTopology)
+               return 0;
+       if (bondIndex < 0 || bondIndex >= mp->nbonds)
+               return -1;
+       roots[0] = mp->bonds[bondIndex * 2];
+       roots[1] = mp->bonds[bondIndex * 2 + 1];
+       roots[2] = kInvalidIndex;
+       rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
+       rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
+       VecSub(dr, rootp[0]->r, rootp[1]->r);
+       for (i = 0; i < 2; i++) {
+               float w;
+               nap = &na[i];
+               memmove(nap, rootp[i], sizeof(na));
+               nap->aname[0] = '*';
+               strcpy(nap->element, "Du");
+               nap->type = 0;
+               nap->charge = nap->weight = 0.0;
+               nap->atomicNumber = 0;
+               nap->nconnects = 0;
+               w = (i == 0 ? 0.4 : -0.4);
+               VecScaleInc(nap->r, dr, w);
+               VecZero(nap->v);
+               VecZero(nap->f);
+               nap->intCharge = 0;
+               nap->exflags = 0;
+       }
+
+       /*  Expand atoms array and append the dummy atoms at the end  */
+       __MoleculeLock(mp);
+       natoms = mp->natoms;
+       if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
+               goto panic;
+       memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
+       dummyIndices[0] = natoms;
+       dummyIndices[1] = natoms + 1;
+
+       /*  Remove the old bond and create new bonds  */
+/*     ig = IntGroupNewWithPoints(bondIndex, 1, -1);
+       if (ig == NULL)
+               goto panic;
+       MoleculeDeleteBonds(mp, NULL, ig);
+       IntGroupRelease(ig); */
+       MoleculeDeleteBonds(mp, 1, roots);
+       newBonds[0] = roots[0];
+       newBonds[1] = dummyIndices[0];
+       newBonds[2] = roots[1];
+       newBonds[3] = dummyIndices[1];
+       newBonds[4] = kInvalidIndex;
+       
+       i = (MoleculeAddBonds(mp, 2, newBonds) < 0 ? -1 : 0);
+       mp->needsMDRebuild = 1;
+       __MoleculeUnlock(mp);
+       return i;
+
+panic:
+       __MoleculeUnlock(mp);
+       Panic("Low memory during creating dummy atoms");
+       return 1;
+}
+
+/*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
+    a bond between the two root atoms. The value bondIndex is used as a
+       hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
+       the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
+       is ignored and the new bond is stored at the end of bonds[].  */
+int
+MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
+{
+       return 0;
+}
+
+/*
+Int
+MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
+{
+       Int n1, *np1;
+       if (mol == NULL || mol->noModifyTopology)
+               return -1;
+       n1 = mol->nangles;
+       np1 = mol->angles;
+       mol->nangles = 0;
+       mol->angles = NULL;
+       if (nangles > 0) {
+               __MoleculeLock(mol);
+               NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
+               memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
+               mol->needsMDRebuild = 1;
+               __MoleculeUnlock(mol);
+       }
+       *outAngles = np1;
+       return n1;
+}
+                                               
+Int
+MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
+{
+       Int n1, *np1;
+       if (mol == NULL || mol->noModifyTopology)
+               return -1;
+       n1 = mol->ndihedrals;
+       np1 = mol->dihedrals;
+       mol->ndihedrals = 0;
+       mol->dihedrals = NULL;
+       if (ndihedrals > 0) {
+               __MoleculeLock(mol);
+               NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
+               memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
+               mol->needsMDRebuild = 1;
+               __MoleculeUnlock(mol);
+       }
+       *outDihedrals = np1;
+       return n1;
+}
+
+Int
+MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
+{
+       Int n1, *np1;
+       if (mol == NULL || mol->noModifyTopology)
+               return -1;
+       n1 = mol->nimpropers;
+       np1 = mol->impropers;
+       mol->nimpropers = 0;
+       mol->impropers = NULL;
+       if (nimpropers > 0) {
+               __MoleculeLock(mol);
+               NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
+               memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
+               mol->needsMDRebuild = 1;
+               __MoleculeUnlock(mol);
+       }
+       *outImpropers = np1;
+       return n1;
+}
+*/
+
+Int
+MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
+{
+       Int i, j, k, *ip;
+       Atom *ap;
+       Int nangles;
+       Int *angles;
+       
+       if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
+               return 0;  /*  molecule is empty  */
+       if (mol->noModifyTopology)
+               return -1;
+       nangles = 0;
+       angles = NULL;
+       for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
+               Int *cp = ap->connects;
+               for (j = 0; j < ap->nconnects; j++) {
+                       Int j0 = cp[j];
+                       for (k = j + 1; k < ap->nconnects; k++) {
+                               Int k0 = cp[k];
+                               if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
+                                       ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
+                                       ip[0] = j0;
+                                       ip[1] = i;
+                                       ip[2] = k0;
+                               }
+                       }
+               }
+       }
+       if (nangles > 0) {
+               ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
+               ip[0] = -1;
+               nangles--;
+       }
+       if (outAngles != NULL)
+               *outAngles = angles;
+       return nangles;
+}
+
+Int
+MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
+{
+       Int n1, n2, n3, n4, *ip;
+       Atom *ap2, *ap3;
+       Int ndihedrals;
+       Int *dihedrals;
+       
+       if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
+               return 0;  /*  molecule is empty  */
+       ndihedrals = 0;
+       dihedrals = NULL;
+       for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
+               Int i1, i3, i4, *ip;
+               for (i3 = 0; i3 < ap2->nconnects; i3++) {
+                       n3 = ap2->connects[i3];
+                       if (n2 > n3)
+                               continue;
+                       ap3 = ATOM_AT_INDEX(mol->atoms, n3);
+                       for (i1 = 0; i1 < ap2->nconnects; i1++) {
+                               n1 = ap2->connects[i1];
+                               if (n1 == n3)
+                                       continue;
+                               for (i4 = 0; i4 < ap3->nconnects; i4++) {
+                                       n4 = ap3->connects[i4];
+                                       if (n2 == n4 || n1 == n4)
+                                               continue;
+                                       if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
+                                               ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
+                                               ip[0] = n1;
+                                               ip[1] = n2;
+                                               ip[2] = n3;
+                                               ip[3] = n4;
+                                       }
+                               }
+                       }
+               }
+       }
+       if (ndihedrals > 0) {
+               ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
+               ip[0] = -1;
+               ndihedrals--;
+       }
+       if (outDihedrals != NULL)
+               *outDihedrals = dihedrals;
+       return ndihedrals;
+}
+
+Int
+MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
+{
+       Int n1, n2, n3, n4, t1, t2, t3, t4, *ip;
+       Parameter *par = mol->par;
+       Atom *ap, *ap3;
+       Int nimpropers;
+       Int *impropers;
+       
+       if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
+               return 0;  /*  molecule is empty  */
+       if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
+               return 0;  /*  No improper parameters are defined  */
+       nimpropers = 0;
+       impropers = NULL;
+       ap = mol->atoms;
+       for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
+               Int i1, i2, i4, found, *ip;
+               t3 = ap3->type;
+               for (i1 = 0; i1 < ap3->nconnects; i1++) {
+                       n1 = ap3->connects[i1];
+                       t1 = ATOM_AT_INDEX(ap, n1)->type;
+                       for (i2 = i1 + 1; i2 < ap3->nconnects; i2++) {
+                               n2 = ap3->connects[i2];
+                               t2 = ATOM_AT_INDEX(ap, n2)->type;
+                               for (i4 = i2 + 1; i4 < ap3->nconnects; i4++) {
+                                       n4 = ap3->connects[i4];
+                                       t4 = ATOM_AT_INDEX(ap, n4)->type;
+                                       found = 0;
+                                       if (ParameterLookupImproperPar(par, n1, n2, n3, n4, 1) != NULL)
+                                               found = 1;
+                                       else if (ParameterLookupImproperPar(par, t1, t2, t3, t4, 0) != NULL)
+                                               found = 1;
+                                       else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, 0) != NULL)
+                                               found = 1;
+                                       if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
+                                               ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
+                                               ip[0] = n1;
+                                               ip[1] = n2;
+                                               ip[2] = n3;
+                                               ip[3] = n4;
+                                       }
+                               }
+                       }
+               }
+       }
+       if (nimpropers > 0) {
+               ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
+               ip[0] = -1;
+               nimpropers--;
+       }
+       if (outImpropers != NULL)
+               *outImpropers = impropers;
+       return nimpropers;
+}
+
+#pragma mark ====== Residues ======
+
+void
+MoleculeCleanUpResidueTable(Molecule *mp)
+{
+       int i, maxres;
+       Atom *ap;
+       if (mp == NULL || mp->natoms == 0)
+               return;
+       maxres = 0;
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (ap->resSeq >= maxres)
+                       maxres = ap->resSeq + 1;
+               if (ap->resSeq < mp->nresidues) {
+                       if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
+                               strncpy(ap->resName, mp->residues[ap->resSeq], 4);
+               } else {
+                       AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
+               }
+       }
+       if (maxres < mp->nresidues)
+               mp->nresidues = maxres;
+       __MoleculeUnlock(mp);
+}
+
+/*  Change the number of residues. If nresidues is greater than the current value,
+    then the array mp->residues is expanded with null names. If nresidues is smaller
+       than the current value, mp->nresidues is set to the smallest possible value
+       that is no smaller than nresidues and larger than any of the resSeq values.  */
+int
+MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
+{
+       int n;
+       if (mp == NULL)
+               return 0;
+       if (mp->nresidues == nresidues)
+               return nresidues;
+       else if (mp->nresidues < nresidues) {
+               __MoleculeLock(mp);
+               n = mp->nresidues;
+               AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
+               while (n < nresidues)
+                       mp->residues[n++][0] = 0;
+               __MoleculeUnlock(mp);
+               return nresidues;
+       } else {
+               int i;
+               Atom *ap;
+               n = nresidues;
+               for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       if (ap->resSeq >= n)
+                               n = ap->resSeq + 1;
+               }
+               mp->nresidues = n;
+               return n;
+       }
+}
+
+int
+MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
+{
+       IntGroupIterator iter;
+       int withArray, resSeq, maxSeq;
+       int i, j;
+       Atom *ap;
+       
+       /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
+       if (((int)resSeqs & 1) == 0) {
+               withArray = 1;
+               resSeq = 0;
+       } else {
+               withArray = 0;
+               resSeq = ((int)resSeqs - 1) / 2;
+       }
+       
+       IntGroupIteratorInit(group, &iter);
+
+       /*  Change resSeqs  */
+       maxSeq = 0;
+       j = 0;
+       __MoleculeLock(mp);
+       while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+               ap = ATOM_AT_INDEX(mp->atoms, i);
+               if (withArray)
+                       resSeq = resSeqs[j++];
+               if (resSeq > maxSeq)
+                       maxSeq = resSeq;
+               ap->resSeq = resSeq;
+       }
+       __MoleculeUnlock(mp);
+
+       /*  Expand array if necessary  */
+       if (maxSeq >= mp->nresidues)
+               MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
+
+       /*  Synchronize resName and residues[]  */
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               strncpy(ap->resName, mp->residues[ap->resSeq], 4);
+       }
+       IntGroupIteratorRelease(&iter);
+       __MoleculeUnlock(mp);
+       
+       MoleculeIncrementModifyCount(mp);
+       
+       return 0;
+}
+
+int
+MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
+{
+       return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
+}
+
+/*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
+    specifies the mp->nresidues after modifying the residue numbers.
+       If all atoms are modified, then the table of residue names is also shifted. Otherwise,
+       the table of residue names is not touched. */
+int
+MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
+{
+       int i, maxSeq, nmodatoms;
+       Atom *ap;
+       IntGroupIterator iter;
+       IntGroupIteratorInit(group, &iter);
+       maxSeq = 0;
+       if (nresidues < 0)
+               nresidues = mp->nresidues;
+       nmodatoms = 0;
+       __MoleculeLock(mp);
+       while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+               ap = ATOM_AT_INDEX(mp->atoms, i);
+               ap->resSeq += offset;
+               if (ap->resSeq < 0) {
+                       /*  Bad argument; undo change and returns this index + 1  */
+                       int bad_index = i;
+                       ap->resSeq -= offset;
+                       while ((i = IntGroupIteratorLast(&iter)) >= 0) {
+                               ap = ATOM_AT_INDEX(mp->atoms, i);
+                               ap->resSeq -= offset;
+                       }
+                       IntGroupIteratorRelease(&iter);
+                       return bad_index + 1;
+               }
+               if (ap->resSeq > maxSeq)
+                       maxSeq = ap->resSeq;
+               nmodatoms++;
+       }
+       if (maxSeq >= nresidues)
+               nresidues = maxSeq + 1;
+       if (offset < 0 && nmodatoms == mp->natoms) {
+               /*  Shift the residue names downward  */
+               memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
+       }
+       __MoleculeUnlock(mp);
+       MoleculeChangeNumberOfResidues(mp, nresidues);
+       if (offset > 0 && nmodatoms == mp->natoms) {
+               /*  Shift the residue names upward  */
+               __MoleculeLock(mp);
+               memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
+               __MoleculeUnlock(mp);
+       }
+       IntGroupIteratorRelease(&iter);
+
+       MoleculeIncrementModifyCount(mp);
+       
+       return 0;
+}
+
+/*  Change residue names for the specified residue numbers. Names is an array of
+    chars containing argc*4 characters, and every 4 characters represent a
+       residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
+       names to be handled as a C string.  */
+int
+MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
+{
+       int i, maxSeq;
+       Atom *ap;
+       maxSeq = 0;
+       for (i = 0; i < argc; i++) {
+               if (maxSeq < resSeqs[i])
+                       maxSeq = resSeqs[i];
+       }
+       if (maxSeq >= mp->nresidues)
+               MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
+       __MoleculeLock(mp);
+       for (i = 0; i < argc; i++) {
+               char *p = mp->residues[resSeqs[i]];
+               int j;
+               strncpy(p, names + i * 4, 4);
+               for (j = 0; j < 4; j++) {
+                       if (p[j] >= 0 && p[j] < 0x20)
+                               p[j] = 0;
+               }
+       }
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               strncpy(ap->resName, mp->residues[ap->resSeq], 4);
+       }
+       __MoleculeUnlock(mp);
+
+       MoleculeIncrementModifyCount(mp);
+       
+       return 0;
+}
+
+/*  Returns the maximum residue number actually used  */
+int
+MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
+{
+       int i, maxSeq;
+       Atom *ap;
+       maxSeq = -1;
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               if (ap->resSeq > maxSeq)
+                       maxSeq = ap->resSeq;
+       }
+       return maxSeq;
+}
+
+/*  Returns the minimum residue number actually used  */
+int
+MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
+{
+       int i, minSeq;
+       Atom *ap;
+       minSeq = ATOMS_MAX_NUMBER;
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               if (ap->resSeq < minSeq)
+                       minSeq = ap->resSeq;
+       }
+       return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
+}
+
+#pragma mark ====== Sort by Residues ======
+
+static int
+sAtomSortComparator(const void *a, const void *b)
+{
+       const Atom *ap, *bp;
+       ap = *((const Atom **)a);
+       bp = *((const Atom **)b);
+       if (ap->resSeq == bp->resSeq) {
+               /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
+               if (ap < bp)
+                       return -1;
+               else if (ap > bp)
+                       return 1;
+               else return 0;
+       } else {
+               /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
+               if (ap->resSeq == 0)
+                       return 1;
+               else if (bp->resSeq == 0)
+                       return -1;
+               else if (ap->resSeq < bp->resSeq)
+                       return -1;
+               else if (ap->resSeq > bp->resSeq)
+                       return 1;
+               else return 0;
+       }
+}
+
+static void
+sMoleculeReorder(Molecule *mp)
+{
+       int i, res, prevRes;
+       Atom **apArray;
+       Int *old2new;
+       Atom *newAtoms;
+       if (mp == NULL || mp->natoms <= 1)
+               return;
+
+       /*  Sort the atoms, bonds, etc. */
+       apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
+       old2new = (Int *)calloc(sizeof(Int), mp->natoms);
+       newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
+       if (apArray == NULL || old2new == NULL || newAtoms == NULL)
+               Panic("Low memory during reordering atoms");
+       for (i = 0; i < mp->natoms; i++)
+               apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
+
+       /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
+       qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
+       
+       /*  Make a table of 'which atom becomes which'  */
+       for (i = 0; i < mp->natoms; i++) {
+               int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
+               old2new[j] = i;
+       }
+       
+       /*  Renumber the bonds, etc.  */
+       for (i = 0; i < mp->nbonds * 2; i++) {
+               mp->bonds[i] = old2new[mp->bonds[i]];
+       }
+       for (i = 0; i < mp->nangles * 3; i++) {
+               mp->angles[i] = old2new[mp->angles[i]];
+       }
+       for (i = 0; i < mp->ndihedrals * 4; i++) {
+               mp->dihedrals[i] = old2new[mp->dihedrals[i]];
+       }
+       for (i = 0; i < mp->nimpropers * 4; i++) {
+               mp->impropers[i] = old2new[mp->impropers[i]];
+       }
+       for (i = 0; i < mp->natoms; i++) {
+               Int *ip, j;
+               for (j = 0, ip = apArray[i]->connects; j < apArray[i]->nconnects; j++, ip++)
+                       *ip = old2new[*ip];
+       }
+       
+       /*  Renumber the residues so that the residue numbers are contiguous  */
+       res = prevRes = 0;
+       for (i = 0; i < mp->natoms; i++) {
+               if (apArray[i]->resSeq == 0)
+                       break;
+               if (apArray[i]->resSeq != prevRes) {
+                       res++;
+                       prevRes = apArray[i]->resSeq;
+                       if (prevRes != res) {
+                               strncpy(mp->residues[res], mp->residues[prevRes], 4);
+                       }
+               }
+               apArray[i]->resSeq = res;
+       }
+       mp->nresidues = res + 1;
+
+       /*  Sort the atoms and copy back to atoms[] */
+       for (i = 0; i < mp->natoms; i++) {
+               memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
+       }
+       memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
+       
+       /*  Free the locally allocated storage  */
+       free(newAtoms);
+       free(old2new);
+       free(apArray);
+
+}
+
+/*  Renumber atoms  */
+int
+MoleculeReorderAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
+{
+       Int *old2new, i, j, retval;
+       Atom *saveAtoms;
+       if (mp == NULL)
+               return 0;
+       if (mp->noModifyTopology)
+               return -1;
+       if (old2new_out != NULL)
+               old2new = old2new_out;
+       else
+               old2new = (Int *)calloc(sizeof(Int), mp->natoms);
+       saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
+       if (old2new == NULL || saveAtoms == NULL)
+               Panic("Low memory during reordering atoms");
+       memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
+       __MoleculeLock(mp);
+       for (i = 0; i < mp->natoms; i++)
+               old2new[i] = -1;
+       for (i = 0; i < isize && i < mp->natoms; i++) {
+               j = new2old[i];
+               if (j < 0 || j >= mp->natoms) {
+                       retval = 1; /* Out of range */
+                       goto end;
+               }
+               if (old2new[j] != -1) {
+                       retval = 2;  /*  Duplicate entry  */
+                       goto end;
+               }
+               old2new[j] = i;
+       }
+       if (i < mp->natoms) {
+               for (j = 0; j < mp->natoms; j++) {
+                       if (old2new[j] != -1)
+                               continue;
+                       old2new[j] = i++;
+               }
+       }
+       if (i != mp->natoms) {
+               retval = 3;  /*  Internal inconsistency  */
+               goto end;
+       }
+
+       /*  Renumber the bonds, etc.  */
+       for (i = 0; i < mp->nbonds * 2; i++) {
+               mp->bonds[i] = old2new[mp->bonds[i]];
+       }
+       for (i = 0; i < mp->nangles * 3; i++) {
+               mp->angles[i] = old2new[mp->angles[i]];
+       }
+       for (i = 0; i < mp->ndihedrals * 4; i++) {
+               mp->dihedrals[i] = old2new[mp->dihedrals[i]];
+       }
+       for (i = 0; i < mp->nimpropers * 4; i++) {
+               mp->impropers[i] = old2new[mp->impropers[i]];
+       }
+       for (i = 0; i < mp->natoms; i++) {
+               Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
+               Int *ip;
+               for (j = 0, ip = ap->connects; j < ap->nconnects; j++, ip++)
+                       *ip = old2new[*ip];
+       }
+       
+       /*  Renumber the atoms  */
+       for (i = 0; i < mp->natoms; i++)
+               memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
+       retval = 0;
+
+       MoleculeIncrementModifyCount(mp);
+       mp->needsMDRebuild = 1;
+
+  end:
+       __MoleculeUnlock(mp);
+       free(saveAtoms);
+       if (old2new_out == NULL)
+               free(old2new);
+       return retval;
+}
+
+#pragma mark ====== Coordinate Transform ======
+
+void
+MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
+{
+       int i;
+       Atom *ap;
+       if (mp == NULL || tr == NULL)
+               return;
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               TransformVec(&ap->r, tr, &ap->r);
+       }
+       __MoleculeUnlock(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+}
+
+void
+MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
+{
+       int i;
+       Atom *ap;
+       if (mp == NULL || tr == NULL)
+               return;
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               TransformVec(&ap->r, tr, &ap->r);
+       }
+       __MoleculeUnlock(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+}
+
+void
+MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
+{
+       int i;
+       Atom *ap;
+       if (mp == NULL || vp == NULL)
+               return;
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               VecInc(ap->r, *vp);
+       }
+       __MoleculeUnlock(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+}
+
+void
+MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
+{
+       int i;
+       Double w;
+       Transform tr;
+       Vector cv;
+       Atom *ap;
+       if (mp == NULL || axis == NULL)
+               return;
+       w = VecLength(*axis);
+       if (w < 1e-7)
+               return;
+       __MoleculeLock(mp);
+       /*  Construct a rotation transform: p' = c + A * (p - c)  */
+       if (center == NULL)
+               cv.x = cv.y = cv.z = 0.0;
+       else
+               cv = *center;
+       TransformForRotation(tr, axis, angle, &cv);
+
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               TransformVec(&ap->r, tr, &ap->r);
+       }
+       __MoleculeUnlock(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+}
+
+void
+MoleculeReaxis(Molecule *mp, const Vector *xaxis, const Vector *yaxis, const Vector *zaxis, IntGroup *group)
+{
+       int i;
+       Atom *ap;
+       Vector v;
+       if (mp == NULL || xaxis == NULL || yaxis == NULL || zaxis == NULL)
+               return;
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               v.x = VecDot(ap->r, *xaxis);
+               v.y = VecDot(ap->r, *yaxis);
+               v.z = VecDot(ap->r, *zaxis);
+               ap->r = v;
+       }
+       __MoleculeUnlock(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+}
+
+int
+MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
+{
+       int i;
+       Atom *ap;
+       Double w;
+       if (mp == NULL || center == NULL)
+               return 1;
+       if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
+               return 2;   /*  Empty molecule  */
+       w = 0.0;
+       center->x = center->y = center->z = 0.0;
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               VecScaleInc(*center, ap->r, ap->weight);
+               w += ap->weight;
+       }
+       if (w < 1e-7)
+               return 3;  /*  Atomic weights are not defined?  */
+       w = 1.0 / w;
+       VecScaleSelf(*center, w);
+       return 0;
+}
+
+int
+MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
+{
+       Vector vmin, vmax;
+       int i;
+       Atom *ap;
+       if (mp == NULL)
+               return 1;
+       if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
+               return 2;   /*  Empty molecule  */
+       vmin.x = vmin.y = vmin.z = 1e50;
+       vmax.x = vmax.y = vmax.z = -1e50;
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
+                       continue;
+               if (vmin.x > ap->r.x)
+                       vmin.x = ap->r.x;
+               if (vmin.y > ap->r.y)
+                       vmin.y = ap->r.y;
+               if (vmin.z > ap->r.z)
+                       vmin.z = ap->r.z;
+               if (vmax.x < ap->r.x)
+                       vmax.x = ap->r.x;
+               if (vmax.y < ap->r.y)
+                       vmax.y = ap->r.y;
+               if (vmax.z < ap->r.z)
+                       vmax.z = ap->r.z;
+       }
+       if (min != NULL)
+               *min = vmin;
+       if (max != NULL)
+               *max = vmax;
+       return 0;       
+}
+
+#pragma mark ====== Measurements ======
+
+Double
+MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
+{
+       Vector r1, r2;
+/*     if (mp->is_xtal_coord) {
+               TransformVec(&r1, mp->cell->tr, vp1);
+               TransformVec(&r2, mp->cell->tr, vp2);
+       } else */ {
+               r1 = *vp1;
+               r2 = *vp2;
+       }
+       VecDec(r1, r2);
+       return VecLength(r1);
+}
+
+Double
+MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
+{
+       Vector r1, r2, r3;
+       double w;
+/*     if (mp->is_xtal_coord) {
+               TransformVec(&r1, mp->cell->tr, vp1);
+               TransformVec(&r2, mp->cell->tr, vp2);
+               TransformVec(&r3, mp->cell->tr, vp3);
+       } else */ {
+               r1 = *vp1;
+               r2 = *vp2;
+               r3 = *vp3;
+       }
+       VecDec(r1, r2);
+       VecDec(r3, r2);
+       w = VecLength(r1) * VecLength(r3);
+       if (w < 1e-20)
+               return NAN;
+       return acos(VecDot(r1, r3) / w) * 180.0 / 3.1415927;
+}
+
+Double
+MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
+{
+       Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
+       double w1, w2, w3;
+/*     if (mp->is_xtal_coord) {
+               TransformVec(&r1, mp->cell->tr, vp1);
+               TransformVec(&r2, mp->cell->tr, vp2);
+               TransformVec(&r3, mp->cell->tr, vp3);
+               TransformVec(&r4, mp->cell->tr, vp4);
+       } else */ {
+               r1 = *vp1;
+               r2 = *vp2;
+               r3 = *vp3;
+               r4 = *vp4;
+       }
+       VecSub(r21, r1, r2);
+       VecSub(r32, r2, r3);
+       VecSub(r43, r3, r4);
+       VecCross(v1, r21, r32);
+       VecCross(v2, r32, r43);
+       VecCross(v3, r32, v1);
+       w1 = VecLength(v1);
+       w2 = VecLength(v2);
+       w3 = VecLength(v3);
+       if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
+               return NAN;
+       } else {
+               w1 = 1.0 / w1;
+               w2 = 1.0 / w2;
+               w3 = 1.0 / w3;
+               VecScaleSelf(v1, w1);
+               VecScaleSelf(v2, w2);
+               VecScaleSelf(v3, w3);
+               return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * 180.0 / 3.1415927;
+       }
+}
+
+#pragma mark ====== XtalCell Parameters ======
+
+void
+MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
+{
+       if (mp->cell != NULL) {
+               TransformVec(dst, mp->cell->tr, src);
+       } else *dst = *src;
+}
+
+void
+MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
+{
+       if (mp->cell != NULL) {
+               TransformVec(dst, mp->cell->rtr, src);
+       } else *dst = *src;
+}
+
+int
+MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
+{
+       static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
+       int n1, n2, n3;
+       Vector *vp1, *vp2, *vp3;
+       Vector v1, v2;
+
+       if (cp == NULL)
+               return 0;
+       for (n1 = 0; n1 < 3; n1++) {
+               if (cp->flags[n1] != 0)
+                       break;
+       }
+       if (n1 == 3) {
+               /*  All directions are non-periodic  */
+               memmove(&(cp->tr), &identityTransform, sizeof(Transform));
+               memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
+       } else {
+               n2 = (n1 + 1) % 3;
+               n3 = (n1 + 2) % 3;
+               vp1 = &(cp->axes[n1]);
+               vp2 = &(cp->axes[n2]);
+               vp3 = &(cp->axes[n3]);
+               cp->tr[n1] = vp1->x;
+               cp->tr[n1 + 3] = vp1->y;
+               cp->tr[n1 + 6] = vp1->z;
+               cp->tr[9] = cp->origin.x;
+               cp->tr[10] = cp->origin.y;
+               cp->tr[11] = cp->origin.z;
+               if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
+                       /*  1-dimensional or 2-dimensional system  */
+                       /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
+                        possible with a single matrix  */
+                       if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
+                               /*  1-dimensional  */
+                               static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
+                               VecCross(v1, *vp1, xvec);
+                               VecCross(v2, *vp1, yvec);
+                               if (VecLength2(v1) < VecLength2(v2))
+                                       v1 = v2;
+                               VecCross(v2, *vp1, v1);
+                               if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
+                                       return -1;   /*  Non-regular transform  */
+                       } else if (cp->flags[n2] == 0) {
+                               v2 = *vp3;
+                               VecCross(v1, v2, *vp1);
+                               if (NormalizeVec(&v1, &v1))
+                                       return -1;  /*  Non-regular transform  */
+                       } else {
+                               v1 = *vp2;
+                               VecCross(v2, *vp1, v1);
+                               if (NormalizeVec(&v2, &v2))
+                                       return -1;  /*  Non-regular transform  */
+                       }
+                       cp->tr[n2] = v1.x;
+                       cp->tr[n2 + 3] = v1.y;
+                       cp->tr[n2 + 6] = v1.z;
+                       cp->tr[n3] = v2.x;
+                       cp->tr[n3 + 3] = v2.y;
+                       cp->tr[n3 + 6] = v2.z;
+               } else {
+                       VecCross(v1, *vp1, *vp2);
+                       if (fabs(VecDot(v1, *vp3)) < 1e-7)
+                               return -1;  /*  Non-regular transform  */
+                       cp->tr[n2] = vp2->x;
+                       cp->tr[n2 + 3] = vp2->y;
+                       cp->tr[n2 + 6] = vp2->z;
+                       cp->tr[n3] = vp3->x;
+                       cp->tr[n3 + 3] = vp3->y;
+                       cp->tr[n3 + 6] = vp3->z;
+               }
+       }
+       if (TransformInvert(cp->rtr, cp->tr))
+               return -1;  /*  Non-regular transform  */
+
+       /*  Calculate the reciprocal cell parameters  */
+       cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[1] * cp->rtr[1] + cp->rtr[2] * cp->rtr[2]);
+       cp->rcell[1] = sqrt(cp->rtr[3] * cp->rtr[3] + cp->rtr[4] * cp->rtr[4] + cp->rtr[5] * cp->rtr[5]);
+       cp->rcell[2] = sqrt(cp->rtr[6] * cp->rtr[6] + cp->rtr[7] * cp->rtr[7] + cp->rtr[8] * cp->rtr[8]);
+       cp->rcell[3] = acos((cp->rtr[3] * cp->rtr[6] + cp->rtr[4] * cp->rtr[7] + cp->rtr[5] * cp->rtr[8]) / (cp->rcell[1] * cp->rcell[2])) * kRad2Deg;
+       cp->rcell[4] = acos((cp->rtr[6] * cp->rtr[0] + cp->rtr[7] * cp->rtr[1] + cp->rtr[8] * cp->rtr[2]) / (cp->rcell[2] * cp->rcell[0])) * kRad2Deg;
+       cp->rcell[5] = acos((cp->rtr[0] * cp->rtr[3] + cp->rtr[1] * cp->rtr[4] + cp->rtr[2] * cp->rtr[5]) / (cp->rcell[0] * cp->rcell[1])) * kRad2Deg;
+       
+       if (!calc_abc) {
+               /*  Calculate a, b, c, alpha, beta, gamma  */
+               cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
+               cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
+               cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
+               cp->cell[3] = acos((cp->tr[3] * cp->tr[6] + cp->tr[4] * cp->tr[7] + cp->tr[5] * cp->tr[8]) / (cp->cell[1] * cp->cell[2])) * kRad2Deg;
+               cp->cell[4] = acos((cp->tr[6] * cp->tr[0] + cp->tr[7] * cp->tr[1] + cp->tr[8] * cp->tr[2]) / (cp->cell[2] * cp->cell[0])) * kRad2Deg;
+               cp->cell[5] = acos((cp->tr[0] * cp->tr[3] + cp->tr[1] * cp->tr[4] + cp->tr[2] * cp->tr[5]) / (cp->cell[0] * cp->cell[1])) * kRad2Deg;
+       }
+       
+       return 0;
+}
+
+void
+MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
+{
+       XtalCell *cp;
+       int i;
+       Atom *ap;
+       Transform cmat;
+       if (mp == NULL)
+               return;
+       __MoleculeLock(mp);
+       memset(&cmat, 0, sizeof(Transform));
+       if (a == 0.0) {
+               if (mp->cell != NULL) {
+                       memmove(&cmat, &(mp->cell->tr), sizeof(Transform));
+                       free(mp->cell);
+               } else {
+                       cmat[0] = cmat[4] = cmat[8] = 1.0;
+               }
+               mp->cell = NULL;
+       /*      mp->is_xtal_coord = 0; */
+       } else {
+               cp = mp->cell;
+               if (cp == NULL) {
+                       cp = (XtalCell *)malloc(sizeof(XtalCell));
+                       if (cp == NULL)
+                               Panic("Low memory during setting cell parameters");
+                       mp->cell = cp;
+                       cmat[0] = cmat[4] = cmat[8] = 1.0;
+               } else {
+               /*      if (mp->is_xtal_coord)
+                               memmove(&cmat, &(cp->tr), sizeof(Transform)); */
+               }
+       /*      mp->is_xtal_coord = 1; */
+               /*  alpha, beta, gamma are in degree  */
+               cp->cell[0] = a;
+               cp->cell[1] = b;
+               cp->cell[2] = c;
+               cp->cell[3] = alpha;
+               cp->cell[4] = beta;
+               cp->cell[5] = gamma;
+               if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
+                       /*  c unique (hexagonal etc.)  */
+                       Double cosa, cosb, sinb, cosg;
+                       cosa = cos(alpha * kDeg2Rad);
+                       cosb = cos(beta * kDeg2Rad);
+                       sinb = sin(beta * kDeg2Rad);
+                       cosg = cos(gamma * kDeg2Rad);
+                       cp->axes[0].x = a * sinb;
+                       cp->axes[0].y = 0;
+                       cp->axes[0].z = a * cosb;
+                       cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
+                       cp->axes[1].z = b * cosa;
+                       cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
+                       cp->axes[2].x = 0;
+                       cp->axes[2].y = 0;
+                       cp->axes[2].z = c;
+               } else {
+                       /*  b unique  */
+                       Double cosg, sing, cosa, cosb;
+                       cosa = cos(alpha * kDeg2Rad);
+                       cosb = cos(beta * kDeg2Rad);
+                       cosg = cos(gamma * kDeg2Rad);
+                       sing = sin(gamma * kDeg2Rad);
+                       cp->axes[0].x = a * sing;
+                       cp->axes[0].y = a * cosg;
+                       cp->axes[0].z = 0;
+                       cp->axes[1].x = 0;
+                       cp->axes[1].y = b;
+                       cp->axes[1].z = 0;
+                       cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
+                       cp->axes[2].y = c * cosa;
+                       cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
+               }
+               cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
+               cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
+               MoleculeCalculateCellFromAxes(cp, 0);
+               TransformMul(cmat, cp->rtr, cmat);
+       }
+       
+       /*  Update the coordinates (if requested)  */
+       if (convertCoordinates) {
+               for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       TransformVec(&(ap->r), cmat, &(ap->r));
+               }
+       }
+       
+       /*  Update the anisotropic parameters  */
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               Aniso *anp = ap->aniso;
+               if (anp != NULL) {
+                       MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5]);
+               }
+       }
+       __MoleculeUnlock(mp);
+       sMoleculeNotifyChangeAppearance(mp);
+}
+
+void
+MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x23, Double x31)
+{
+       Double d, dx;
+       int u = 0;
+       const Double log2 = 0.693147180559945;
+       const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
+       Transform m1, m2;
+       Aniso *anp;
+       XtalCell *cp;
+       Vector axis[3];
+       Double val[3];
+       if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
+               return;
+       anp = mp->atoms[n1].aniso;
+       __MoleculeLock(mp);
+       if (anp == NULL) {
+               anp = (Aniso *)malloc(sizeof(Aniso));
+               if (anp == NULL) {
+                       __MoleculeUnlock(mp);
+                       Panic("Low memory during setting anisotropic atom parameters");
+               }
+               mp->atoms[n1].aniso = anp;
+       }
+       switch (type) {
+               case 1: d = 1; dx = 0.5; break;
+               case 2: d = log2; dx = log2; break;
+               case 3: d = log2; dx = log2 * 0.5; break;
+               case 4: u = 1; d = 0.25; dx = 0.25; break;
+               case 5: u = 1; d = 0.25; dx = 0.125; break;
+               case 8: u = 1; d = pi22; dx = pi22; break;
+               case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
+               case 10: d = pi22; dx = pi22; break;
+               default: d = dx = 1; break;
+       }
+       anp->bij[0] = x11 * d;
+       anp->bij[1] = x22 * d;
+       anp->bij[2] = x33 * d;
+       anp->bij[3] = x12 * dx;
+       anp->bij[4] = x23 * dx;
+       anp->bij[5] = x31 * dx;
+       cp = mp->cell;
+       if (cp != NULL && u == 1) {
+               anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
+               anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
+               anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
+               anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
+               anp->bij[4] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[3] * kDeg2Rad); */
+               anp->bij[5] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[4] * kDeg2Rad); */
+       }
+       
+       /*  Calculate the principal axes (in Cartesian coordinates)  */
+       /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
+               B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
+               in which x and z are the crystal-space and cartesian coordinates. */
+       m1[0] = anp->bij[0] / pi22;
+       m1[4] = anp->bij[1] / pi22;
+       m1[8] = anp->bij[2] / pi22;
+       m1[1] = m1[3] = anp->bij[3] / pi22;
+       m1[5] = m1[7] = anp->bij[4] / pi22;
+       m1[2] = m1[6] = anp->bij[5] / pi22;
+       MatrixInvert(m1, m1);
+       if (cp != NULL) {
+               memmove(m2, cp->rtr, sizeof(Mat33));
+               MatrixMul(m1, m1, m2);
+               MatrixTranspose(m2, m2);
+               MatrixMul(m1, m2, m1);
+       }
+       MatrixSymDiagonalize(m1, val, axis);
+       for (u = 0; u < 3; u++) {
+               if (val[u] < 0) {
+                       fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
+                       val[u] = 0.001;
+               } else {
+                       val[u] = 1 / sqrt(val[u]);
+               }
+               anp->pmat[u] = axis[u].x * val[u];
+               anp->pmat[u+3] = axis[u].y * val[u];
+               anp->pmat[u+6] = axis[u].z * val[u];
+       }
+       __MoleculeUnlock(mp);
+}
+
+int
+MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic)
+{
+       static Vector zeroVec = {0, 0, 0};
+/*     static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; */
+       XtalCell b;
+       int n;
+       if (mp == NULL)
+               return 0;
+       if (ax == NULL) {
+               if (mp->cell != NULL)
+                       free(mp->cell);
+               mp->cell = NULL;
+       /*      mp->is_xtal_coord = 0; */
+               return 0;
+       }
+       b.axes[0] = (ax != NULL ? *ax : zeroVec);
+       b.axes[1] = (ay != NULL ? *ay : zeroVec);
+       b.axes[2] = (az != NULL ? *az : zeroVec);
+       b.origin = *ao;
+       memmove(b.flags, periodic, 3);
+       if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
+               return -1;
+       __MoleculeLock(mp);
+       if (mp->cell != NULL)
+               free(mp->cell);
+       mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
+       if (mp->cell != NULL) {
+               memmove(mp->cell, &b, sizeof(XtalCell));
+               n = 0;
+       } else n = -2;  /*  Out of memory  */
+       __MoleculeUnlock(mp);
+       return n;
+}
+
+#pragma mark ====== Fragment manipulation ======
+
+static void
+sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
+{
+       Atom *ap;
+       int i;
+       if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
+               return;
+       IntGroupAdd(result, idx, 1);
+       ap = ATOM_AT_INDEX(mp->atoms, idx);
+       for (i = 0; i < ap->nconnects; i++) {
+               int idx2 = ap->connects[i];
+               if (IntGroupLookup(result, idx2, NULL))
+                       continue;
+               sMoleculeFragmentSub(mp, idx2, result, exatoms);
+       }
+}
+
+/*  The molecular fragment (= interconnected atoms) containing the atom n1 and
+    not containing the atoms in exatoms  */
+IntGroup *
+MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
+{
+       IntGroup *result;
+       if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
+               return NULL;
+       result = IntGroupNew();
+       sMoleculeFragmentSub(mp, n1, result, exatoms);
+       return result;
+}
+
+/*  The molecular fragment (= interconnected atoms) containing the atom n1 and
+    not containing the atoms n2, n3, ... (terminated by -1)  */
+IntGroup *
+MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
+{
+       int i;
+       IntGroup *exatoms, *result;
+       if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
+               return NULL;
+       exatoms = IntGroupNew();
+       for (i = 0; i < argc; i++)
+               IntGroupAdd(exatoms, argv[i], 1);
+       result = IntGroupNew();
+       sMoleculeFragmentSub(mp, n1, result, exatoms);
+       IntGroupRelease(exatoms);
+       return result;
+}
+
+/*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
+    not containing the atoms in exatoms  */
+IntGroup *
+MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
+{
+       IntGroupIterator iter;
+       IntGroup *result;
+       int i;
+       if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
+               return NULL;
+       IntGroupIteratorInit(inatoms, &iter);
+       result = IntGroupNew();
+       while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+               sMoleculeFragmentSub(mp, i, result, exatoms);
+       }
+       IntGroupIteratorRelease(&iter);
+       return result;
+}
+
+/*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
+    group is bound to the rest of the molecule via only one bond.
+       If the result is true, then the atoms belonging to the (only) bond are returned
+       in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
+       and n2 can be NULL, if those informations are not needed.  */
+int
+MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
+{
+       int i, i1, i2, j, k, bond_count, nval1, nval2;
+       Atom *ap;
+       if (mp == NULL || mp->natoms == 0 || group == NULL)
+               return 0;  /*  Invalid arguments  */
+       bond_count = 0;
+       for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
+               i2 = IntGroupGetEndPoint(group, i);
+               for (j = i1; j < i2; j++) {
+                       if (j < 0 || j >= mp->natoms)
+                               return 0;  /*  Invalid atom group  */
+                       ap = ATOM_AT_INDEX(mp->atoms, j);
+                       for (k = 0; k < ap->nconnects; k++) {
+                               if (IntGroupLookup(group, ap->connects[k], NULL) == 0) {
+                                       bond_count++;
+                                       nval1 = j;
+                                       nval2 = ap->connects[k];
+                                       if (bond_count > 1)
+                                               return 0;  /*  Too many bonds  */
+                               }
+                       }
+               }
+       }
+       if (bond_count == 1) {
+               if (n1 != NULL)
+                       *n1 = nval1;
+               if (n2 != NULL)
+                       *n2 = nval2;
+               return 1;
+       } else {
+               return 0;
+       }       
+}
+
+/*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
+    is said to be 'rotatable' when either of the following conditions are met; (1)
+       the group is detachable, or (2) the group consists of two bonded atoms that define
+       a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
+       (either a new IntGroup or 'group' with incremented reference count; thus the caller
+       is responsible for releasing the returned value).  */
+int
+MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
+{
+       int i1, i2;
+       if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
+               if (rotGroup != NULL) {
+                       IntGroupRetain(group);
+                       *rotGroup = group;
+               }
+               return 1;
+       }
+       if (group != NULL && IntGroupGetCount(group) == 2) {
+               i1 = IntGroupGetNthPoint(group, 0);
+               i2 = IntGroupGetNthPoint(group, 1);
+               if (MoleculeAreAtomsConnected(mp, i1, i2)) {
+                       IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
+                       if (frag == NULL)
+                               return 0;
+                       i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
+                       if (i1 == 0) {
+                               IntGroupRelease(frag);
+                               if (rotGroup != NULL)
+                                       *rotGroup = NULL;
+                               return 0;
+                       }
+                       if (rotGroup != NULL)
+                               *rotGroup = frag;
+                       else if (frag != NULL)
+                               IntGroupRelease(frag);
+                       return i1;
+               }
+       }
+       return 0;
+}
+
+#pragma mark ====== Multiple frame ======
+
+int
+MoleculeGetNumberOfFrames(Molecule *mp)
+{
+       if (mp == NULL)
+               return 0;
+       if (mp->nframes < 0) {
+               /*  Recalculate  */
+               int i, n;
+               Atom *ap;
+               for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+                       if (ap->nframes > n)
+                               n = ap->nframes;
+               }
+               mp->nframes = n;
+       }
+       return mp->nframes;
+}
+
+int
+MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame)
+{
+       int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
+       Vector *tempv, *vp;
+       Atom *ap;
+       if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
+               return -1;
+
+       n_old = MoleculeGetNumberOfFrames(mp);
+       n_new = n_old + count;
+       last_inserted = IntGroupGetNthPoint(group, count - 1);
+       if (n_new <= last_inserted) {
+               exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
+               n_new += exframes;
+       } else exframes = 0;
+
+       tempv = (Vector *)malloc(sizeof(Vector) * n_new);
+       if (tempv == NULL)
+               return -1;
+
+       __MoleculeLock(mp);
+       /*  Expand ap->frames for all atoms  */
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (ap->frames == NULL)
+                       vp = (Vector *)calloc(sizeof(Vector), n_new);
+               else
+                       vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
+               if (vp == NULL) {
+                       __MoleculeUnlock(mp);
+                       return -1;
+               }
+               for (j = ap->nframes; j < n_new; j++)
+                       vp[j] = ap->r;
+               ap->frames = vp;
+       }
+       
+       /*  group = [n0..n1-1, n2..n3-1, ...]  */
+       /*  s = t = 0,  */
+       /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
+           tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
+               tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
+               tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
+               ...
+               tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
+               At last, s will become n_old and t will become count.  */
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               int s, t, ns, ne;
+               Vector cr = ap->r;
+               ne = s = t = 0;
+               vp = ap->frames;
+               for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
+                       if (ns > ne) {
+                               memmove(tempv + ne, vp + s, sizeof(Vector) * (ns - ne));
+                               s += ns - ne;
+                       }
+                       ne = IntGroupGetEndPoint(group, j);
+                       while (ns < ne) {
+                               if (inFrame != NULL)
+                                       tempv[ns] = inFrame[natoms * t + i];
+                               else tempv[ns] = cr;
+                               t++;
+                               ns++;
+                       }
+               }
+               if (n_new > ne) {
+                       memmove(tempv + ne, vp + s, sizeof(Vector) * (n_new - ne));
+                       s += n_new - ne;
+               }
+               ap->nframes = n_new;
+               memmove(vp, tempv, sizeof(Vector) * n_new);
+       }
+       free(tempv);
+       mp->nframes = n_new;
+       MoleculeSelectFrame(mp, last_inserted, 0);
+       MoleculeIncrementModifyCount(mp);
+       __MoleculeUnlock(mp);
+       return count;
+}
+
+int
+MoleculeRemoveFrames(Molecule *mp, IntGroup *group, Vector *outFrame)
+{
+       int i, count, n_new, n_old, natoms, nframes;
+       Vector *tempv, *vp;
+       Atom *ap;
+       if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
+               return -1;
+
+       /*  outFrame[] should have enough size for Vector * natoms * group.count  */
+       memset(outFrame, 0, sizeof(Vector) * natoms * count);
+
+       n_old = MoleculeGetNumberOfFrames(mp);
+       n_new = n_old - count;
+       if (n_new < 0)
+               n_new = 0;
+       tempv = (Vector *)malloc(sizeof(Vector) * n_old);
+       if (tempv == NULL)
+               return -1;
+
+       /*  group = [n0..n1-1, n2..n3-1, ...]  */
+       /*  s = t = 0, */
+       /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
+           tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
+               tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
+               tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
+               ...
+               tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
+               At last, s will become n_new and t will become count.  */
+       __MoleculeLock(mp);
+       nframes = 0;
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               int s, t, j, ns, ne;
+               /*  Copy ap->frames to tempv  */
+               memset(tempv, 0, sizeof(Vector) * n_old);
+               vp = ap->frames;
+               if (vp != NULL)
+                       memmove(tempv, vp, sizeof(Vector) * (ap->nframes > n_old ? n_old : ap->nframes));
+               else {
+                       ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
+                       if (vp == NULL) {
+                               __MoleculeUnlock(mp);
+                               return -1;
+                       }
+               }
+               ne = ns = s = t = 0;
+               for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
+                       if (ns > n_old)
+                               ns = n_old;
+                       if (ns > ne) {
+                               memmove(vp + s, tempv + ne, sizeof(Vector) * (ns - ne));
+                               s += ns - ne;
+                       }
+                       ne = IntGroupGetEndPoint(group, j);
+                       if (ne > n_old)
+                               ne = n_old;
+                       while (ns < ne) {
+                               outFrame[natoms * t + i] = tempv[ns];
+                               t++;
+                               ns++;
+                       }
+               }
+               if (n_old > ne) {
+                       memmove(vp + s, tempv + ne, sizeof(Vector) * (n_old - ne));
+                       s += n_old - ne;
+               }
+               ap->nframes = s;
+               if (nframes < s)
+                       nframes = s;
+               if (s == 0) {
+                       free(ap->frames);
+                       ap->frames = NULL;
+               } else {
+                       ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
+               }
+       }
+       free(tempv);
+       mp->nframes = nframes;
+       
+       /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
+       i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe);
+       MoleculeSelectFrame(mp, i, 0);
+       
+       MoleculeIncrementModifyCount(mp);
+       __MoleculeUnlock(mp);
+       return count;
+}
+
+int
+MoleculeInsertFrame(Molecule *mp, int index, const Vector *inFrame)
+{
+       int i;
+       Atom *ap;
+       if (mp == NULL || mp->natoms == 0)
+               return -1;
+       i = MoleculeGetNumberOfFrames(mp);
+       if (index < 0 || index > i)
+               index = i;
+
+       /*  Insert a new frame at index for each atom  */
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               Vector *vp;
+               int nframes;
+               if (ap->nframes > index)
+                       nframes = ap->nframes + 1;
+               else
+                       nframes = index + 1;
+               if (ap->frames == NULL)
+                       vp = (Vector *)malloc(sizeof(Vector) * nframes);
+               else
+                       vp = (Vector *)realloc(ap->frames, sizeof(Vector) * nframes);
+               if (vp == NULL) {
+                       __MoleculeUnlock(mp);
+                       return -1;
+               }
+               ap->frames = vp;
+               /*  Clear newly allocated memory  */
+               memset(ap->frames + ap->nframes, 0, sizeof(Vector) * (nframes - ap->nframes));
+               /*  Move backward frames[index..nframes-1] by one  */
+               memmove(ap->frames + index + 1, ap->frames + index, sizeof(Vector) * (nframes - index - 1));
+               ap->nframes = nframes;
+               ap->frames[index] = (inFrame != NULL ? inFrame[i] : ap->r);
+       }
+       mp->nframes = -1;
+       MoleculeIncrementModifyCount(mp);
+       __MoleculeUnlock(mp);
+       return index;
+}
+
+int
+MoleculeRemoveFrame(Molecule *mp, int frame, Vector *outFrame)
+{
+       int i, ok, n;
+       Atom *ap;
+       static Vector zero = {0, 0, 0};
+       if (mp == NULL || mp->natoms == 0 || frame < 0)
+               return -1;
+       ok = 0;
+       n = 0;
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (frame < ap->nframes) {
+                       /*  If outFrame != NULL, then copy the coordinates to outFrame[i]  */
+                       if (outFrame != NULL)
+                               outFrame[i] = ap->frames[frame];
+                       /*  Remove this frame  */
+                       memmove(ap->frames + frame, ap->frames + frame + 1, sizeof(Vector) * (ap->nframes - frame - 1));
+                       ap->nframes--;
+                       if (ap->nframes == 0) {
+                               free(ap->frames);
+                               ap->frames = NULL;
+                       }
+                       ok = 1;
+               } else {
+                       if (outFrame != NULL)
+                               outFrame[i] = zero;
+               }
+               if (n < ap->nframes)
+                       n = ap->nframes;
+       }
+       mp->nframes = n;
+       if (mp->cframe >= n)
+               mp->cframe = (n > 0 ? n - 1 : 0);
+       else if (mp->cframe > frame)
+               mp->cframe--;
+       MoleculeIncrementModifyCount(mp);
+       __MoleculeUnlock(mp);
+       return frame;
+}
+
+int
+MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
+{
+       int i, cframe, ok;
+       Atom *ap;
+       if (mp == NULL || mp->natoms == 0)
+               return -1;
+       cframe = mp->cframe;
+       ok = 0;
+       __MoleculeLock(mp);
+       for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
+               if (copyback && cframe >= 0 && cframe < ap->nframes) {
+                       /*  Write the current coordinate back to the frame array  */
+                       /*  TODO: This behavior may interfere with undo. How to work around this?  */
+                       ap->frames[cframe] = ap->r;
+               }
+               if (frame >= 0 && frame < ap->nframes) {
+                       /*  Read the coordinate from the frame array  */
+                       ap->r = ap->frames[frame];
+                       ok = 1;
+               }
+       }
+       __MoleculeUnlock(mp);
+       if (ok) {
+               mp->cframe = frame;
+               sMoleculeNotifyChangeAppearance(mp);
+               return frame;
+       } else return -1;
+}
+
+#pragma mark ====== MO calculation ======
+
+/*  Calculate an MO value for a single point.  */
+/*  Index is the MO number (1-based)  */
+/*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
+static Double
+sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
+{
+       ShellInfo *sp;
+       PrimInfo *pp;
+       Double val, tval, *cnp, *tmpp, *mobasep, *mop;
+       Int i, j;
+       /*  Cache dr and |dr|^2  */
+       for (i = 0; i < bset->natoms; i++) {
+               Vector r = bset->pos[i];
+               tmp[i * 4] = r.x = vp->x - r.x;
+               tmp[i * 4 + 1] = r.y = vp->y - r.y;
+               tmp[i * 4 + 2] = r.z = vp->z - r.z;
+               tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
+       }
+       /*  Iterate over all shells  */
+       val = 0.0;
+       mobasep = bset->mo + (index - 1) * bset->nmos;
+       for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
+               pp = bset->priminfos + sp->p_idx;
+               cnp = bset->cns + sp->cn_idx;
+               tmpp = tmp + sp->a_idx * 4;
+               mop = mobasep + sp->m_idx;
+               switch (sp->sym) {
+                       case kGTOType_S: {
+                               tval = 0;
+                               for (j = 0; j < sp->nprim; j++) {
+                                       tval += *cnp++ * exp(-pp->A * tmpp[3]);
+                                       pp++;
+                               }
+                               val += mop[0] * tval;
+                               break;
+                       }
+                       case kGTOType_P: {
+                               Double x, y, z;
+                               x = y = z = 0;
+                               for (j = 0; j < sp->nprim; j++) {
+                                       tval = exp(-pp->A * tmpp[3]);
+                                       x += *cnp++ * tval;
+                                       y += *cnp++ * tval;
+                                       z += *cnp++ * tval;
+                                       pp++;
+                               }
+                               x *= mop[0] * tmpp[0];
+                               y *= mop[1] * tmpp[1];
+                               z *= mop[2] * tmpp[2];
+                               val += x + y + z;
+                               break;
+                       }
+                       case kGTOType_SP: {
+                               Double t, x, y, z;
+                               t = x = y = z = 0;
+                               for (j = 0; j < sp->nprim; j++) {
+                                       tval = exp(-pp->A * tmpp[3]);
+                                       t += *cnp++ * tval;
+                                       x += *cnp++ * tval;
+                                       y += *cnp++ * tval;
+                                       z += *cnp++ * tval;
+                                       pp++;
+                               }
+                               t *= mop[0];
+                               x *= mop[1] * tmpp[0];
+                               y *= mop[2] * tmpp[1];
+                               z *= mop[3] * tmpp[2];
+                               val += t + x + y + z;
+                               break;
+                       }
+                       case kGTOType_D: {
+                               Double xx, yy, zz, xy, xz, yz;
+                               xx = yy = zz = xy = xz = yz = 0;
+                               for (j = 0; j < sp->nprim; j++) {
+                                       tval = exp(-pp->A * tmpp[3]);
+                                       xx += *cnp++ * tval;
+                                       yy += *cnp++ * tval;
+                                       zz += *cnp++ * tval;
+                                       xy += *cnp++ * tval;
+                                       xz += *cnp++ * tval;
+                                       yz += *cnp++ * tval;
+                                       pp++;
+                               }
+                               xx *= mop[0] * tmpp[0] * tmpp[0];
+                               yy *= mop[1] * tmpp[1] * tmpp[1];
+                               zz *= mop[2] * tmpp[2] * tmpp[2];
+                               xy *= mop[3] * tmpp[0] * tmpp[1];
+                               xz *= mop[4] * tmpp[0] * tmpp[2];
+                               yz *= mop[5] * tmpp[1] * tmpp[2];
+                               val += xx + yy + zz + xy + xz + yz;
+                               break;
+                       }
+                       case kGTOType_D5: {
+                               Double d0, d1p, d1n, d2p, d2n;
+                               d0 = d1p = d1n = d2p = d2n = 0;
+                               for (j = 0; j < sp->nprim; j++) {
+                                       tval = exp(-pp->A * tmpp[3]);
+                                       d0 += *cnp++ * tval;
+                                       d1p += *cnp++ * tval;
+                                       d1n += *cnp++ * tval;
+                                       d2p += *cnp++ * tval;
+                                       d2n += *cnp++ * tval;
+                                       pp++;
+                               }
+                               d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
+                               d1p *= mop[1] * tmpp[0] * tmpp[2];
+                               d1n *= mop[2] * tmpp[1] * tmpp[2];
+                               d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
+                               d2n *= mop[4] * tmpp[0] * tmpp[1];
+                               val += d0 + d1p + d1n + d2p + d2n;
+                               break;
+                       }
+               }
+       }
+       return val;
+}
+
+/*  Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr).  */
+/*  mono is the MO number (1-based)  */
+int
+MoleculeCalcMO(Molecule *mp, Int mono, const Vector *op, const Vector *dxp, const Vector *dyp, const Vector *dzp, Int nx, Int ny, Int nz, int (*callback)(double progress, void *ref), void *ref)
+{
+       int ix, iy, iz, n, nn;
+       Cube *cp;
+       Double *tmp;
+       if (mp == NULL || mp->bset == NULL)
+               return -1;
+       cp = (Cube *)calloc(sizeof(Cube), 1);
+       if (cp == NULL) {
+               return -1;
+       }
+       cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
+       if (cp->dp == NULL) {
+               free(cp);
+               return -1;
+       }
+       cp->idn = mono;
+       cp->origin = *op;
+       cp->dx = *dxp;
+       cp->dy = *dyp;
+       cp->dz = *dzp;
+       cp->nx = nx;
+       cp->ny = ny;
+       cp->nz = nz;
+       
+       /*  TODO: use multithread  */
+       tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
+       if (tmp == NULL) {
+               free(cp->dp);
+               free(cp);
+               return -1;
+       }
+       n = nn = 0;
+       for (ix = 0; ix < nx; ix++) {
+               Vector p;
+               for (iy = 0; iy < ny; iy++) {
+                       for (iz = 0; iz < nz; iz++) {
+                               p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
+                               p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
+                               p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
+                               cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
+                       }
+                       if (callback != NULL && n - nn > 100) {
+                               nn = n;
+                               if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
+                                       free(cp->dp);
+                                       free(cp);
+                                       free(tmp);
+                                       return -2;  /*  User interrupt  */
+                               }
+                       }
+               }
+       }
+       free(tmp);
+
+       AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
+       return mp->bset->ncubes - 1;
+}
+
+int
+MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
+{
+       int i;
+       Vector rmin, rmax, *vp;
+       Double dr, dx, dy, dz;
+       if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
+               return -1;
+       if (npoints <= 0)
+               npoints = 1000000;
+       rmin.x = rmin.y = rmin.z = 1e10;
+       rmax.x = rmax.y = rmax.z = -1e10;
+       for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
+               dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
+               if (dr == 0.0)
+                       dr = 1.0;
+               dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
+               if (rmin.x > vp->x - dr)
+                       rmin.x = vp->x - dr;
+               if (rmin.y > vp->y - dr)
+                       rmin.y = vp->y - dr;
+               if (rmin.z > vp->z - dr)
+                       rmin.z = vp->z - dr;
+               if (rmax.x < vp->x + dr)
+                       rmax.x = vp->x + dr;
+               if (rmax.y < vp->y + dr)
+                       rmax.y = vp->y + dr;
+               if (rmax.z < vp->z + dr)
+                       rmax.z = vp->z + dr;
+       }
+       dx = rmax.x - rmin.x;
+       dy = rmax.y - rmin.y;
+       dz = rmax.z - rmin.z;
+       dr = pow(dx * dy * dz / npoints, 1.0/3.0);
+       *nx = floor(dx / dr + 0.5);
+       *ny = floor(dy / dr + 0.5);
+       *nz = floor(dz / dr + 0.5);
+       if (*nx == 0)
+               *nx = 1;
+       if (*ny == 0)
+               *ny = 1;
+       if (*nz == 0)
+               *nz = 1;
+       *op = rmin;
+       xp->x = yp->y = zp->z = dr;
+       xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
+       return 0;
+}
+
+const Cube *
+MoleculeGetCubeAtIndex(Molecule *mp, Int index)
+{
+       if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
+               return NULL;
+       return mp->bset->cubes[index];
+}
+
+int
+MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
+{
+       int i;
+       if (mp == NULL || mp->bset == NULL)
+               return -1;
+       for (i = 0; i < mp->bset->ncubes; i++) {
+               if (mp->bset->cubes[i]->idn == mono)
+                       return i;
+       }
+       return -1;
+}
+
+int
+MoleculeClearCubeAtIndex(Molecule *mp, Int index)
+{
+       int n;
+       if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
+               return -1;
+       CubeRelease(mp->bset->cubes[index]);
+       if (index < n - 1)
+               memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
+       if (--(mp->bset->ncubes) == 0) {
+               free(mp->bset->cubes);
+               mp->bset->cubes = NULL;
+       }
+       return mp->bset->ncubes;
+}
+
+int
+MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
+{
+       const Cube *cp;
+       int i, j, k, n;
+       FILE *fp;
+       if (mp == NULL || mp->bset == NULL)
+               return -1;  /*  Molecule or the basis set information is empty  */
+       cp = MoleculeGetCubeAtIndex(mp, index);
+       if (cp == NULL)
+               return -2;  /*  MO not yet calculated  */
+       fp = fopen(fname, "wb");
+       if (fp == NULL)
+               return -3;  /*  Cannot create file  */
+
+       /*  Comment lines  */
+       fprintf(fp, "%s MO=%d\n", comment, cp->idn);
+       fprintf(fp, " MO coefficients\n");
+       
+       fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
+       fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
+       fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
+       fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
+       
+       /*  Atomic information  */
+       for (i = 0; i < mp->bset->natoms; i++) {
+               Vector *vp = mp->bset->pos + i;
+               Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
+               /*  The second number should actually be the effective charge  */
+               fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
+       }
+       fprintf(fp, "%5d%5d\n", 1, 1);
+       
+       /*  3D data  */
+       for (i = n = 0; i < cp->nx; i++) {
+               for (j = 0; j < cp->ny; j++) {
+                       for (k = 0; k < cp->nz; k++) {
+                               fprintf(fp, " %12.5e", cp->dp[n++]);
+                               if (k == cp->nz - 1 || k % 6 == 5)
+                                       fprintf(fp, "\n");
+                       }
+               }
+       }
+       fclose(fp);
+       return 0;
+}
diff --git a/MolLib/Molecule.h b/MolLib/Molecule.h
new file mode 100755 (executable)
index 0000000..944e546
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ *  Molecule.h
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __Molecule_h__
+#define __Molecule_h__
+
+#include "Types.h"
+#include "Object.h"
+#include "IntGroup.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+#define ATOMS_MAX_CONNECTS 12
+#define ATOMS_MAX_SYMMETRY 12
+#define ATOMS_MAX_NUMBER 100000000  /*  Sufficiently large value  */
+
+/*  Conversion between kcal/mol and internal energy unit (am*ang^2/fs^2, am = atomic mass) */
+#define KCAL2INTERNAL (4.184e-4)
+#define INTERNAL2KCAL (1.0/KCAL2INTERNAL)
+#define J2INTERNAL (1e-4)
+#define INTERNAL2J (1.0/J2INTERNAL)
+       
+#define BOLTZMANN (8.31441e-3*J2INTERNAL)
+#define PI 3.14159265358979
+               
+/*  Anisotropic thermal parameter  */
+typedef struct Aniso {
+       Double  bij[6];    /*  b11, b22, b33, b12, b23, b31 (ORTEP type 0) */
+       Mat33  pmat;      /*  A 3x3 matrix whose three row vectors are the principal axes of the ellipsoid. Note: If the B matrix is not positive definite, the axis length corresponding to the negative eigenvalue is replaced with 0.001.  */
+} Aniso;
+
+/*  Symmetry operation  */
+/*  If periodic box is defined, dx/dy/dz denote multiples of the axes of the periodic box.
+    Otherwise, dx/dy/dz denote offset to the x/y/z coordinates of the atoms.  */
+typedef struct Symop {
+       signed int dx : 4;
+       signed int dy : 4;
+       signed int dz : 4;
+       unsigned int sym : 8;
+       unsigned int alive: 1;
+} Symop;
+
+/*  Exflags  */
+enum {
+       kAtomHiddenFlag = 1
+};
+
+/*  Atom record  */
+typedef struct Atom {
+       Int    segSeq;
+       char   segName[4];
+       Int    resSeq;
+       char   resName[4];
+       char   aname[4];
+       UInt   type;
+       Double  charge;
+       Double  weight;
+       char   element[4];
+       Int    atomicNumber;
+       Int    nconnects;   /*  Number of connections (= bonds)  */
+       Int    connects[ATOMS_MAX_CONNECTS];
+       Vector r;  /*  position  */
+       Vector v;  /*  velocity  */
+       Vector f;  /*  force  */
+       Double  occupancy;
+       Double  tempFactor;
+       Aniso  *aniso;
+       Int    intCharge;
+       Int    exflags;
+       Int    nframes;  /*  Multiple frames  */
+       Vector *frames;
+       Symop  symop;    /*  For symmetry-expanded atom  */
+       Int    symbase;  /*  The index of original atom for symmetry-expansion  */
+       Int    labelid;  /*  The label ID; 0 for no label  */
+       short  wrap_dx, wrap_dy, wrap_dz; /*  Calculated by md_wrap_coordinates; used only in wrapped output.  */
+       Double fix_force; /*  0: no fix, >0: fix at fix_pos with harmonic potential, <0: fix at fix_pos without force  */
+       Vector fix_pos;
+       Byte   periodic_exclude;  /*  If nonzero, then this atom is excluded from periodic calculations  */
+} Atom;
+
+extern Int gSizeOfAtomRecord;
+
+#define ATOM_AT_INDEX(p, i)  ((Atom *)((char *)(p) + (i) * gSizeOfAtomRecord))
+#define ATOM_NEXT(p)         ((Atom *)((char *)(p) + gSizeOfAtomRecord))
+#define ATOM_PREV(p)         ((Atom *)((char *)(p) - gSizeOfAtomRecord))
+
+#define SYMOP_ALIVE(s) (s.dx || s.dy || s.dz || s.sym)
+#define SYMOP_EQUAL(s1, s2) (s1.dx == s2.dx && s1.dy == s2.dy && s1.dz == s2.dz && s1.sym == s2.sym)
+
+/*  Duplicate an atom. If dst is non-NULL, *src is copied to *dst and dst is returned. If dst is NULL, a new atom is allocated by malloc() and that atom is returned. It is the called's responsibility to release the returned memory. */
+extern Atom *AtomDuplicate(Atom *dst, const Atom *src);
+/*  Clean the content of an atom record  */
+extern void AtomClean(Atom *ap);
+
+/*  MolEnumerable type code  */
+enum {
+       kAtomKind = 0,
+       kBondKind = 1,
+       kAngleKind = 2,
+       kDihedralKind = 3,
+       kImproperKind = 4,
+       kResidueKind = 5,
+       kEndKind
+};
+
+/*  Enumerable class to access to atoms, bonds, etc.  */
+typedef struct MolEnumerable {
+       struct Molecule *mol;
+       int    kind;
+} MolEnumerable;
+
+/*  Atom reference  */
+typedef struct AtomRef {
+       struct Molecule *mol;
+       int idx;
+} AtomRef;
+
+/*  Crystallographic cell parameter (also used as periodic box in MD) */
+typedef struct XtalCell {
+       Double  cell[6];     /*  a, b, c, alpha, beta, gamma (in degree)  */
+       Double  rcell[6];    /*  Reciprocal cell  */
+       Vector  axes[3];     /*  Cartesian unit vectors along the three axis  */
+       Vector  origin;      /*  Cartesian origin of the periodic box  */
+       char    flags[3];    /*  1 for periodic, 0 for non-periodic  */
+       Transform tr;        /*  Crystal coord -> cartesian  */
+       Transform rtr;       /*  Cartesian -> crystal coord  */
+} XtalCell;
+
+/*  Periodic box parameter  */
+#if 0
+typedef struct PeriodicBox {
+       Vector  axes[3];     /*  Unit vectors along the three axis  */
+       Vector  origin;      /*  Origin of the periodic box  */
+       char    flags[3];    /*  1 for periodic, 0 for non-periodic  */
+       Transform tr;        /*  Internal coord -> cartesian  */
+       Transform rtr;       /*  Cartesian -> internal coord  */        
+} PeriodicBox;
+#endif
+
+/*  Expanded atoms  */
+typedef struct ExAtom {
+       Int    index;        /*  Base atom index  */
+       Vector dr;           /*  Translational offset  */
+       Int    symop;        /*  Symmetry operation  */
+       Int    labelid;      /*  Label ID; 0 for no label  */
+} ExAtom;
+
+/*  3-Dimensional distribution  */
+typedef struct Cube {
+       Int idn;             /*  Integer identifier (such as MO number)  */
+       Vector origin;
+       Vector dx, dy, dz;
+       Int nx, ny, nz;
+       Double *dp;          /*  Value for point (ix, iy, iz) is in dp[(ix*ny+iy)*nz+iz]  */
+} Cube;
+
+/*  Gaussian orbital symmetry types  */
+enum {
+       kGTOType_S,
+       kGTOType_SP,
+       kGTOType_P,
+       kGTOType_D,
+       kGTOType_D5,
+       kGTOtype_F,
+       kGTOType_F7,
+       kGTOType_UU
+};
+
+/*  Exponent/coefficient info for a single gaussian primitive  */
+typedef struct PrimInfo {
+       Double A;            /*  Exponent  */
+       Double C;            /*  Contraction coefficient  */
+       Double Csp;          /*  P(S=P) contraction coefficient  */
+} PrimInfo;
+
+/*  Gaussian orbital shell information  */
+typedef struct ShellInfo {
+       signed char sym;     /*  Symmetry of the basis; S, P, ... */
+       signed char ncomp;   /*  Number of components (S: 1, P: 3, SP: 4, etc.)  */
+       signed char nprim;   /*  Number of primitives for this shell  */
+       Int p_idx;           /*  Index to the PrimInfo (exponent/coefficient) table  */
+       Int cn_idx;          /*  Index to the normalized (cached) contraction coefficient table  */
+       Int a_idx;           /*  Index to the atom which this primitive belongs to */
+       Int m_idx;           /*  Index to the MO matrix  */
+} ShellInfo;
+
+/*  Basis set and MO information  */
+typedef struct BasisSet {
+       Int nshells;         /*  Number of gaussian orbital shells  */
+       ShellInfo *shells;   /*  Gaussian orbital shells  */
+       Int npriminfos;      /*  Size of primitive information table  */
+       PrimInfo *priminfos; /*  Primitive information table  */
+       Int ncns;            /*  Number of normalized (cached) contraction coefficient values  */
+       Double *cns;         /*  Normalized (cached) contraction coefficients; (up to 10 values for each primitive)  */
+       Int natoms;          /*  Number of atoms; separately cached here because MO info should be invariant during editing */
+       Vector *pos;         /*  Positions of atoms; the unit is bohr, not angstrom  */
+       Double *nuccharges;  /*  Nuclear charges (for ECP atoms)  */
+       Int nelectrons;      /*  Number of electrons  */
+       Int nmos;            /*  Number of MOs */
+       Double *mo;          /*  MO matrix (mo[i][j] represents the j-th AO coefficient for the i-th MO)  */
+       Double *moenergies;  /*  MO energies  */
+       Double *scfdensities; /*  SCF densities; lower triangle of a symmetric matrix (size nmos*(nmos+1)/2)  */
+       Int ncubes;          /*  Number of calculated MOs  */
+       Cube **cubes;        /*  Calculated MOs (an array of pointers to Cubes)  */
+} BasisSet;
+
+/*  Electrostatic potential  */
+typedef struct Elpot {
+       Vector pos;
+       Double esp;
+} Elpot;
+
+/*  Molecule record  */
+typedef struct Molecule {
+       Object base;
+       Int    natoms;
+       Atom   *atoms;
+       Int    nbonds;
+       Int    *bonds;       /*  The size of array is 2*nbonds  */
+       Int    nangles;
+       Int    *angles;      /*  The size of array is 3*nangles  */
+       Int    ndihedrals;
+       Int    *dihedrals;   /*  The size of array is 4*ndihedrals  */
+       Int    nimpropers;
+       Int    *impropers;   /*  The size of array is 4*nimpropers  */
+       Int    nresidues;    /*  Number of residues; maximum residue number + 1 (because residue 0 is 'undefined residue')  */
+       char   (*residues)[4];
+       XtalCell   *cell;
+/*     char   is_xtal_coord; *//*  True if the coordinates are measured in crystallographic units  */
+       Int    nsyms;        /*  Symmetry operations; syms are always described in crystallographic units (even when is_xtal_coord is false)  */
+       Transform *syms;
+/*     PeriodicBox *box;    *//*  Periodic box  */
+       IntGroup *selection;
+       Int    nexatoms;
+       ExAtom *exatoms;
+       Int    nexbonds;
+       Int    *exbonds;     /*  The size of array is 2*nbonds; Atom index >= 0 : base atoms, < 0 : expanded atoms at index -exbonds[n]-1  */
+       Int    nframes;      /*  The number of frames. This is a cached value, and should be
+                                                        recalculated from the atoms if it is -1  */
+       Int    cframe;       /*  The current frame number  */
+       struct MainView *mview;  /*  Reference to the MainView object if present (no retain)  */
+       Int    modifyCount;  /*  Internal counter for modification. This value is not to be modified
+                                manually; instead, call MoleculeIncrementModifyCount() whenever
+                                                    modification is done, which also takes care necessary notification
+                                                        to the other part of the application (system dependent)  */
+
+       struct MDArena *arena;  /*  Reference to the MDArena record during MM/MD run (no retain)  */
+
+       /*  Information from the dcd files  */
+       Int    startStep;     /*  the timestep for frame 0  */
+       Int    stepsPerFrame; /*  the number of timesteps between neighboring frames  */
+       Double psPerStep;     /*  picosecond per step  */
+
+       /*  Information for basis sets and MOs  */
+       BasisSet *bset;
+       
+       /*  Electrostatic potential  */
+       Int    nelpots;
+       Elpot  *elpots;
+
+       /*  Parameters specific for this molecule  */
+       struct Parameter *par;
+       
+       /*  Flag to request rebuilding MD internal information  */
+       Byte   needsMDRebuild;
+       
+       /*  Prohibit modification of the topology (to avoid interfering MD) */
+       Byte   noModifyTopology;
+       
+       /*  Flag to request aborting a subthread  */
+       Byte   requestAbortThread;
+
+       /*  Flag to signal that a subthread is terminated  */
+       Byte   threadTerminated;
+
+       /*  Mutex object. If non-NULL, it should be locked before modifying molecule  */
+       void *mutex;
+
+} Molecule;
+
+int strlen_limit(const char *s, int limit);
+
+Molecule *MoleculeNew(void);
+int MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize);
+int MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+Molecule *MoleculeNewWithName(const char *name);
+Molecule *MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms);
+Molecule *MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp);
+void MoleculeSetName(Molecule *par, const char *name);
+const char *MoleculeGetName(Molecule *mp);
+Molecule *MoleculeWithName(const char *name);
+Molecule *MoleculeRetain(Molecule *mp);
+void MoleculeRelease(Molecule *mp);
+void MoleculeExchange(Molecule *mp1, Molecule *mp2);
+
+void MoleculeIncrementModifyCount(Molecule *mp);
+void MoleculeClearModifyCount(Molecule *mp);
+
+MolEnumerable *MolEnumerableNew(Molecule *mol, int kind);
+void MolEnumerableRelease(MolEnumerable *mseq);
+AtomRef *AtomRefNew(Molecule *mol, int idx);
+void AtomRefRelease(AtomRef *aref);
+
+void MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates);
+void MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x23, Double x31);
+int MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic);
+
+int MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize);
+int MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+
+int MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+
+int MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize);
+int MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+int MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize);
+void MoleculeDump(Molecule *mol);
+
+char *MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep);
+Molecule *MoleculeDeserialize(const char *data, Int length, Int *timep);
+
+void MoleculeCleanUpResidueTable(Molecule *mp);
+int MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues);
+int MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs);
+int MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq);
+int MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues);
+int MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names);
+int MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group);
+int MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group);
+
+int MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos);
+int MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset);
+int MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset);
+int MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag);
+int MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds);
+int MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds);
+int MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where);
+int MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where);
+int MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where);
+int MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where);
+int MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where);
+int MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where);
+int MoleculeLookupBond(Molecule *mp, Int n1, Int n2);
+int MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3);
+int MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4);
+int MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4);
+
+/*
+Int    MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles);
+Int MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals);
+Int MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers);
+
+Int MoleculeFindAllAngles(Molecule *mol, Int **outAngles);
+Int MoleculeFindAllDihedrals(Molecule *mol, Int **outDihedrals);
+Int MoleculeFindAllImpropers(Molecule *mol, Int **outImpropers);
+*/
+
+Int MoleculeFindMissingAngles(Molecule *mol, Int **outAngles);
+Int MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals);
+Int MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers);
+       
+IntGroup *MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup);
+IntGroup *MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup);
+IntGroup *MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup);
+IntGroup *MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup);
+
+IntGroup *MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup);
+
+IntGroup *MoleculeSearchAnglesIncludingBond(Molecule *mp, int n1, int n2);
+IntGroup *MoleculeSearchDihedralsIncludingBond(Molecule *mp, int n1, int n2);
+IntGroup *MoleculeSearchImpropersIncludingBond(Molecule *mp, int n1, int n2);
+
+int MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno);
+int MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName);
+int MoleculeAtomIndexFromString(Molecule *mp, const char *s);
+
+int MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds);
+int MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2);
+int MoleculeRebuildTablesFromConnects(Molecule *mp);
+
+void MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize);
+
+void MoleculeSetSelection(Molecule *mp, IntGroup *select);
+IntGroup *MoleculeGetSelection(Molecule *mp);
+void MoleculeSelectAtom(Molecule *mp, int n1, int extending);
+void MoleculeUnselectAtom(Molecule *mp, int n1);
+void MoleculeToggleSelectionOfAtom(Molecule *mp, int n1);
+int MoleculeIsAtomSelected(Molecule *mp, int n1);
+int MoleculeIsBondSelected(Molecule *mp, int n1, int n2);
+IntGroup *MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove);
+
+int MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop);
+int MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group);
+int MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout);
+
+int MoleculeShowAllAtoms(Molecule *mp);
+int MoleculeShowReverse(Molecule *mp);
+int MoleculeHideAtoms(Molecule *mp, IntGroup *ig);
+
+int MoleculeReorderAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize);
+
+void MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group);
+void MoleculeMove(Molecule *mp, Transform tr, IntGroup *group);
+void MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group);
+void MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group);
+void MoleculeReaxis(Molecule *mp, const Vector *xaxis, const Vector *yaxis, const Vector *zaxis, IntGroup *group);
+int MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group);
+int MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group);
+       
+Int *MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig);
+       
+void MoleculeAddExpansion(Molecule *mp, Vector dr, Int symop, IntGroup *group, Double limit);
+void MoleculeClearExpansion(Molecule *mp, IntGroup *group);
+void MoleculeRemoveExpansion(Molecule *mp, Vector dr, Int symop, IntGroup *group);
+void MoleculeAutoExpansion(Molecule *mp, const float *boxstart, const float *boxend, IntGroup *group, Double limit);
+
+void MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src);
+void MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src);
+Double MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2);
+Double MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3);
+Double MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4);
+
+IntGroup *MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms);
+IntGroup *MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv);
+IntGroup *MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms);
+int MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2);
+int MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup);
+
+int MoleculeGetNumberOfFrames(Molecule *mp);
+int MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame);
+int MoleculeRemoveFrames(Molecule *mp, IntGroup *group, Vector *outFrame);
+int MoleculeInsertFrame(Molecule *mp, int index, const Vector *inFrame);
+int MoleculeRemoveFrame(Molecule *mp, int frame, Vector *outFrame);
+int MoleculeSelectFrame(Molecule *mp, int frame, int copyback);
+
+int MoleculeCalcMO(Molecule *mp, Int mono, const Vector *op, const Vector *dxp, const Vector *dyp, const Vector *dzp, Int nx, Int ny, Int nz, int (*callback)(double progress, void *ref), void *ref);
+int MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz);
+const Cube *MoleculeGetCubeAtIndex(Molecule *mp, Int index);
+int MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono);
+int MoleculeClearCubeAtIndex(Molecule *mp, Int index);
+int MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment);
+
+#define kMoleculePasteboardType "Molecule"
+#define kParameterPasteboardType "Parameter"
+
+STUB int MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length);
+STUB int MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length);
+STUB int MoleculeCallback_isDataInPasteboard(const char *type);
+
+STUB Molecule *MoleculeCallback_openNewMolecule(const char *fname);
+STUB void MoleculeCallback_notifyModification(Molecule *mp, int now_flag);
+STUB Molecule *MoleculeCallback_currentMolecule(void);
+STUB Molecule *MoleculeCallback_moleculeAtIndex(int idx);
+STUB Molecule *MoleculeCallback_moleculeAtOrderedIndex(int idx);
+STUB void MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize);
+STUB void MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize);
+
+STUB void MoleculeCallback_lockMutex(void *mutex);
+STUB void MoleculeCallback_unlockMutex(void *mutex);
+STUB void MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol);
+
+void MoleculeLock(Molecule *mol);
+void MoleculeUnlock(Molecule *mol);
+
+#if 0
+#define __MoleculeLock(mol) MoleculeLock(mol)
+#define __MoleculeUnlock(mol) MoleculeUnlock(mol)
+#else
+#define __MoleculeLock(mol)
+#define __MoleculeUnlock(mol)
+#endif
+       
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __Molecule_h__ */
diff --git a/MolLib/Object.c b/MolLib/Object.c
new file mode 100755 (executable)
index 0000000..f8f083a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ *  Object.c
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "Object.h"
+#include "Types.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+void
+ObjectInit(Object *obj, Object **objRootPtr, const char *name)
+{
+       obj->refCount = 1;
+       obj->next = *objRootPtr;
+       *objRootPtr = obj;
+       ObjectSetName(obj, name, *objRootPtr);
+}
+
+int
+ObjectIncrRefCount(Object *obj)
+{
+       if (obj != NULL)
+               return ++(obj->refCount);
+       else return -1;
+}
+
+int
+ObjectDecrRefCount(Object *obj)
+{
+       if (obj != NULL)
+               return --(obj->refCount);
+       else return -1;
+}
+
+void
+ObjectDealloc(Object *obj, Object **objRootPtr)
+{
+       Object **objp;
+       free((void *)(obj->name));
+       for (objp = objRootPtr; *objp != NULL; objp = &((*objp)->next)) {
+               if (*objp == obj) {
+                       *objp = obj->next;
+                       break;
+               }
+       }
+       free(obj);
+}
+
+void
+ObjectSetName(Object *obj, const char *name, Object *objRoot)
+{
+       obj->name = strdup(name);
+       return;
+#if 0
+       char *buf, *p;
+       int n;
+       size_t size;
+       /* Allocate a copy of string with some space to add a number to the end */
+       size = strlen(name) + 6;
+       buf = (char *)malloc(size);
+       if (buf == NULL)
+               Panic("Cannot set object name");
+       strcpy(buf, name);
+       /* Determine the position to place the number; if name ends with space + number,
+          then the number will be replaced. Otherwise, the number is appended at the
+          end of the string with one space */
+       n = 0;
+       p = buf + strlen(name) - 1;
+       while (p >= buf && *p == ' ')
+               p--;
+       if (p >= buf && isdigit(*p)) {
+               char *p1 = p;
+               while (p1 >= buf && isdigit(*p1))
+                       p1--;
+               if (p1 >= buf && *p1 == ' ') {
+                       n = atoi(p1 + 1);
+                       p = p1;
+               }
+       }
+       p++;
+       while (n < 1000) {
+               Object *op;
+               for (op = objRoot; op != NULL; op = op->next) {
+                       if (op == obj)
+                               continue;
+                       if (strcmp(op->name, buf) == 0)
+                               break;
+               }
+               if (op == NULL)
+                       break;
+               if (p > buf && p[-1] != ' ')
+                       *p++ = ' ';
+               snprintf(p, size - (p - buf), "%d", ++n);
+       }
+       if (n >= 1000)
+               Panic("Cannot set object name");
+       if (obj->name != NULL)
+               free((void *)(obj->name));
+       obj->name = buf;
+#endif
+}
+
+const char *
+ObjectGetName(Object *obj)
+{
+       if (obj == NULL)
+               return NULL;
+       else return obj->name;
+}
+
+Object *
+ObjectWithName(const char *name, Object *objRoot)
+{
+       Object *obj;
+       for (obj = objRoot; obj != NULL; obj = obj->next) {
+               if (strcmp(obj->name, name) == 0)
+                       return obj;
+       }
+       return NULL;
+}
+
diff --git a/MolLib/Object.h b/MolLib/Object.h
new file mode 100755 (executable)
index 0000000..5542411
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  Object.h
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __Object_h__
+#define __Object_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+/*  A "base object type" to implement reference counting
+    and management by unique names  */
+typedef struct Object {
+       int    refCount;
+       struct Object *next;
+       const char *name;
+} Object;
+
+void ObjectInit(Object *obj, Object **objRootPtr, const char *name);
+int ObjectIncrRefCount(Object *obj);
+int ObjectDecrRefCount(Object *obj);
+void ObjectDealloc(Object *obj, Object **objRootPtr);
+void ObjectSetName(Object *obj, const char *name, Object *objRoot);
+const char *ObjectGetName(Object *obj);
+Object *ObjectWithName(const char *name, Object *objRoot);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __Object_h__ */
diff --git a/MolLib/Parameter.c b/MolLib/Parameter.c
new file mode 100755 (executable)
index 0000000..365cd53
--- /dev/null
@@ -0,0 +1,2049 @@
+/*
+ *  Parameter.c
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MolLib.h"
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <math.h>
+
+/*  Global parameter: it is initialized by the first call to ParameterReadFromFile()  */
+Parameter *gBuiltinParameters = NULL;
+
+/*  Global parameter  */
+AtomPar *gDispAtomParameters = NULL;
+Int gCountDispAtomParameters = 0;
+
+static Parameter *sParameterRoot = NULL;
+static int sParameterUntitledCount = 0;
+
+/*  Global struct for storing information for global parameter files  */
+GlobalParInfoRecord gGlobalParInfo;
+
+#pragma mark ====== Parameter Alloc/Release ======
+
+Parameter *
+ParameterNew(void)
+{
+       char name[40];
+       Parameter *par = (Parameter *)calloc(sizeof(Parameter), 1);
+       if (par == NULL)
+               Panic("Cannot allocate new parameter record");
+       snprintf(name, sizeof name, "Untitled %d", sParameterUntitledCount++);
+       ObjectInit((Object *)par, (Object **)&sParameterRoot, name);
+       return par;
+}
+
+Parameter *
+ParameterDuplicate(const Parameter *par)
+{
+       Parameter *npar = ParameterNew();
+       if (par->bondPars != NULL) {
+               npar->bondPars = (BondPar *)malloc(sizeof(BondPar) * par->nbondPars);
+               if (npar->bondPars == NULL)
+                       goto release;
+               memmove(npar->bondPars, par->bondPars, sizeof(BondPar) * par->nbondPars);
+               npar->nbondPars = par->nbondPars;
+       }
+       if (par->anglePars != NULL) {
+               npar->anglePars = (AnglePar *)malloc(sizeof(AnglePar) * par->nanglePars);
+               if (npar->anglePars == NULL)
+                       goto release;
+               memmove(npar->anglePars, par->anglePars, sizeof(AnglePar) * par->nanglePars);
+               npar->nanglePars = par->nanglePars;
+       }
+       if (par->dihedralPars != NULL) {
+               npar->dihedralPars = (TorsionPar *)malloc(sizeof(TorsionPar) * par->ndihedralPars);
+               if (npar->dihedralPars == NULL)
+                       goto release;
+               memmove(npar->dihedralPars, par->dihedralPars, sizeof(TorsionPar) * par->ndihedralPars);
+               npar->ndihedralPars = par->ndihedralPars;
+       }
+       if (par->improperPars != NULL) {
+               npar->improperPars = (TorsionPar *)malloc(sizeof(TorsionPar) * par->nimproperPars);
+               if (npar->improperPars == NULL)
+                       goto release;
+               memmove(npar->improperPars, par->improperPars, sizeof(TorsionPar) * par->nimproperPars);
+               npar->nimproperPars = par->nimproperPars;
+       }
+       if (par->vdwPars != NULL) {
+               npar->vdwPars = (VdwPar *)malloc(sizeof(VdwPar) * par->nvdwPars);
+               if (npar->vdwPars == NULL)
+                       goto release;
+               memmove(npar->vdwPars, par->vdwPars, sizeof(VdwPar) * par->nvdwPars);
+               npar->nvdwPars = par->nvdwPars;
+       }
+       if (par->vdwpPars != NULL) {
+               npar->vdwpPars = (VdwPairPar *)malloc(sizeof(VdwPairPar) * par->nvdwpPars);
+               if (npar->vdwpPars == NULL)
+                       goto release;
+               memmove(npar->vdwpPars, par->vdwpPars, sizeof(VdwPairPar) * par->nvdwpPars);
+               npar->nvdwpPars = par->nvdwpPars;
+       }
+       if (par->vdwCutoffPars != NULL) {
+               npar->vdwCutoffPars = (VdwCutoffPar *)malloc(sizeof(VdwCutoffPar) * par->nvdwCutoffPars);
+               if (npar->vdwCutoffPars == NULL)
+                       goto release;
+               memmove(npar->vdwCutoffPars, par->vdwCutoffPars, sizeof(VdwCutoffPar) * par->nvdwCutoffPars);
+               npar->nvdwCutoffPars = par->nvdwCutoffPars;
+       }
+/*     if (par->atomPars != NULL) {
+               npar->atomPars = (AtomPar *)malloc(sizeof(AtomPar) * par->natomPars);
+               if (npar->atomPars == NULL)
+                       goto release;
+               memmove(npar->atomPars, par->atomPars, sizeof(AtomPar) * par->natomPars);
+               npar->natomPars = par->natomPars;
+       } */
+       return npar;
+release:
+       ParameterRelease(npar);
+       return NULL;
+}
+
+Parameter *
+ParameterWithName(const char *name)
+{
+       return (Parameter *)ObjectWithName(name, (Object *)sParameterRoot);
+}
+
+/*  Assign a unique name to this parameter record  */
+void
+ParameterSetName(Parameter *par, const char *name)
+{
+       ObjectSetName((Object *)par, name, (Object *)sParameterRoot);
+}
+
+const char *
+ParameterGetName(Parameter *par)
+{
+       return ObjectGetName((Object *)par);
+}
+
+void
+ParameterRetain(Parameter *par)
+{
+       ObjectIncrRefCount((Object *)par);
+}
+
+void
+ParameterRelease(Parameter *par)
+{
+       if (ObjectDecrRefCount((Object *)par) == 0) {
+               if (par->bondPars != NULL)
+                       free(par->bondPars);
+               if (par->anglePars != NULL)
+                       free(par->anglePars);
+               if (par->dihedralPars != NULL)
+                       free(par->dihedralPars);
+               if (par->improperPars != NULL)
+                       free(par->improperPars);
+               if (par->vdwPars != NULL)
+                       free(par->vdwPars);
+               if (par->vdwpPars != NULL)
+                       free(par->vdwpPars);
+               if (par->vdwCutoffPars != NULL)
+                       free(par->vdwCutoffPars);
+       /*      if (par->atomPars != NULL)
+                       free(par->atomPars); */
+               ObjectDealloc((Object *)par, (Object **)&sParameterRoot);
+       }
+}
+
+#pragma mark ====== ParameterRef Definitions ======
+
+ParameterRef *
+ParameterRefNew(struct Molecule *mol, int type, int idx)
+{
+       ParameterRef *pref = (ParameterRef *)calloc(sizeof(ParameterRef), 1);
+       if (pref != NULL) {
+               pref->mol = mol;
+               if (mol != NULL)
+                       MoleculeRetain(mol);
+               pref->parType = type;
+               pref->idx = idx;
+       }
+       return pref;
+}
+
+void
+ParameterRefRelease(ParameterRef *pref)
+{
+       if (pref != NULL) {
+               if (pref->mol != NULL)
+                       MoleculeRelease(pref->mol);
+               free(pref);
+       }
+}
+
+UnionPar *
+ParameterGetUnionParFromTypeAndIndex(Parameter *par, int type, int index)
+{
+       if (par == NULL)
+               return NULL;
+       switch (type) {
+               case kBondParType:
+                       if (index >= 0 && index < par->nbondPars)
+                               return (UnionPar *)(par->bondPars + index);
+                       else return NULL;
+               case kAngleParType:
+                       if (index >= 0 && index < par->nanglePars)
+                               return (UnionPar *)(par->anglePars + index);
+                       else return NULL;
+               case kDihedralParType:
+                       if (index >= 0 && index < par->ndihedralPars)
+                               return (UnionPar *)(par->dihedralPars + index);
+                       else return NULL;
+               case kImproperParType:
+                       if (index >= 0 && index < par->nimproperPars)
+                               return (UnionPar *)(par->improperPars + index);
+                       else return NULL;
+               case kVdwParType:
+                       if (index >= 0 && index < par->nvdwPars)
+                               return (UnionPar *)(par->vdwPars + index);
+                       else return NULL;
+               case kVdwPairParType:
+                       if (index >= 0 && index < par->nvdwpPars)
+                               return (UnionPar *)(par->vdwpPars + index);
+                       else return NULL;
+               case kVdwCutoffParType:
+                       if (index >= 0 && index < par->nvdwCutoffPars)
+                               return (UnionPar *)(par->vdwCutoffPars + index);
+                       else return NULL;
+       /*      case kAtomParType:
+                       if (index >= 0 && index < par->natomPars)
+                               return (UnionPar *)(par->atomPars + index);
+                       else return NULL; */
+               default:
+                       return NULL;
+       }
+}
+
+Int
+ParameterGetCountForType(Parameter *par, int type)
+{
+       if (par == NULL)
+               return 0;
+       switch (type) {
+               case kBondParType:
+                       return par->nbondPars;
+               case kAngleParType:
+                       return par->nanglePars;
+               case kDihedralParType:
+                       return par->ndihedralPars;
+               case kImproperParType:
+                       return par->nimproperPars;
+               case kVdwParType:
+                       return par->nvdwPars;
+               case kVdwPairParType:
+                       return par->nvdwpPars;
+               case kVdwCutoffParType:
+                       return par->nvdwCutoffPars;
+       /*      case kAtomParType:
+                       return par->natomPars; */
+               default:
+                       return 0;
+       }
+}
+
+Int
+ParameterGetSizeForType(int type)
+{
+       switch (type) {
+               case kBondParType:
+                       return sizeof(BondPar);
+               case kAngleParType:
+                       return sizeof(AnglePar);
+               case kDihedralParType:
+                       return sizeof(TorsionPar);
+               case kImproperParType:
+                       return sizeof(TorsionPar);
+               case kVdwParType:
+                       return sizeof(VdwPar);
+               case kVdwPairParType:
+                       return sizeof(VdwPairPar);
+               case kVdwCutoffParType:
+                       return sizeof(VdwCutoffPar);
+                       /*      case kAtomParType:
+                        return par->natomPars; */
+               default:
+                       return 0;
+       }
+}
+
+UnionPar *
+ParameterRefGetPar(ParameterRef *pref)
+{
+       Parameter *par;
+       if (pref == NULL)
+               return NULL;
+       if (pref->mol != NULL)
+               par = pref->mol->par;
+       else par = gBuiltinParameters;
+       if (par == NULL)
+               return NULL;
+       return ParameterGetUnionParFromTypeAndIndex(par, pref->parType, pref->idx);
+}
+
+#pragma mark ====== Insert/Delete (for MolAction) ======
+
+int
+ParameterInsert(Parameter *par, Int type, const UnionPar *up, struct IntGroup *where)
+{
+       Int i, n1, n2, size, *ip;
+       void *p;
+       if (par == NULL)
+               return -1;
+       switch (type) {
+               case kBondParType:
+                       p = &par->bondPars;
+                       ip = &par->nbondPars;
+                       size = sizeof(BondPar);
+                       break;
+               case kAngleParType:
+                       p = &par->anglePars;
+                       ip = &par->nanglePars;
+                       size = sizeof(AnglePar);
+                       break;
+               case kDihedralParType:
+                       p = &par->dihedralPars;
+                       ip = &par->ndihedralPars;
+                       size = sizeof(TorsionPar);
+                       break;
+               case kImproperParType:
+                       p = &par->improperPars;
+                       ip = &par->nimproperPars;
+                       size = sizeof(TorsionPar);
+                       break;
+               case kVdwParType:
+                       p = &par->vdwPars;
+                       ip = &par->nvdwPars;
+                       size = sizeof(VdwPar);
+                       break;
+               case kVdwPairParType:
+                       p = &par->vdwpPars;
+                       ip = &par->nvdwpPars;
+                       size = sizeof(VdwPairPar);
+                       break;
+               case kVdwCutoffParType:
+                       p = &par->vdwCutoffPars;
+                       ip = &par->nvdwCutoffPars;
+                       size = sizeof(VdwCutoffPar);
+                       break;
+       /*      case kAtomParType:
+                       p = &par->atomPars;
+                       ip = &par->natomPars;
+                       size = sizeof(AtomPar);
+                       break; */
+               default:
+                       return -1;
+       }
+       n2 = 0;
+       for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++) {
+               if (InsertArray(p, ip, size, n1, 1, up + i) == NULL)
+                       return n2;
+               n2++;
+       }
+       return n2;
+}
+
+static int
+sParameterDeleteOrCopy(Parameter *par, Int type, UnionPar *up, struct IntGroup *where, Int copyflag)
+{
+       Int i, n1, n2, size, *ip;
+       char **p;
+       if (par == NULL)
+               return -1;
+       switch (type) {
+               case kBondParType:
+                       p = (char **)&par->bondPars;
+                       ip = &par->nbondPars;
+                       size = sizeof(BondPar);
+                       break;
+               case kAngleParType:
+                       p = (char **)&par->anglePars;
+                       ip = &par->nanglePars;
+                       size = sizeof(AnglePar);
+                       break;
+               case kDihedralParType:
+                       p = (char **)&par->dihedralPars;
+                       ip = &par->ndihedralPars;
+                       size = sizeof(TorsionPar);
+                       break;
+               case kImproperParType:
+                       p = (char **)&par->improperPars;
+                       ip = &par->nimproperPars;
+                       size = sizeof(TorsionPar);
+                       break;
+               case kVdwParType:
+                       p = (char **)&par->vdwPars;
+                       ip = &par->nvdwPars;
+                       size = sizeof(VdwPar);
+                       break;
+               case kVdwPairParType:
+                       p = (char **)&par->vdwpPars;
+                       ip = &par->nvdwpPars;
+                       size = sizeof(VdwPairPar);
+                       break;
+               case kVdwCutoffParType:
+                       p = (char **)&par->vdwCutoffPars;
+                       ip = &par->nvdwCutoffPars;
+                       size = sizeof(VdwCutoffPar);
+                       break;
+       /*      case kAtomParType:
+                       p = (char **)&par->atomPars;
+                       ip = &par->natomPars;
+                       size = sizeof(AtomPar);
+                       break; */
+               default:
+                       return -1;
+       }
+       n2 = 0;
+       for (i = IntGroupGetCount(where) - 1; i >= 0 && (n1 = IntGroupGetNthPoint(where, i)) >= 0; i--) {
+               if (n1 >= *ip)
+                       return -1; /*  Internal error  */
+               if (copyflag) {
+                       if (up != NULL)
+                               memmove(up + i, *p + size * n1, size);
+               } else {
+                       if (DeleteArray(p, ip, size, n1, 1, (up ? up + i : NULL)) == NULL)
+                               return n2;
+               }
+               n2++;
+       }
+       return n2;
+}
+
+int
+ParameterDelete(Parameter *par, Int type, UnionPar *up, struct IntGroup *where)
+{
+       return sParameterDeleteOrCopy(par, type, up, where, 0);
+}
+
+int
+ParameterCopy(Parameter *par, Int type, UnionPar *up, struct IntGroup *where)
+{
+       return sParameterDeleteOrCopy(par, type, up, where, 1);
+}
+
+/*  Renumber the atom index field according to the conversion table.  If the atom index
+    points to a non-existing atom, returns non-zero.  */
+int
+ParameterRenumberAtoms(Int type, UnionPar *up, Int oldnatoms, const Int *old2new)
+{
+#define RENUMBER_FIELD(_tp, _field) \
+  (((_tp *)up)->_field >= 0 && ((_tp *)up)->_field < oldnatoms && \
+       (old2new[((_tp *)up)->_field] >= 0 ? \
+      ((((_tp *)up)->_field = old2new[((_tp *)up)->_field]), 0) : \
+      ((((_tp *)up)->_field = kAtomTypeWildcard), 1)))
+       switch (type) {
+               case kBondParType:
+                       return RENUMBER_FIELD(BondPar, type1) + RENUMBER_FIELD(BondPar, type2);
+               case kAngleParType:
+                       return RENUMBER_FIELD(AnglePar, type1) + RENUMBER_FIELD(AnglePar, type2) + RENUMBER_FIELD(AnglePar, type3);
+               case kDihedralParType:
+               case kImproperParType:
+                       return RENUMBER_FIELD(TorsionPar, type1) + RENUMBER_FIELD(TorsionPar, type2) + RENUMBER_FIELD(TorsionPar, type3) + RENUMBER_FIELD(TorsionPar, type4);
+               case kVdwParType:
+                       return RENUMBER_FIELD(VdwPar, type1);
+               case kVdwPairParType:
+                       return RENUMBER_FIELD(VdwPairPar, type1) + RENUMBER_FIELD(VdwPairPar, type2);
+               case kVdwCutoffParType:
+                       if (((VdwCutoffPar *)up)->type == 1)
+                               return RENUMBER_FIELD(VdwCutoffPar, n1) + RENUMBER_FIELD(VdwCutoffPar, n2) + RENUMBER_FIELD(VdwCutoffPar, n3) || RENUMBER_FIELD(VdwCutoffPar, n4);
+                       else return 0;
+               default:
+                       return -1;
+       }
+}
+
+#pragma mark ====== Load from Files ======
+
+static int
+s_AppendWarning(char **ptr, const char *fmt, ...)
+{
+       char *buf;
+       int len, n, nn;
+       va_list ap;
+       if (ptr == NULL)
+               return 0;
+       if (*ptr == NULL) {
+               *ptr = (char *)malloc(128);
+               if (*ptr == NULL)
+                       return -1;
+               (*ptr)[0] = 0;
+               len = 128;
+               n = 0;
+       } else {
+               n = strlen(*ptr);
+               for (len = 128; len <= n; len *= 2);
+       }
+       va_start(ap, fmt);
+       nn = vasprintf(&buf, fmt, ap);
+       if (nn < 0)
+               return nn;
+       if (len <= n + nn) {
+               while (len <= n + nn)
+                       len *= 2;
+               *ptr = (char *)realloc(*ptr, len);
+               if (*ptr == NULL)
+                       return -1;
+       }
+       strncpy(*ptr + n, buf, nn);
+       *(*ptr + n + nn) = 0;
+       free(buf);
+       return nn;
+}
+
+int
+ParameterGlobalParIndexForSrcIndex(int src)
+{
+       int i;
+       if (src == 0 || src == -1)
+               return src - 1;
+       for (i = 0; i < gGlobalParInfo.count; i++) {
+               if (gGlobalParInfo.files[i].src == src)
+                       return i;
+       }
+       return -2;
+}
+
+int
+ParameterCommentIndexForGlobalFileName(const char *p)
+{
+       const char *pp = p + strlen(p), *p1 = NULL, *p2 = NULL;
+       char buf[128];
+       while (--pp >= p) {
+               if (p2 == NULL && *pp == '.')
+                       p2 = pp;
+               if (p1 == NULL && (*pp == '\'' || *pp == '/'))
+                       p1 = pp + 1;
+       }
+       if (p1 == NULL)
+               p1 = p;
+       if (p2 == NULL)
+               p2 = p + strlen(p);
+       snprintf(buf, sizeof(buf), "%.*s", (int)(p2 - p1), p1);
+       return ParameterCommentIndex(buf);
+}
+
+int
+ParameterCompare(const UnionPar *up1, const UnionPar *up2, int type)
+{
+       switch (type) {
+               case kBondParType: {
+                       const BondPar *bp1 = &up1->bond;
+                       const BondPar *bp2 = &up2->bond;
+                       return (((bp1->type1 == bp2->type1 && bp1->type2 == bp2->type2)
+                                        || (bp1->type1 == bp2->type2 && bp1->type2 == bp2->type1))
+                                       && fabs(bp1->k - bp2->k) < 1e-8 && fabs(bp1->r0 - bp2->r0) < 1e-8);
+               }
+               case kAngleParType: {
+                       const AnglePar *ap1 = &up1->angle;
+                       const AnglePar *ap2 = &up2->angle;
+                       return (ap1->type2 == ap2->type2
+                                       && ((ap1->type1 == ap2->type1 && ap1->type3 == ap2->type3)
+                                               || (ap1->type1 == ap2->type3 && ap1->type3 == ap2->type1))
+                                       && fabs(ap1->k - ap2->k) < 1e-8 && fabs(ap1->a0 - ap2->a0) < 1e-8);
+               }
+               case kDihedralParType:
+               case kImproperParType: {
+                       const TorsionPar *tp1 = &up1->torsion;
+                       const TorsionPar *tp2 = &up2->torsion;
+                       int i;
+                       if (tp1->mult != tp2->mult)
+                               return 0;
+                       if (type == kDihedralParType) {
+                               if ((tp1->type1 != tp2->type1 || tp1->type2 != tp2->type2 || tp1->type3 != tp2->type3 || tp1->type4 != tp2->type4) 
+                                       && (tp1->type1 != tp2->type4 || tp1->type2 != tp2->type3 || tp1->type3 != tp2->type2 || tp1->type4 != tp2->type1))
+                                       return 0;
+                       } else {
+                               if (tp1->type3 != tp2->type3)
+                                       return 0;
+                               if ((tp1->type1 != tp2->type1 || tp1->type2 != tp2->type2 || tp1->type4 != tp2->type4)
+                                       && (tp1->type1 != tp2->type1 || tp1->type2 != tp2->type4 || tp1->type4 != tp2->type2)
+                                       && (tp1->type1 != tp2->type2 || tp1->type2 != tp2->type1 || tp1->type4 != tp2->type4)
+                                       && (tp1->type1 != tp2->type2 || tp1->type2 != tp2->type4 || tp1->type4 != tp2->type1)
+                                       && (tp1->type1 != tp2->type4 || tp1->type2 != tp2->type1 || tp1->type4 != tp2->type2)
+                                       && (tp1->type1 != tp2->type4 || tp1->type2 != tp2->type2 || tp1->type4 != tp2->type1))
+                                       return 0;
+                       }
+                       for (i = 0; i < tp1->mult; i++) {
+                               if (tp1->period[i] != tp2->period[i] || fabs(tp1->k[i] - tp2->k[i]) > 1e-8 || fabs(tp1->phi0[i] - tp2->phi0[i]) > 1e-8)
+                                       return 0;
+                       }
+                       return 1;
+               }
+               case kVdwParType: {
+                       const VdwPar *vp1 = &up1->vdw;
+                       const VdwPar *vp2 = &up2->vdw;
+                       return (vp1->type1 == vp2->type1 && fabs(vp1->eps - vp2->eps) < 1e-6 && fabs(vp1->r_eq - vp2->r_eq) < 1e-6 && fabs(vp1->eps14 - vp2->eps14) < 1e-4 && fabs(vp1->r_eq14 - vp2->r_eq14) < 1e-4);
+               }
+               case kVdwPairParType: {
+                       const VdwPairPar *vp1 = &up1->vdwp;
+                       const VdwPairPar *vp2 = &up2->vdwp;
+                       return (vp1->type1 == vp2->type1 && fabs(vp1->eps - vp2->eps) < 1e-6 && fabs(vp1->r_eq - vp2->r_eq) < 1e-6 && fabs(vp1->eps14 - vp2->eps14) < 1e-4 && fabs(vp1->r_eq14 - vp2->r_eq14) < 1e-4);
+               }
+               case kVdwCutoffParType: {
+                       const VdwCutoffPar *vp1 = &up1->vdwcutoff;
+                       const VdwCutoffPar *vp2 = &up2->vdwcutoff;
+                       if (vp1->type != vp2->type)
+                               return 0;
+                       if (vp1->type == 0) {
+                               if ((vp1->n1 != vp2->n1 || vp1->n2 != vp2->n2) && (vp1->n1 != vp2->n2 || vp1->n2 != vp2->n1))
+                                       return 0;
+                       } else {
+                               if (vp1->n1 != vp2->n1 || vp1->n2 != vp2->n2 || vp1->n3 != vp2->n3 || vp1->n4 != vp2->n4)
+                                       return 0;
+                       }
+                       return fabs(vp1->cutoff - vp2->cutoff) < 1e-8;
+               }
+       }
+       return 0;
+}
+
+/*  bp can also be AnglePar *, TorsionPar *, etc.  */
+static void
+s_StoreComment(int parType, BondPar *bp, char *p, const char *src)
+{
+       char *s, *pp, *pcom, buf[40];
+       int embedded = 0, src_idx;
+       if (p == NULL)
+               return;
+       while (isspace(*p) || *p == ';' || *p == '!')
+               p++;
+       pcom = p;
+       if (src == NULL && *p == '[') {
+               /*  Source is embedded  */
+               int len;
+               s = p + 1;
+               p += 2;
+               while (*p != ']' && *p != 0)
+                       p++;
+               len = p - s;
+               if (len >= sizeof(buf))
+                       len = sizeof(buf) - 1;
+               strncpy(buf, s, len);
+               buf[len] = 0;
+               src = buf;
+               if (*p != 0) {
+                       p++;
+                       while (isspace(*p))
+                               p++;
+               }
+               embedded = 1;
+       }
+       pp = p;
+       while (*pp != 0 && *pp != '\r' && *pp != '\n')
+               pp++;
+       *pp = 0;
+       if (src != NULL && *src != 0) {
+               src_idx = ParameterCommentIndex(src);
+               if (embedded) {
+                       /*  Compare with already known global parameters, and src is set if the same one is found  */
+                       int i;
+                       UnionPar *up2;
+                       for (i = 0; (up2 = ParameterGetUnionParFromTypeAndIndex(gBuiltinParameters, parType, i)) != NULL; i++) {
+                               if (up2->bond.src == src_idx && ParameterCompare((UnionPar *)bp, up2, parType)) {
+                                       bp->src = src_idx;
+                                       break;
+                               }
+                       }
+                       if (up2 == NULL) {
+                               /*  Not found: embedded source is retained, and this entry is regarded as "no source"  */
+                               bp->src = 0;
+                               p = pcom;
+                       }
+               } else bp->src = src_idx;
+       }
+       if (*p != 0)
+               bp->com = ParameterCommentIndex(p);
+}
+
+/*  bp can also be BondPar*, AnglePar *, TorsionPar *, etc.  */
+static void
+s_CommentToString(char *buf, int bufsize, void *bp)
+{
+       const char *src, *com;
+       src = ParameterGetComment(((BondPar *)bp)->src);
+       com = ParameterGetComment(((BondPar *)bp)->com);
+       if (src == NULL && com == NULL)
+               buf[0] = 0;
+       else if (src != NULL)
+               snprintf(buf, bufsize, " ![%s] %s", src, (com == NULL ? "" : com));
+       else
+               snprintf(buf, bufsize, " ! %s", com);
+}
+
+int
+ParameterReadFromString(Parameter *par, char *buf, char **wbufp, const char *fname, int lineNumber, int src_idx)
+{
+       int i, len;
+       char com[12], com2[12], type[4][8];
+       Int itype[4];
+       float val[6];  /*  not Double  */
+       Int ival[12];
+       int n;
+       int retval = 0;
+       const char *src;
+       if (sscanf(buf, " %11s", com) <= 0 || !isalpha(com[0]))
+               return 0;
+       len = strlen(com);
+       for (i = 0; i < len; i++)
+               com[i] = tolower(com[i]);
+       if (strncmp(com, "include", len) == 0) {
+               char c, *p, *pp;
+               int wc1;
+               char *wbuf1;
+               i = 0;
+               for ( ; (c = buf[len]) != 0; len++) {
+                       if (c == ' ' || c == '\t')
+                               continue;
+                       if (c == '\"') {
+                               if (i == 0)
+                                       continue;
+                               else break;
+                       }
+                       if (c == '\\') {
+                               len++;
+                               buf[i] = buf[len];
+                               if (buf[len] == 0)
+                                       break;
+                               i++;
+                               continue;
+                       }
+                       buf[i++] = c;
+               }
+               buf[i] = 0;
+               p = (char *)malloc(strlen(fname) + i + 1);
+               if (p == NULL) {
+                       return -1;
+               }
+               strcpy(p, fname);
+               pp = strrchr(p, '/');
+               if (pp == NULL)
+                       strcpy(p, buf);
+               else
+                       strcpy(p + 1, buf);
+               i = ParameterReadFromFile(par, p, &wbuf1, &wc1);
+               if (wbuf1 != NULL) {
+                       s_AppendWarning(wbufp, "In included file %s:\n%s", p, wbuf1);
+                       free(wbuf1);
+                       retval = wc1;
+               }
+               free(p);
+               if (i < 0) {
+                       retval = i;
+               }
+               return retval;
+       }
+       if (par == gBuiltinParameters && src_idx == 0) {
+               /*  Comes here only when the reading file is "default.par" at the initialization of the built-in parameters. */
+               /*  In this case, only "include" statements are allowed.  */
+               return -1;
+       }
+       if (src_idx == 0)
+               src = NULL;
+       else src = ParameterGetComment(src_idx);
+       if (strncmp(com, "bond", len) == 0) {
+               BondPar *bp;
+               if (sscanf(buf, " %11s %4s %4s %f %f %n", com2, type[0], type[1], &val[0], &val[1], &n) < 5) {
+                       s_AppendWarning(wbufp, "%s:%d: missing parameter in BOND record\n", fname, lineNumber);
+                       return 1;
+               }
+               itype[0] = AtomTypeEncodeToUInt(type[0]);
+               itype[1] = AtomTypeEncodeToUInt(type[1]);
+               if (itype[0] > itype[1]) {
+                       i = itype[0];
+                       itype[0] = itype[1];
+                       itype[1] = i;
+               }
+               val[0] *= KCAL2INTERNAL;
+               bp = ParameterLookupBondPar(par, itype[0], itype[1], 1);
+               if (bp != NULL) {
+                       if (bp->k != val[0] || bp->r0 != val[1]) {
+                               s_AppendWarning(wbufp, "%s:%d: The BOND %s-%s parameter appeared twice; the values (%f, %f) are used\n", fname, lineNumber, AtomTypeDecodeToString(itype[0], type[0]), AtomTypeDecodeToString(itype[1], type[1]), val[0], val[1]);
+                               retval = 1;
+                       }
+               }
+               bp = AssignArray(&par->bondPars, &par->nbondPars, sizeof(*bp), par->nbondPars, NULL);
+               bp->type1 = itype[0];
+               bp->type2 = itype[1];
+               bp->k = val[0];
+               bp->r0 = val[1];
+               s_StoreComment(kBondParType, bp, buf + n, src);
+       } else if (strncmp(com, "angle", len) == 0) {
+               AnglePar *ap;
+               if (sscanf(buf, " %11s %4s %4s %4s %f %f %n", com2, type[0], type[1], type[2], &val[0], &val[1], &n) < 6) {
+                       s_AppendWarning(wbufp, "%s:%d: missing parameter in ANGLE record\n", fname, lineNumber);
+                       return 1;
+               }
+               itype[0] = AtomTypeEncodeToUInt(type[0]);
+               itype[1] = AtomTypeEncodeToUInt(type[1]);
+               itype[2] = AtomTypeEncodeToUInt(type[2]);
+               if (itype[0] > itype[2]) {
+                       i = itype[0];
+                       itype[0] = itype[2];
+                       itype[2] = i;
+               }
+               val[0] *= KCAL2INTERNAL;
+               val[1] *= (3.14159265358979 / 180.0);
+               ap = ParameterLookupAnglePar(par, itype[0], itype[1], itype[2], 1);
+               if (ap != NULL) {
+                       if (ap->k != val[0] || ap->a0 != val[1]) {
+                               s_AppendWarning(wbufp, "%s:%d: The ANGLE %s-%s-%s parameter appeared twice; the values (%f, %f) are used\n", fname, lineNumber, AtomTypeDecodeToString(itype[0], type[0]), AtomTypeDecodeToString(itype[1], type[1]), AtomTypeDecodeToString(itype[2], type[2]), val[0], val[1]);
+                               retval = 1;
+                       }
+               }
+               ap = AssignArray(&par->anglePars, &par->nanglePars, sizeof(*ap), par->nanglePars, NULL);
+               ap->type1 = itype[0];
+               ap->type2 = itype[1];
+               ap->type3 = itype[2];
+               ap->k = val[0];
+               ap->a0 = val[1];
+               s_StoreComment(kAngleParType, (BondPar *)ap, buf + n, src);
+       } else if (strncmp(com, "dihedral", len) == 0) {
+               TorsionPar *dp;
+               if (sscanf(buf, " %11s %4s %4s %4s %4s %f %d %f %n", com2, type[0], type[1], type[2], type[3], &val[0], &ival[0], &val[1], &n) < 8) {
+                       s_AppendWarning(wbufp, "%s:%d: missing parameter in DIHEDRAL record\n", fname, lineNumber);
+                       return 1;
+               }
+               itype[0] = AtomTypeEncodeToUInt(type[0]);
+               itype[1] = AtomTypeEncodeToUInt(type[1]);
+               itype[2] = AtomTypeEncodeToUInt(type[2]);
+               itype[3] = AtomTypeEncodeToUInt(type[3]);
+               if (itype[0] > itype[3]) {
+                       i = itype[0];
+                       itype[0] = itype[3];
+                       itype[3] = i;
+                       i = itype[1];
+                       itype[1] = itype[2];
+                       itype[2] = i;
+               }
+               dp = ParameterLookupDihedralPar(par, itype[0], itype[1], itype[2], itype[3], 1);
+               val[0] *= KCAL2INTERNAL;
+               val[1] *= 3.14159265358979 / 180.0;
+               if (dp != NULL) {
+                       if (dp->mult != 1 || dp->k[0] != val[0] || dp->period[0] != ival[0] || dp->phi0[0] != val[1]) {
+                               s_AppendWarning(wbufp, "%s:%d: The DIHEDRAL %s-%s-%s-%s parameter appeared twice; the values (%f, %d, %f) are used\n", fname, lineNumber, AtomTypeDecodeToString(itype[0], type[0]), AtomTypeDecodeToString(itype[1], type[1]), AtomTypeDecodeToString(itype[2], type[2]), AtomTypeDecodeToString(itype[3], type[3]), val[0], ival[0], val[1]);
+                               retval = 1;
+                       }
+               }
+               dp = AssignArray(&par->dihedralPars, &par->ndihedralPars, sizeof(*dp), par->ndihedralPars, NULL);
+               dp->type1 = itype[0];
+               dp->type2 = itype[1];
+               dp->type3 = itype[2];
+               dp->type4 = itype[3];
+               dp->k[0] = val[0];
+               dp->period[0] = ival[0];
+               dp->phi0[0] = val[1];
+               dp->mult = 1;
+               s_StoreComment(kDihedralParType, (BondPar *)dp, buf + n, src);
+       } else if (strncmp(com, "improper", len) == 0) {
+               TorsionPar *ip;
+               if (sscanf(buf, " %11s %4s %4s %4s %4s %f %d %f %n", com2, type[0], type[1], type[2], type[3], &val[0], &ival[0], &val[1], &n) < 8) {
+                       s_AppendWarning(wbufp, "%s:%d: missing parameter in IMPROPER record\n", fname, lineNumber);
+                       return 1;
+               }
+               itype[0] = AtomTypeEncodeToUInt(type[0]);
+               itype[1] = AtomTypeEncodeToUInt(type[1]);
+               itype[2] = AtomTypeEncodeToUInt(type[2]);
+               itype[3] = AtomTypeEncodeToUInt(type[3]);
+               if (itype[0] > itype[1]) {
+                       i = itype[0];
+                       itype[0] = itype[1];
+                       itype[1] = i;
+               }
+               if (itype[0] > itype[3]) {
+                       i = itype[0];
+                       itype[0] = itype[3];
+                       itype[3] = i;
+               }
+               if (itype[1] > itype[3]) {
+                       i = itype[1];
+                       itype[1] = itype[3];
+                       itype[3] = i;
+               }
+               ip = ParameterLookupImproperPar(par, itype[0], itype[1], itype[2], itype[3], 1);
+               val[0] *= KCAL2INTERNAL;
+               val[1] *= 3.14159265358979 / 180.0;
+               if (ip != NULL) {
+                       if (ip->mult != 1 || ip->k[0] != val[0] || ip->period[0] != ival[0] || ip->phi0[0] != val[1]) {
+                               s_AppendWarning(wbufp, "%s:%d: The IMPROPER %s-%s-%s-%s parameter appeared twice; the values (%f, %d, %f) are used\n", fname, lineNumber, AtomTypeDecodeToString(itype[0], type[0]), AtomTypeDecodeToString(itype[1], type[1]), AtomTypeDecodeToString(itype[2], type[2]), AtomTypeDecodeToString(itype[3], type[3]), val[0], ival[0], val[1]);
+                               retval = 1;
+                       }
+               }
+               ip = AssignArray(&par->improperPars, &par->nimproperPars, sizeof(*ip), par->nimproperPars, NULL);
+               ip->type1 = itype[0];
+               ip->type2 = itype[1];
+               ip->type3 = itype[2];
+               ip->type4 = itype[3];
+               ip->k[0] = val[0];
+               ip->period[0] = ival[0];
+               ip->phi0[0] = val[1];
+               ip->mult = 1;   
+               s_StoreComment(kImproperParType, (BondPar *)ip, buf + n, src);
+       } else if (strncmp(com, "nonbonded", len) == 0 || strncmp(com, "vdw", len) == 0) {
+               VdwPar *vp, vtemp;
+               char *p;
+               /*  NOTE: the nonbonded record lists "2*sigma", not "sigma"!  */
+               int flag = (com[0] == 'v');
+               if (sscanf(buf, " %11s %4s %f %f %f %f %n", com2, type[0], &val[0], &val[1], &val[2], &val[3], &n) < 6) {
+                       s_AppendWarning(wbufp, "%s:%d: missing parameter in %s record\n", fname, lineNumber, (flag ? "VDW" : "NONBONDED"));
+                       return 1;
+               }
+               itype[0] = AtomTypeEncodeToUInt(type[0]);
+               memset(&vtemp, 0, sizeof(vtemp));
+               vtemp.type1 = itype[0];
+               vtemp.atomicNumber = 0;  /*  No definition given  */
+               vtemp.eps = val[0] * KCAL2INTERNAL;
+               vtemp.r_eq = val[1] * (flag ? 1.0 : 0.561231024154687); /* 1/2 * 2**(1/6)  */
+               vtemp.A = pow(vtemp.r_eq * 2, 12) * vtemp.eps;
+               vtemp.B = 2 * pow(vtemp.r_eq * 2, 6) * vtemp.eps;
+               vtemp.eps14 = val[2] * KCAL2INTERNAL;
+               vtemp.r_eq14 = val[3] * (flag ? 1.0 : 0.561231024154687); /* 1/2 * 2**(1/6)  */
+               vtemp.A14 = pow(vtemp.r_eq14 * 2, 12) * vtemp.eps14;
+               vtemp.B14 = 2 * pow(vtemp.r_eq14 * 2, 6) * vtemp.eps14;
+               p = buf + n;
+               ival[0] = 0;
+               val[4] = val[5] = 0.0;
+               if (sscanf(p, "%d %n", &ival[0], &n) == 1) {
+                       vtemp.atomicNumber = ival[0];
+                       p += n;
+               }
+               if (sscanf(p, "%f %n", &val[4], &n) == 1) {
+                       vtemp.weight = val[4];
+                       p += n;
+               }
+               if (val[4] == 0.0 && ival[0] != 0)
+                       vtemp.weight = WeightForAtomicNumber(ival[0]);
+               if (sscanf(p, "%f %n", &val[5], &n) == 1) {
+                       vtemp.polarizability = val[5];
+                       p += n;
+               }
+               n = p - buf;
+               if (ival[0] == 0 && val[4] != 0.0) {
+                       for (i = 1; (val[5] = WeightForAtomicNumber(i)) != 0.0; i++) {
+                               if (fabs(val[4] - val[5]) < 0.1) {
+                                       vtemp.atomicNumber = i;
+                                       break;
+                               }
+                       }
+               }
+               vp = NULL;
+               for (i = 0; i < par->nvdwPars; i++) {
+                       if (itype[0] == par->vdwPars[i].type1) {
+                               vp = par->vdwPars + i;
+                               if (vp->A != vtemp.A || vp->B != vtemp.B || vp->A14 != vtemp.A14 || vp->B14 != vtemp.B14) {
+                                       s_AppendWarning(wbufp, "%s:%d: The %s %s parameter appeared twice; the values (%f, %f, %f, %f, %d, %f, %f) are used\n", fname, lineNumber, (flag ? "VDW" : "NONBONDED"), AtomTypeDecodeToString(itype[0], type[0]), val[0], val[1], val[2], val[3], ival[0], val[4], val[5]);
+                                       retval = 1;
+                               }
+                               break;
+                       }
+               }
+               vp = AssignArray(&par->vdwPars, &par->nvdwPars, sizeof(*vp), i, NULL);
+               vtemp.com = vp->com;  /*  Keep comment field  */
+               *vp = vtemp;
+               s_StoreComment(kVdwParType, (BondPar *)vp, buf + n, src);
+       } else if (strncmp(com, "nbfi", len) == 0 || strncmp(com, "vdwpair", len) == 0) {
+               VdwPairPar *vp, vtemp;
+               int flag = (com[0] == 'v');
+               if (sscanf(buf, " %11s %4s %4s %f %f %f %f %n", com2, type[0], type[1], &val[0], &val[1], &val[2], &val[3], &n) < 6) {
+                       s_AppendWarning(wbufp, "%s:%d: missing parameter in %s record\n", fname, lineNumber, (flag ? "VDWP" : "NBFI"));
+                       return 1;
+               }
+               itype[0] = AtomTypeEncodeToUInt(type[0]);
+               itype[1] = AtomTypeEncodeToUInt(type[1]);
+               if (itype[0] > itype[1]) {
+                       i = itype[0];
+                       itype[0] = itype[1];
+                       itype[1] = i;
+               }
+               vtemp.type1 = itype[0];
+               vtemp.type2 = itype[1];
+               if (flag) {  /*  eps/r_eq representation  */
+                       vtemp.eps = val[0] * KCAL2INTERNAL;
+                       vtemp.r_eq = val[1];
+                       vtemp.A = pow(val[1] * 2, 12) * vtemp.eps;
+                       vtemp.B = 2 * pow(val[1] * 2, 6) * vtemp.eps;
+                       vtemp.eps14 = val[2] * KCAL2INTERNAL;
+                       vtemp.r_eq14 = val[3];
+                       vtemp.A14 = pow(val[3] * 2, 12) * vtemp.eps14;
+                       vtemp.B14 = 2 * pow(val[3] * 2, 6) * vtemp.eps14;
+               } else {    /*  A/B representation  */
+                       vtemp.A = val[0] * KCAL2INTERNAL;
+                       vtemp.B = val[1] * KCAL2INTERNAL;
+                       vtemp.eps = pow(0.25 * vtemp.B * vtemp.B / vtemp.A, 0.16666666667);
+                       vtemp.r_eq = pow(vtemp.A / vtemp.B * 2.0, 0.16666666667) * 0.5;
+                       vtemp.A14 = val[2] * KCAL2INTERNAL;
+                       vtemp.B14 = val[3] * KCAL2INTERNAL;
+                       vtemp.eps14 = pow(0.25 * vtemp.B14 * vtemp.B14 / vtemp.A14, 0.16666666667);
+                       vtemp.r_eq14 = pow(vtemp.A14 / vtemp.B14 * 2.0, 0.16666666667) * 0.5;
+               }
+               vp = NULL;
+               for (i = 0; i < par->nvdwpPars; i++) {
+                       if (itype[0] == par->vdwpPars[i].type1 && itype[1] == par->vdwpPars[i].type2) {
+                               vp = par->vdwpPars + i;
+                               if (vp->A != vtemp.A || vp->B != vtemp.B || vp->A14 != vtemp.A14 || vp->B14 != vtemp.B14) {
+                                       s_AppendWarning(wbufp, "%s:%d: The %s %s-%s parameter appeared twice; the values (%f, %f, %f, %f) are used\n", fname, lineNumber, (flag ? "VDWP" : "NBFI"), AtomTypeDecodeToString(itype[0], type[0]), AtomTypeDecodeToString(itype[1], type[1]), val[0], val[1], val[2], val[3]);
+                                       retval = 1;
+                               }
+                               break;
+                       }
+               }
+               vp = AssignArray(&par->vdwpPars, &par->nvdwpPars, sizeof(*vp), i, NULL);
+               vtemp.com = vp->com;  /*  Keep comment field  */
+               *vp = vtemp;
+               s_StoreComment(kVdwPairParType, (BondPar *)vp, buf + n, src);
+       } else {
+               s_AppendWarning(wbufp, "%s:%d: unknown keyword %s\n", fname, lineNumber, com);
+               return 1;
+       }
+       return retval;
+}
+
+int
+ParameterReadFromFile(Parameter *par, const char *fname, char **outWarningMessage, int *outWarningCount)
+{
+       char *wbuf;
+       char buf[1024];
+       FILE *fp = NULL;
+       int first = 0;
+       int wcount;
+       int lineNumber;
+       int src_idx;
+       
+       if (par == NULL) {
+               par = gBuiltinParameters;
+               if (par == NULL) {
+                       par = ParameterNew();
+                       gBuiltinParameters = par;
+                       first = 1;
+               }
+       }
+       
+       wcount = 0;
+       wbuf = NULL;
+
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               s_AppendWarning(&wbuf, "Cannot open parameter file %s\n", fname);
+               wcount = -1;
+               goto exit;
+       }
+
+       if (par != gBuiltinParameters || first)
+               src_idx = 0;
+       else
+               src_idx = ParameterCommentIndexForGlobalFileName(fname);
+
+       if (par == gBuiltinParameters && !first) {
+               /*  Ensure the "source" field is unique  */
+               int i;
+               ParFileInfo *ip;
+               const char *p;
+               for (i = 0; i < gGlobalParInfo.count; i++) {
+                       if (gGlobalParInfo.files[i].src == src_idx)
+                               break;
+               }
+               if (i < gGlobalParInfo.count) {
+                       /*  Delete the existing Parameters from the same source  */
+                       ParameterDeleteAllEntriesForSource(par, src_idx);
+               }
+               
+               /*  Register the global file info  */
+               ip = AssignArray(&(gGlobalParInfo.files), &(gGlobalParInfo.count), sizeof(ParFileInfo), gGlobalParInfo.count, NULL);
+               for (p = fname + strlen(fname) - 1; p >= fname; p--) {
+                       if (*p == '/' || *p == '\\')
+                               break;
+               }
+               if (p < fname)
+                       ip->dir = NULL;
+               else {
+                       i = p - fname;
+                       ip->dir = (char *)malloc(i + 1);
+                       if (ip->dir != NULL) {
+                               strncpy(ip->dir, fname, i);
+                               ip->dir[i] = 0;
+                       }
+               }
+               p++;
+               ip->name = strdup(p);
+               ip->src = src_idx;
+       }
+       
+       lineNumber = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               int wc1 = ParameterReadFromString(par, buf, &wbuf, fname, lineNumber, src_idx);
+               if (wc1 >= 0)
+                       wcount += wc1;
+               else {
+                       wcount = wc1;
+                       break;
+               }
+       }
+       if (first)
+               gGlobalParInfo.builtinCount = gGlobalParInfo.count;
+
+exit:
+       if (fp != NULL)
+               fclose(fp);
+       if (outWarningMessage != NULL)
+               *outWarningMessage = wbuf;
+       else if (wbuf != NULL)
+               free(wbuf);
+       if (outWarningCount != NULL)
+               *outWarningCount = (wcount >= 0 ? wcount : 0);
+
+       return (wcount >= 0 ? 0 : wcount);
+}
+
+int
+ParameterAppendToFile(Parameter *par, FILE *fp)
+{
+       int i, n;
+       char cbuf[6][8];
+       char buf[120];
+       int bufsize = sizeof(buf);
+
+       if (par == NULL)
+               return 0;
+       
+       n = 0;
+       if (par->nbondPars > 0)
+               fprintf(fp, "! type1 type2 k r0\n");
+       for (i = 0; i < par->nbondPars; i++) {
+               BondPar *bp = par->bondPars + i;
+               AtomTypeDecodeToString(bp->type1, cbuf[0]);
+               AtomTypeDecodeToString(bp->type2, cbuf[1]);
+               s_CommentToString(buf, bufsize, bp);
+               fprintf(fp, "bond %s %s %.6f %f%s\n", cbuf[0], cbuf[1], bp->k * INTERNAL2KCAL, bp->r0, buf);
+               n++;
+       }
+       if (par->nanglePars > 0)
+               fprintf(fp, "! type1 type2 type3 k a0\n");
+       for (i = 0; i < par->nanglePars; i++) {
+               AnglePar *ap = par->anglePars + i;
+               AtomTypeDecodeToString(ap->type1, cbuf[0]);
+               AtomTypeDecodeToString(ap->type2, cbuf[1]);
+               AtomTypeDecodeToString(ap->type3, cbuf[2]);
+               s_CommentToString(buf, bufsize, ap);
+               fprintf(fp, "angle %s %s %s %.6f %f%s\n", cbuf[0], cbuf[1], cbuf[2], ap->k * INTERNAL2KCAL, ap->a0 * kRad2Deg, buf);
+               n++;
+       }
+       if (par->ndihedralPars > 0)
+               fprintf(fp, "! type1 type2 type3 type4 k periodicity phi0\n");
+       for (i = 0; i < par->ndihedralPars; i++) {
+               TorsionPar *tp = par->dihedralPars + i;
+               AtomTypeDecodeToString(tp->type1, cbuf[0]);
+               AtomTypeDecodeToString(tp->type2, cbuf[1]);
+               AtomTypeDecodeToString(tp->type3, cbuf[2]);
+               AtomTypeDecodeToString(tp->type4, cbuf[3]);
+               s_CommentToString(buf, bufsize, tp);
+               fprintf(fp, "dihe %s %s %s %s %.6f %d %f%s\n", cbuf[0], cbuf[1], cbuf[2], cbuf[3], tp->k[0] * INTERNAL2KCAL, tp->period[0], tp->phi0[0] * kRad2Deg, buf);
+               n++;
+       }
+       if (par->nimproperPars > 0)
+               fprintf(fp, "! type1 type2 type3 type4 k periodicity phi0\n");
+       for (i = 0; i < par->nimproperPars; i++) {
+               TorsionPar *tp = par->improperPars + i;
+               AtomTypeDecodeToString(tp->type1, cbuf[0]);
+               AtomTypeDecodeToString(tp->type2, cbuf[1]);
+               AtomTypeDecodeToString(tp->type3, cbuf[2]);
+               AtomTypeDecodeToString(tp->type4, cbuf[3]);
+               s_CommentToString(buf, bufsize, tp);
+               fprintf(fp, "impr %s %s %s %s %.6f %d %f%s\n", cbuf[0], cbuf[1], cbuf[2], cbuf[3], tp->k[0] * INTERNAL2KCAL, tp->period[0], tp->phi0[0] * kRad2Deg, buf);
+               n++;
+       }
+       if (par->nvdwPars > 0)
+               fprintf(fp, "! type eps r_eq eps14 r_eq14 atomic_number weight\n");
+       for (i = 0; i < par->nvdwPars; i++) {
+               VdwPar *vp = par->vdwPars + i;
+       /*      Double eps, eps14;  */
+               AtomTypeDecodeToString(vp->type1, cbuf[0]);
+       /*      eps = (vp->A == 0.0 ? 0.0 : vp->B * vp->B / vp->A * 0.25 * INTERNAL2KCAL);
+               eps14 = (vp->A14 == 0.0 ? 0.0 : vp->B14 * vp->B14 / vp->A14 * 0.25 * INTERNAL2KCAL);  */
+               s_CommentToString(buf, bufsize, vp);
+               fprintf(fp, "vdw %s %.6f %.6f %.6f %.6f %d %f%s\n", cbuf[0], vp->eps * INTERNAL2KCAL, vp->r_eq, vp->eps14 * INTERNAL2KCAL, vp->r_eq14, vp->atomicNumber, vp->weight, buf);  /*  polarizability is not written because it is not used now  */
+               n++;
+       }
+       if (par->nvdwpPars > 0)
+               fprintf(fp, "! type1 type2 eps r_eq eps14 r_eq14\n");
+       for (i = 0; i < par->nvdwpPars; i++) {
+               VdwPairPar *vpp = par->vdwpPars + i;
+       /*      Double eps, eps14;  */
+               AtomTypeDecodeToString(vpp->type1, cbuf[0]);
+               AtomTypeDecodeToString(vpp->type2, cbuf[1]);
+       /*      eps = (vpp->A == 0.0 ? 0.0 : vpp->B * vpp->B / vpp->A * 0.25 * INTERNAL2KCAL);
+               eps14 = (vpp->A14 == 0.0 ? 0.0 : vpp->B14 * vpp->B14 / vpp->A14 * 0.25 * INTERNAL2KCAL); */
+               s_CommentToString(buf, bufsize, vpp);
+               fprintf(fp, "vdwp %s %s %.6f %.6f %.6f %.6f%s\n", cbuf[0], cbuf[1], vpp->eps * INTERNAL2KCAL, vpp->r_eq, vpp->eps14 * INTERNAL2KCAL, vpp->r_eq14, buf);
+               n++;
+       }
+/*     if (par->natomPars > 0)
+               fprintf(fp, "! name atomic_number radius red green blue weight\n");
+       for (i = 0; i < par->natomPars; i++) {
+               AtomPar *app = par->atomPars + i;
+               s_CommentToString(buf, bufsize, app);
+               fprintf(fp, "dispatom %.4s %d %f %f %f %f %f%s\n", app->name, app->number, app->radius, app->r, app->g, app->b, app->weight, buf);
+               n++;
+       } */
+       return n;
+}
+
+int
+ParameterDeleteAllEntriesForSource(Parameter *par, int src_idx)
+{
+       int i, n, type;
+       UnionPar *up;
+       IntGroup *ig;
+/*     if (fname == NULL)
+               return -1;
+       src_idx = ParameterCommentIndexForGlobalFileName(fname); */
+       if (src_idx == 0)
+               return -1;
+       n = 0;
+       for (type = kFirstParType; type <= kLastParType; type++) {
+               ig = IntGroupNew();
+               for (i = ParameterGetCountForType(par, type) - 1; i >= 0; i--) {
+                       up = ParameterGetUnionParFromTypeAndIndex(par, type, i);
+                       if (up != NULL && up->bond.src == src_idx)
+                               IntGroupAdd(ig, i, 1);
+               }
+               i = IntGroupGetCount(ig);
+               if (i > 0) {
+                       ParameterDelete(par, type, NULL, ig);
+                       n += i;
+               }
+               IntGroupRelease(ig);
+       }
+       if (par == gBuiltinParameters) {
+               /*  Unregister from the global info  */
+               for (i = gGlobalParInfo.builtinCount; i < gGlobalParInfo.count; i++) {
+                       if (gGlobalParInfo.files[i].src == src_idx) {
+                               DeleteArray(&gGlobalParInfo.files, &gGlobalParInfo.count, sizeof(ParFileInfo), i, 1, NULL);
+                               break;
+                       }
+               }
+       }
+       return n;
+}
+
+#pragma mark ====== Parameter Comments ======
+
+static const char **sParameterComments;
+static Int sNumberOfParameterComments;
+
+int
+ParameterCommentIndex(const char *comment)
+{
+       int i;
+       char *p, *pp;
+       if (comment == NULL || comment[0] == 0)
+               return 0;
+       /*  Duplicate the comment, convert whitespaces to ' ', and chop trailing whitespaces  */
+       p = strdup(comment);
+       i = 0;
+       for (pp = p + strlen(p) - 1; pp >= p; pp--) {
+               if (isspace(*pp)) {
+                       if (i == 0)
+                               *pp = 0;
+                       else *pp = ' ';
+               } else i++;
+       }
+       for (i = 1; i < sNumberOfParameterComments; i++) {
+               if (strcmp(sParameterComments[i], p) == 0) {
+                       free(p);
+                       return i;
+               }
+       }
+       if (sNumberOfParameterComments == 0) {
+               /*  Index 0 is skipped  */
+               AssignArray(&sParameterComments, &sNumberOfParameterComments, sizeof(char *), 1, &p);
+       } else {
+               AssignArray(&sParameterComments, &sNumberOfParameterComments, sizeof(char *), sNumberOfParameterComments, &p);
+       }
+       return sNumberOfParameterComments - 1;
+}
+
+const char *
+ParameterGetComment(int idx)
+{
+       if (idx <= 0 || idx >= sNumberOfParameterComments)
+               return NULL;  /*  No such number  */
+       return sParameterComments[idx];
+}
+
+#pragma mark ====== Parameter Lookup ======
+
+#define s_ParMatch(_t1, _t2, _nowildcard) (_t1 == _t2 || (!_nowildcard && _t1 == kAtomTypeWildcard))
+
+/*  Returns non-zero if the parameter record contains designated atom_type.
+ The atom_type can also be an atom index.  */
+int
+ParameterDoesContainAtom(Int type, UnionPar *up, UInt atom_type, Int options)
+{
+#define CHECK_FIELD(_tp, _field) s_ParMatch((((_tp *)up)->_field), atom_type, nowildcard)
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       switch (type) {
+               case kBondParType:
+                       return CHECK_FIELD(BondPar, type1) || CHECK_FIELD(BondPar, type2);
+               case kAngleParType:
+                       return CHECK_FIELD(AnglePar, type1) || CHECK_FIELD(AnglePar, type2) || CHECK_FIELD(AnglePar, type3);
+               case kDihedralParType:
+               case kImproperParType:
+                       return CHECK_FIELD(TorsionPar, type1) || CHECK_FIELD(TorsionPar, type2) || CHECK_FIELD(TorsionPar, type3) || CHECK_FIELD(TorsionPar, type4);
+               case kVdwParType:
+                       return CHECK_FIELD(VdwPar, type1);
+               case kVdwPairParType:
+                       return CHECK_FIELD(VdwPairPar, type1) || CHECK_FIELD(VdwPairPar, type2);
+               case kVdwCutoffParType:
+                       if (((VdwCutoffPar *)up)->type == 1)
+                               return CHECK_FIELD(VdwCutoffPar, n1) || CHECK_FIELD(VdwCutoffPar, n2) || CHECK_FIELD(VdwCutoffPar, n3) || CHECK_FIELD(VdwCutoffPar, n4);
+                       else return 0;
+               default: return 0;
+       }
+}
+
+BondPar *
+ParameterLookupBondPar(Parameter *par, UInt t1, UInt t2, Int options)
+{
+       int i;
+       BondPar *bp;
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       if (par == NULL)
+               return NULL;
+       if ((options & (kParameterLookupGlobal | kParameterLookupLocal | kParameterLookupMissing)) == 0)
+               options |= kParameterLookupGlobal | kParameterLookupLocal;
+       for (i = par->nbondPars - 1, bp = par->bondPars + i; i >= 0; i--, bp--) {
+               if ((bp->src > 0 && !(options & kParameterLookupGlobal))
+                       || (bp->src == 0 && !(options & kParameterLookupLocal))
+                       || (bp->src < 0 && !(options & kParameterLookupMissing)))
+                       continue;
+               if (s_ParMatch(bp->type1, t1, nowildcard) && s_ParMatch(bp->type2, t2, nowildcard))
+                       return bp;
+               if (s_ParMatch(bp->type1, t2, nowildcard) && s_ParMatch(bp->type2, t1, nowildcard))
+                       return bp;              
+       }
+       if (options & kParameterLookupNoBaseAtomType)
+               return NULL;
+       return ParameterLookupBondPar(par, t1 % kAtomTypeVariantBase, t2 % kAtomTypeVariantBase, options | kParameterLookupNoBaseAtomType);
+}
+
+AnglePar *
+ParameterLookupAnglePar(Parameter *par, UInt t1, UInt t2, UInt t3, Int options)
+{
+       int i;
+       AnglePar *ap;
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       if (par == NULL)
+               return NULL;
+       if ((options & (kParameterLookupGlobal | kParameterLookupLocal | kParameterLookupMissing)) == 0)
+               options |= kParameterLookupGlobal | kParameterLookupLocal;
+       for (i = par->nanglePars - 1, ap = par->anglePars + i; i >= 0; i--, ap--) {
+               if ((ap->src > 0 && !(options & kParameterLookupGlobal))
+                       || (ap->src == 0 && !(options & kParameterLookupLocal))
+                       || (ap->src < 0 && !(options & kParameterLookupMissing)))
+                       continue;
+               if (s_ParMatch(ap->type1, t1, nowildcard) && s_ParMatch(ap->type2, t2, nowildcard) && s_ParMatch(ap->type3, t3, nowildcard))
+                       return ap;
+               if (s_ParMatch(ap->type1, t3, nowildcard) && s_ParMatch(ap->type2, t2, nowildcard) && s_ParMatch(ap->type3, t1, nowildcard))
+                       return ap;
+       }
+       if (options & kParameterLookupNoBaseAtomType)
+               return NULL;
+       return ParameterLookupAnglePar(par, t1 % kAtomTypeVariantBase, t2 % kAtomTypeVariantBase, t3 % kAtomTypeVariantBase, options | kParameterLookupNoBaseAtomType);
+}
+
+TorsionPar *
+ParameterLookupDihedralPar(Parameter *par, UInt t1, UInt t2, UInt t3, UInt t4, Int options)
+{
+       int i;
+       TorsionPar *tp;
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       if (par == NULL)
+               return NULL;
+       if ((options & (kParameterLookupGlobal | kParameterLookupLocal | kParameterLookupMissing)) == 0)
+               options |= kParameterLookupGlobal | kParameterLookupLocal;
+       for (i = par->ndihedralPars - 1, tp = par->dihedralPars + i; i >= 0; i--, tp--) {
+               if ((tp->src > 0 && !(options & kParameterLookupGlobal))
+                       || (tp->src == 0 && !(options & kParameterLookupLocal))
+                       || (tp->src < 0 && !(options & kParameterLookupMissing)))
+                       continue;
+               if (s_ParMatch(tp->type1, t1, nowildcard) && s_ParMatch(tp->type2, t2, nowildcard) && s_ParMatch(tp->type3, t3, nowildcard) && s_ParMatch(tp->type4, t4, nowildcard))
+                       return tp;
+               if (s_ParMatch(tp->type1, t4, nowildcard) && s_ParMatch(tp->type2, t3, nowildcard) && s_ParMatch(tp->type3, t2, nowildcard) && s_ParMatch(tp->type4, t1, nowildcard))
+                       return tp;
+       }
+       if (options & kParameterLookupNoBaseAtomType)
+               return NULL;
+       return ParameterLookupDihedralPar(par, t1 % kAtomTypeVariantBase, t2 % kAtomTypeVariantBase, t3 % kAtomTypeVariantBase, t4 % kAtomTypeVariantBase, options | kParameterLookupNoBaseAtomType);
+}
+
+TorsionPar *
+ParameterLookupImproperPar(Parameter *par, UInt t1, UInt t2, UInt t3, UInt t4, Int options)
+{
+       int i;
+       TorsionPar *tp;
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       if (par == NULL)
+               return NULL;
+       if ((options & (kParameterLookupGlobal | kParameterLookupLocal | kParameterLookupMissing)) == 0)
+               options |= kParameterLookupGlobal | kParameterLookupLocal;
+       for (i = par->nimproperPars - 1, tp = par->improperPars + i; i >= 0; i--, tp--) {
+               if ((tp->src > 0 && !(options & kParameterLookupGlobal))
+                       || (tp->src == 0 && !(options & kParameterLookupLocal))
+                       || (tp->src < 0 && !(options & kParameterLookupMissing)))
+                       continue;
+               if (!s_ParMatch(tp->type3, t3, nowildcard))
+                       continue;
+               if ((s_ParMatch(tp->type1, t1, nowildcard) && s_ParMatch(tp->type2, t2, nowildcard) && s_ParMatch(tp->type4, t4, nowildcard))
+                       || (s_ParMatch(tp->type1, t1, nowildcard) && s_ParMatch(tp->type2, t4, nowildcard) && s_ParMatch(tp->type4, t2, nowildcard))
+                       || (s_ParMatch(tp->type1, t2, nowildcard) && s_ParMatch(tp->type2, t1, nowildcard) && s_ParMatch(tp->type4, t4, nowildcard))
+                       || (s_ParMatch(tp->type1, t2, nowildcard) && s_ParMatch(tp->type2, t4, nowildcard) && s_ParMatch(tp->type4, t1, nowildcard))
+                       || (s_ParMatch(tp->type1, t4, nowildcard) && s_ParMatch(tp->type2, t1, nowildcard) && s_ParMatch(tp->type4, t2, nowildcard))
+                       || (s_ParMatch(tp->type1, t4, nowildcard) && s_ParMatch(tp->type2, t2, nowildcard) && s_ParMatch(tp->type4, t1, nowildcard)))
+                       return tp;
+       }
+       if (options & kParameterLookupNoBaseAtomType)
+               return NULL;
+       return ParameterLookupImproperPar(par, t1 % kAtomTypeVariantBase, t2 % kAtomTypeVariantBase, t3 % kAtomTypeVariantBase, t4 % kAtomTypeVariantBase, options | kParameterLookupNoBaseAtomType);
+}
+
+VdwPar *
+ParameterLookupVdwPar(Parameter *par, UInt t1, Int options)
+{
+       int i;
+       VdwPar *vp;
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       if (par == NULL)
+               return NULL;
+       if ((options & (kParameterLookupGlobal | kParameterLookupLocal | kParameterLookupMissing)) == 0)
+               options |= kParameterLookupGlobal | kParameterLookupLocal;
+       for (i = par->nvdwPars - 1, vp = par->vdwPars + i; i >= 0; i--, vp--) {
+               if ((vp->src > 0 && !(options & kParameterLookupGlobal))
+                       || (vp->src == 0 && !(options & kParameterLookupLocal))
+                       || (vp->src < 0 && !(options & kParameterLookupMissing)))
+                       continue;
+               if (s_ParMatch(vp->type1, t1, nowildcard))
+                       return vp;
+       }
+       if (options & kParameterLookupNoBaseAtomType)
+               return NULL;
+       return ParameterLookupVdwPar(par, t1 % kAtomTypeVariantBase, options | kParameterLookupNoBaseAtomType);
+}
+
+VdwPairPar *
+ParameterLookupVdwPairPar(Parameter *par, UInt t1, UInt t2, Int options)
+{
+       int i;
+       VdwPairPar *vp;
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       if (par == NULL)
+               return NULL;
+       if ((options & (kParameterLookupGlobal | kParameterLookupLocal | kParameterLookupMissing)) == 0)
+               options |= kParameterLookupGlobal | kParameterLookupLocal;
+       for (i = par->nvdwpPars - 1, vp = par->vdwpPars + i; i >= 0; i--, vp--) {
+               if ((vp->src > 0 && !(options & kParameterLookupGlobal))
+                       || (vp->src == 0 && !(options & kParameterLookupLocal))
+                       || (vp->src < 0 && !(options & kParameterLookupMissing)))
+                       continue;
+               if ((s_ParMatch(vp->type1, t1, nowildcard) && s_ParMatch(vp->type2, t2, nowildcard))
+                       || (s_ParMatch(vp->type1, t2, nowildcard) && s_ParMatch(vp->type2, t1, nowildcard)))
+                       return vp;
+       }
+       if (options & kParameterLookupNoBaseAtomType)
+               return NULL;
+       return ParameterLookupVdwPairPar(par, t1 % kAtomTypeVariantBase, t2 % kAtomTypeVariantBase, options | kParameterLookupNoBaseAtomType);
+}
+
+VdwCutoffPar *
+ParameterLookupVdwCutoffPar(Parameter *par, UInt t1, UInt t2, Int options)
+{
+       int i;
+       VdwCutoffPar *vp;
+       Int nowildcard = (options & kParameterLookupNoWildcard);
+       if (par == NULL)
+               return NULL;
+       if ((options & (kParameterLookupGlobal | kParameterLookupLocal | kParameterLookupMissing)) == 0)
+               options |= kParameterLookupGlobal | kParameterLookupLocal;
+       for (i = par->nvdwCutoffPars - 1, vp = par->vdwCutoffPars + i; i >= 0; i--, vp--) {
+               if ((vp->src > 0 && !(options & kParameterLookupGlobal))
+                       || (vp->src == 0 && !(options & kParameterLookupLocal))
+                       || (vp->src < 0 && !(options & kParameterLookupMissing)))
+                       continue;
+               if (vp->type == 0) {
+                       if (s_ParMatch(vp->n1, t1, nowildcard) && s_ParMatch(vp->n2, t2, nowildcard))
+                               return vp;
+                       if (s_ParMatch(vp->n1, t2, nowildcard) && s_ParMatch(vp->n2, t1, nowildcard))
+                               return vp;
+               } else {
+                       if (vp->n1 <= t1 && vp->n2 >= t1 && vp->n3 <= t2 && vp->n4 <= t2)
+                               return vp;
+                       if (vp->n1 <= t2 && vp->n2 >= t2 && vp->n3 <= t1 && vp->n4 >= t1)
+                               return vp;
+               }
+       }
+       if (options & kParameterLookupNoBaseAtomType)
+               return NULL;
+       return ParameterLookupVdwCutoffPar(par, t1 % kAtomTypeVariantBase, t2 % kAtomTypeVariantBase, options | kParameterLookupNoBaseAtomType);
+}
+
+#pragma mark ====== Table View Support ======
+
+int
+ParameterTableNumberOfRows(Parameter *par)
+{
+       if (par == NULL)
+               return 0;
+       return par->nvdwPars + par->nbondPars + par->nanglePars + par->ndihedralPars + par->nimproperPars + par->nvdwpPars + 6;
+}
+
+int
+ParameterTableGetItemIndex(Parameter *par, int row, int *type)
+{
+       if (par == NULL || row < 0) {
+               *type = kInvalidParType;
+               return -1;
+       }
+       if (--row < par->nvdwPars) {
+               *type = kVdwParType;
+       } else if ((row -= par->nvdwPars + 1) < par->nbondPars) {
+               *type = kBondParType;
+       } else if ((row -= par->nbondPars + 1) < par->nanglePars) {
+               *type = kAngleParType;
+       } else if ((row -= par->nanglePars + 1) < par->ndihedralPars) {
+               *type = kDihedralParType;
+       } else if ((row -= par->ndihedralPars + 1) < par->nimproperPars) {
+               *type = kImproperParType;
+       } else if ((row -= par->nimproperPars + 1) < par->nvdwpPars) {
+               *type = kVdwPairParType;
+       } else {
+               *type = kInvalidParType;
+               return -1;
+       }
+       return row;
+}
+
+UnionPar *
+ParameterTableGetItemPtr(Parameter *par, int row, int *type)
+{
+       if (par == NULL || row < 0) {
+               *type = kInvalidParType;
+               return NULL;
+       }
+       if (--row < par->nvdwPars) {
+               *type = kVdwParType;
+               return (UnionPar *)(row >= 0 ? par->vdwPars + row : NULL);
+       } else if ((row -= par->nvdwPars + 1) < par->nbondPars) {
+               *type = kBondParType;
+               return (UnionPar *)(row >= 0 ? par->bondPars + row : NULL);
+       } else if ((row -= par->nbondPars + 1) < par->nanglePars) {
+               *type = kAngleParType;
+               return (UnionPar *)(row >= 0 ? par->anglePars + row : NULL);
+       } else if ((row -= par->nanglePars + 1) < par->ndihedralPars) {
+               *type = kDihedralParType;
+               return (UnionPar *)(row >= 0 ? par->dihedralPars + row : NULL);
+       } else if ((row -= par->ndihedralPars + 1) < par->nimproperPars) {
+               *type = kImproperParType;
+               return (UnionPar *)(row >= 0 ? par->improperPars + row : NULL);
+       } else if ((row -= par->nimproperPars + 1) < par->nvdwpPars) {
+               *type = kVdwPairParType;
+               return (UnionPar *)(row >= 0 ? par->vdwpPars + row : NULL);
+/*     } else if ((row -= par->nvdwpPars + 1) < par->natomPars) {
+               *type = kAtomParType;
+               return (UnionPar *)(row >= 0 ? par->atomPars + row : NULL); */
+       } else {
+               *type = kInvalidParType;
+               return NULL;
+       }
+}
+
+void
+ParameterTableGetItemText(Parameter *par, int column, int row, char *buf, int bufsize)
+{
+       static char *sBondParTitles[] = {"", "Bonds", "k", "r0"};
+       static char *sAngleParTitles[] = {"", "Angles", "k", "a0"};
+       static char *sDihedralParTitles[] = {"", "Dihedrals", "k", "period", "phi0"};
+       static char *sImproperParTitles[] = {"", "Impropers", "k", "period", "phi0"};
+       static char *sVdwParTitles[] = {"", "VDWs", "eps", "r", "eps14", "r14", "atomNo", "weight"};
+       static char *sVdwPairParTitles[] = {"", "VDW Pairs", "eps", "r", "eps14", "r14"};
+/*     static char *sAtomParTitles[] = {"", "Atom Display", "atomNo", "radius", "red", "green", "blue", "weight"}; */
+       const char *p;
+       int type;
+       UnionPar *up = NULL;
+       char types[4][8];
+       buf[0] = 0;
+       if (par == NULL || row < 0)
+               return;
+       up = ParameterTableGetItemPtr(par, row, &type);
+       switch (type) {
+               case kVdwParType: {
+                       VdwPar *vp = (VdwPar *)up;
+                       if (vp == NULL) {
+                               if (column >= 0 && column < 8)
+                                       snprintf(buf, bufsize, "%s", sVdwParTitles[column]);
+                               return;
+                       }
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "vdw"); break;
+                               case 1:
+                                       AtomTypeDecodeToString(vp->type1, types[0]);
+                                       snprintf(buf, bufsize, "%s", types[0]);
+                                       break;
+                               case 2:
+                                       snprintf(buf, bufsize, "%.5f", vp->eps * INTERNAL2KCAL);
+                                       break;
+                               case 3:
+                                       snprintf(buf, bufsize, "%.5f", vp->r_eq);
+                                       break;
+                               case 4:
+                                       snprintf(buf, bufsize, "%.5f", vp->eps14 * INTERNAL2KCAL);
+                                       break;
+                               case 5:
+                                       snprintf(buf, bufsize, "%.5f", vp->r_eq14);
+                                       break;
+                               case 6:
+                                       snprintf(buf, bufsize, "%d", vp->atomicNumber);
+                                       break;
+                               case 7:
+                                       snprintf(buf, bufsize, "%.3f", vp->weight);
+                                       break;
+                       }
+                       break;
+               }
+               case kBondParType: {
+                       BondPar *bp = (BondPar *)up;
+                       if (bp == NULL) {
+                               if (column >= 0 && column < 4)
+                                       snprintf(buf, bufsize, "%s", sBondParTitles[column]);
+                               return;
+                       }
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "bond"); break;
+                               case 1:
+                                       AtomTypeDecodeToString(bp->type1, types[0]);
+                                       AtomTypeDecodeToString(bp->type2, types[1]);
+                                       snprintf(buf, bufsize, "%s-%s", types[0], types[1]);
+                                       break;
+                               case 2:
+                               case 3:
+                                       snprintf(buf, bufsize, "%.3f", (column == 2 ? bp->k * INTERNAL2KCAL : bp->r0));
+                                       break;
+                       }
+                       break;
+               }
+               case kAngleParType: {
+                       AnglePar *ap = (AnglePar *)up;
+                       if (ap == NULL) {
+                               if (column >= 0 && column < 4)
+                                       snprintf(buf, bufsize, "%s", sAngleParTitles[column]);
+                               return;
+                       }
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "angle"); break;
+                               case 1:
+                                       AtomTypeDecodeToString(ap->type1, types[0]);
+                                       AtomTypeDecodeToString(ap->type2, types[1]);
+                                       AtomTypeDecodeToString(ap->type3, types[2]);
+                                       snprintf(buf, bufsize, "%s-%s-%s", types[0], types[1], types[2]);
+                                       break;
+                               case 2:
+                               case 3:
+                                       snprintf(buf, bufsize, "%.3f", (column == 2 ? ap->k * INTERNAL2KCAL : ap->a0 * kRad2Deg));
+                                       break;
+                       }
+                       break;
+               }
+               case kDihedralParType: {
+                       TorsionPar *tp = (TorsionPar *)up;
+                       if (tp == NULL) {
+                               if (column >= 0 && column < 5)
+                                       snprintf(buf, bufsize, "%s", sDihedralParTitles[column]);
+                               return;
+                       }
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "dihe"); break;
+                               case 1:
+                                       AtomTypeDecodeToString(tp->type1, types[0]);
+                                       AtomTypeDecodeToString(tp->type2, types[1]);
+                                       AtomTypeDecodeToString(tp->type3, types[2]);
+                                       AtomTypeDecodeToString(tp->type4, types[3]);
+                                       snprintf(buf, bufsize, "%s-%s-%s-%s", types[0], types[1], types[2], types[3]);
+                                       break;
+                               case 3:
+                                       snprintf(buf, bufsize, "%d", tp->period[0]);
+                                       break;
+                               case 2:
+                               case 4:
+                                       snprintf(buf, bufsize, "%.3f", (column == 2 ? tp->k[0] * INTERNAL2KCAL : tp->phi0[0] * kRad2Deg));
+                                       break;
+                       }
+                       break;
+               }
+               case kImproperParType: {
+                       TorsionPar *tp = (TorsionPar *)up;
+                       if (tp == NULL) {
+                               if (column >= 0 && column < 5)
+                                       snprintf(buf, bufsize, "%s", sImproperParTitles[column]);
+                               return;
+                       }               
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "impr"); break;
+                               case 1:
+                                       AtomTypeDecodeToString(tp->type1, types[0]);
+                                       AtomTypeDecodeToString(tp->type2, types[1]);
+                                       AtomTypeDecodeToString(tp->type3, types[2]);
+                                       AtomTypeDecodeToString(tp->type4, types[3]);
+                                       snprintf(buf, bufsize, "%s-%s-%s-%s", types[0], types[1], types[2], types[3]);
+                                       break;
+                               case 3:
+                                       snprintf(buf, bufsize, "%d", tp->period[0]);
+                                       break;
+                               case 2:
+                               case 4:
+                                       snprintf(buf, bufsize, "%.3f", (column == 2 ? tp->k[0] * INTERNAL2KCAL : tp->phi0[0] * kRad2Deg));
+                                       break;
+                       }
+                       break;
+               }
+               case kVdwPairParType: {
+                       VdwPairPar *vp = (VdwPairPar *)up;
+                       if (vp == NULL) {
+                               if (column >= 0 && column < 6)
+                                       snprintf(buf, bufsize, "%s", sVdwPairParTitles[column]);
+                               return;
+                       }
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "pvdw"); break;
+                               case 1:
+                                       AtomTypeDecodeToString(vp->type1, types[0]);
+                                       AtomTypeDecodeToString(vp->type2, types[1]);
+                                       snprintf(buf, bufsize, "%s-%s", types[0], types[1]);
+                                       break;
+                               case 2:
+                                       snprintf(buf, bufsize, "%.6f", vp->eps * INTERNAL2KCAL);
+                                       break;
+                               case 3:
+                                       snprintf(buf, bufsize, "%.6f", vp->r_eq);
+                                       break;
+                               case 4:
+                                       snprintf(buf, bufsize, "%.6f", (vp->A14 == 0.0 ? 0.0 : vp->B14 * vp->B14 / vp->A14 * 0.25 * INTERNAL2KCAL));
+                                       break;
+                               case 5:
+                                       snprintf(buf, bufsize, "%.6f", vp->eps14 * INTERNAL2KCAL);
+                                       break;
+                       }
+                       break;
+               }
+/*             case kAtomParType: {
+                       AtomPar *ap = (AtomPar *)up;
+                       if (ap == NULL) {
+                               if (column >= 0 && column < 8)
+                                       snprintf(buf, bufsize, "%s", sAtomParTitles[column]);
+                               return;
+                       }
+                       switch (column) {
+                               case 0: snprintf(buf, bufsize, "disp"); break;
+                               case 1: snprintf(buf, bufsize, "%.4s", ap->name); break;
+                               case 2: snprintf(buf, bufsize, "%d", ap->number); break;
+                               case 3: snprintf(buf, bufsize, "%.2f", ap->radius); break;
+                               case 4: snprintf(buf, bufsize, "%.3f", ap->r); break;
+                               case 5: snprintf(buf, bufsize, "%.3f", ap->g); break;
+                               case 6: snprintf(buf, bufsize, "%.3f", ap->b); break;
+                               case 7: snprintf(buf, bufsize, "%.3f", ap->weight); break;
+                       }
+                       break;
+               } */
+               default: return;
+       }
+       if (up != NULL && (column == 8 || column == 9)) {
+               if (column == 8 && ((BondPar *)up)->src == -1)
+                       snprintf(buf, bufsize, "!NONE!");
+               else if ((p = ParameterGetComment(column == 8 ? ((BondPar *)up)->src : ((BondPar *)up)->com)) != NULL)
+                       snprintf(buf, bufsize, "%s", p);
+       }
+}
+
+/*  Return values:
+    -3: invalid
+    -2: separator item
+    -1: missing parameter
+     0: molecule-local parameter
+     1 and larger: global parameter values (gGlobalParInfo index + 1)  */
+int
+ParameterTableGetItemSource(Parameter *par, int row)
+{
+       int src, type;
+       UnionPar *up;
+       if (par == NULL || row < 0)
+               return -3;
+       up = ParameterTableGetItemPtr(par, row, &type);
+       src = (type == kInvalidParType ? -3 : (up == NULL ? -2 : ((BondPar *)up)->src));
+       if (type == kInvalidParType)
+               src = -3;
+       else if (up == NULL)
+               src = -2;
+       else src = ((BondPar *)up)->src;
+       if (src > 0) {
+               /*  Search src in gGlobalParInfo  */
+               int i;
+               for (i = 0; i < gGlobalParInfo.count; i++) {
+                       if (gGlobalParInfo.files[i].src == src)
+                               return i + 1;
+               }
+               return -3;  /*  Must not happen  */
+       }
+       return src;
+}
+
+int
+ParameterTableIsItemEditable(Parameter *par, int column, int row)
+{
+       UnionPar *up;
+       int type, f;
+       if (par == NULL || row < 0)
+               return 0;
+       up = ParameterTableGetItemPtr(par, row, &type);
+       if (type != kInvalidParType && up != NULL) {
+               /*  Valid type, not separator row, molecule-local value  */
+               int src = ((BondPar *)up)->src;
+               f = (src == 0 || src == -1);
+       } else f = 0;
+       switch (type) {
+               case kVdwParType: return (f && column > 0 && column < 8);
+               case kBondParType: return (f && column > 0 && column < 4);
+               case kAngleParType: return (f && column > 0 && column < 4);
+               case kDihedralParType: return (f && column > 0 && column < 5);
+               case kImproperParType: return (f && column > 0 && column < 5);
+               case kVdwPairParType: return (f && column > 0 && column < 5);
+       /*      case kAtomParType: return (f && column > 0 && column < 7); */
+               default: return 0;
+       }
+}
+
+#pragma mark ====== Utility Functions ======
+
+int
+AtomParameterInitialize(const char *fname, char **outWarningMessage)
+{
+       char buf[1024], name[6], fullname[16];
+       float val[6];
+       FILE *fp = NULL;
+       int i, lineNumber, retval = 0;
+       char *wbuf = NULL;
+
+       fp = fopen(fname, "rb");
+       if (fp == NULL) {
+               retval = -1;
+               goto exit;
+       }
+       lineNumber = 0;
+       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+               AtomPar *ap;
+               if (strncmp(buf, "dispatom ", 9) != 0)
+                       continue;  /*  Skip non-relevant lines  */
+               fullname[0] = 0;
+               if (sscanf(buf + 9, " %4s %f %f %f %f %f %f %15s", name, &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], fullname) < 7) {
+                       asprintf(&wbuf, "%s:%d: missing parameter in DISPATOM record", fname, lineNumber);
+                       retval = 1;
+                       goto exit;
+               }
+               i = (int)val[0];
+               if (i < 0 || i >= 200) {
+                       asprintf(&wbuf, "%s:%d: The atomic number (%d) in DISPATOM record is out of range", fname, lineNumber, i);
+                       retval = 2;
+                       goto exit;
+               }
+               ap = AssignArray(&gDispAtomParameters, &gCountDispAtomParameters, sizeof(AtomPar), i, NULL);
+               memmove(ap->name, name, 4);
+               ap->number = i;
+               ap->radius = val[1];
+               ap->r = val[2];
+               ap->g = val[3];
+               ap->b = val[4];
+               ap->weight = val[5];
+               fullname[15] = 0;
+               memmove(ap->fullname, fullname, 16);
+       }
+exit:
+       if (fp != NULL)
+               fclose(fp);
+       if (outWarningMessage != NULL)
+               *outWarningMessage = wbuf;
+       return retval;
+}
+
+UInt
+AtomTypeEncodeToUInt(const char *s)
+{
+       Int i;
+       UInt n, t;
+       if ((s[0] == 'x' || s[0] == 'X') && s[1] == 0)
+               return kAtomTypeWildcard;
+       if (s[0] >= '0' && s[0] <= '9')
+               return atoi(s);
+       for (i = t = 0; i < 4; i++, s++) {
+               /*  Encode: variant*96*96*96*96 + a[0]*96*96*96 + a[1]*96*96 + a[2] * 96 + a[3]  */
+               static const UInt s_coeff[4] = {96*96*96, 96*96, 96, 1};
+               while (*s == ' ')
+                       s++;
+               if (*s == 0)
+                       break;
+               if (*s == ',' || *s == '.' || *s == ';' || *s == ':') {
+                       /*  Variant (only [0-9a-z] are allowed) */
+                       s++;
+                       if (*s >= '0' && *s <= '9')
+                               n = *s - '0' + 1;
+                       else if (*s >= 'A' && *s <= 'Z')
+                               n = *s - 'A' + 11;
+                       else if (*s >= 'a' && *s <= 'z')
+                               n = *s - 'a' + 11;
+                       else n = (*s % 36) + 1;  /*  Map to something non-zero  */
+                       t += n * (96*96*96*96);
+                       break;
+               }
+               n = (*s - 0x20) % 96;  /*  Map to 1..95  */
+               if (i == 0 && n < 32)
+                       n = 32;
+               t += n * s_coeff[i];
+       }
+       return t;
+}
+
+char *
+AtomTypeDecodeToString(UInt type, char *s)
+{
+       static const UInt s_coeff[4] = {96*96*96, 96*96, 96, 1};
+       static char buf[8];
+       int i;
+       UInt variant, n;
+       if (s == NULL) {
+               s = buf;
+               buf[6] = 0;
+       }
+       if (type == kAtomTypeWildcard) {
+               s[0] = 'X';
+               s[1] = 0;
+               return s;
+       }
+       if (type < kAtomTypeMinimum) {
+               snprintf(s, 6, "%d", type);
+               return s;
+       }
+       for (i = 0; i < 4; i++) {
+               s[i] = (type / s_coeff[i]) % 96;
+               if (s[i] != 0)
+                       s[i] += 0x20;
+       }
+       s[4] = 0;
+       n = strlen(s);
+       if ((variant = (type / (96*96*96*96)) % 96) != 0) {
+               s[n] = '.';
+               s[n + 1] = (variant <= 10 ? '0' + variant - 1 : 'a' + variant - 11);
+               s[n + 2] = 0;
+       }
+       return s;
+}
+
+Int
+ElementToInt(const char *s)
+{
+       int i;
+       AtomPar *p;
+       for (i = 0, p = gDispAtomParameters; i < gCountDispAtomParameters; i++, p++) {
+               if (p->name[0] == toupper(s[0]) && p->name[1] == tolower(s[1]))
+                       return p->number;
+       }
+       return 0;
+}
+
+char *
+ElementToString(Int elem, char *s)
+{
+       if (elem >= 0 && elem < gCountDispAtomParameters) {
+               const char *cs = gDispAtomParameters[elem].name;
+               s[0] = cs[0];
+               s[1] = cs[1];
+               s[2] = 0;
+               return s;
+       } else return NULL;
+}
+
+Int
+AtomNameToElement(const char *s)
+{
+       char element[4];
+       const char *p;
+       /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
+       element[0] = 0;
+       for (p = s; *p != 0; p++) {
+               if (isalpha(*p) && *p != '_') {
+                       element[0] = toupper(*p);
+                       if (isalpha(p[1]) && p[1] != '_') {
+                               element[1] = toupper(p[1]);
+                               element[2] = 0;
+                       } else {
+                               element[1] = 0;
+                       }
+                       break;
+               }
+       }
+       return ElementToInt(element);
+}
+
+Int
+GuessAtomicNumber(const char *name, Double weight)
+{
+       int i;
+       AtomPar *p;
+       char buf[4];
+       const char *cp;
+       for (i = 0, p = gDispAtomParameters; i < gCountDispAtomParameters; i++, p++) {
+               if (p->weight > 0.0 && fabs(weight - p->weight) < 0.1)
+                       return p->number;
+       }
+       buf[0] = 0;
+       for (cp = name; *cp != 0 && cp < name + 4; cp++) {
+               if (isalpha(*cp) && *cp != '_') {
+                       buf[0] = toupper(*cp);
+                       if (isalpha(cp[1]) && cp[1] != '_') {
+                               buf[1] = toupper(cp[1]);
+                               buf[2] = 0;
+                       } else {
+                               buf[1] = 0;
+                       }
+                       break;
+               }
+       }
+       return ElementToInt(buf);
+}
+
+Double
+WeightForAtomicNumber(Int elem)
+{
+       if (elem >= 1 && elem < gCountDispAtomParameters)
+               return gDispAtomParameters[elem].weight;
+       else return 0.0;
+}
+
+Double
+RadiusForAtomicNumber(Int elem)
+{
+       if (elem >= 1 && elem < gCountDispAtomParameters)
+               return gDispAtomParameters[elem].radius;
+       else return 0.0;
+}
+
diff --git a/MolLib/Parameter.h b/MolLib/Parameter.h
new file mode 100755 (executable)
index 0000000..8b1728d
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ *  Parameter.h
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __Parameter_h__
+#define __Parameter_h__
+
+#include "Types.h"
+#include "Object.h"
+#include <stdio.h> /* for FILE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*  Information record for global parameter files  */
+typedef struct ParFileInfo {
+       char *name;   /* basename */
+       char *dir;    /* directory */
+       int src;      /* comment index */
+} ParFileInfo;
+
+typedef struct GlobalParInfoRecord {
+       Int count;
+       Int builtinCount;  /*  Number of 'built-in' global parameter files  */
+       ParFileInfo *files;
+} GlobalParInfoRecord;
+
+extern struct GlobalParInfoRecord gGlobalParInfo;
+       
+/*  Parameter types  */
+enum {
+       kInvalidParType,
+       kBondParType, kAngleParType, kDihedralParType, kImproperParType,
+       kVdwParType, kVdwPairParType, kVdwCutoffParType,
+       kFirstParType = kBondParType,
+       kLastParType = kVdwCutoffParType,
+       kAtomParType = 100  /*  Only used in rb_cParameterRef  */
+};
+       
+/*  Parameter Lookup options  */
+enum {
+       kParameterLookupNoWildcard = 1,
+       kParameterLookupGlobal = 2,
+       kParameterLookupLocal = 4,
+       kParameterLookupMissing = 8,
+       kParameterLookupNoBaseAtomType = 16
+};
+
+/*  The atom type is a 32-bit signed integer; a "variant" number and 4 ascii characters are
+    encoded as: variant*96*96*96*96 + a[0]*96*96*96 + a[1]*96*96 + a[2]*96 + a[3].
+    variant is [0-9a-z] mapped to [1..36].
+    a[n] (n = 0..3) is a printable ASCII character mapped to [1..95].
+    The first character must be no smaller than '@'.  */
+
+#define kAtomTypeMinimum 28311552  /* 32*96*96*96 */
+#define kAtomTypeWildcard 0x7fffffff
+#define kAtomTypeVariantBase 84934656  /* 96*96*96*96 */
+
+/*  Parameters  */
+typedef struct BondPar {
+       Int    com, src;   /*  Index to the comment array  */
+       UInt    type1, type2;
+       Double k, r0;
+} BondPar;
+
+typedef struct AnglePar {
+       Int    com, src;   /*  Index to the comment array  */
+       UInt    type1, type2, type3;
+       Double k, a0;
+} AnglePar;
+
+typedef struct TorsionPar {
+       Int    com, src;   /*  Index to the comment array  */
+       UInt    type1, type2, type3, type4;
+       Int    mult;  /*  The multiple term for CHARMm parameter sets  */
+       Double k[3];
+       Int    period[3];
+       Double phi0[3];
+} TorsionPar;
+
+typedef struct VdwPar {
+       Int    com, src;   /*  Index to the comment array  */
+       UInt    type1;
+       Int    atomicNumber;
+       Double A, B;  /* A = 4*(sigma**12)*eps = ((2*r_eq)**12)*eps, B = 4*(sigma**6)*eps = 2*((2*r_eq)**6)*eps, sigma = (2*r_eq)*((1/2)**(1/6)), where r_eq is a van der Waals radius (not interatomic distance at the lowest energy!)  */
+       Double eps, r_eq;   /*  These are redundant, but useful from the user's point of view  */
+       Double A14, B14;
+       Double eps14, r_eq14;
+       Double weight; /*  Atomic weight  */
+       Double polarizability;  /*  Not used at present  */
+} VdwPar;
+
+typedef struct VdwPairPar {
+       Int    com, src;   /*  Index to the comment array  */
+       UInt    type1, type2;
+       Double A, B;  /* A = 4*(sigma**12)*eps = ((2*r_eq)**12)*eps, B = 4*(sigma**6)*eps = 2*((2*r_eq)**6)*eps, sigma = (2*r_eq)*((1/2)**(1/6))  */
+       Double eps, r_eq;   /*  These are redundant, but useful from the user's point of view  */
+       Double A14, B14;
+       Double eps14, r_eq14;
+} VdwPairPar;
+
+typedef struct VdwCutoffPar {
+       Int    com, src;   /*  Index to the comment array  */
+       Byte   type;       /* 0: atom type specific, 1: atom number specific */
+       UInt    n1, n2, n3, n4;  /*  atom type specific: n1 and n2 are atom types  */
+                               /*  atom number specific: (n1,n2) and (n3,n4) are atom ranges */
+                                                       /*  (n1<=n2, n3<=n4, including the bounds)  */
+       Double cutoff;
+} VdwCutoffPar;
+
+/*  Display parameters (defined for elements)  */
+typedef struct AtomPar {
+       Int    com, src;   /*  Index to the comment array  */
+       Int    number;   /*  Atomic number  */
+       char   name[4];
+       Double radius;
+       Double r, g, b;  /*  Color: [0.0, 1.0] for each component  */
+       Double weight;
+       char   fullname[16];
+} AtomPar;
+       
+typedef struct Parameter {
+       Object base;
+       Int    nbondPars;
+       BondPar *bondPars;
+       Int    nanglePars;
+       AnglePar *anglePars;
+       Int    ndihedralPars;
+       TorsionPar *dihedralPars;
+       Int    nimproperPars;
+       TorsionPar *improperPars;
+       Int    nvdwPars;
+       VdwPar *vdwPars;
+       Int    nvdwpPars;
+       VdwPairPar *vdwpPars;
+       Int    nvdwCutoffPars;
+       VdwCutoffPar *vdwCutoffPars;
+/*     Int    natomPars;
+       AtomPar *atomPars;
+       Int    ncomments;
+       char **comments;  */
+} Parameter;
+
+/*  Parameter reference (for MolAction and Ruby types)  */
+typedef struct ParameterRef {
+       struct Molecule *mol;  /*  If non-NULL, points to mol->par; otherwise, points to gBuiltinParameters  */
+       Int parType;
+       Int idx;
+} ParameterRef;
+
+/*  For return value for parameter pointer  */
+typedef union UnionPar {
+       BondPar bond;
+       AnglePar angle;
+       TorsionPar torsion;
+       VdwPar vdw;
+       VdwPairPar vdwp;
+       VdwCutoffPar vdwcutoff;
+       AtomPar atom;
+} UnionPar;
+
+Parameter *ParameterNew(void);
+Parameter *ParameterDuplicate(const Parameter *par);
+Parameter *ParameterWithName(const char *name);
+void ParameterSetName(Parameter *par, const char *name);
+const char *ParameterGetName(Parameter *par);
+void ParameterRetain(Parameter *par);
+void ParameterRelease(Parameter *par);
+int ParameterReadFromFile(Parameter *par, const char *fname, char **outWarningMessage, int *outWarningCount);
+int ParameterReadFromString(Parameter *par, char *buf, char **wbufp, const char *fname, int lineNumber, int src_idx);
+int ParameterAppendToFile(Parameter *par, FILE *fp);
+int ParameterDeleteAllEntriesForSource(Parameter *par, int src_idx);
+
+int ParameterGlobalParIndexForSrcIndex(int src);
+int ParameterCommentIndexForGlobalFileName(const char *p);
+int ParameterCommentIndex(const char *comment);
+const char *ParameterGetComment(int idx);
+       
+UnionPar *ParameterGetUnionParFromTypeAndIndex(Parameter *par, int type, int index);
+Int ParameterGetCountForType(Parameter *par, int type);
+Int ParameterGetSizeForType(int type);
+       
+ParameterRef *ParameterRefNew(struct Molecule *mol, int type, int idx);
+void ParameterRefRelease(ParameterRef *pref);
+UnionPar *ParameterRefGetPar(ParameterRef *pref);
+       
+struct IntGroup;  /*  forward declaration  */
+int ParameterInsert(Parameter *par, Int type, const UnionPar *up, struct IntGroup *where);
+int ParameterDelete(Parameter *par, Int type, UnionPar *up, struct IntGroup *where);
+int ParameterCopy(Parameter *par, Int type, UnionPar *up, struct IntGroup *where);
+
+int ParameterRenumberAtoms(Int type, UnionPar *up, Int oldnatoms, const Int *old2new);
+int ParameterDoesContainAtom(Int type, UnionPar *up, UInt atom_type, Int options);
+
+BondPar *ParameterLookupBondPar(Parameter *par, UInt t1, UInt t2, Int options);
+AnglePar *ParameterLookupAnglePar(Parameter *par, UInt t1, UInt t2, UInt t3, Int options);
+TorsionPar *ParameterLookupDihedralPar(Parameter *par, UInt t1, UInt t2, UInt t3, UInt t4, Int options);
+TorsionPar *ParameterLookupImproperPar(Parameter *par, UInt t1, UInt t2, UInt t3, UInt t4, Int options);
+VdwPar *ParameterLookupVdwPar(Parameter *par, UInt t1, Int options);
+VdwPairPar *ParameterLookupVdwPairPar(Parameter *par, UInt t1, UInt t2, Int options);
+VdwCutoffPar *ParameterLookupVdwCutoffPar(Parameter *par, UInt t1, UInt t2, Int options);
+
+int AtomParameterInitialize(const char *fname, char **outWarningMessage);
+UInt AtomTypeEncodeToUInt(const char *s);
+char *AtomTypeDecodeToString(UInt type, char *s);
+Int ElementToInt(const char *s);
+char *ElementToString(Int type, char *s);
+Int AtomNameToElement(const char *s);
+
+Int GuessAtomicNumber(const char *name, Double weight);
+Double WeightForAtomicNumber(Int elem);
+Double RadiusForAtomicNumber(Int elem);
+       
+int ParameterTableNumberOfRows(Parameter *par);
+int ParameterTableGetItemIndex(Parameter *par, int row, int *type);
+UnionPar *ParameterTableGetItemPtr(Parameter *par, int row, int *type);
+void ParameterTableGetItemText(Parameter *par, int column, int row, char *buf, int bufsize);
+int ParameterTableGetItemSource(Parameter *par, int row);
+int ParameterTableIsItemEditable(Parameter *par, int column, int row);
+
+extern Parameter *gBuiltinParameters;
+extern AtomPar *gDispAtomParameters;
+extern Int gCountDispAtomParameters;
+       
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __Parameter_h__ */
diff --git a/MolLib/Ruby_bind/Makefile b/MolLib/Ruby_bind/Makefile
new file mode 100755 (executable)
index 0000000..bc6cff8
--- /dev/null
@@ -0,0 +1,20 @@
+ifdef DESTDIR
+OBJDIR = $(DESTDIR)/MolLib
+else
+DESTDIR = .
+OBJDIR = .
+endif
+
+OBJECTS = ruby_types.o ruby_bind.o ruby_dialog.o ruby_md.o
+ALL_OBJECTS = $(addprefix $(OBJDIR)/,$(OBJECTS))
+
+$(OBJDIR)/%.o: %.c
+       $(CC) -c $< -o $@ $(CFLAGS)
+
+$(DESTDIR)/Ruby_bind.a : $(ALL_OBJECTS)
+       $(AR) $(ARFLAGS) $(DESTDIR)/Ruby_bind.a $(ALL_OBJECTS)
+       ranlib $(DESTDIR)/Ruby_bind.a
+
+clean:
+       rm -f $(OBJDIR)/*.o
+       rm -f $(DESTDIR)/*.a
diff --git a/MolLib/Ruby_bind/Molby.h b/MolLib/Ruby_bind/Molby.h
new file mode 100755 (executable)
index 0000000..cb51775
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *  Molby.h
+ *
+ *  Created by Toshi Nagata on 2005/06/03.
+ *  Copyright 2005-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __Molby_h__
+#define __Molby_h__
+
+#include <ruby.h>
+#include "../MolLib.h"
+
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(_s) (RSTRING(_s)->ptr)
+#endif
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(_s) (RSTRING(_s)->len)
+#endif
+
+#ifndef RARRAY_PTR
+#define RARRAY_PTR(_a) (RARRAY(_a)->ptr)
+#endif
+#ifndef RARRAY_LEN
+#define RARRAY_LEN(_a) (RARRAY(_a)->len)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*extern VALUE Ruby_IncrementInterruptLevel(VALUE dummy);
+extern VALUE Ruby_DecrementInterruptLevel(VALUE dummy); */
+
+/*extern VALUE Ruby_EnableInterrupt(VALUE dummy);
+extern VALUE Ruby_DisableInterrupt(VALUE dummy); */
+
+/*  Flag to avoid multiple running ruby interpreter  */
+extern int gMolbyRunLevel;
+extern int gMolbyIsCheckingInterrupt;
+extern VALUE gMolbyBacktrace;
+
+extern VALUE Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status);
+extern VALUE Ruby_GetInterruptFlag(void);
+extern VALUE Ruby_SetInterruptFlag(VALUE val);
+
+extern VALUE Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status);
+extern VALUE Ruby_ObjectAtIndex(VALUE ary, int idx);
+       
+extern VALUE rb_eMolbyError;
+extern VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef, rb_cIntGroup;
+extern VALUE rb_cVector3D, rb_cTransform;
+extern VALUE rb_cMDArena;
+       
+extern void Init_MolbyTypes(void);
+extern void Init_MolbyMDTypes(void);
+       
+extern VALUE ValueFromMolecule(Molecule *mol);
+extern VALUE ValueFromIntGroup(IntGroup *ig);
+extern VALUE ValueFromVector(const Vector *vp);
+extern VALUE ValueFromTransform(Transform *tp);
+
+extern void VectorFromValue(VALUE val, Vector *vp);
+extern void TransformFromValue(VALUE val, Transform *tp);
+extern IntGroup *IntGroupFromValue(VALUE val);
+extern Molecule *MoleculeFromValue(VALUE val);
+
+extern struct MDArena *MDArenaFromValue(VALUE val);
+extern VALUE ValueFromMDArena(struct MDArena *arena);
+
+extern void IntGroup_RaiseIfError(int err);
+extern VALUE IntGroup_Alloc(VALUE klass);
+extern VALUE ValueFromVector(const Vector *vp);
+
+#define FileStringValuePtr(val) Ruby_FileStringValuePtr(&val)
+extern char *Ruby_FileStringValuePtr(VALUE *valp);
+extern VALUE Ruby_NewFileStringValue(const char *fstr);
+       
+/*
+STUB VALUE MyAppCallback_getGlobalSettings(const char *key);
+STUB void MyAppCallback_setGlobalSettings(const char *key, VALUE value);
+STUB int MyAppCallback_showScriptMessage(const char *fmt, ...);
+STUB void MyAppCallback_showScriptError(int status);
+STUB int MyAppCallback_checkInterrupt(void);
+STUB void MyAppCallback_showProgressPanel(const char *msg);
+STUB void MyAppCallback_hideProgressPanel(void);
+STUB void MyAppCallback_setProgressValue(double dval);
+STUB void MyAppCallback_setProgressMessage(const char *msg);
+STUB int MyAppCallback_processUIWithTimeout(double seconds);
+STUB int MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize);
+STUB void MyAppCallback_registerScriptMenu(const char *cmd, const char *title);
+*/
+
+#ifdef __cplusplus
+}
+#endif
+               
+#include "Molby_extern.h"
+
+#endif /* __Molby_h__ */
diff --git a/MolLib/Ruby_bind/Molby_extern.h b/MolLib/Ruby_bind/Molby_extern.h
new file mode 100755 (executable)
index 0000000..03c3a61
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  Molby_extern.h
+ *
+ *  Created by Toshi Nagata on 2008/11/05.
+ *  Copyright 2005-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __Molby_extern_h__
+#define __Molby_extern_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../MolLib.h"
+
+/*  This definition is to work around 'VALUE' type in sources without "ruby.h"  */
+typedef void *RubyValue;
+       
+extern char *gRubyVersion;
+extern char *gRubyCopyright;
+
+extern void Molby_startup(const char *script_path, const char *dir);
+extern void Molby_showError(int status);
+extern RubyValue Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, int *status);
+/* extern RubyValue Molby_evalRubyScript(const char *script, int *status);
+extern RubyValue Molby_evalRubyScriptOnActiveMoleculeWithInterrupt(const char *script, int *status); */
+extern void Molby_showRubyValue(RubyValue value);
+extern int Ruby_methodType(const char *className, const char *methodName);
+
+STUB char *MyAppCallback_getGlobalSettings(const char *key);
+STUB void MyAppCallback_setGlobalSettings(const char *key, const char *value);
+STUB int MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr);
+STUB int MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr);
+STUB int MyAppCallback_showScriptMessage(const char *fmt, ...);
+STUB void MyAppCallback_setConsoleColor(int color);
+STUB void MyAppCallback_showRubyPrompt(void);
+STUB int MyAppCallback_checkInterrupt(void);
+STUB void MyAppCallback_showProgressPanel(const char *msg);
+STUB void MyAppCallback_hideProgressPanel(void);
+STUB void MyAppCallback_setProgressValue(double dval);
+STUB void MyAppCallback_setProgressMessage(const char *msg);
+//STUB int MyAppCallback_processUIWithTimeout(double seconds);
+STUB int MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize);
+STUB int MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon);
+STUB void MyAppCallback_errorMessageBox(const char *fmt, ...);
+STUB char *MyAppCallback_getHomeDir(void);
+STUB char *MyAppCallback_getDocumentHomeDir(void);
+STUB void MyAppCallback_registerScriptMenu(const char *cmd, const char *title);
+STUB RubyValue MyAppCallback_executeScriptFromFile(const char *path, int *status);
+STUB int MyAppCallback_callSubProcess(const char *cmdline, const char *procname);
+STUB void MyAppCallback_beginUndoGrouping(void);
+STUB void MyAppCallback_endUndoGrouping(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __Molby_h__ */
diff --git a/MolLib/Ruby_bind/ruby_bind.c b/MolLib/Ruby_bind/ruby_bind.c
new file mode 100644 (file)
index 0000000..726aaf2
--- /dev/null
@@ -0,0 +1,8129 @@
+/*
+ *  ruby_bind.c
+ *  Ruby binding
+ *
+ *  Created by Toshi Nagata on 07/11/09.
+ *  Copyright 2007-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+*/
+
+#include "Molby.h"
+#include "ruby_dialog.h"
+#include "../MD/MDCore.h"
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include <version.h>  /*  for Ruby version  */
+#include <node.h>     /*  for rb_add_event_hook()  */
+
+#if __WXMAC__
+#include <signal.h>   /*  for sigaction()  */
+#endif
+
+#include "../Missing.h"
+
+#pragma mark ====== Global Values ======
+
+VALUE rb_eMolbyError;
+VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
+VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
+
+VALUE gMolbyBacktrace;
+
+int gMolbyRunLevel = 0;
+int gMolbyIsCheckingInterrupt = 0;
+
+char *gRubyVersion, *gRubyCopyright;
+
+/*  This library depends on matrix library  */
+/* static VALUE rb_cVector; */
+
+/*  Standard output object (to show message in the window)  */
+static VALUE rb_cMessageOutput;
+/* static VALUE sStdout; */
+
+/*  For convenience  */
+static VALUE s_ID_equal;  /*  rb_intern("==")  */
+
+/*  Symbols for atom attributes  */
+static VALUE
+       s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
+       s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
+       s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
+       s_RSym, s_XSym, s_YSym, s_ZSym,
+       s_CrSym, s_CxSym, s_CySym, s_CzSym,
+       s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
+       s_AnisoSym, s_IntChargeSym, s_FixForceSym, s_FixPosSym;
+
+/*  Symbols for parameter attributes  */
+static VALUE
+       s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
+       s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
+       s_ASym, s_BSym, s_ReqSym, s_EpsSym,
+       s_A14Sym, s_B14Sym, s_Req14Sym, s_Eps14Sym,
+       s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
+       s_CommentSym, s_SourceSym;
+
+/*
+ *  Utility function
+ *  Get ary[i] by calling "[]" method
+ */
+VALUE
+Ruby_ObjectAtIndex(VALUE ary, int idx)
+{
+       static ID index_method = 0;
+       if (TYPE(ary) == T_ARRAY) {
+               int len = RARRAY_LEN(ary);
+               if (idx >= 0 && idx < len)
+                       return (RARRAY_PTR(ary))[idx];
+               else return Qnil;
+       }
+       if (index_method == 0)
+               index_method = rb_intern("[]");
+       return rb_funcall(ary, index_method, 1, INT2NUM(idx));
+}
+
+char *
+Ruby_FileStringValuePtr(VALUE *valp)
+{
+#if __WXMSW__
+       char *p = strdup(StringValuePtr(*valp));
+       translate_char(p, '/', '\\');
+       *valp = rb_str_new2(p);
+       free(p);
+       return StringValuePtr(*valp);
+#else
+       return StringValuePtr(*valp);
+#endif
+}
+
+VALUE
+Ruby_NewFileStringValue(const char *fstr)
+{
+#if __WXMSW__
+       VALUE retval;
+       char *p = strdup(fstr);
+       translate_char(p, '\\', '/');
+       retval = rb_str_new2(p);
+       free(p);
+       return retval;
+#else
+       return rb_str_new2(fstr);
+#endif
+}
+
+#pragma mark ====== Message output ======
+
+extern int MyAppControllerAppendMessage(const char *p);
+extern int MyAppControllerCheckInterrupt(void);
+extern int MyAppControllerGetTextWithPrompt(const char *prompt, char *buf, int bufsize);
+
+/*
+ *  call-seq:
+ *     MessageOutput.write(str)
+ *
+ *  Put the message in the main text view.
+ */
+static VALUE
+s_MessageOutput_Write(VALUE self, VALUE str)
+{
+       int n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Kernel.message_box(str, title, button = nil, icon = :info)
+ *
+ *  Show a message box.
+ *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
+ *  Icon: :info, :warning, :error
+ */
+static VALUE
+s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
+{
+       char *str, *title, *s;
+       int buttons, icon;
+       VALUE sval, tval, bval, ival;
+       rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
+       str = StringValuePtr(sval);
+       title = StringValuePtr(tval);
+       if (bval != Qnil) {
+               bval = rb_str_to_str(bval);
+               s = RSTRING_PTR(bval);
+               if (strncmp(s, "ok", 2) == 0)
+                       buttons = 1;
+               else if (strncmp(s, "cancel", 6) == 0)
+                       buttons = 2;
+               else
+                       rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
+       } else buttons = 3;
+       if (ival != Qnil) {
+               ival = rb_str_to_str(ival);
+               s = RSTRING_PTR(ival);
+               if (strncmp(s, "info", 4) == 0)
+                       icon = 1;
+               else if (strncmp(s, "warn", 4) == 0)
+                       icon = 2;
+               else if (strncmp(s, "err", 3) == 0)
+                       icon = 3;
+               else
+                       rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
+       } else icon = 1;
+       MyAppCallback_messageBox(str, title, buttons, icon);
+       return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     Kernel.error_message_box(str)
+ *
+ *  Show an error message box.
+ */
+static VALUE
+s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
+{
+       char *str = StringValuePtr(sval);
+       MyAppCallback_errorMessageBox("%s", str);
+       return Qnil;
+}
+
+#pragma mark ====== Track key events ======
+
+/*  User interrupt handling
+ *  User interrupt (command-period on Mac OS) is handled by periodic polling of
+ *  key events. This polling should only be enabled during "normal" execution
+ *  of scripts and must be disabled when the rest of the application (or Ruby
+ *  script itself) is handling GUI. This is ensured by appropriate calls to
+ *  enable_interrupt and disable_interrupt.  */
+
+static VALUE s_interrupt_flag = Qfalse;
+
+static VALUE
+s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
+{
+       volatile VALUE message;
+       const char *p;
+       if (Ruby_GetInterruptFlag() == Qtrue) {
+               rb_scan_args(argc, argv, "01", &message);
+               if (message != Qnil)
+                       p = StringValuePtr(message);
+               else
+                       p = NULL;
+               MyAppCallback_showProgressPanel(p);
+       }
+       return Qnil;
+}
+
+static VALUE
+s_HideProgressPanel(VALUE self)
+{
+       MyAppCallback_hideProgressPanel();
+       return Qnil;
+}
+
+static VALUE
+s_SetProgressValue(VALUE self, VALUE val)
+{
+       double dval = NUM2DBL(rb_Float(val));
+       MyAppCallback_setProgressValue(dval);
+       return Qnil;
+}
+
+static VALUE
+s_SetProgressMessage(VALUE self, VALUE msg)
+{
+       const char *p;
+       if (msg == Qnil)
+               p = NULL;
+       else p = StringValuePtr(msg);
+       MyAppCallback_setProgressMessage(p);
+       return Qnil;
+}
+
+static VALUE
+s_SetInterruptFlag(VALUE self, VALUE val)
+{
+       VALUE oldval;
+       if (val != Qundef) {
+               if (val == Qfalse || val == Qnil)
+                       val = Qfalse;
+               else val = Qtrue;
+       }
+       oldval = s_interrupt_flag;
+       if (val != Qundef) {
+               s_interrupt_flag = val;
+               if (val == Qfalse) {
+                       s_HideProgressPanel(self);
+               }
+       }
+       return oldval;
+}
+
+static VALUE
+s_GetInterruptFlag(VALUE self)
+{
+       return s_SetInterruptFlag(self, Qundef);
+}
+
+#if 0
+static VALUE
+s_Ruby_CallMethod(VALUE val)
+{
+       void **ptr = (void **)val;
+       VALUE receiver = (VALUE)ptr[0];
+       ID method_id = (ID)ptr[1];
+       VALUE args = (VALUE)ptr[2];
+       VALUE retval;
+       if (method_id == 0) {
+               /*  args should be a string, which is evaluated  */
+               if (receiver == Qnil) {
+                       retval = rb_eval_string(StringValuePtr(args));
+               } else {
+                       retval = rb_obj_instance_eval(1, &args, receiver);
+               }
+       } else {
+               /*  args should be an array of arguments  */
+               retval = rb_apply(receiver, method_id, args);
+       }
+       return retval;
+}
+
+VALUE
+Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
+{
+       VALUE retval, save_interrupt_flag;
+       void *ptr[3];
+       save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
+       ptr[0] = (void *)receiver;
+       ptr[1] = (void *)method_id;
+       ptr[2] = (void *)args;
+       MyAppCallback_beginUndoGrouping();
+       retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
+       MyAppCallback_endUndoGrouping();
+       s_SetInterruptFlag(Qnil, save_interrupt_flag);
+       MyAppCallback_hideProgressPanel();  /*  In case when the progress panel is still onscreen */\v
+       return retval;
+}
+#endif
+
+VALUE
+Ruby_SetInterruptFlag(VALUE val)
+{
+       return s_SetInterruptFlag(Qnil, val);
+}
+
+VALUE
+Ruby_GetInterruptFlag(void)
+{
+       return s_SetInterruptFlag(Qnil, Qundef);
+}
+
+/*
+ *  call-seq:
+ *     check_interrupt -> integer
+ *
+ *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
+ */
+static VALUE
+s_Kernel_CheckInterrupt(VALUE self)
+{
+       if (Ruby_GetInterruptFlag() == Qfalse)
+               return INT2NUM(-1);
+       else if (MyAppCallback_checkInterrupt())
+               return INT2NUM(1);
+       else return INT2NUM(0);
+}
+
+static volatile unsigned long sITimerCount = 0;
+
+#if __WXMSW__
+static HANDLE sITimerEvent;
+static HANDLE sITimerThread;
+static int sITimerInterval;
+
+static __stdcall unsigned
+s_ITimerThreadFunc(void *p)
+{
+       while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
+               sITimerCount++;
+       }
+       return 0;
+}
+
+#endif
+
+static void
+s_SignalAction(int n)
+{
+       sITimerCount++;
+}
+
+static void
+s_SetIntervalTimer(int n, int msec)
+{
+#if __WXMSW__
+       if (n == 0) {
+               /*  Start interval timer  */
+               sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+               sITimerInterval = msec;
+               if (sITimerEvent) {
+                       sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
+               }
+       } else {
+               /*  Stop interval timer  */
+               if (sITimerEvent)
+                       SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
+               if (sITimerThread) {
+                       WaitForSingleObject(sITimerThread, 1000);
+                       CloseHandle(sITimerThread);
+               }
+               if (sITimerEvent)
+                       CloseHandle(sITimerEvent);
+               sITimerEvent = NULL;
+               sITimerThread = NULL;
+       }
+#else
+       static struct itimerval sOldValue;
+       static struct sigaction sOldAction;
+       struct itimerval val;
+       struct sigaction act;
+       if (n == 0) {
+               sITimerCount = 0;
+               act.sa_handler = s_SignalAction;
+               act.sa_mask = 0;
+               act.sa_flags = 0;
+               sigaction(SIGALRM, &act, &sOldAction);
+               val.it_value.tv_sec = 0;
+               val.it_value.tv_usec = msec * 1000;
+               val.it_interval.tv_sec = 0;
+               val.it_interval.tv_usec = msec * 1000;
+               setitimer(ITIMER_REAL, &val, &sOldValue);
+       } else {
+               setitimer(ITIMER_REAL, &sOldValue, &val);
+               sigaction(SIGALRM, &sOldAction, &act);
+       }
+#endif
+}
+
+static unsigned long
+s_GetTimerCount(void)
+{
+       return sITimerCount;
+}
+
+static void
+s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
+{
+       if (s_interrupt_flag != Qfalse) {
+               static unsigned long sLastTime = 0;
+               unsigned long currentTime;
+               int flag;
+               currentTime = s_GetTimerCount();
+               if (currentTime != sLastTime) {
+                       sLastTime = currentTime;
+                       gMolbyIsCheckingInterrupt = 1;
+                       flag = MyAppCallback_checkInterrupt();
+                       gMolbyIsCheckingInterrupt = 0;
+                       if (flag) {
+                               s_SetInterruptFlag(Qnil, Qfalse);
+                               rb_interrupt();
+                       }
+               }
+       }
+}
+
+#if 0
+#pragma mark ------ Obsolete ------
+
+/*  User interrupt handling
+ *  User interrupt (command-period on Mac OS) is handled by periodic polling of
+ *  key events. This polling should only be enabled during "normal" execution
+ *  of scripts and must be disabled when the rest of the application (or Ruby
+ *  script itself) is handling GUI. This is ensured by appropriate calls to
+ *  enable_interrupt and disable_interrupt.  */
+
+static int s_interrupt_var_initialized = 0;
+static VALUE s_interrupt_thread = Qnil;
+static VALUE s_interrupt_proc = Qnil;
+static VALUE s_interrupt_lock = Qnil;
+static VALUE s_interrupt_flag = Qfalse;
+static VALUE s_eval_arguments[3] = {Qnil, Qnil, Qnil};
+
+static VALUE
+s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
+{
+       volatile VALUE message;
+       const char *p;
+       if (Ruby_GetInterruptFlag() == Qtrue) {
+               if (s_interrupt_lock != Qnil)
+                       rb_funcall(s_interrupt_lock, rb_intern("lock"), 0);
+               rb_scan_args(argc, argv, "01", &message);
+               if (message != Qnil)
+                       p = StringValuePtr(message);
+               else
+                       p = NULL;
+               MyAppCallback_showProgressPanel(p);
+               if (s_interrupt_lock != Qnil)
+                       rb_funcall(s_interrupt_lock, rb_intern("unlock"), 0);   
+       }
+       return Qnil;
+}
+
+static VALUE
+s_HideProgressPanel(VALUE self)
+{
+       MyAppCallback_hideProgressPanel();
+       return Qnil;
+}
+
+static VALUE
+s_SetProgressValue(VALUE self, VALUE val)
+{
+       double dval = NUM2DBL(rb_Float(val));
+       MyAppCallback_setProgressValue(dval);
+       return Qnil;
+}
+
+static VALUE
+s_SetProgressMessage(VALUE self, VALUE msg)
+{
+       const char *p;
+       if (msg == Qnil)
+               p = NULL;
+       else p = StringValuePtr(msg);
+       MyAppCallback_setProgressMessage(p);
+       return Qnil;
+}
+
+static VALUE
+s_SetInterruptFlag(VALUE self, VALUE val)
+{
+       VALUE oldval;
+       if (val != Qundef) {
+               if (val == Qfalse || val == Qnil)
+                       val = Qfalse;
+               else val = Qtrue;
+       }
+       if (s_interrupt_lock != Qnil)
+               rb_funcall(s_interrupt_lock, rb_intern("lock"), 0);
+       oldval = s_interrupt_flag;
+       if (val != Qundef) {
+               s_interrupt_flag = val;
+               if (val == Qfalse) {
+                       s_HideProgressPanel(self);
+               }
+       }
+       if (s_interrupt_lock != Qnil)
+               rb_funcall(s_interrupt_lock, rb_intern("unlock"), 0);
+       if (s_interrupt_thread != Qnil) {
+               if (val == Qtrue)
+                       rb_funcall(s_interrupt_thread, rb_intern("wakeup"), 0);
+               else if (val == Qfalse)
+                       rb_eval_string("while !$interrupt_thread['stopped']; Thread.pass; end");
+       }
+       return oldval;
+}
+
+static VALUE
+s_GetInterruptFlag(VALUE self)
+{
+       return s_SetInterruptFlag(self, Qundef);
+}
+
+static VALUE
+s_Ruby_CallMethod(VALUE val)
+{
+       void **ptr = (void **)val;
+       VALUE receiver = (VALUE)ptr[0];
+       ID method_id = (ID)ptr[1];
+       VALUE args = (VALUE)ptr[2];
+       return rb_funcall(s_interrupt_proc, rb_intern("call"), 3, receiver, ID2SYM(method_id), args);
+}
+
+VALUE
+Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
+{
+       VALUE *temp_arguments[3];
+       VALUE retval;
+       if (s_interrupt_var_initialized == 0) {
+               rb_define_variable("$interrupt_thread", &s_interrupt_thread);
+               rb_define_variable("$interrupt_proc", &s_interrupt_proc);
+               rb_define_variable("$interrupt_lock", &s_interrupt_lock);
+               rb_define_variable("$interrupt_flag", &s_interrupt_flag);
+               rb_define_variable("$eval_target", &s_eval_arguments[0]);
+               rb_define_variable("$eval_method", &s_eval_arguments[1]);
+               rb_define_variable("$eval_args", &s_eval_arguments[2]);
+               rb_eval_string_protect(
+                       "require 'thread'; $interrupt_lock = Mutex.new \n"
+                       "$interrupt_stack = Array.new \n"
+                       "def start_interrupt_check \n"
+                       "  f = set_interrupt_flag(false)     #  Suspend $interrupt_thread if running \n"
+                       "  $interrupt_stack.push([f, $interrupt_thread]) # Save $interrupt_thread \n"
+                       "  $interrupt_thread = Thread.new {  # Create a new $interrupt_thread \n"
+                       "    while 1 \n"
+                       "      10.times { \n"
+                       "        sleep 0.01 \n"
+                       "        if !get_interrupt_flag \n"
+                       "          Thread.current['stopped'] = true \n"
+                       "          Thread.stop \n"
+                       "          Thread.current['stopped'] = nil \n"
+                       "        end \n"
+                       "      } \n"
+                       "      if check_interrupt > 0 \n"
+                                       /* "raise Interrupt" does not work in Ruby1.8.6 */
+                       "        Thread.main.raise Interrupt.new(nil) \n"
+                       "      end \n"
+                       "    end \n"
+                       "  } \n"
+                       "  set_interrupt_flag(true)          # Let $interrupt_thread alive \n"
+                       "end \n"
+                       "def stop_interrupt_check \n"
+                       "  $interrupt_thread.kill            # Stop $interrupt_thread \n"
+                       "  c = $interrupt_stack.pop \n"
+                       "  $interrupt_thread = c[1]          # Restore the previous thread \n"
+                       "  set_interrupt_flag(c[0])          # Wake up $interrupt_thread if necessary \n"
+                       "end \n", status);
+               if (*status != 0)
+                       return Qnil;
+       /*      s_interrupt_proc = rb_eval_string_protect(
+                       "Proc.new { |rec, method, args| \n"
+                       "  start_interrupt_check \n"
+                       "  begin \n"
+                       "    rec.send(method, *args)         # Do method call \n"
+                       "  ensure \n"
+                       "    stop_interrupt_check \n"
+                       "  end \n"
+                       "}", status);
+               if (*status != 0)
+                       return Qnil; */
+               s_interrupt_var_initialized = 1;
+       }
+       
+       memmove(temp_arguments, s_eval_arguments, sizeof(temp_arguments));
+       s_eval_arguments[0] = receiver;
+       s_eval_arguments[2] = args;
+       if (method_id == 0) {
+               /*  args should be a string, which is evaluated  */
+               if (receiver == Qnil) {
+                       retval = rb_eval_string_protect(
+                               "start_interrupt_check \n"
+                               "begin \n"
+                               " eval $eval_args \n"
+                               "ensure \n"
+                               " stop_interrupt_check \n"
+                               "end \n", status);
+               } else {
+                       retval = rb_eval_string_protect(
+                               "start_interrupt_check \n"
+                               "begin \n"
+                               " $eval_target.instance_eval $eval_args \n"
+                               "ensure \n"
+                               " stop_interrupt_check \n"
+                               "end \n", status);
+               }
+       } else {
+               s_eval_arguments[1] = ID2SYM(method_id);
+               retval = rb_eval_string_protect(
+                       "start_interrupt_check \n"
+                       "begin \n"
+                       " $eval_target.send($eval_method, *$eval_args) \n"
+                       "ensure \n"
+                       " stop_interrupt_check \n"
+                       "end \n", status);
+       }
+       /*      char *s = StringValuePtr(args);
+               char *p;
+               asprintf(&p, "start_interrupt_check; begin; %s; ensure; stop_interrupt_check; end", s);
+               retval = rb_eval_string_protect(p, status);
+               free(p); */
+/*     } else { */
+               /*  args should be a Ruby array of arguments  */
+/*             ptr[0] = (void *)receiver;
+               ptr[1] = (void *)method_id;
+               ptr[2] = (void *)args;
+               retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
+       } */
+       memmove(s_eval_arguments, temp_arguments, sizeof(temp_arguments));
+
+       MyAppCallback_hideProgressPanel();  /*  In case when the progress panel is still onscreen */\v
+       return retval;
+}
+
+VALUE
+Ruby_SetInterruptFlag(VALUE val)
+{
+       return s_SetInterruptFlag(Qnil, val);
+}
+
+VALUE
+Ruby_GetInterruptFlag(void)
+{
+       return s_SetInterruptFlag(Qnil, Qundef);
+}
+
+/*
+ *  call-seq:
+ *     check_interrupt -> integer
+ *
+ *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
+ */
+static VALUE
+s_Kernel_CheckInterrupt(VALUE self)
+{
+       if (Ruby_GetInterruptFlag() == Qfalse)
+               return INT2NUM(-1);
+       else if (MyAppCallback_checkInterrupt())
+               return INT2NUM(1);
+       else return INT2NUM(0);
+}
+
+/*
+ *  call-seq:
+ *     idle(seconds)
+ *
+ *  Wait for seconds with handling GUI. Raises Interrupt exception if command-. is
+ *  pressed.
+ */
+/*static VALUE
+s_Kernel_Idle(VALUE self, VALUE seconds)
+{
+       double dval = NUM2DBL(seconds);
+       VALUE iflag;
+       int retval;
+       iflag = Ruby_SetInterruptFlag(Qfalse);
+       retval = MyAppCallback_processUIWithTimeout(dval);
+       Ruby_SetInterruptFlag(iflag);
+       if (retval == 1)
+               rb_interrupt();
+       return Qnil;
+}
+*/
+
+#pragma mark ------ End Obsolete ------
+#endif
+
+/*
+ *  call-seq:
+ *     ask(prompt, default = nil) -> string
+ *
+ *  Open a modal dialog and get a line of text.
+ */
+static VALUE
+s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
+{
+       volatile VALUE prompt, message;
+       char buf[1024];
+       int retval;
+       rb_scan_args(argc, argv, "11", &prompt, &message);
+       if (message != Qnil) {
+               strncpy(buf, StringValuePtr(message), sizeof buf - 1);
+               buf[sizeof buf - 1] = 0;
+       } else buf[0] = 0;
+       retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
+       if (retval)
+               return rb_str_new2(buf);
+       else
+               return Qnil;    
+}
+
+#pragma mark ====== Menu handling ======
+
+/*
+ *  call-seq:
+ *     register_menu(title, method)
+ *
+ *  Register the method (specified as a symbol) in the script menu.
+ *  The method must be either an instance method of Molecule with no argument,
+ *  or a class method of Molecule with one argument (the current molecule).
+ *  The menu associated with the class method can be invoked even when no document
+ *  is open (the argument is set to Qnil in this case). On the other hand, the
+ *  menu associated with the instance method can only be invoked when at least one 
+ *  document is active.
+ */
+static VALUE
+s_Kernel_RegisterMenu(VALUE self, VALUE title, VALUE method)
+{
+       if (TYPE(method) == T_SYMBOL) {
+               method = rb_funcall(method, rb_intern("to_s"), 0);
+       }
+       MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title));
+       return self;
+}
+
+static VALUE
+s_Ruby_methodType_sub(VALUE data)
+{
+       const char **p = (const char **)data;
+       VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
+       ID mid = rb_intern(p[1]);
+       int ival;
+       if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
+               ival = 1;
+       else if (rb_respond_to(klass, mid))
+               ival = 2;
+       else ival = 0;
+       return INT2FIX(ival);
+}
+       
+/*  Returns 1 if the class defines the instance method with the given name, 2 if the class
+    has the singleton method (class method) with the given name, 0 otherwise.  */
+int
+Ruby_methodType(const char *className, const char *methodName)
+{
+       int status;
+       VALUE retval;
+       const char *p[2];
+       p[0] = className;
+       p[1] = methodName;
+       retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
+       if (status == 0)
+               return FIX2INT(retval);
+       else return 0;
+}
+
+/*
+ *  call-seq:
+ *     execute_script_file(fname)
+ *
+ *  Execute the script in the given file. If a molecule is active, then
+ *  the script is evaluated as Molecule.current.instance_eval(script).
+ *  Before entering the script, the current directory is set to the parent
+ *  directory of the script.
+ */
+static VALUE
+s_Kernel_ExecuteScript(VALUE self, VALUE fname)
+{
+       int status;
+       VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
+       if (status != 0)
+               rb_jump_tag(status);
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     document_home
+ *
+ *  Get the directory suitable for storing user documents. On Windows
+ *  it is the home directory + "My Documents". On other platforms
+ *  it is the home directory.
+ */
+static VALUE
+s_Kernel_DocumentHome(VALUE self)
+{
+       char *s = MyAppCallback_getDocumentHomeDir();
+       VALUE retval = Ruby_NewFileStringValue(s);
+       free(s);
+       return retval;
+}
+
+#pragma mark ====== User defaults ======
+
+/*
+ *  call-seq:
+ *     Kernel.get_global_settings(key)
+ *
+ *  Get a setting data for key from the application preferences.
+ */
+static VALUE
+s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
+{
+       char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
+       if (p != NULL) {
+               VALUE retval = rb_eval_string(p);
+               free(p);
+               return retval;
+       } else return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     Kernel.set_global_settings(key, value)
+ *
+ *  Set a setting data for key to the application preferences.
+ */
+static VALUE
+s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
+{
+       VALUE sval = rb_inspect(value);
+       MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
+       return value;
+}
+
+#pragma mark ====== Utility functions (protected funcall) ======
+
+struct Ruby_funcall2_record {
+       VALUE recv;
+       ID mid;
+       int argc;
+       VALUE *argv;
+};
+
+static VALUE
+s_Ruby_funcall2_sub(VALUE data)
+{
+       struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
+       return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
+}
+
+VALUE
+Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
+{
+       struct Ruby_funcall2_record rec;
+       rec.recv = recv;
+       rec.mid = mid;
+       rec.argc = argc;
+       rec.argv = argv;
+       return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
+}
+
+#pragma mark ====== ParameterRef Class ======
+
+static UnionPar *
+s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
+{
+       UnionPar *up;
+       ParameterRef *pref;
+       Data_Get_Struct(self, ParameterRef, pref);
+       if (typep != NULL)
+               *typep = pref->parType;
+       if (pref->parType == kAtomParType) {
+               up = (UnionPar *)&gDispAtomParameters[pref->idx];
+       } else {
+               up = ParameterRefGetPar(pref);
+               if (checkEditable && up->bond.src != 0 && up->bond.src != -1)
+                       rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
+       }
+       return up;
+}
+
+static void
+s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
+{
+       UnionPar *up;
+       ParameterRef *pref;
+       Data_Get_Struct(self, ParameterRef, pref);
+       if (pref->mol == NULL)
+               return;
+       up = ParameterRefGetPar(pref);
+       up->bond.src = 0;  /*  Becomes automatically molecule-local  */
+       if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
+               /*  Register undo  */
+               MolAction *act;
+               act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
+               MolActionCallback_registerUndo(pref->mol, act);
+               MoleculeCallback_notifyModification(pref->mol, 0);
+               pref->mol->needsMDRebuild = 1;
+       }
+}
+
+VALUE
+ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
+{
+       ParameterRef *pref = ParameterRefNew(mol, type, idx1);
+       if (pref != NULL)
+               return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
+       else
+               rb_raise(rb_eMolbyError, "Cannot create parameter reference");
+}
+
+static int
+s_AtomTypeIndexFromValue(VALUE val)
+{
+       if (rb_obj_is_kind_of(val, rb_cNumeric))
+               return NUM2INT(val);
+       else
+               return AtomTypeEncodeToUInt(StringValuePtr(val));
+}
+
+static const char *s_ParameterTypeNames[] = {
+       "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "atom"
+};
+
+static VALUE s_ParameterRef_GetIndex(VALUE self) {
+       ParameterRef *pref;
+       Data_Get_Struct(self, ParameterRef, pref);
+       return INT2NUM(pref->idx);
+}
+
+static VALUE s_ParameterRef_GetParType(VALUE self) {
+       Int tp;
+       s_UnionParFromValue(self, &tp, 0);
+       if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
+               return rb_str_new2(s_ParameterTypeNames[tp]);
+       else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
+}
+
+static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
+       UnionPar *up;
+       Int tp, i, n, types[4];
+       VALUE vals[4];
+       up = s_UnionParFromValue(self, &tp, 0);
+       switch (tp) {
+               case kBondParType:
+                       n = 2;
+                       types[0] = up->bond.type1;
+                       types[1] = up->bond.type2;
+                       break;
+               case kAngleParType:
+                       n = 3;
+                       types[0] = up->angle.type1;
+                       types[1] = up->angle.type2;
+                       types[2] = up->angle.type3;
+                       break;
+               case kDihedralParType:
+               case kImproperParType:
+                       n = 4;
+                       types[0] = up->torsion.type1; 
+                       types[1] = up->torsion.type2;
+                       types[2] = up->torsion.type3;
+                       types[3] = up->torsion.type4;
+                       break;
+               case kVdwParType:
+                       n = 1;
+                       types[0] = up->vdw.type1;
+                       break;
+               case kVdwPairParType:
+                       n = 2;
+                       types[0] = up->vdwp.type1;
+                       types[1] = up->vdwp.type2;
+                       break;
+               case kVdwCutoffParType:
+                       n = 2;
+                       types[0] = up->vdwcutoff.n1;
+                       types[1] = up->vdwcutoff.n2;
+                       if (up->vdwcutoff.type == 1) {
+                               n = 4;
+                               types[2] = up->vdwcutoff.n3;
+                               types[3] = up->vdwcutoff.n4;
+                       }
+                       break;
+               default:
+                       rb_raise(rb_eMolbyError, "invalid member atom_types");
+       }
+       for (i = 0; i < n; i++) {
+               if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
+                       vals[i] = INT2NUM(types[i]);
+               else
+                       vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
+       }
+       if (n == 1)
+               return vals[0];
+       else
+               return rb_ary_new4(n, vals);
+}
+
+static VALUE s_ParameterRef_GetK(VALUE self) {
+       UnionPar *up;
+       Int tp, i, n;
+       VALUE vals[3];
+       up = s_UnionParFromValue(self, &tp, 0);
+       switch (tp) {
+               case kBondParType:
+                       return rb_float_new(up->bond.k * INTERNAL2KCAL);
+               case kAngleParType:
+                       return rb_float_new(up->angle.k * INTERNAL2KCAL);
+               case kDihedralParType:
+               case kImproperParType:
+                       if (up->torsion.mult == 1)
+                               return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
+                       n = up->torsion.mult;
+                       if (n > 3)
+                               n = 3;
+                       for (i = 0; i < n; i++)
+                               vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
+                       return rb_ary_new4(n, vals);
+               default:
+                       rb_raise(rb_eMolbyError, "invalid member k");
+       }
+}
+
+static VALUE s_ParameterRef_GetR0(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kBondParType)
+               return rb_float_new(up->bond.r0);
+       else rb_raise(rb_eMolbyError, "invalid member r0");
+}
+
+static VALUE s_ParameterRef_GetA0(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kAngleParType)
+               return rb_float_new(up->angle.a0 * kRad2Deg);
+       else rb_raise(rb_eMolbyError, "invalid member a0");
+}
+
+static VALUE s_ParameterRef_GetMult(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kDihedralParType || tp == kImproperParType)
+               return rb_float_new(up->torsion.mult);
+       else rb_raise(rb_eMolbyError, "invalid member mult");
+}
+
+static VALUE s_ParameterRef_GetPeriod(VALUE self) {
+       UnionPar *up;
+       Int tp, i, n;
+       VALUE vals[3];
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kDihedralParType || tp == kImproperParType) {
+               if (up->torsion.mult == 1)
+                       return rb_float_new(up->torsion.period[0]);
+               n = up->torsion.mult;
+               if (n > 3)
+                       n = 3;
+               for (i = 0; i < n; i++)
+                       vals[i] = rb_float_new(up->torsion.period[i]);
+               return rb_ary_new4(n, vals);
+       } else rb_raise(rb_eMolbyError, "invalid member period");
+}
+
+static VALUE s_ParameterRef_GetPhi0(VALUE self) {
+       UnionPar *up;
+       Int tp, i, n;
+       VALUE vals[3];
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kDihedralParType || tp == kImproperParType) {
+               if (up->torsion.mult == 1)
+                       return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
+               n = up->torsion.mult;
+               if (n > 3)
+                       n = 3;
+               for (i = 0; i < n; i++)
+                       vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
+               return rb_ary_new4(n, vals);
+       } else rb_raise(rb_eMolbyError, "invalid member phi0");
+}
+
+static VALUE s_ParameterRef_GetA(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType)
+               return rb_float_new(up->vdw.A);
+       else if (tp == kVdwPairParType)
+               return rb_float_new(up->vdwp.A);
+       else rb_raise(rb_eMolbyError, "invalid member A");
+}
+
+static VALUE s_ParameterRef_GetB(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType)
+               return rb_float_new(up->vdw.B);
+       else if (tp == kVdwPairParType)
+               return rb_float_new(up->vdwp.B);
+       else rb_raise(rb_eMolbyError, "invalid member B");
+}
+
+static VALUE s_ParameterRef_GetReq(VALUE self) {
+       UnionPar *up;
+       Int tp;
+/*     Double a, b, r; */
+       Double r;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType) {
+       /*      a = up->vdw.A;
+               b = up->vdw.B;  */
+               r = up->vdw.r_eq;
+       } else if (tp == kVdwPairParType) {
+       /*      a = up->vdwp.A;
+               b = up->vdwp.B;  */
+               r = up->vdwp.r_eq;
+       } else rb_raise(rb_eMolbyError, "invalid member r_eq");
+/*     if (a == 0.0 || b == 0.0) */
+       return rb_float_new(r);
+/*     else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
+}
+
+static VALUE s_ParameterRef_GetEps(VALUE self) {
+       UnionPar *up;
+       Int tp;
+/*     Double a, b; */
+       Double eps;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType) {
+       /*      a = up->vdw.A;
+               b = up->vdw.B;  */
+               eps = up->vdw.eps;
+       } else if (tp == kVdwPairParType) {
+       /*      a = up->vdwp.A;
+               b = up->vdwp.B; */
+               eps = up->vdwp.eps;
+       } else rb_raise(rb_eMolbyError, "invalid member eps");
+/*     if (a == 0.0 || b == 0.0)  */
+               return rb_float_new(eps * INTERNAL2KCAL);
+/*     else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
+}
+
+static VALUE s_ParameterRef_GetA14(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType)
+               return rb_float_new(up->vdw.A14);
+       else if (tp == kVdwPairParType)
+               return rb_float_new(up->vdwp.A14);
+       else rb_raise(rb_eMolbyError, "invalid member A14");
+}
+
+static VALUE s_ParameterRef_GetB14(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType)
+               return rb_float_new(up->vdw.B14);
+       else if (tp == kVdwPairParType)
+               return rb_float_new(up->vdwp.B14);
+       else rb_raise(rb_eMolbyError, "invalid member B14");
+}
+
+static VALUE s_ParameterRef_GetReq14(VALUE self) {
+       UnionPar *up;
+       Int tp;
+/*     Double a, b, r; */
+       Double r;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType) {
+       /*      a = up->vdw.A14;
+               b = up->vdw.B14; */
+               r = up->vdw.r_eq14;
+       } else if (tp == kVdwPairParType) {
+       /*      a = up->vdwp.A14;
+               b = up->vdwp.B14;  */
+               r = up->vdwp.r_eq14;
+       } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
+/*     if (a == 0.0 || b == 0.0)  */
+       return rb_float_new(r);
+/*     else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
+}
+
+static VALUE s_ParameterRef_GetEps14(VALUE self) {
+       UnionPar *up;
+       Int tp;
+/*     Double a, b;  */
+       Double eps;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwParType) {
+       /*      a = up->vdw.A14;
+               b = up->vdw.B14;  */
+               eps = up->vdw.eps;
+       } else if (tp == kVdwPairParType) {
+       /*      a = up->vdwp.A14;
+               b = up->vdwp.B14; */
+               eps = up->vdwp.eps;
+       } else rb_raise(rb_eMolbyError, "invalid member eps14");
+/*     if (a == 0.0 || b == 0.0) */
+       return rb_float_new(eps * INTERNAL2KCAL);
+/*     else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
+}
+
+static VALUE s_ParameterRef_GetCutoff(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kVdwCutoffParType)
+               return rb_float_new(up->vdwcutoff.cutoff);
+       else rb_raise(rb_eMolbyError, "invalid member cutoff");
+}
+
+static VALUE s_ParameterRef_GetRadius(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kAtomParType)
+               return rb_float_new(up->atom.radius);
+       else rb_raise(rb_eMolbyError, "invalid member radius");
+}
+
+static VALUE s_ParameterRef_GetColor(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kAtomParType)
+               return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
+       else rb_raise(rb_eMolbyError, "invalid member color");
+}
+
+static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kAtomParType)
+               return INT2NUM(up->atom.number);
+       else rb_raise(rb_eMolbyError, "invalid member atomic_number");
+}
+
+static VALUE s_ParameterRef_GetName(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kAtomParType) {
+               char name[5];
+               strncpy(name, up->atom.name, 4);
+               name[4] = 0;
+               return rb_str_new2(name);
+       } else rb_raise(rb_eMolbyError, "invalid member name");
+}
+
+static VALUE s_ParameterRef_GetWeight(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kAtomParType)
+               return rb_float_new(up->atom.weight);
+       else if (tp == kVdwParType)
+               return rb_float_new(up->vdw.weight);
+       else rb_raise(rb_eMolbyError, "invalid member weight");
+}
+
+static VALUE s_ParameterRef_GetFullName(VALUE self) {
+       UnionPar *up;
+       Int tp;
+       up = s_UnionParFromValue(self, &tp, 0);
+       if (tp == kAtomParType) {
+               char fullname[16];
+               strncpy(fullname, up->atom.fullname, 15);
+               fullname[15] = 0;
+               return rb_str_new2(fullname);
+       } else rb_raise(rb_eMolbyError, "invalid member fullname");
+}
+
+static VALUE s_ParameterRef_GetComment(VALUE self) {
+       UnionPar *up;
+       Int tp, com;
+       up = s_UnionParFromValue(self, &tp, 0);
+       com = up->bond.com;
+       if (com == 0)
+               return Qnil;
+       else return rb_str_new2(ParameterGetComment(com));
+}
+
+static VALUE s_ParameterRef_GetSource(VALUE self) {
+       UnionPar *up;
+       Int tp, src;
+       up = s_UnionParFromValue(self, &tp, 0);
+       src = up->bond.src;
+       if (src < 0)
+               return Qfalse;  /* undefined */
+       else if (src == 0)
+               return Qnil;  /*  local  */
+       else return rb_str_new2(ParameterGetComment(src));
+}
+
+static void
+s_ScanAtomTypes(VALUE val, Int n, UInt *types)
+{
+       VALUE *valp;
+       int i;
+       if (n == 1)
+               valp = &val;
+       else {
+               if (rb_obj_is_kind_of(val, rb_cString)) {
+                       char *s = StringValuePtr(val);
+                       char *p;
+                       for (i = 0; i < n; i++) {
+                               char buf[40];
+                               int len;
+                               /*  Skip leading blanks  */
+                               while (*s == ' ' || *s == '\t')
+                                       s++;
+                               p = strchr(s, '-');
+                               if (p == NULL)
+                                       len = strlen(s);
+                               else len = p - s;
+                               if (len >= sizeof(buf))
+                                       len = sizeof(buf) - 1;
+                               strncpy(buf, s, len);
+                               buf[len] = 0;
+                               /*  Skip trailing blanks  */
+                               while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
+                                       buf[len] = 0;
+                               if (buf[0] == 0)
+                                       rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
+                               if (buf[0] >= '0' && buf[0] <= '9')
+                                       types[i] = atoi(buf);
+                               else
+                                       types[i] = AtomTypeEncodeToUInt(buf);
+                               if (p == NULL) {
+                                       i++;
+                                       break;
+                               } else s = p + 1;
+                       }
+                       if (i < n)
+                               rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given", n, i);
+                       return;
+               }
+               val = rb_ary_to_ary(val);
+               if (RARRAY_LEN(val) != n)
+                       rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
+               valp = RARRAY_PTR(val);
+       }
+       for (i = 0; i < n; i++) {
+               if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
+                       types[i] = NUM2INT(rb_Integer(valp[i]));
+               else {
+                       VALUE sval = valp[i];
+                       types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
+               }
+       }
+}
+
+static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
+       UnionPar *up;
+       VALUE oldval;
+       Int oldsrc, tp;
+       UInt types[4];
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetAtomTypes(self);
+       oldsrc = up->bond.src;
+       switch (tp) {
+               case kBondParType:
+                       s_ScanAtomTypes(val, 2, types);
+                       up->bond.type1 = types[0];
+                       up->bond.type2 = types[1];
+                       break;
+               case kAngleParType:
+                       s_ScanAtomTypes(val, 3, types);
+                       up->angle.type1 = types[0];
+                       up->angle.type2 = types[1];
+                       up->angle.type3 = types[2];
+                       break;
+               case kDihedralParType:
+               case kImproperParType:
+                       s_ScanAtomTypes(val, 4, types);
+                       up->torsion.type1 = types[0];
+                       up->torsion.type2 = types[1];
+                       up->torsion.type3 = types[2];
+                       up->torsion.type4 = types[3];
+                       break;
+               case kVdwParType:
+                       s_ScanAtomTypes(val, 1, types);
+                       up->vdw.type1 = types[0];
+                       break;
+               case kVdwPairParType:
+                       s_ScanAtomTypes(val, 2, types);
+                       up->vdwp.type1 = types[0];
+                       up->vdwp.type2 = types[1];
+                       break;
+               case kVdwCutoffParType:
+                       if (up->vdwcutoff.type == 0) {
+                               s_ScanAtomTypes(val, 2, types);
+                               if ((types[0] >= 0 && types[0] < kAtomTypeMinimum) || (types[1] >= 0 && types[1] < kAtomTypeMinimum))
+                                       rb_raise(rb_eMolbyError, "the atom type parameters should be string");
+                               up->vdwcutoff.n1 = types[0];
+                               up->vdwcutoff.n2 = types[1];
+                       } else {
+                               s_ScanAtomTypes(val, 4, types);
+                               if (types[0] < 0 || types[0] >= kAtomTypeMinimum || types[1] < 0 || types[1] >= kAtomTypeMinimum || types[2] < 0 || types[2] >= kAtomTypeMinimum || types[3] < 0 || types[3] >= kAtomTypeMinimum)
+                                       rb_raise(rb_eMolbyError, "the atom type parameters should be atom indices");
+                               up->vdwcutoff.n1 = types[0];
+                               up->vdwcutoff.n2 = types[1];
+                               up->vdwcutoff.n3 = types[2];
+                               up->vdwcutoff.n4 = types[3];
+                       }
+                       break;
+               default:
+                       return Qnil;
+       }
+       s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, i, n, oldsrc;
+       VALUE *valp, oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetK(self);
+       oldsrc = up->bond.src;
+       switch (tp) {
+               case kBondParType:
+                       val = rb_Float(val);
+                       up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
+                       break;
+               case kAngleParType:
+                       val = rb_Float(val);
+                       up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
+                       break;
+               case kDihedralParType:
+               case kImproperParType:
+                       if (up->torsion.mult == 1 || up->torsion.mult == 0) {
+                               up->torsion.mult = 1;
+                               val = rb_Float(val);
+                               up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
+                               break;
+                       }
+                       n = up->torsion.mult;
+                       if (n > 3)
+                               n = 3;
+                       val = rb_ary_to_ary(val);
+                       if (RARRAY_LEN(val) != n)
+                               rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
+                       valp = RARRAY_PTR(val);
+                       for (i = 0; i < n; i++) {
+                               up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
+                       }
+                       break;
+               default:
+                       rb_raise(rb_eMolbyError, "invalid member k");
+       }
+       s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetR0(self);
+       oldsrc = up->bond.src;
+       if (tp == kBondParType) {
+               val = rb_Float(val);
+               up->bond.r0 = NUM2DBL(val);
+       } else rb_raise(rb_eMolbyError, "invalid member r0");
+       s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetA0(self);
+       oldsrc = up->bond.src;
+       if (tp == kAngleParType) {
+               val = rb_Float(val);
+               up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
+       } else rb_raise(rb_eMolbyError, "invalid member a0");
+       s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetMult(self);
+       oldsrc = up->bond.src;
+       if (tp == kDihedralParType || tp == kImproperParType) {
+               int i;
+               val = rb_Integer(val);
+               i = NUM2INT(val);
+               if (i <= 0 || i > 3)
+                       rb_raise(rb_eMolbyError, "torsion multiplicity should be 1..3");
+               up->torsion.mult = i;
+       } else rb_raise(rb_eMolbyError, "invalid member mult");
+       s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, i, n, oldsrc;
+       VALUE *valp, oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetPeriod(self);
+       oldsrc = up->bond.src;
+       if (tp == kDihedralParType || tp == kImproperParType) {
+               if (up->torsion.mult == 1 || up->torsion.mult == 0) {
+                       up->torsion.mult = 1;
+                       val = rb_Integer(val);
+                       up->torsion.period[0] = NUM2INT(val);
+               } else {
+                       n = up->torsion.mult;
+                       if (n > 3)
+                               n = 3;
+                       val = rb_ary_to_ary(val);
+                       if (RARRAY_LEN(val) != n)
+                               rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
+                       valp = RARRAY_PTR(val);
+                       for (i = 0; i < n; i++) {
+                               up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
+                       }
+               }
+       } else rb_raise(rb_eMolbyError, "invalid member period");
+       s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, i, n, oldsrc;
+       VALUE *valp, oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetPhi0(self);
+       oldsrc = up->bond.src;
+       if (tp == kDihedralParType || tp == kImproperParType) {
+               if (up->torsion.mult == 1 || up->torsion.mult == 0) {
+                       up->torsion.mult = 1;
+                       val = rb_Float(val);
+                       up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
+               } else {
+                       n = up->torsion.mult;
+                       if (n > 3)
+                               n = 3;
+                       val = rb_ary_to_ary(val);
+                       if (RARRAY_LEN(val) != n)
+                               rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
+                       valp = RARRAY_PTR(val);
+                       for (i = 0; i < n; i++)
+                               up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
+               }
+       } else rb_raise(rb_eMolbyError, "invalid member phi0");
+       s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
+       return val;
+}
+
+/*
+static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       double d;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetA(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       d = NUM2DBL(val);
+       if (tp == kVdwParType)
+               up->vdw.A = d;
+       else if (tp == kVdwPairParType)
+               up->vdwp.A = d;
+       else rb_raise(rb_eMolbyError, "invalid member A");
+       s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       double d;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetB(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       d = NUM2DBL(val);
+       if (tp == kVdwParType)
+               up->vdw.B = d;
+       else if (tp == kVdwPairParType)
+               up->vdwp.B = d;
+       else rb_raise(rb_eMolbyError, "invalid member B");
+       s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
+       return val;
+}
+*/
+
+static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       Double r;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetReq(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       r = NUM2DBL(val);
+       if (tp == kVdwParType) {
+               up->vdw.r_eq = r;
+               up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
+               up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
+       } else if (tp == kVdwPairParType) {
+               up->vdwp.r_eq = r;
+               up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
+               up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
+       } else rb_raise(rb_eMolbyError, "invalid member r_eq");
+       s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
+       return val;
+}
+
+static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       Double e;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetEps(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       e = NUM2DBL(val) * KCAL2INTERNAL;
+       if (tp == kVdwParType) {
+               up->vdw.eps = e;
+               up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
+               up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
+       } else if (tp == kVdwPairParType) {
+               up->vdwp.eps = e;
+               up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
+               up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
+       } else rb_raise(rb_eMolbyError, "invalid member eps");
+       s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
+       return val;
+}
+
+/*
+static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       double d;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetA14(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       d = NUM2DBL(val);
+       if (tp == kVdwParType)
+               up->vdw.A14 = d;
+       else if (tp == kVdwPairParType)
+               up->vdwp.A14 = d;
+       else rb_raise(rb_eMolbyError, "invalid member A14");
+       s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
+       return val;
+}
+
+static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       double d;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetB14(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       d = NUM2DBL(val);
+       if (tp == kVdwParType)
+               up->vdw.B14 = d;
+       else if (tp == kVdwPairParType)
+               up->vdwp.B14 = d;
+       else rb_raise(rb_eMolbyError, "invalid member B14");
+       s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
+       return val;
+}
+*/
+
+static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       Double r;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetReq14(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       r = NUM2DBL(val);
+       if (tp == kVdwParType) {
+               up->vdw.r_eq14 = r;
+               up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
+               up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
+       } else if (tp == kVdwPairParType) {
+               up->vdwp.r_eq14 = r;
+               up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
+               up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
+       } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
+       s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
+       return val;
+}
+
+static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       Double e;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetEps14(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       e = NUM2DBL(val) * KCAL2INTERNAL;
+       if (tp == kVdwParType) {
+               up->vdw.eps14 = e;
+               up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
+               up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
+       } else if (tp == kVdwPairParType) {
+               up->vdwp.eps14 = e;
+               up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
+               up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
+       } else rb_raise(rb_eMolbyError, "invalid member eps14");
+       s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
+       return val;
+}
+
+static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       oldval = s_ParameterRef_GetCutoff(self);
+       oldsrc = up->bond.src;
+       up = s_UnionParFromValue(self, &tp, 1);
+       val = rb_Float(val);
+       if (tp == kVdwCutoffParType) {
+               up->vdwcutoff.cutoff = NUM2DBL(val);
+       } else rb_raise(rb_eMolbyError, "invalid member cutoff");
+       s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
+       return val;
+}
+
+static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetRadius(self);
+       oldsrc = up->bond.src;
+       val = rb_Float(val);
+       if (tp == kAtomParType) {
+               up->atom.radius = NUM2DBL(val);
+       } else rb_raise(rb_eMolbyError, "invalid member radius");
+       s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
+       return val;
+}
+
+static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE *valp, oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetColor(self);
+       oldsrc = up->bond.src;
+       val = rb_ary_to_ary(val);
+       if (RARRAY_LEN(val) != 3)
+               rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
+       valp = RARRAY_PTR(val);
+       if (tp == kAtomParType) {
+               up->atom.r = NUM2DBL(rb_Float(valp[0]));
+               up->atom.g = NUM2DBL(rb_Float(valp[1]));
+               up->atom.b = NUM2DBL(rb_Float(valp[2]));
+       } else rb_raise(rb_eMolbyError, "invalid member color");
+       s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
+       return val;
+}
+
+static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetAtomicNumber(self);
+       oldsrc = up->bond.src;
+       val = rb_Integer(val);
+       if (tp == kAtomParType)
+               up->atom.number = NUM2INT(val);
+       else rb_raise(rb_eMolbyError, "invalid member atomic_number");
+       s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
+       return val;
+}
+
+static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetName(self);
+       oldsrc = up->bond.src;
+       if (tp == kAtomParType) {
+               strncpy(up->atom.name, StringValuePtr(val), 4);
+       } else rb_raise(rb_eMolbyError, "invalid member name");
+       s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
+       return val;
+}
+
+static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       val = rb_Float(val);
+       oldval = s_ParameterRef_GetWeight(self);
+       oldsrc = up->bond.src;
+       up = s_UnionParFromValue(self, &tp, 1);
+       if (tp == kAtomParType)
+               up->atom.weight = NUM2DBL(val);
+       else if (tp == kVdwParType)
+               up->vdw.weight = NUM2DBL(val);
+       else rb_raise(rb_eMolbyError, "invalid member weight");
+       s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
+       return val;
+}
+
+static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetFullName(self);
+       oldsrc = up->bond.src;
+       if (tp == kAtomParType) {
+               strncpy(up->atom.fullname, StringValuePtr(val), 15);
+               up->atom.fullname[15] = 0;
+       } else rb_raise(rb_eMolbyError, "invalid member fullname");
+       s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
+       return val;
+}
+
+static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
+       UnionPar *up;
+       Int tp, com, oldsrc;
+       VALUE oldval;
+       up = s_UnionParFromValue(self, &tp, 1);
+       oldval = s_ParameterRef_GetComment(self);
+       oldsrc = up->bond.src;
+       com = ParameterCommentIndex(StringValuePtr(val));
+       up->bond.com = com;
+       s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
+       return val;     
+}
+
+static struct s_ParameterAttrDef {
+       char *name;
+       VALUE *symref;  /*  Address of s_IndexSymbol etc. */
+       ID id;                  /*  Will be set within InitMolby()  */
+       VALUE (*getter)(VALUE);
+       VALUE (*setter)(VALUE, VALUE);
+} s_ParameterAttrDefTable[] = {
+       {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
+       {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
+       {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
+       {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
+       {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
+       {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
+       {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
+       {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
+       {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
+       {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
+       {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
+       {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL},
+       {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
+       {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
+       {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
+       {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL},
+       {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
+       {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
+       {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
+       {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
+       {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
+       {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
+       {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
+       {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
+       {"fullname",         &s_FullNameSym,         0, s_ParameterRef_GetFullName,         s_ParameterRef_SetFullName},
+       {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
+       {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       NULL},
+       {NULL} /* Sentinel */
+};
+
+static VALUE
+s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
+{
+       int i;
+       ID kid;
+       if (TYPE(key) != T_SYMBOL) {
+               kid = rb_intern(StringValuePtr(key));
+               key = ID2SYM(kid);
+       } else kid = SYM2ID(key);
+       for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
+               if (s_ParameterAttrDefTable[i].id == kid) {
+                       if (value == Qundef)
+                               return (*(s_ParameterAttrDefTable[i].getter))(self);
+                       else
+                               return (*(s_ParameterAttrDefTable[i].setter))(self, value);
+               }
+       }
+       rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
+       return Qnil; /* not reached */
+}
+
+static VALUE
+s_ParameterRef_GetAttr(VALUE self, VALUE key)
+{
+       return s_ParameterRef_SetAttr(self, key, Qundef);
+}
+
+/*
+ *  call-seq:
+ *     parameter.keys(idx)          -> array of valid parameter attributes
+ *  
+ *  Returns an array of valid parameter attributes (as Symbols).
+ */
+static VALUE
+s_ParameterRef_Keys(VALUE self)
+{
+       ParameterRef *pref;
+       Data_Get_Struct(self, ParameterRef, pref);
+       switch (pref->parType) {
+               case kBondParType:
+                       return rb_ary_new3(5, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym);
+               case kAngleParType:
+                       return rb_ary_new3(5, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym);
+               case kDihedralParType:
+               case kImproperParType:
+                       return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym);
+               case kVdwParType:
+                       return rb_ary_new3(12, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_AtomicNumberSym, s_ASym, s_BSym, s_ReqSym, s_EpsSym, s_A14Sym, s_B14Sym, s_Req14Sym, s_Eps14Sym);
+               case kVdwPairParType:
+                       return rb_ary_new3(11, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ASym, s_BSym, s_ReqSym, s_EpsSym, s_A14Sym, s_B14Sym, s_Req14Sym, s_Eps14Sym);
+               case kVdwCutoffParType:
+                       return rb_ary_new3(4, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym);
+               case kAtomParType:
+                       return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_RadiusSym, s_ColorSym, s_WeightSym);
+               default:
+                       rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
+       }
+       return Qnil;  /*  Not reached  */
+}
+
+/*
+ *  call-seq:
+ *     parameter.to_hash(idx)          -> hash
+ *  
+ *  Returns a hash containing valid parameter names and values
+ */
+static VALUE
+s_ParameterRef_ToHash(VALUE self)
+{
+       VALUE keys = s_ParameterRef_Keys(self);
+       VALUE retval;
+       int i;
+       if (keys == Qnil)
+               return Qnil;
+       retval = rb_hash_new();
+       for (i = 0; i < RARRAY_LEN(keys); i++) {
+               VALUE key = RARRAY_PTR(keys)[i];
+               VALUE val = s_ParameterRef_GetAttr(self, key);
+               rb_hash_aset(retval, key, val);
+       }
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     parameter.to_s(idx)          -> string
+ *  
+ *  Returns a string representation of the given parameter
+ */
+static VALUE
+s_ParameterRef_ToString(VALUE self)
+{
+       Int tp, i, n;
+       char buf[1024], types[4][8];
+       UnionPar *up = s_UnionParFromValue(self, &tp, 0);
+       switch (tp) {
+               case kBondParType:
+                       snprintf(buf, sizeof buf, "bond %4.6s %4.6s %8.2f %8.3f", AtomTypeDecodeToString(up->bond.type1, types[0]), AtomTypeDecodeToString(up->bond.type2, types[1]), up->bond.k * INTERNAL2KCAL, up->bond.r0);
+                       break;
+               case kAngleParType:
+                       snprintf(buf, sizeof buf, "angle %4.6s %4.6s %4.6s %8.2f %8.3f", AtomTypeDecodeToString(up->angle.type1, types[0]), AtomTypeDecodeToString(up->angle.type2, types[1]), AtomTypeDecodeToString(up->angle.type3, types[2]), up->angle.k * INTERNAL2KCAL, up->angle.a0 * kRad2Deg);
+                       break;
+               case kDihedralParType:
+               case kImproperParType:
+                       snprintf(buf, sizeof buf, "%s %4.6s %4.6s %4.6s %4.6s", (tp == kDihedralParType ? "dihe" : "impr"), AtomTypeDecodeToString(up->torsion.type1, types[0]), AtomTypeDecodeToString(up->torsion.type2, types[1]), AtomTypeDecodeToString(up->torsion.type3, types[2]), AtomTypeDecodeToString(up->torsion.type4, types[3]));
+                       n = strlen(buf);
+                       for (i = 0; i < up->torsion.mult; i++) {
+                               snprintf(buf + n, sizeof buf - n, " %8.2f %2d %8.3f", up->torsion.k[i] * INTERNAL2KCAL, up->torsion.period[i], up->torsion.phi0[i] * kRad2Deg);
+                               n = strlen(buf);
+                       }
+                       break;
+               case kVdwParType:
+                       snprintf(buf, sizeof buf, "nonbonded %4.6s %8.4f %8.4f %8.4f %8.4f", AtomTypeDecodeToString(up->vdw.type1, types[0]), up->vdw.A * INTERNAL2KCAL / pow(up->vdw.r_eq, 12.0), up->vdw.r_eq / 1.12246204830937, up->vdw.A14 * INTERNAL2KCAL / pow(up->vdw.r_eq14, 12.0), up->vdw.r_eq14 / 1.12246204830937);
+                       break;
+               case kVdwPairParType:
+                       snprintf(buf, sizeof buf, "nbfi %4.6s %4.6s %12.8e %12.8e %12.8e %12.8e", AtomTypeDecodeToString(up->vdwp.type1, types[0]), AtomTypeDecodeToString(up->vdwp.type2, types[1]), up->vdwp.A * INTERNAL2KCAL, up->vdwp.B * INTERNAL2KCAL, up->vdwp.A14 * INTERNAL2KCAL, up->vdwp.B14 * INTERNAL2KCAL);
+                       break;
+               case kVdwCutoffParType:
+                       if (up->vdwcutoff.type == 0)
+                               snprintf(buf, sizeof buf, "vdwcutoff %4.6s %4.6s %8.4f", AtomTypeDecodeToString(up->vdwcutoff.n1, types[0]), AtomTypeDecodeToString(up->vdwcutoff.n2, types[1]), up->vdwcutoff.cutoff);
+                       else
+                               snprintf(buf, sizeof buf, "vdwcutoff %d %d %d %d %8.4f", up->vdwcutoff.n1, up->vdwcutoff.n2, up->vdwcutoff.n3, up->vdwcutoff.n4, up->vdwcutoff.cutoff);
+                       break;
+               case kAtomParType:
+                       snprintf(buf, sizeof buf, "dispatom %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s", up->atom.name, up->atom.number, up->atom.radius, up->atom.r, up->atom.g, up->atom.b, up->atom.weight, up->atom.fullname);
+                       break;
+       }
+       return rb_str_new2(buf);
+}
+
+#pragma mark ====== Parameter Class ======
+
+/*  The Parameter class actually encapsulate Molecule record. If the record pointer
+ *  is NULL, then the global parameters are looked for.  */
+
+static VALUE
+s_NewParameterValueFromValue(VALUE val)
+{
+       Molecule *mol;
+       if (rb_obj_is_kind_of(val, rb_cMolecule)) {
+               Data_Get_Struct(val, Molecule, mol);
+               MoleculeRetain(mol);
+               return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
+       } else {
+               mol = NULL;
+               return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
+       }
+}
+
+static Molecule *
+s_MoleculeFromParameterValue(VALUE val)
+{
+       Molecule *mol;
+       Data_Get_Struct(val, Molecule, mol);
+       return mol;
+}
+
+static Parameter *
+s_ParameterFromParameterValue(VALUE val)
+{
+       Molecule *mol;
+       Data_Get_Struct(val, Molecule, mol);
+       if (mol != NULL)
+               return mol->par;
+       return gBuiltinParameters;
+}
+
+/*  Forward declarations  */
+static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
+static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
+
+static Molecule *
+s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
+{
+       if (rb_obj_is_kind_of(val, rb_cParameter)) {
+               return s_MoleculeFromParameterValue(val);
+       } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
+               return s_MoleculeFromParEnumerableValue(val);
+       } else return NULL;
+}
+
+/*
+ *  call-seq:
+ *     Parameter.builtin    -> parameter
+ *  
+ *  Returns a parameter value that points to the global (builtin) parameters.
+ */
+static VALUE
+s_Parameter_Builtin(VALUE self)
+{
+       Molecule *mol = NULL;
+       return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.bond(idx)          -> parameterRef
+ *     Parameter.bond(t1, t2)       -> parameterRef
+ *  
+ *  In the first form, the index-th bond parameter record is returned. In the second
+ *  form, the bond parameter for t1-t2 is looked up (the last index first). t1, t2
+ *  are the atom type string (up to 4 characters).
+ *  If the method is used as a singleton method, then the default parameters are looked up.
+ *  Otherwise, the specific parameters are looked up first, and then the default parameters.
+ *  In this case, t1 and t2 can be the atom index (0-based).
+ */
+static VALUE
+s_Parameter_Bond(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       int idx, idx2;
+       mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       if (argc == 1) {
+               int n = gBuiltinParameters->nbondPars + (mol && mol->par ? mol->par->nbondPars : 0);
+               idx = NUM2INT(rb_Integer(argv[0]));
+               if (idx < -n || idx >= n)
+                       rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
+               if (idx < 0)
+                       idx += n;
+               if (idx < gBuiltinParameters->nbondPars)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kBondParType, idx);
+               else
+                       return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx - gBuiltinParameters->nbondPars);
+       } else if (argc == 2) {
+               BondPar *bp;
+               idx = s_AtomTypeIndexFromValue(argv[0]);
+               idx2 = s_AtomTypeIndexFromValue(argv[1]);
+               if (mol != NULL) {
+                       if (idx < kAtomTypeMinimum || idx2 < kAtomTypeMinimum)
+                               rb_raise(rb_eMolbyError, "Atom type indices should be given in strings");
+                       bp = ParameterLookupBondPar(mol->par, idx, idx2, 0);
+                       if (bp != NULL)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
+               }
+               bp = ParameterLookupBondPar(gBuiltinParameters, idx, idx2, 0);
+               if (bp != NULL)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kBondParType, bp - gBuiltinParameters->bondPars);
+               else return Qnil;
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.angle(idx)          -> parameterRef
+ *     Parameter.angle(t1, t2, t3)   -> parameterRef
+ *  
+ *  In the first form, the index-th angle parameter record is returned. In the second
+ *  form, the bond parameter for t1-t2-t3 is looked up (the last index first). t1, t2, t3
+ *  are the atom type string (up to 4 characters).
+ *  If the method is used as a singleton method, then the default parameters are looked up.
+ *  Otherwise, the specific parameters are looked up first, and then the default parameters.
+ *  In this case, t1-t3 can be the atom index (0-based).
+ */
+static VALUE
+s_Parameter_Angle(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       int idx1, idx2, idx3;
+       mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       if (argc == 1) {
+               int n = gBuiltinParameters->nanglePars + (mol && mol->par ? mol->par->nanglePars : 0);
+               idx1 = NUM2INT(rb_Integer(argv[0]));
+               if (idx1 < -n || idx1 >= n)
+                       rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx1);
+               if (idx1 < 0)
+                       idx1 += n;
+               if (idx1 < gBuiltinParameters->nanglePars)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kAngleParType, idx1);
+               else
+                       return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx1 - gBuiltinParameters->nanglePars);
+       } else if (argc == 3) {
+               AnglePar *ap;
+               idx1 = s_AtomTypeIndexFromValue(argv[0]);
+               idx2 = s_AtomTypeIndexFromValue(argv[1]);
+               idx3 = s_AtomTypeIndexFromValue(argv[2]);
+               if (mol != NULL) {
+                       if (idx1 < kAtomTypeMinimum || idx2 < kAtomTypeMinimum || idx3 < kAtomTypeMinimum)
+                               rb_raise(rb_eMolbyError, "Atom type indices should be given in strings");
+                       ap = ParameterLookupAnglePar(mol->par, idx1, idx2, idx3, 0);
+                       if (ap != NULL)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
+               }
+               ap = ParameterLookupAnglePar(gBuiltinParameters, idx1, idx2, idx3, 0);
+               if (ap != NULL)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kAngleParType, ap - gBuiltinParameters->anglePars);
+               else return Qnil;
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.dihedral(idx)            -> parameterRef
+ *     Parameter.dihedral(t1, t2, t3, t4) -> parameterRef
+ *  
+ *  In the first form, the index-th dihedral parameter record is returned. In the second
+ *  form, the bond parameter for t1-t2-t3-t4 is looked up (the last index first). t1, t2, t3, t4
+ *  are the atom type string (up to 4 characters).
+ *  If the method is used as a singleton method, then the default parameters are looked up.
+ *  Otherwise, the specific parameters are looked up first, and then the default parameters.
+ *  In this case, t1-t4 can be the atom index (0-based).
+ */
+static VALUE
+s_Parameter_Dihedral(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       int idx1, idx2, idx3, idx4;
+       mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       if (argc == 1) {
+               int n = gBuiltinParameters->ndihedralPars + (mol && mol->par ? mol->par->ndihedralPars : 0);
+               idx1 = NUM2INT(rb_Integer(argv[0]));
+               if (idx1 < -n || idx1 >= n)
+                       rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx1);
+               if (idx1 < 0)
+                       idx1 += n;
+               if (idx1 < gBuiltinParameters->ndihedralPars)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kDihedralParType, idx1);
+               else
+                       return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx1 - gBuiltinParameters->ndihedralPars);
+       } else if (argc == 4) {
+               TorsionPar *tp;
+               idx1 = s_AtomTypeIndexFromValue(argv[0]);
+               idx2 = s_AtomTypeIndexFromValue(argv[1]);
+               idx3 = s_AtomTypeIndexFromValue(argv[2]);
+               idx4 = s_AtomTypeIndexFromValue(argv[2]);
+               if (mol != NULL) {
+                       if (idx1 < kAtomTypeMinimum || idx2 < kAtomTypeMinimum || idx3 < kAtomTypeMinimum || idx4 < kAtomTypeMinimum)
+                               rb_raise(rb_eMolbyError, "Atom type indices should be given in strings");
+                       tp = ParameterLookupDihedralPar(mol->par, idx1, idx2, idx3, idx4, 0);
+                       if (tp != NULL)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
+               }
+               tp = ParameterLookupDihedralPar(gBuiltinParameters, idx1, idx2, idx3, idx4, 0);
+               if (tp != NULL)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kDihedralParType, tp - gBuiltinParameters->dihedralPars);
+               else return Qnil;
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.improper(idx)            -> parameterRef
+ *     Parameter.improper(t1, t2, t3, t4) -> parameterRef
+ *  
+ *  In the first form, the index-th improper parameter record is returned. In the second
+ *  form, the bond parameter for t1-t2-t3-t4 is looked up (the last index first). t1, t2, t3, t4
+ *  are the atom type string (up to 4 characters).
+ *  If the method is used as a singleton method, then the default parameters are looked up.
+ *  Otherwise, the specific parameters are looked up first, and then the default parameters.
+ *  In this case, t1-t4 can be the atom index (0-based).
+ */
+static VALUE
+s_Parameter_Improper(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       int idx1, idx2, idx3, idx4;
+       mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       if (argc == 1) {
+               int n = gBuiltinParameters->nimproperPars + (mol && mol->par ? mol->par->nimproperPars : 0);
+               idx1 = NUM2INT(rb_Integer(argv[0]));
+               if (idx1 < -n || idx1 >= n)
+                       rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx1);
+               if (idx1 < 0)
+                       idx1 += n;
+               if (idx1 < gBuiltinParameters->nimproperPars)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kImproperParType, idx1);
+               else
+                       return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx1 - gBuiltinParameters->nimproperPars);
+       } else if (argc == 4) {
+               TorsionPar *tp;
+               idx1 = s_AtomTypeIndexFromValue(argv[0]);
+               idx2 = s_AtomTypeIndexFromValue(argv[1]);
+               idx3 = s_AtomTypeIndexFromValue(argv[2]);
+               idx4 = s_AtomTypeIndexFromValue(argv[2]);
+               if (mol != NULL) {
+                       if (idx1 < kAtomTypeMinimum || idx2 < kAtomTypeMinimum || idx3 < kAtomTypeMinimum || idx4 < kAtomTypeMinimum)
+                               rb_raise(rb_eMolbyError, "Atom type indices should be given in strings");
+                       tp = ParameterLookupImproperPar(mol->par, idx1, idx2, idx3, idx4, 0);
+                       if (tp != NULL)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
+               }
+               tp = ParameterLookupImproperPar(gBuiltinParameters, idx1, idx2, idx3, idx4, 0);
+               if (tp != NULL)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kImproperParType, tp - gBuiltinParameters->improperPars);
+               else return Qnil;
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.vdw(idx)            -> parameterRef
+ *     Parameter.vdw(t1)             -> parameterRef
+ *  
+ *  In the first form, the index-th vdw parameter record is returned. In the second
+ *  form, the vdw parameter for t1 is looked up (the last index first). t1
+ *  are the atom type string (up to 4 characters).
+ *  If the method is used as a singleton method, then the default parameters are looked up.
+ *  Otherwise, the specific parameters are looked up first, and then the default parameters.
+ *  In this case, t1 can be the atom index (0-based).
+ */
+static VALUE
+s_Parameter_Vdw(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       int idx1;
+       mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       if (argc == 1) {
+               if (rb_obj_is_kind_of(argv[0], rb_cNumeric)) {
+                       int n = gBuiltinParameters->nvdwPars + (mol && mol->par ? mol->par->nvdwPars : 0);
+                       idx1 = NUM2INT(rb_Integer(argv[0]));
+                       if (idx1 < -n || idx1 >= n)
+                               rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx1);
+                       if (idx1 < 0)
+                               idx1 += n;
+                       if (idx1 < gBuiltinParameters->nvdwPars)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kVdwParType, idx1);
+                       else
+                               return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx1 - gBuiltinParameters->nvdwPars);
+               } else {
+                       VdwPar *vp;
+                       idx1 = s_AtomTypeIndexFromValue(argv[0]);
+                       if (mol != NULL) {
+                               if (idx1 < kAtomTypeMinimum)
+                                       rb_raise(rb_eMolbyError, "Atom type indices should be given in strings");
+                               vp = ParameterLookupVdwPar(mol->par, idx1, 0);
+                               if (vp != NULL)
+                                       return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, vp - mol->par->vdwPars);
+                       }
+                       vp = ParameterLookupVdwPar(gBuiltinParameters, idx1, 0);
+                       if (vp != NULL)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kVdwParType, vp - gBuiltinParameters->vdwPars);
+                       else return Qnil;
+               }
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.vdw_pair(idx)          -> parameterRef
+ *     Parameter.vdw_pair(t1, t2)       -> parameterRef
+ *  
+ *  In the first form, the index-th vdw-pair parameter record is returned. In the second
+ *  form, the vdw-pair parameter for t1-t2 is looked up (the last index first). t1, t2
+ *  are the atom type string (up to 4 characters).
+ *  If the method is used as a singleton method, then the default parameters are looked up.
+ *  Otherwise, the specific parameters are looked up first, and then the default parameters.
+ *  In this case, t1 and t2 can be the atom index (0-based).
+ */
+static VALUE
+s_Parameter_VdwPair(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       int idx1, idx2;
+       mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       if (argc == 1) {
+               int n = gBuiltinParameters->nvdwpPars + (mol && mol->par ? mol->par->nvdwpPars : 0);
+               idx1 = NUM2INT(rb_Integer(argv[0]));
+               if (idx1 < -n || idx1 >= n)
+                       rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx1);
+               if (idx1 < 0)
+                       idx1 += n;
+               if (idx1 < gBuiltinParameters->nvdwpPars)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kVdwPairParType, idx1);
+               else
+                       return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx1 - gBuiltinParameters->nvdwpPars);
+       } else if (argc == 2) {
+               VdwPairPar *vp;
+               idx1 = s_AtomTypeIndexFromValue(argv[0]);
+               idx2 = s_AtomTypeIndexFromValue(argv[1]);
+               if (mol != NULL) {
+                       if (idx1 < kAtomTypeMinimum || idx2 < kAtomTypeMinimum)
+                               rb_raise(rb_eMolbyError, "Atom type indices should be given in strings");
+                       vp = ParameterLookupVdwPairPar(mol->par, idx1, idx2, 0);
+                       if (vp != NULL)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, vp - mol->par->vdwpPars);
+               }
+               vp = ParameterLookupVdwPairPar(gBuiltinParameters, idx1, idx2, 0);
+               if (vp != NULL)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kVdwPairParType, vp - gBuiltinParameters->vdwpPars);
+               else return Qnil;
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.vdw_cutoff(idx)          -> parameterRef
+ *     Parameter.vdw_cutoff(t1, t2)       -> parameterRef
+ *  
+ *  In the first form, the index-th vdw-cutoff parameter record is returned. In the second
+ *  form, the vdw-pair parameter for t1-t2 is looked up (the last index first). t1, t2
+ *  are the atom type string (up to 4 characters).
+ *  If the method is used as a singleton method, then the default parameters are looked up.
+ *  Otherwise, the specific parameters are looked up first, and then the default parameters.
+ *  In this case, t1 and t2 can be the atom index (0-based).
+ */
+static VALUE
+s_Parameter_VdwCutoff(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       int idx1, idx2;
+       mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       if (argc == 1) {
+               int n = gBuiltinParameters->nvdwCutoffPars + (mol && mol->par ? mol->par->nvdwCutoffPars : 0);
+               idx1 = NUM2INT(rb_Integer(argv[0]));
+               if (idx1 < -n || idx1 >= n)
+                       rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx1);
+               if (idx1 < 0)
+                       idx1 += n;
+               if (idx1 < gBuiltinParameters->nvdwCutoffPars)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kVdwCutoffParType, idx1);
+               else
+                       return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx1 - gBuiltinParameters->nvdwCutoffPars);
+       } else if (argc == 2) {
+               VdwCutoffPar *vp;
+               idx1 = s_AtomTypeIndexFromValue(argv[0]);
+               idx2 = s_AtomTypeIndexFromValue(argv[1]);
+               if (mol != NULL) {
+                       if (idx1 < kAtomTypeMinimum || idx2 < kAtomTypeMinimum)
+                               rb_raise(rb_eMolbyError, "Atom type indices should be given in strings");
+                       vp = ParameterLookupVdwCutoffPar(mol->par, idx1, idx2, 0);
+                       if (vp != NULL)
+                               return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, vp - mol->par->vdwCutoffPars);
+               }
+               vp = ParameterLookupVdwCutoffPar(gBuiltinParameters, idx1, idx2, 0);
+               if (vp != NULL)
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kVdwCutoffParType, vp - gBuiltinParameters->vdwCutoffPars);
+               else return Qnil;
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.atom(idx)            -> parameterRef
+ *     Parameter.atom(t1)             -> parameterRef
+ *  
+ *  In the first form, the index-th atom parameter record is returned. In the second
+ *  form, the atom parameter for t1 is looked up (the last index first). t1
+ *  are the element name string (up to 4 characters).
+ *  Unlike other Parameter methods, this is used only as a singleton method, because
+ *  the all atom parameters are global.
+ */
+static VALUE
+s_Parameter_Atom(int argc, VALUE *argv, VALUE self)
+{
+       int idx1;
+       if (argc == 1) {
+               if (rb_obj_is_kind_of(argv[0], rb_cNumeric)) {
+                       int n = gCountDispAtomParameters;
+                       idx1 = NUM2INT(rb_Integer(argv[0]));
+                       if (idx1 < -n || idx1 >= n)
+                               rb_raise(rb_eMolbyError, "Atom parameter index (%d) out of range", idx1);
+                       if (idx1 < 0)
+                               idx1 += n;
+                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kAtomParType, idx1);
+               } else {
+                       AtomPar *ap;
+                       char name[6];
+                       int i;
+                       strncpy(name, StringValuePtr(argv[0]), 4);
+                       name[4] = 0;
+                       for (i = gCountDispAtomParameters - 1, ap = gDispAtomParameters + i; i >= 0; i--) {
+                               if (strncmp(ap->name, name, 4) == 0)
+                                       return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kAtomParType, i);
+                       }
+                       return Qnil;
+               }
+       }
+       rb_raise(rb_eMolbyError, "Wrong number of arguments");
+}
+
+/*
+ *  call-seq:
+ *     Parameter.nbonds          -> integer
+ *  
+ *  Returns the number of bond parameters. If the method is used as a singleton method,
+ *  then only the default parameters are examined. Otherwise, the total number of the
+ *  specific parameters _and_ the default (global) parameters are returned.
+ */
+static VALUE
+s_Parameter_Nbonds(VALUE self)
+{
+       Int n;
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       n = gBuiltinParameters->nbondPars;
+       if (mol && mol->par)
+               n += mol->par->nbondPars;
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.nangles          -> integer
+ *  
+ *  Returns the number of angle parameters. If the method is used as a singleton method,
+ *  then only the default parameters are examined. Otherwise, the total number of the
+ *  specific parameters _and_ the default (global) parameters are returned.
+ */
+static VALUE
+s_Parameter_Nangles(VALUE self)
+{
+       Int n;
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       n = gBuiltinParameters->nanglePars;
+       if (mol && mol->par)
+               n += mol->par->nanglePars;
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.ndihedrals          -> integer
+ *  
+ *  Returns the number of dihedral parameters. If the method is used as a singleton method,
+ *  then only the default parameters are examined. Otherwise, the total number of the
+ *  specific parameters _and_ the default (global) parameters are returned.
+ */
+static VALUE
+s_Parameter_Ndihedrals(VALUE self)
+{
+       Int n;
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       n = gBuiltinParameters->ndihedralPars;
+       if (mol && mol->par)
+               n += mol->par->ndihedralPars;
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.nimpropers          -> integer
+ *  
+ *  Returns the number of improper parameters. If the method is used as a singleton method,
+ *  then only the default parameters are examined. Otherwise, the total number of the
+ *  specific parameters _and_ the default (global) parameters are returned.
+ */
+static VALUE
+s_Parameter_Nimpropers(VALUE self)
+{
+       Int n;
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       n = gBuiltinParameters->nimproperPars;
+       if (mol && mol->par)
+               n += mol->par->nimproperPars;
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.nvdws          -> integer
+ *  
+ *  Returns the number of vdw parameters. If the method is used as a singleton method,
+ *  then only the default parameters are examined. Otherwise, the total number of the
+ *  specific parameters _and_ the default (global) parameters are returned.
+ */
+static VALUE
+s_Parameter_Nvdws(VALUE self)
+{
+       Int n;
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       n = gBuiltinParameters->nvdwPars;
+       if (mol && mol->par)
+               n += mol->par->nvdwPars;
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.nvdw_pairs          -> integer
+ *  
+ *  Returns the number of vdw pair parameters. If the method is used as a singleton method,
+ *  then only the default parameters are examined. Otherwise, the total number of the
+ *  specific parameters _and_ the default (global) parameters are returned.
+ */
+static VALUE
+s_Parameter_NvdwPairs(VALUE self)
+{
+       Int n;
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       n = gBuiltinParameters->nvdwpPars;
+       if (mol && mol->par)
+               n += mol->par->nvdwpPars;
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.nvdw_cutoffs          -> integer
+ *  
+ *  Returns the number of vdw cutoff parameters. If the method is used as a singleton method,
+ *  then only the default parameters are examined. Otherwise, the total number of the
+ *  specific parameters _and_ the default (global) parameters are returned.
+ */
+static VALUE
+s_Parameter_NvdwCutoffs(VALUE self)
+{
+       Int n;
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       n = gBuiltinParameters->nvdwCutoffPars;
+       if (mol && mol->par)
+               n += mol->par->nvdwCutoffPars;
+       return INT2NUM(n);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.natoms          -> integer
+ *  
+ *  Returns the number of atom parameters. Unlike other Parameter methods, this
+ *  method is used only as a singleton method, because all atom parameters are global.
+ */
+static VALUE
+s_Parameter_Natoms(VALUE self)
+{
+       return INT2NUM(gCountDispAtomParameters);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.bonds          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
+ *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_Bonds(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.angles          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
+ *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_Angles(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.dihedrals          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
+ *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_Dihedrals(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.impropers          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
+ *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_Impropers(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.vdws          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
+ *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_Vdws(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.vdw_pairs          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
+ *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_VdwPairs(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.vdw_cutoffs          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
+ *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_VdwCutoffs(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
+}
+
+/*
+ *  call-seq:
+ *     Parameter.atoms          -> ParEnumerable
+ *  
+ *  Returns a ParEnumerable value that (formally) points to the collection of atom parameters.
+ *  Parameter.atoms[x] is equivalent to Parameter.atom(x). ParEnumerable class is
+ *  useful when all accessible parameters should be examined by use of 'each' method.
+ */
+static VALUE
+s_Parameter_Atoms(VALUE self)
+{
+       Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
+       return s_NewParEnumerableValueFromMoleculeAndType(mol, kAtomParType);
+}
+
+#pragma mark ====== ParEnumerable Class ======
+
+/*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
+ and the parameter type. If the Molecule is NULL, then it refers to the
+ global (built-in) parameters. Note that, even when the Molecule is not NULL,
+ the global parameters are always accessible. */
+
+typedef struct ParEnumerable {
+       Molecule *mol;
+       Int parType;   /*  Same as parType in ParameterRef  */
+} ParEnumerable;
+
+static ParEnumerable *
+s_ParEnumerableNew(Molecule *mol, Int parType)
+{
+       ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
+       if (pen != NULL) {
+               pen->mol = mol;
+               if (mol != NULL)
+                       MoleculeRetain(mol);
+               pen->parType = parType;
+       }
+       return pen;
+}
+
+static void
+s_ParEnumerableRelease(ParEnumerable *pen)
+{
+       if (pen != NULL) {
+               if (pen->mol != NULL)
+                       MoleculeRelease(pen->mol);
+               free(pen);
+       }
+}
+
+static Molecule *
+s_MoleculeFromParEnumerableValue(VALUE val)
+{
+       ParEnumerable *pen;
+    Data_Get_Struct(val, ParEnumerable, pen);
+       return pen->mol;
+}
+
+static VALUE
+s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
+{
+       ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
+       if (pen == NULL)
+               rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
+       return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
+}
+
+/*  []  */
+static VALUE
+s_ParEnumerable_Aref(int argc, VALUE *argv, VALUE self)
+{
+       ParEnumerable *pen;
+    Data_Get_Struct(self, ParEnumerable, pen);
+       switch (pen->parType) {
+                       /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
+               case kBondParType:      return s_Parameter_Bond(argc, argv, self);
+               case kAngleParType:     return s_Parameter_Angle(argc, argv, self); 
+               case kDihedralParType:  return s_Parameter_Dihedral(argc, argv, self);
+               case kImproperParType:  return s_Parameter_Improper(argc, argv, self);
+               case kVdwParType:       return s_Parameter_Vdw(argc, argv, self);
+               case kVdwPairParType:   return s_Parameter_VdwPair(argc, argv, self);
+               case kVdwCutoffParType: return s_Parameter_VdwCutoff(argc, argv, self);
+               case kAtomParType:      return s_Parameter_Atom(argc, argv, self);
+               default:
+                       rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
+       }
+       return Qnil;  /*  Not reached  */
+}
+
+static VALUE
+s_ParEnumerable_Length(VALUE self)
+{
+       ParEnumerable *pen;
+    Data_Get_Struct(self, ParEnumerable, pen);
+       switch (pen->parType) {
+                       /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
+               case kBondParType:      return s_Parameter_Nbonds(self);
+               case kAngleParType:     return s_Parameter_Nangles(self); 
+               case kDihedralParType:  return s_Parameter_Ndihedrals(self);
+               case kImproperParType:  return s_Parameter_Nimpropers(self);
+               case kVdwParType:       return s_Parameter_Nvdws(self);
+               case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
+               case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
+               case kAtomParType:      return s_Parameter_Natoms(self);
+               default:
+                       rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
+       }
+       return Qnil;  /*  Not reached  */
+}
+
+VALUE
+s_ParEnumerable_Each(VALUE self)
+{
+       VALUE aval;
+       ParEnumerable *pen;
+       ParameterRef *pref;
+       int i;
+       Int *np1, *np2;
+    Data_Get_Struct(self, ParEnumerable, pen);
+       switch (pen->parType) {
+               case kBondParType:      np1 = &gBuiltinParameters->nbondPars; break;
+               case kAngleParType:     np1 = &gBuiltinParameters->nanglePars; break;
+               case kDihedralParType:  np1 = &gBuiltinParameters->ndihedralPars; break;
+               case kImproperParType:  np1 = &gBuiltinParameters->nimproperPars; break;
+               case kVdwParType:       np1 = &gBuiltinParameters->nvdwPars; break;
+               case kVdwPairParType:   np1 = &gBuiltinParameters->nvdwpPars; break;
+               case kVdwCutoffParType: np1 = &gBuiltinParameters->nvdwCutoffPars; break;
+               case kAtomParType:      np1 = &gCountDispAtomParameters; break;
+               default:
+                       rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
+       }
+       aval = ValueFromMoleculeWithParameterTypeAndIndex(NULL, pen->parType, 0);
+       Data_Get_Struct(aval, ParameterRef, pref);
+       for (i = 0; i < *np1; i++) {
+               pref->idx = i;
+               rb_yield(aval);
+       }
+       if (pen->mol != NULL && pen->mol->par != NULL && pen->parType != kAtomParType) {
+               /*  (char *)np1 - (char *)gBuiltinParameters is 'offsetof(nXXXXPars)'  */
+               np2 = (Int *)((char *)(pen->mol->par) + ((char *)np1 - (char *)gBuiltinParameters));
+               /*  A new aval is allocated, to avoid intercepting the release mechanism  */
+               /*  (Manually releasing and retaining pref->par should work, but I don't like it)  */
+               aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
+               Data_Get_Struct(aval, ParameterRef, pref);
+               for (i = 0; i < *np2; i++) {
+                       pref->idx = i;
+                       rb_yield(aval);
+               }
+       }
+    return self;
+}
+
+VALUE
+s_ParEnumerable_ReverseEach(VALUE self)
+{
+       VALUE aval;
+       ParEnumerable *pen;
+       ParameterRef *pref;
+       int i;
+       Int *np1, *np2;
+    Data_Get_Struct(self, ParEnumerable, pen);
+       switch (pen->parType) {
+               case kBondParType:      np1 = &gBuiltinParameters->nbondPars; break;
+               case kAngleParType:     np1 = &gBuiltinParameters->nanglePars; break;
+               case kDihedralParType:  np1 = &gBuiltinParameters->ndihedralPars; break;
+               case kImproperParType:  np1 = &gBuiltinParameters->nimproperPars; break;
+               case kVdwParType:       np1 = &gBuiltinParameters->nvdwPars; break;
+               case kVdwPairParType:   np1 = &gBuiltinParameters->nvdwpPars; break;
+               case kVdwCutoffParType: np1 = &gBuiltinParameters->nvdwCutoffPars; break;
+               case kAtomParType:      np1 = &gCountDispAtomParameters; break;
+               default:
+                       rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
+       }
+       if (pen->mol != NULL && pen->mol->par != NULL && pen->parType != kAtomParType) {
+               /*  (char *)np1 - (char *)gBuiltinParameters is 'offsetof(nXXXXPars)'  */
+               np2 = (Int *)((char *)(pen->mol->par) + ((char *)np1 - (char *)gBuiltinParameters));
+               aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
+               Data_Get_Struct(aval, ParameterRef, pref);
+               for (i = *np2 - 1; i >= 0; i--) {
+                       pref->idx = i;
+                       rb_yield(aval);
+               }
+       }
+       aval = ValueFromMoleculeWithParameterTypeAndIndex(NULL, pen->parType, 0);
+       Data_Get_Struct(aval, ParameterRef, pref);
+       for (i = *np1 - 1; i >= 0; i--) {
+               pref->idx = i;
+               rb_yield(aval);
+       }
+    return self;
+}
+
+/*
+ *  call-seq:
+ *     ParEnumerable.insert(idx = nil, pref = nil)       -> ParameterRef
+ *  
+ *  Insert a new parameter at the specified position (if idx is nil, then at the end).
+ *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
+ *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
+ *  parameter is left undefined.
+ *  Throws an exception if ParEnumerable points to the global parameter.
+ */
+static VALUE
+s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
+{
+       VALUE ival, pval;
+       ParEnumerable *pen;
+       int i, n;
+       IntGroup *ig;
+       UnionPar u;
+       MolAction *act;
+    Data_Get_Struct(self, ParEnumerable, pen);
+       if (pen->mol == NULL)
+               rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
+       n = ParameterGetCountForType(pen->mol->par, pen->parType);
+       rb_scan_args(argc, argv, "02", &ival, &pval);
+       if (ival != Qnil) {
+               i = NUM2INT(rb_Integer(ival));
+               if (i < 0 || i > n)
+                       rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
+               n = i;
+       }
+       if (pval != Qnil) {
+               Int type;
+               UnionPar *up = s_UnionParFromValue(pval, &type, 0);
+               if (up == NULL || type != pen->parType)
+                       rb_raise(rb_eMolbyError, "the parameter specification is not correct");
+               u = *up;
+               u.bond.src = 0;
+       } else {
+               memset(&u, 0, sizeof(u));
+               u.bond.src = -1;
+       }
+       ig = IntGroupNewWithPoints(n, 1, -1);
+       ParameterInsert(pen->mol->par, pen->parType, &u, ig);
+
+       act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
+       MolActionCallback_registerUndo(pen->mol, act);
+       MolActionRelease(act);
+       
+       IntGroupRelease(ig);
+       return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
+}
+
+/*
+ *  call-seq:
+ *     ParEnumerable.delete(int)
+ *     ParEnumerable.delete(intgroup)
+ *  
+ *  Delete the parameter(s) specified by the argument.
+ */
+static VALUE
+s_ParEnumerable_Delete(VALUE self, VALUE ival)
+{
+       ParEnumerable *pen;
+       int i, n;
+       IntGroup *ig;
+    Data_Get_Struct(self, ParEnumerable, pen);
+       if (pen->mol == NULL)
+               rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
+       n = ParameterGetCountForType(pen->mol->par, pen->parType);
+       if (rb_obj_is_kind_of(ival, rb_cInteger)) {
+               ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
+               i = 1;
+       } else {
+               ig = IntGroupFromValue(ival);
+               if ((i = IntGroupGetCount(ig)) == 0) {
+                       IntGroupRelease(ig);
+                       return Qnil;
+               }
+       }
+       if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
+               rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
+       n = i;
+
+       MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
+       IntGroupRelease(ig);
+       return ival;
+}
+
+/*
+ *  call-seq:
+ *     ParEnumerable.lookup(atom_types, options, ...) -> ParameterRef
+ *     ParEnumerable.lookup(atom_type_string, options, ...) -> ParameterRef
+ *
+ *  Find the parameter record that matches the given atom types. The atom types are given
+ *  either as an array of string, or a single string delimited by whitespaces.
+ *  Options are given as symbols. Valid values are :global (look for global parameters), :local
+ *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
+ *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
+ */
+static VALUE
+s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
+{
+       VALUE atval, optval;
+       ParEnumerable *pen;
+       Molecule *mol;
+       UInt t[4];
+       int i, n, idx, flags;
+
+    Data_Get_Struct(self, ParEnumerable, pen);
+       rb_scan_args(argc, argv, "1*", &atval, &optval);
+
+       /*  Get the atom types  */
+       switch (pen->parType) {
+               case kBondParType: n = 2; break;
+               case kAngleParType: n = 3; break;
+               case kDihedralParType: n = 4; break;
+               case kImproperParType: n = 4; break;
+               case kVdwParType: n = 1; break;
+               case kVdwPairParType: n = 2; break;
+               default: return Qnil;
+       }
+       s_ScanAtomTypes(atval, n, t);
+
+       /*  Analyze options  */
+       flags = 0;
+       n = RARRAY_LEN(optval);
+       for (i = 0; i < n; i++) {
+               VALUE oval = RARRAY_PTR(optval)[i];
+               if (oval == ID2SYM(rb_intern("global")))
+                       flags |= kParameterLookupGlobal;
+               else if (oval == ID2SYM(rb_intern("local")))
+                       flags |= kParameterLookupLocal;
+               else if (oval == ID2SYM(rb_intern("missing")))
+                       flags |= kParameterLookupMissing;
+               else if (oval == ID2SYM(rb_intern("nowildcard")))
+                       flags |= kParameterLookupNoWildcard;
+               else if (oval == ID2SYM(rb_intern("nobasetype")))
+                       flags |= kParameterLookupNoBaseAtomType;
+               else if (oval == ID2SYM(rb_intern("create")))
+                       flags |= 256;
+       }
+       if (flags == 0)
+               flags = kParameterLookupGlobal | kParameterLookupLocal;
+
+       idx = -1;
+       mol = pen->mol;
+       switch (pen->parType) {
+               case kBondParType: {
+                       BondPar *bp;
+                       if (pen->mol != NULL) {
+                               bp = ParameterLookupBondPar(pen->mol->par, t[0], t[1], flags);
+                               if (bp != NULL) {
+                                       idx = bp - pen->mol->par->bondPars;
+                                       break;
+                               }
+                       }
+                       bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], flags);
+                       if (bp != NULL) {
+                               idx = bp - gBuiltinParameters->bondPars;
+                               mol = NULL;
+                       }
+                       break;
+               }
+               case kAngleParType: {
+                       AnglePar *ap;
+                       if (pen->mol != NULL) {
+                               ap = ParameterLookupAnglePar(pen->mol->par, t[0], t[1], t[2], flags);
+                               if (ap != NULL) {
+                                       idx = ap - pen->mol->par->anglePars;
+                                       break;
+                               }
+                       }
+                       ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], flags);
+                       if (ap != NULL) {
+                               idx = ap - gBuiltinParameters->anglePars;
+                               mol = NULL;
+                       }
+                       break;
+               }
+               case kDihedralParType: {
+                       TorsionPar *tp;
+                       if (pen->mol != NULL) {
+                               tp = ParameterLookupDihedralPar(pen->mol->par, t[0], t[1], t[2], t[3], flags);
+                               if (tp != NULL) {
+                                       idx = tp - pen->mol->par->dihedralPars;
+                                       break;
+                               }
+                       }
+                       tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], flags);
+                       if (tp != NULL) {
+                               idx = tp - gBuiltinParameters->dihedralPars;
+                               mol = NULL;
+                       }
+                       break;
+               }
+               case kImproperParType: {
+                       TorsionPar *tp;
+                       if (pen->mol != NULL) {
+                               tp = ParameterLookupImproperPar(pen->mol->par, t[0], t[1], t[2], t[3], flags);
+                               if (tp != NULL) {
+                                       idx = tp - pen->mol->par->improperPars;
+                                       break;
+                               }
+                       }
+                       tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], flags);
+                       if (tp != NULL) {
+                               idx = tp - gBuiltinParameters->improperPars;
+                               mol = NULL;
+                       }
+                       break;
+               }       
+               case kVdwParType: {
+                       VdwPar *vp;
+                       if (pen->mol != NULL) {
+                               vp = ParameterLookupVdwPar(pen->mol->par, t[0], flags);
+                               if (vp != NULL) {
+                                       idx = vp - pen->mol->par->vdwPars;
+                                       break;
+                               }
+                       }
+                       vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], flags);
+                       if (vp != NULL) {
+                               idx = vp - gBuiltinParameters->vdwPars;
+                               mol = NULL;
+                       }
+                       break;
+               }       
+               case kVdwPairParType: {
+                       VdwPairPar *vp;
+                       if (pen->mol != NULL) {
+                               vp = ParameterLookupVdwPairPar(pen->mol->par, t[0], t[1], flags);
+                               if (vp != NULL) {
+                                       idx = vp - pen->mol->par->vdwpPars;
+                                       break;
+                               }
+                       }
+                       vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], flags);
+                       if (vp != NULL) {
+                               idx = vp - gBuiltinParameters->vdwpPars;
+                               mol = NULL;
+                       }
+                       break;
+               }
+               default:
+                       return Qnil;
+       }
+       if (idx < 0) {
+               if (flags & 256) {
+                       VALUE rval = s_ParEnumerable_Insert(0, NULL, self);
+                       s_ParameterRef_SetAtomTypes(rval, atval);
+                       return rval;
+               } else return Qnil;
+       }
+       return ValueFromMoleculeWithParameterTypeAndIndex(mol, pen->parType, idx);
+}
+
+#pragma mark ====== AtomRef Class ======
+
+/*  Forward declaration for register undo  */
+static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
+
+/*  Ruby string "set_atom_attr"  */
+static VALUE s_SetAtomAttrString;
+
+static int
+s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
+{
+       AtomRef *aref;
+       int idx;
+       Data_Get_Struct(self, AtomRef, aref);
+       idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
+       if (idx < 0 || idx >= aref->mol->natoms)
+               rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
+       if (app != NULL)
+               *app = aref->mol->atoms + idx;
+       if (mpp != NULL)
+               *mpp = aref->mol;
+       return idx;
+}
+
+static Atom *
+s_AtomFromValue(VALUE self)
+{
+       Atom *ap;
+       s_AtomIndexFromValue(self, &ap, NULL);
+       return ap;
+}
+
+static void
+s_NotifyModificationForAtomRef(VALUE self)
+{
+       AtomRef *aref;
+       Data_Get_Struct(self, AtomRef, aref);
+       MoleculeIncrementModifyCount(aref->mol);
+}
+
+static void
+s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
+{
+       AtomRef *aref;
+       Data_Get_Struct(self, AtomRef, aref);
+       if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
+               /*  Register undo  */
+               MolAction *act;
+               act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
+               MolActionCallback_registerUndo(aref->mol, act);
+               MoleculeCallback_notifyModification(aref->mol, 0);
+               /*  Request MD rebuilt if necessary  */
+               if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
+                       aref->mol->needsMDRebuild = 1;
+       }
+}
+
+VALUE
+ValueFromMoleculeAndIndex(Molecule *mol, int idx)
+{
+       AtomRef *aref;
+       aref = AtomRefNew(mol, idx);
+       return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
+}
+
+static VALUE s_AtomRef_GetIndex(VALUE self) {
+       return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
+}
+
+static VALUE s_AtomRef_GetSegSeq(VALUE self) {
+       return INT2NUM(s_AtomFromValue(self)->segSeq);
+}
+
+static VALUE s_AtomRef_GetSegName(VALUE self) {
+       char *p = s_AtomFromValue(self)->segName;
+       return rb_str_new(p, strlen_limit(p, 4));
+}
+
+static VALUE s_AtomRef_GetResSeq(VALUE self) {
+       return INT2NUM(s_AtomFromValue(self)->resSeq);
+}
+
+static VALUE s_AtomRef_GetResName(VALUE self) {
+       char *p = s_AtomFromValue(self)->resName;
+       return rb_str_new(p, strlen_limit(p, 4));
+}
+
+static VALUE s_AtomRef_GetName(VALUE self) {
+       char *p = s_AtomFromValue(self)->aname;
+       return rb_str_new(p, strlen_limit(p, 4));
+}
+
+static VALUE s_AtomRef_GetAtomType(VALUE self) {
+       int type = s_AtomFromValue(self)->type;
+       char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
+       return rb_str_new(p, strlen_limit(p, 6));
+}
+
+static VALUE s_AtomRef_GetCharge(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->charge);
+}
+
+static VALUE s_AtomRef_GetWeight(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->weight);
+}
+
+static VALUE s_AtomRef_GetElement(VALUE self) {
+       char *p = s_AtomFromValue(self)->element;
+       return rb_str_new(p, strlen_limit(p, 4));
+}
+
+static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
+       return INT2NUM(s_AtomFromValue(self)->atomicNumber);
+}
+
+static VALUE s_AtomRef_GetConnects(VALUE self) {
+       VALUE retval;
+       int i;
+       Atom *ap = s_AtomFromValue(self);
+       retval = rb_ary_new();
+       for (i = 0; i < ap->nconnects; i++)
+               rb_ary_push(retval, INT2NUM(ap->connects[i]));
+       return retval;
+}
+
+static VALUE s_AtomRef_GetR(VALUE self) {
+       return ValueFromVector(&(s_AtomFromValue(self)->r));
+}
+
+static VALUE s_AtomRef_GetX(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->r.x);
+}
+
+static VALUE s_AtomRef_GetY(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->r.y);
+}
+
+static VALUE s_AtomRef_GetZ(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->r.z);
+}
+
+static Vector s_AtomRef_GetCrAsVector(VALUE self) {
+       Atom *ap;
+       Molecule *mp;
+       Vector r1;
+       s_AtomIndexFromValue(self, &ap, &mp);
+       r1 = ap->r;
+/*     if (mp->is_xtal_coord)
+               TransformVec(&r1, mp->cell->tr, &r1); */
+       return r1;
+}
+
+static VALUE s_AtomRef_GetCr(VALUE self) {
+       Vector r1 = s_AtomRef_GetCrAsVector(self);
+       return ValueFromVector(&r1);
+}
+
+static VALUE s_AtomRef_GetCx(VALUE self) {
+       return rb_float_new(s_AtomRef_GetCrAsVector(self).x);
+}
+
+static VALUE s_AtomRef_GetCy(VALUE self) {
+       return rb_float_new(s_AtomRef_GetCrAsVector(self).y);
+}
+
+static VALUE s_AtomRef_GetCz(VALUE self) {
+       return rb_float_new(s_AtomRef_GetCrAsVector(self).z);
+}
+
+static VALUE s_AtomRef_GetV(VALUE self) {
+       return ValueFromVector(&(s_AtomFromValue(self)->v));
+}
+
+static VALUE s_AtomRef_GetF(VALUE self) {
+       return ValueFromVector(&(s_AtomFromValue(self)->f));
+}
+
+static VALUE s_AtomRef_GetOccupancy(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->occupancy);
+}
+
+static VALUE s_AtomRef_GetTempFactor(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->tempFactor);
+}
+
+static VALUE s_AtomRef_GetAniso(VALUE self) {
+       VALUE retval;
+       int i;
+       Atom *ap = s_AtomFromValue(self);
+       if (ap->aniso == NULL)
+               return Qnil;
+       retval = rb_ary_new();
+       for (i = 0; i < 6; i++)
+               rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
+       return retval;
+}
+
+static VALUE s_AtomRef_GetIntCharge(VALUE self) {
+       return INT2NUM(s_AtomFromValue(self)->intCharge);
+}
+
+static VALUE s_AtomRef_GetFixForce(VALUE self) {
+       return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
+}
+
+static VALUE s_AtomRef_GetFixPos(VALUE self) {
+       return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
+}
+
+static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
+       rb_raise(rb_eMolbyError, "index cannot be directly set");
+       return Qnil;
+}
+
+static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
+       VALUE oval = s_AtomRef_GetSegSeq(self);
+       val = rb_Integer(val);
+       s_AtomFromValue(self)->segSeq = NUM2INT(val);
+       s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
+       char *p = StringValuePtr(val);
+       VALUE oval = s_AtomRef_GetSegName(self);
+       strncpy(s_AtomFromValue(self)->segName, p, 4);
+       s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
+       rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
+       return val; /* Not reached */
+}
+
+static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
+       char *p = StringValuePtr(val);
+       VALUE oval = s_AtomRef_GetName(self);
+       strncpy(s_AtomFromValue(self)->aname, p, 4);
+       s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
+       char *p = StringValuePtr(val);
+       VALUE oval = s_AtomRef_GetAtomType(self);
+       int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
+       if (type != 0 && type < kAtomTypeMinimum)
+               rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
+       s_AtomFromValue(self)->type = type;
+       s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
+       VALUE oval = s_AtomRef_GetCharge(self);
+       val = rb_Float(val);
+       s_AtomFromValue(self)->charge = NUM2DBL(val);
+       s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
+       VALUE oval = s_AtomRef_GetWeight(self);
+       val = rb_Float(val);
+       s_AtomFromValue(self)->weight = NUM2DBL(val);
+       s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
+       Double w;
+       Atom *ap = s_AtomFromValue(self);
+       char *p = StringValuePtr(val);
+       VALUE oval = s_AtomRef_GetElement(self);
+       ap->atomicNumber = ElementToInt(p);
+       ElementToString(ap->atomicNumber, ap->element);
+       if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
+               ap->weight = w;
+       s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
+       Double w;
+       Atom *ap = s_AtomFromValue(self);
+       VALUE oval = s_AtomRef_GetAtomicNumber(self);
+       val = rb_Integer(val);
+       ap->atomicNumber = NUM2INT(val);
+       ElementToString(ap->atomicNumber, ap->element);
+       if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
+               ap->weight = w;
+       s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
+       rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
+       return val; /* Not reached */
+}
+
+static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
+       Vector v;
+       VALUE oval = s_AtomRef_GetR(self);
+       VectorFromValue(val, &v);
+       s_AtomFromValue(self)->r = v;
+       s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
+       Double f;
+       VALUE oval = s_AtomRef_GetX(self);
+       val = rb_Float(val);
+       f = NUM2DBL(val);
+       s_AtomFromValue(self)->r.x = f;
+       s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
+       Double f;
+       VALUE oval = s_AtomRef_GetY(self);
+       val = rb_Float(val);
+       f = NUM2DBL(val);
+       s_AtomFromValue(self)->r.y = f;
+       s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
+       Double f;
+       VALUE oval = s_AtomRef_GetZ(self);
+       val = rb_Float(val);
+       f = NUM2DBL(val);
+       s_AtomFromValue(self)->r.z = f;
+       s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetCr(VALUE self, VALUE val) {
+       Vector v, ov;
+       Atom *ap;
+       Molecule *mp;
+       s_AtomIndexFromValue(self, &ap, &mp);
+       ov = ap->r;
+       VectorFromValue(val, &v);
+/*     if (mp->is_xtal_coord)
+               TransformVec(&v, mp->cell->rtr, &v); */
+       ap->r = v;
+       s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
+       return val;
+}
+
+static VALUE s_AtomRef_SetCx(VALUE self, VALUE val) {
+       double f;
+       Vector v, ov;
+       Atom *ap;
+       Molecule *mp;
+       s_AtomIndexFromValue(self, &ap, &mp);
+       ov = v = ap->r;
+       val = rb_Float(val);
+       f = NUM2DBL(val);
+/*     if (mp->is_xtal_coord) {
+               TransformVec(&v, mp->cell->tr, &v);
+               v.x = f;
+               TransformVec(&v, mp->cell->rtr, &v);
+       } else */ v.x = f;
+       ap->r = v;
+       s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
+       return val;
+}
+
+static VALUE s_AtomRef_SetCy(VALUE self, VALUE val) {
+       double f;
+       Vector v, ov;
+       Atom *ap;
+       Molecule *mp;
+       s_AtomIndexFromValue(self, &ap, &mp);
+       ov = v = ap->r;
+       val = rb_Float(val);
+       f = NUM2DBL(val);
+/*     if (mp->is_xtal_coord) {
+               TransformVec(&v, mp->cell->tr, &v);
+               v.y = f;
+               TransformVec(&v, mp->cell->rtr, &v);
+       } else */ v.y = f;
+       ap->r = v;
+       s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
+       return val;
+}
+
+static VALUE s_AtomRef_SetCz(VALUE self, VALUE val) {
+       double f;
+       Vector v, ov;
+       Atom *ap;
+       Molecule *mp;
+       s_AtomIndexFromValue(self, &ap, &mp);
+       ov = v = ap->r;
+       val = rb_Float(val);
+       f = NUM2DBL(val);
+/*     if (mp->is_xtal_coord) {
+               TransformVec(&v, mp->cell->tr, &v);
+               v.z = f;
+               TransformVec(&v, mp->cell->rtr, &v);
+       } else */ v.z = f;
+       ap->r = v;
+       s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
+       return val;
+}
+
+
+static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
+       Vector v;
+       Atom *ap;
+       Molecule *mp;
+       VALUE oval = s_AtomRef_GetV(self);
+       VectorFromValue(val, &v);
+       s_AtomIndexFromValue(self, &ap, &mp);
+       ap->v = v;
+       s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
+       Vector v;
+       VALUE oval = s_AtomRef_GetF(self);
+       VectorFromValue(val, &v);
+       s_AtomFromValue(self)->f = v;
+       s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
+       VALUE oval = s_AtomRef_GetOccupancy(self);
+       val = rb_Float(val);
+       s_AtomFromValue(self)->occupancy = NUM2DBL(val);
+       s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
+       VALUE oval = s_AtomRef_GetTempFactor(self);
+       val = rb_Float(val);
+       s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
+       s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
+       AtomRef *aref;
+       int i, n;
+       VALUE *valp;
+       Double f[6];
+       VALUE oval = s_AtomRef_GetAniso(self);
+       Data_Get_Struct(self, AtomRef, aref);
+       val = rb_funcall(val, rb_intern("to_a"), 0);
+       n = RARRAY_LEN(val);
+       valp = RARRAY_PTR(val);
+       for (i = 0; i < 6; i++) {
+               if (i < n)
+                       f[i] = NUM2DBL(rb_Float(valp[i]));
+               else f[i] = 0.0;
+       }
+       i = s_AtomIndexFromValue(self, NULL, NULL);
+       MoleculeSetAniso(aref->mol, i, 0, f[0], f[1], f[2], f[3], f[4], f[5]);
+       s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
+       VALUE oval = s_AtomRef_GetIntCharge(self);
+       val = rb_Integer(val);
+       s_AtomFromValue(self)->intCharge = NUM2INT(val);
+       s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
+       VALUE oval = s_AtomRef_GetFixForce(self);
+       val = rb_Float(val);
+       s_AtomFromValue(self)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
+       s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
+       return val;
+}
+
+static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
+       Vector v;
+       VALUE oval = s_AtomRef_GetFixPos(self);
+       VectorFromValue(val, &v);
+       s_AtomFromValue(self)->fix_pos = v;
+       s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
+       return val;
+}
+
+static struct s_AtomAttrDef {
+       char *name;
+       VALUE *symref;  /*  Address of s_IndexSymbol etc. */
+       ID id;                  /*  Will be set within InitMolby()  */
+       VALUE (*getter)(VALUE);
+       VALUE (*setter)(VALUE, VALUE);
+} s_AtomAttrDefTable[] = {
+       {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
+       {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
+       {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
+       {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
+       {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
+       {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
+       {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
+       {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
+       {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
+       {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
+       {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
+       {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
+       {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
+       {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
+       {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
+    {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
+       {"cr",           &s_CrSym,           0, s_AtomRef_GetCr,           s_AtomRef_SetCr},
+       {"cx",           &s_CxSym,           0, s_AtomRef_GetCx,           s_AtomRef_SetCx},
+       {"cy",           &s_CySym,           0, s_AtomRef_GetCy,           s_AtomRef_SetCy},
+       {"cz",           &s_CzSym,           0, s_AtomRef_GetCz,           s_AtomRef_SetCz},
+       {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
+       {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
+       {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
+       {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
+       {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
+       {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
+       {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
+       {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
+       {NULL} /* Sentinel */
+};
+
+static VALUE
+s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
+{
+       int i;
+       ID kid;
+       if (TYPE(key) != T_SYMBOL) {
+               kid = rb_intern(StringValuePtr(key));
+               key = ID2SYM(kid);
+       } else kid = SYM2ID(key);
+       for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
+               if (s_AtomAttrDefTable[i].id == kid) {
+                       if (value == Qundef)
+                               return (*(s_AtomAttrDefTable[i].getter))(self);
+                       else
+                               return (*(s_AtomAttrDefTable[i].setter))(self, value);
+               }
+       }
+       rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
+       return Qnil; /* not reached */
+}
+
+static VALUE
+s_AtomRef_GetAttr(VALUE self, VALUE key)
+{
+       return s_AtomRef_SetAttr(self, key, Qundef);
+}
+
+#pragma mark ====== MolEnumerable Class ======
+
+static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
+
+/*  []  */
+static VALUE
+s_MolEnumerable_Aref(VALUE self, VALUE arg1)
+{
+       MolEnumerable *mseq;
+       Molecule *mol;
+       int idx1, idx2;
+    Data_Get_Struct(self, MolEnumerable, mseq);
+       mol = mseq->mol;
+       if (mseq->kind == kAtomKind) {
+               return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
+       }
+       idx1 = NUM2INT(arg1);
+       switch (mseq->kind) {
+               case kBondKind: {
+                       idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
+                       if (idx2 < 0 || idx2 >= mol->nbonds)
+                               rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
+                       return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
+               }
+               case kAngleKind: {
+                       idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
+                       if (idx2 < 0 || idx2 >= mol->nangles)
+                               rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
+                       return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
+               }
+               case kDihedralKind: {
+                       idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
+                       if (idx2 < 0 || idx2 >= mol->ndihedrals)
+                               rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
+                       return rb_ary_new3(4, INT2NUM(mol->dihedrals[idx2 * 4]), INT2NUM(mol->dihedrals[idx2 * 4 + 1]), INT2NUM(mol->dihedrals[idx2 * 4 + 2]), INT2NUM(mol->dihedrals[idx2 * 4 + 3]));
+               }
+               case kImproperKind: {
+                       idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
+                       if (idx2 < 0 || idx2 >= mol->nimpropers)
+                               rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
+                       return rb_ary_new3(4, INT2NUM(mol->impropers[idx2 * 4]), INT2NUM(mol->impropers[idx2 * 4 + 1]), INT2NUM(mol->impropers[idx2 * 4 + 2]), INT2NUM(mol->impropers[idx2 * 4 + 3]));
+               }
+               case kResidueKind: {
+                       char *p;
+                       idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
+                       if (idx2 < 0 || idx2 >= mol->nresidues)
+                               rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
+                       p = mol->residues[idx2];
+                       return rb_str_new(p, strlen_limit(p, 4));
+               }
+       }
+       return Qnil;
+}
+
+static VALUE
+s_MolEnumerable_Length(VALUE self)
+{
+       MolEnumerable *mseq;
+    Data_Get_Struct(self, MolEnumerable, mseq);
+       switch (mseq->kind) {
+               case kAtomKind:
+                       return INT2NUM(mseq->mol->natoms);
+               case kBondKind:
+                       return INT2NUM(mseq->mol->nbonds);
+               case kAngleKind:
+                       return INT2NUM(mseq->mol->nangles);
+               case kDihedralKind:
+                       return INT2NUM(mseq->mol->ndihedrals);
+               case kImproperKind:
+                       return INT2NUM(mseq->mol->nimpropers);
+               case kResidueKind:
+                       return INT2NUM(mseq->mol->nresidues);
+       }
+       return INT2NUM(-1);
+}
+
+VALUE
+s_MolEnumerable_Each(VALUE self)
+{
+       MolEnumerable *mseq;
+       int i;
+       int len = NUM2INT(s_MolEnumerable_Length(self));
+    Data_Get_Struct(self, MolEnumerable, mseq);
+       if (mseq->kind == kAtomKind) {
+               /*  The same AtomRef object will be used during the loop  */
+               AtomRef *aref = AtomRefNew(mseq->mol, 0);
+               VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
+               for (i = 0; i < len; i++) {
+                       aref->idx = i;
+                       rb_yield(arval);
+               }
+    } else {
+               /*  A new ruby object will be created at each iteration (not very efficient)  */
+               for (i = 0; i < len; i++) {
+                       rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
+               }
+       }
+    return self;
+}
+
+#pragma mark ====== Molecule Class ======
+
+Molecule *
+MoleculeFromValue(VALUE val)
+{
+       Molecule *mol;
+       if (!rb_obj_is_kind_of(val, rb_cMolecule))
+               rb_raise(rb_eMolbyError, "Molecule instance is expected");
+    Data_Get_Struct(val, Molecule, mol);
+       return mol;
+}
+
+VALUE
+ValueFromMolecule(Molecule *mol)
+{
+       if (mol == NULL)
+               return Qnil;
+       MoleculeRetain(mol);
+       return Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeRelease, mol);
+}
+
+static int
+s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
+{
+       int n;
+       char *p;
+       if (FIXNUM_P(val)) {
+               n = FIX2INT(val);
+               if (n < 0 || n >= mol->natoms)
+                       n = -1; /* No such atom */
+       } else {
+               n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
+       }
+       if (n >= 0 && n < mol->natoms)
+               return n;
+       p = StringValuePtr(val);
+       if (n == -1)
+               rb_raise(rb_eMolbyError, "no such atom: %s", p);
+       else if (n == -2)
+               rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
+       else
+               rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
+       return 0; /* Not reached */
+}
+
+static IntGroup *
+s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
+{
+       IntGroup *ig;
+       val = rb_funcall(self, rb_intern("atom_group"), 1, val);
+       if (!rb_obj_is_kind_of(val, rb_cIntGroup))
+               rb_raise(rb_eMolbyError, "IntGroup instance is expected");
+       Data_Get_Struct(val, IntGroup, ig);
+       IntGroupRetain(ig);
+       return ig;
+}
+
+/*  Allocator  */
+static VALUE
+s_Molecule_Alloc(VALUE klass)
+{
+       Molecule *mol = MoleculeNew();
+    return Data_Wrap_Struct(klass, 0, (void (*)(void *))MoleculeRelease, mol);
+}
+
+/*
+ *  call-seq:
+ *     molecule.dup          -> molecule
+ *
+ *  Duplicate a molecule. All entries are deep copied, so modifying the newly
+ *  created object does not affect the old object in any sense.
+ */
+static VALUE
+s_Molecule_InitCopy(VALUE self, VALUE arg)
+{
+       Molecule *mp1, *mp2;
+       Data_Get_Struct(self, Molecule, mp1);
+       mp2 = MoleculeFromValue(arg);
+       if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
+               rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.loadmbsf(file)       -> boolean
+ *
+ *  Read a structure from a mbsf file.
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname;
+       char *fstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1", &fname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeLoadMbsfFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;   
+}
+
+/*
+ *  call-seq:
+ *     molecule.loadpsf(file, pdbfile = nil)       -> boolean
+ *
+ *  Read a structure from a psf file. molecule must be empty. The psf may be
+ *  an "extended" version, which also contains coordinates. If pdbfile 
+ *  is given, then atomic coordinates are read from that file.
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname, pdbname;
+       char *fstr, *pdbstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       if (mol->natoms > 0)
+               return Qnil;  /*  Must be a new molecule  */
+       rb_scan_args(argc, argv, "11", &fname, &pdbname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeLoadPsfFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       pdbstr = NULL;
+       if (!NIL_P(pdbname)) {
+               pdbstr = strdup(FileStringValuePtr(pdbname));
+/*
+       2008.7.19. The pdbfile should be explicitly given
+       } else {
+               int len = strlen(fstr);
+               if (len > 4 && strcmp(fstr + len - 4, ".psf") == 0) {
+                       pdbstr = ALLOC_N(char, len + 1);
+                       strcpy(pdbstr, fstr);
+                       strcpy(pdbstr + len - 4, ".pdb");
+               }
+       }
+       if (pdbstr != NULL) {
+*/
+               retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, errbuf, sizeof errbuf);
+               free(pdbstr);
+       /*      if (retval == -1 && NIL_P(pdbname))
+                       return Qtrue;
+               else 
+       */
+               if (retval != 0)
+                       rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     molecule.loadpdb(file)       -> boolean
+ *
+ *  Read coordinates from a pdb file. If molecule is empty, then structure is build
+ *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname;
+       char *fstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1", &fname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;   
+}
+
+/*
+ *  call-seq:
+ *     molecule.loaddcd(file)       -> boolean
+ *
+ *  Read coordinates from a dcd file. The molecule should not empty.
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname;
+       char *fstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1", &fname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;   
+}
+
+/*
+ *  call-seq:
+ *     molecule.loadtep(file)       -> boolean
+ *
+ *  Read coordinates from an ortep .tep file.
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname;
+       char *fstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1", &fname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeLoadTepFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;   
+}
+
+/*
+ *  call-seq:
+ *     molecule.loadres(file)       -> boolean
+ *
+ *  Read coordinates from a shelx .res file.
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname;
+       char *fstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1", &fname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeLoadShelxFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;   
+}
+
+/*
+ *  call-seq:
+ *     molecule.loadfchk(file)       -> boolean
+ *
+ *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname;
+       char *fstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1", &fname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeLoadGaussianFchkFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;   
+}
+
+/*
+ *  call-seq:
+ *     molecule.loaddat(file)       -> boolean
+ *
+ *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
+ *  Return true if successful.
+ */
+static VALUE
+s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fname;
+       char *fstr;
+       Molecule *mol;
+       char errbuf[128];
+       int retval;
+       Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1", &fname);
+       fstr = FileStringValuePtr(fname);
+       retval = MoleculeLoadGamessDatFile(mol, fstr, errbuf, sizeof errbuf);
+       if (retval != 0) {
+               if (retval == -1)
+                       return Qnil;
+               rb_raise(rb_eMolbyError, errbuf);
+       }
+       return Qtrue;   
+}
+
+/*
+ *  call-seq:
+ *     molecule.savembsf(file)       -> boolean
+ *
+ *  Write structure as a mbsf file. Returns true if successful.
+ */
+static VALUE
+s_Molecule_Savembsf(VALUE self, VALUE fname)
+{
+       char *fstr;
+    Molecule *mol;
+       char errbuf[128];
+    Data_Get_Struct(self, Molecule, mol);
+       fstr = FileStringValuePtr(fname);
+       if (MoleculeWriteToMbsfFile(mol, fstr, errbuf, sizeof errbuf) != 0)
+               rb_raise(rb_eMolbyError, errbuf);
+       return Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     molecule.savepsf(file)       -> boolean
+ *
+ *  Write structure as a psf file. Returns true if successful.
+ */
+static VALUE
+s_Molecule_Savepsf(VALUE self, VALUE fname)
+{
+       char *fstr;
+    Molecule *mol;
+       char errbuf[128];
+    Data_Get_Struct(self, Molecule, mol);
+       fstr = FileStringValuePtr(fname);
+       if (MoleculeWriteToPsfFile(mol, fstr, errbuf, sizeof errbuf) != 0)
+               rb_raise(rb_eMolbyError, errbuf);
+       return Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     molecule.savepdb(file)       -> boolean
+ *
+ *  Write coordinates as a pdb file. Returns true if successful.
+ */
+static VALUE
+s_Molecule_Savepdb(VALUE self, VALUE fname)
+{
+       char *fstr;
+    Molecule *mol;
+       char errbuf[128];
+    Data_Get_Struct(self, Molecule, mol);
+       fstr = FileStringValuePtr(fname);
+       if (MoleculeWriteToPdbFile(mol, fstr, errbuf, sizeof errbuf) != 0)
+               rb_raise(rb_eMolbyError, errbuf);
+       return Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     molecule.savedcd(file)       -> boolean
+ *
+ *  Write coordinates as a dcd file. Returns true if successful.
+ */
+static VALUE
+s_Molecule_Savedcd(VALUE self, VALUE fname)
+{
+       char *fstr;
+    Molecule *mol;
+       char errbuf[128];
+    Data_Get_Struct(self, Molecule, mol);
+       fstr = FileStringValuePtr(fname);
+       if (MoleculeWriteToDcdFile(mol, fstr, errbuf, sizeof errbuf) != 0)
+               rb_raise(rb_eMolbyError, errbuf);
+       return Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     molecule.savetep(file)       -> boolean
+ *
+ *  Write coordinates as an ORTEP file. Returns true if successful.
+ */
+static VALUE
+s_Molecule_Savetep(VALUE self, VALUE fname)
+{
+       char *fstr;
+    Molecule *mol;
+       char errbuf[128];
+    Data_Get_Struct(self, Molecule, mol);
+       fstr = FileStringValuePtr(fname);
+       if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
+               rb_raise(rb_eMolbyError, errbuf);
+       return Qtrue;
+}
+
+/*  load([ftype, ] fname, ...)  */
+static VALUE
+s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
+{
+       VALUE rval;
+       char *argstr, *methname, *p;
+       ID mid = 0;
+       int i;
+       const char *ls = (loadFlag ? "load" : "save");
+       int lslen = strlen(ls);
+
+       if (argc == 0)
+               return Qnil;
+
+       if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
+               rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
+       if (argstr[0] == ':') {
+               /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
+               methname = ALLOC_N(char, lslen + strlen(argstr));
+               strcpy(methname, ls);
+               strcat(methname, argstr + 1);
+               for (i = lslen; methname[i] != 0; i++)
+                       methname[i] = tolower(methname[i]);
+               mid = rb_intern(methname);
+               free(methname);
+               rval = rb_funcall2(self, mid, argc - 1, argv + 1);
+               if (rval == Qnil)
+                       rb_raise(rb_eMolbyError, "the format specification \'%s\' seems to be wrong", argstr);
+               else return rval;
+       }
+       /*  Guess file type from extension  */
+       p = strrchr(argstr, '.');
+       if (p != NULL) {
+               p++;
+               for (methname = p; *methname != 0; methname++) {
+                       if (!isalpha(*methname))
+                               break;
+               }
+               if (*methname == 0) {
+                       methname = ALLOC_N(char, lslen + strlen(p) + 1);
+                       if (methname == NULL)
+                               rb_raise(rb_eMolbyError, "Low memory");
+                       strcpy(methname, ls);
+                       strcat(methname, p);
+                       for (i = lslen; methname[i] != 0; i++)
+                               methname[i] = tolower(methname[i]);
+                       mid = rb_intern(methname);
+                       free(methname);
+                       if (loadFlag) {
+                               if (rb_respond_to(self, mid)) {
+                                       /*  Load: try to call the load procedure only if it is available  */
+                                       rval = rb_funcall2(self, mid, argc, argv);
+                                       if (rval != Qnil)
+                                               return rval; /* Successful */
+                               }
+                       } else {
+                               /*  Save: call the save procedure, and if not found then call 'method_missing'  */
+                               return rb_funcall2(self, mid, argc, argv);
+                       }
+               }
+       }
+#if 0
+       if (loadFlag) {
+               /*  Try all "loadXXX" (public) methods  */
+               ID midsave = mid;
+               rval = rb_funcall(self, rb_intern("methods"), 0);
+               vp = RARRAY_PTR(rval);
+               for (i = RARRAY_LEN(rval) - 1; i >= 0; i--) {
+                       p = RSTRING_PTR(vp[i]);
+                       if (strncmp(p, "load", 4) == 0 && strlen(p) > 4) {
+                               mid = rb_to_id(vp[i]);
+                               if (midsave != 0 && mid == midsave)
+                                       continue;
+                               rval = rb_funcall2(self, mid, argc, argv);
+                               if (rval != Qnil)
+                                       return rval;  /* Successful */
+                       }
+               }
+       }
+#endif
+       rb_raise(rb_eMolbyError, "the file %s cannot be %s", argstr, (loadFlag ? "loaded" : "saved"));
+}
+
+/*
+ *  call-seq:
+ *     molecule.molload(file, *args)       -> boolean
+ *
+ *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
+ *  file type given by the extension). If this method fails, then all defined (public)
+ *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
+ */
+static VALUE
+s_Molecule_Load(int argc, VALUE *argv, VALUE self)
+{
+       return s_Molecule_LoadSave(argc, argv, self, 1);
+}
+
+/*
+ *  call-seq:
+ *     molecule.molsave(file, *args)       -> boolean
+ *
+ *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
+ *  (XXX is the file type given by the extension).
+ */
+static VALUE
+s_Molecule_Save(int argc, VALUE *argv, VALUE self)
+{
+       return s_Molecule_LoadSave(argc, argv, self, 0);
+}
+
+/*
+ *  call-seq:
+ *     molecule.name       -> string
+ *
+ *  Returns the display name of the molecule. If the molecule has no associated
+ *  document, then returns nil.
+ */
+static VALUE
+s_Molecule_Name(VALUE self)
+{
+    Molecule *mol;
+       char buf[1024];
+    Data_Get_Struct(self, Molecule, mol);
+       MoleculeCallback_displayName(mol, buf, sizeof buf);
+       if (buf[0] == 0)
+               return Qnil;
+       else
+               return rb_str_new2(buf);
+}
+
+/*
+ *  call-seq:
+ *     molecule.path       -> string
+ *
+ *  Returns the full path name of the molecule, if it is associated with a file.
+ *  If the molecule has no associated file, then returns nil.
+ */
+static VALUE
+s_Molecule_Path(VALUE self)
+{
+    Molecule *mol;
+       char buf[1024];
+    Data_Get_Struct(self, Molecule, mol);
+       MoleculeCallback_pathName(mol, buf, sizeof buf);
+       if (buf[0] == 0)
+               return Qnil;
+       else
+               return Ruby_NewFileStringValue(buf);
+}
+
+/*
+ *  call-seq:
+ *     molecule.dir       -> string
+ *
+ *  Returns the full path name of the directory in which the file associated with the
+ *  molecule is located. If the molecule has no associated file, then returns nil.
+ */
+static VALUE
+s_Molecule_Dir(VALUE self)
+{
+    Molecule *mol;
+       char buf[1024], *p;
+    Data_Get_Struct(self, Molecule, mol);
+       MoleculeCallback_pathName(mol, buf, sizeof buf);
+#if __WXMSW__
+       translate_char(buf, '\\', '/');
+#endif
+       if (buf[0] == 0)
+               return Qnil;
+       else {
+               p = strrchr(buf, '/');
+               if (p != NULL)
+                       *p = 0;
+               return rb_str_new2(buf);
+       }
+}
+
+/*
+ *  call-seq:
+ *     molecule.inspect       -> string
+ *
+ *  Returns a string in the form "Molecule[name]" if the molecule has the associated
+ *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
+ *  the Molecule structure) is returned.
+ */
+static VALUE
+s_Molecule_Inspect(VALUE self)
+{
+    Molecule *mol;
+       char buf[256];
+    Data_Get_Struct(self, Molecule, mol);
+       MoleculeCallback_displayName(mol, buf, sizeof buf);
+       if (buf[0] == 0) {
+               /*  No associated document  */
+               snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
+               return rb_str_new2(buf);
+       } else {
+               /*  Check whether the document name is duplicate  */
+               char buf2[256];
+               int idx, k, k2;
+               Molecule *mol2;
+               for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
+                       MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
+                       if (strcmp(buf, buf2) == 0) {
+                               k++;
+                               if (mol == mol2)
+                                       k2 = k;
+                       }
+               }
+               if (k > 1) {
+                       snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
+               } else {
+                       snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
+               }
+               return rb_str_new2(buf2);
+       }
+}
+
+/*
+ *  call-seq:
+ *     Molecule.open(file)  -> molecule
+ *
+ *  Create a new molecule from file. This assumes MoleculeCallback_openNewMolecule() is implemented.
+ */
+static VALUE
+s_Molecule_Open(VALUE self, VALUE fname)
+{
+       const char *p = FileStringValuePtr(fname);
+       Molecule *mp;
+       VALUE iflag;
+       iflag = Ruby_SetInterruptFlag(Qfalse);
+       mp = MoleculeCallback_openNewMolecule(p);
+       Ruby_SetInterruptFlag(iflag);
+       if (mp == NULL)
+               rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
+       return ValueFromMolecule(mp);
+}
+
+/*
+ *  call-seq:
+ *     Molecule.new(file, *args)  -> molecule
+ *     molecule.initialize(file, *args)
+ *
+ *  Create a new molecule and call "load" method with the same arguments.
+ */
+static VALUE
+s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
+{
+       if (argc > 0)
+               return s_Molecule_Load(argc, argv, self);
+       else return Qnil;
+}
+
+static VALUE
+s_Molecule_MolEnumerable(VALUE self, int kind)
+{
+    Molecule *mol;
+       MolEnumerable *mseq;
+    Data_Get_Struct(self, Molecule, mol);
+       mseq = MolEnumerableNew(mol, kind);
+       return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
+}
+
+/*
+ *  call-seq:
+ *     molecule.atoms       -> MolEnumerable
+ *
+ *  Returns a MolEnumerable object representing the array of atoms.
+ */
+static VALUE
+s_Molecule_Atoms(VALUE self)
+{
+       return s_Molecule_MolEnumerable(self, kAtomKind);
+}
+
+/*
+ *  call-seq:
+ *     molecule.bonds       -> MolEnumerable
+ *
+ *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
+ *  by an array of two atom indices.
+ */
+static VALUE
+s_Molecule_Bonds(VALUE self)
+{
+       return s_Molecule_MolEnumerable(self, kBondKind);
+}
+
+/*
+ *  call-seq:
+ *     molecule.angles       -> MolEnumerable
+ *
+ *  Returns a MolEnumerable object representing the array of angles. An angle is represented
+ *  by an array of three atom indices.
+ */
+static VALUE
+s_Molecule_Angles(VALUE self)
+{
+       return s_Molecule_MolEnumerable(self, kAngleKind);
+}
+
+/*
+ *  call-seq:
+ *     molecule.dihedrals       -> MolEnumerable
+ *
+ *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
+ *  by an array of four atom indices.
+ */
+static VALUE
+s_Molecule_Dihedrals(VALUE self)
+{
+       return s_Molecule_MolEnumerable(self, kDihedralKind);
+}
+
+/*
+ *  call-seq:
+ *     molecule.impropers       -> MolEnumerable
+ *
+ *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
+ *  by an array of four atom indices.
+ */
+static VALUE
+s_Molecule_Impropers(VALUE self)
+{
+       return s_Molecule_MolEnumerable(self, kImproperKind);
+}
+
+/*
+ *  call-seq:
+ *     molecule.residues       -> MolEnumerable
+ *
+ *  Returns a MolEnumerable object representing the array of residue names.
+ */
+static VALUE
+s_Molecule_Residues(VALUE self)
+{
+       return s_Molecule_MolEnumerable(self, kResidueKind);
+}
+
+/*
+ *  call-seq:
+ *     molecule.natoms       -> int
+ *
+ *  Returns the number of atoms.
+ */
+static VALUE
+s_Molecule_Natoms(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->natoms);
+}
+
+/*
+ *  call-seq:
+ *     molecule.nbonds       -> int
+ *
+ *  Returns the number of bonds.
+ */
+static VALUE
+s_Molecule_Nbonds(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->nbonds);
+}
+
+/*
+ *  call-seq:
+ *     molecule.nangles       -> int
+ *
+ *  Returns the number of angles.
+ */
+static VALUE
+s_Molecule_Nangles(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->nangles);
+}
+
+/*
+ *  call-seq:
+ *     molecule.ndihedrals       -> int
+ *
+ *  Returns the number of dihedrals.
+ */
+static VALUE
+s_Molecule_Ndihedrals(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->ndihedrals);
+}
+
+/*
+ *  call-seq:
+ *     molecule.nimpropers       -> int
+ *
+ *  Returns the number of impropers.
+ */
+static VALUE
+s_Molecule_Nimpropers(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->nimpropers);
+}
+
+/*
+ *  call-seq:
+ *     molecule.nresidues       -> int
+ *
+ *  Returns the number of residues.
+ */
+static VALUE
+s_Molecule_Nresidues(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->nresidues);
+}
+
+/*
+ *  call-seq:
+ *     molecule.start_step       -> int
+ *
+ *  Returns the start step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_StartStep(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->startStep);
+}
+
+/*
+ *  call-seq:
+ *     molecule.start_step = int
+ *
+ *  Set the start step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_SetStartStep(VALUE self, VALUE val)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       mol->startStep = NUM2INT(rb_Integer(val));
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.steps_per_frame       -> int
+ *
+ *  Returns the number of steps between frames (defined by dcd format).
+ */
+static VALUE
+s_Molecule_StepsPerFrame(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->stepsPerFrame);
+}
+
+/*
+ *  call-seq:
+ *     molecule.steps_per_frame = int
+ *
+ *  Set the number of steps between frames (defined by dcd format).
+ */
+static VALUE
+s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       mol->stepsPerFrame = NUM2INT(rb_Integer(val));
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.ps_per_step       -> float
+ *
+ *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_PsPerStep(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return rb_float_new(mol->psPerStep);
+}
+
+/*
+ *  call-seq:
+ *     molecule.ps_per_step = float
+ *
+ *  Set the time increment (in picoseconds) for one step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_SetPsPerStep(VALUE self, VALUE val)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       mol->psPerStep = NUM2DBL(rb_Float(val));
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.find_angles     -> int (added number of angles)
+ *
+ *  Find the angles from the bonds
+ */
+static VALUE
+s_Molecule_FindAngles(VALUE self)
+{
+    Molecule *mol;
+       Atom *ap;
+       int n1, i, j, nc;
+       Int *ip, nip, n[3];
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol == NULL || mol->natoms == 0)
+               return INT2NUM(0);
+       ip = NULL;
+       nip = 0;
+       for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
+               nc = ap->nconnects;
+               n[1] = n1;
+               for (i = 0; i < nc; i++) {
+                       n[0] = ap->connects[i];
+                       for (j = i + 1; j < nc; j++) {
+                               n[2] = ap->connects[j];
+                               if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
+                                       AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
+                       }
+               }
+       }
+       if (nip > 0) {
+               MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);         
+               free(ip);
+       }
+       return INT2NUM(nip);
+}
+
+/*
+ *  call-seq:
+ *     molecule.find_dihedrals     -> int (added number of dihedrals)
+ *
+ *  Find the dihedrals from the bonds
+ */
+static VALUE
+s_Molecule_FindDihedrals(VALUE self)
+{
+    Molecule *mol;
+       Atom *ap1, *ap2;
+       int n1, i, j, k, nc1, nc2;
+       Int *ip, nip, n[4];
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol == NULL || mol->natoms == 0)
+               return INT2NUM(0);
+       ip = NULL;
+       nip = 0;
+       for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
+               nc1 = ap1->nconnects;
+               n[1] = n1;
+               for (i = 0; i < nc1; i++) {
+                       n[2] = ap1->connects[i];
+                       if (n[1] > n[2])
+                               continue;
+                       ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
+                       nc2 = ap2->nconnects;
+                       for (j = 0; j < nc1; j++) {
+                               n[0] = ap1->connects[j];
+                               if (n[0] == n[2])
+                                       continue;
+                               for (k = 0; k < nc2; k++) {
+                                       n[3] = ap2->connects[k];
+                                       if (n[3] == n1 || n[3] == n[0])
+                                               continue;
+                                       if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
+                                               AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
+                               }
+                       }
+               }
+       }
+       if (nip > 0) {
+               MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
+               free(ip);
+       }
+       return INT2NUM(nip);
+}
+
+/*
+ *  call-seq:
+ *     molecule.nresidues = integer
+ *
+ *  Change the number of residues. If the result is not equal to the argument, 
+ *  exception is thrown. This operation is undoable.
+ */
+static VALUE
+s_Molecule_ChangeNresidues(VALUE self, VALUE val)
+{
+    Molecule *mol;
+       int ival = NUM2INT(val);
+    Data_Get_Struct(self, Molecule, mol);
+       MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
+       if (ival != mol->nresidues)
+               rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.max_residue_number(atom_group = nil)     -> int
+ *
+ *  Returns the maximum residue number actually used. If an atom group is given, only
+ *  these atoms are examined. If no atom is present, nil is returned.
+ */
+static VALUE
+s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE gval;
+       int maxSeq;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &gval);
+       ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+       maxSeq = MoleculeMaximumResidueNumber(mol, ig);
+       return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
+}
+
+/*
+ *  call-seq:
+ *     molecule.min_residue_number(atom_group = nil)     -> int
+ *
+ *  Returns the minimum residue number actually used. If an atom group is given, only
+ *  these atoms are examined. If no atom is present, nil is returned.
+ */
+static VALUE
+s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE gval;
+       int minSeq;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &gval);
+       ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+       minSeq = MoleculeMinimumResidueNumber(mol, ig);
+       return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
+}
+
+/*
+ *  call-seq:
+ *     molecule.each_atom block
+ *
+ *  Execute the block, with the AtomRef object for each atom as the argument.
+ *  Equivalent to self.atoms.each, except that the return value is self (a Molecule object).
+ */
+static VALUE
+s_Molecule_EachAtom(VALUE self)
+{
+       int i;
+    Molecule *mol;
+       AtomRef *aref;
+       VALUE arval;
+    Data_Get_Struct(self, Molecule, mol);
+       arval = ValueFromMoleculeAndIndex(mol, 0);
+       Data_Get_Struct(arval, AtomRef, aref);
+       for (i = 0; i < mol->natoms; i++) {
+               aref->idx = i;
+               rb_yield(arval);
+       }
+    return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.cell     -> array [a, b, c, alpha, beta, gamma]
+ *
+ *  Returns the cell parameters. If cell is not set, returns nil.
+ */
+static VALUE
+s_Molecule_Cell(VALUE self)
+{
+    Molecule *mol;
+       int i;
+       VALUE val;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->cell == NULL)
+               return Qnil;
+       val = rb_ary_new2(6);
+       for (i = 0; i < 6; i++)
+               rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.cell = [a, b, c, alpha, beta, gamma]
+ *     set_cell([a, b, c, alpha, beta, gamma], flag = nil)
+ *
+ *  Set the cell parameters. If the cell value is nil, then clear the current cell. 
+    This operation is undoable. If the second argument is given as non-nil, then 
+       the coordinates are transformed so that the cartesian coordinates remain the same.
+ */
+static VALUE
+s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE val, fval;
+       int i, flag;
+       double d[6];
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &val, &fval);
+       flag = RTEST(fval);
+       if (val == Qnil) {
+               MolActionCreateAndPerform(mol, gMolActionSetCell, 0, d, flag);
+       //      MoleculeSetCell(mol, 1, 1, 1, 90, 90, 90);
+               return Qnil;
+       }
+       for (i = 0; i < 6; i++)
+               d[i] = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, i)));
+       MolActionCreateAndPerform(mol, gMolActionSetCell, 6, d, flag);
+//     MoleculeSetCell(mol, d[0], d[1], d[2], d[3], d[4], d[5]);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     cell_transform -> Transform
+ *
+ *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
+ *  If cell is not defined, nil is returned.
+ */
+static VALUE
+s_Molecule_CellTransform(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol == NULL || mol->cell == NULL)
+               return Qnil;
+       return ValueFromTransform(&(mol->cell->tr));
+}
+
+/*
+ *  call-seq:
+ *     box -> [avec, bvec, cvec, origin, flags]
+ *
+ *  Get the unit cell as a periodic bounding box. Avec, bvec, cvec, origin are Vector3D objects, and 
+    flags is a 3-member array of integers. If no unit cell is defined, nil is returned.
+ */
+static VALUE
+s_Molecule_Box(VALUE self)
+{
+    Molecule *mol;
+       VALUE v[5], val;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol == NULL || mol->cell == NULL)
+               return Qnil;
+       v[0] = ValueFromVector(&(mol->cell->axes[0]));
+       v[1] = ValueFromVector(&(mol->cell->axes[1]));
+       v[2] = ValueFromVector(&(mol->cell->axes[2]));
+       v[3] = ValueFromVector(&(mol->cell->origin));
+       v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
+       val = rb_ary_new4(5, v);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1])
+ *     set_box(d, origin = [0, 0, 0])
+ *     set_box
+ *
+ *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
+    If it is a number, the x/y/z axis vector is multiplied with the given number and used
+    as the box vector.
+    Flags, if present, is a 3-member array of integers representing the periodic flags.
+    In the second form, an isotropic box with dimension d is set.
+    In the third form, the existing box is cleared.
+ */
+static VALUE
+s_Molecule_SetBox(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE v[4], fval;
+       static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
+       Vector vv[3];
+       Vector origin = {0, 0, 0};
+       char flags[3];
+       Double d;
+       int i;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "05", &v[0], &v[1], &v[2], &v[3], &fval);
+       if (v[0] == Qnil) {
+               MolActionCreateAndPerform(mol, gMolActionClearBox);
+               return self;
+       }
+       if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
+               d = NUM2DBL(rb_Float(v[0]));
+               for (i = 0; i < 3; i++)
+                       VecScale(vv[i], ax[i], d);
+               if (v[1] != Qnil)
+                       VectorFromValue(v[1], &origin);
+               flags[0] = flags[1] = flags[2] = 1;
+       } else {
+               for (i = 0; i < 3; i++) {
+                       if (v[i] == Qnil) {
+                               VecZero(vv[i]);
+                       } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
+                               d = NUM2DBL(rb_Float(v[i]));
+                               VecScale(vv[i], ax[i], d);
+                       } else {
+                               VectorFromValue(v[i], &vv[i]);
+                       }
+                       flags[i] = (VecLength2(vv[i]) > 0.0);
+               }
+               if (v[3] != Qnil)
+                       VectorFromValue(v[3], &origin);
+               if (fval != Qnil) {
+                       for (i = 0; i < 3; i++) {
+                               VALUE val = Ruby_ObjectAtIndex(fval, i);
+                               flags[i] = (NUM2INT(rb_Integer(val)) != 0);
+                       }
+               }
+       }
+       MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]));
+       return self;
+}
+
+#if 0
+/*
+ *  call-seq:
+ *     box_transform -> Transform
+ *
+ *  Get the transform matrix that converts internal coordinates for the periodic box
+ *  to cartesian coordinates. If the periodic box is not defined, nil is returned.
+ */
+static VALUE
+s_Molecule_BoxTransform(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol == NULL || mol->box == NULL)
+               return Qnil;
+       return ValueFromTransform(&(mol->box->tr));
+}
+#endif
+
+/*
+ *  call-seq:
+ *     molecule.symmetry -> array of Transform
+ *
+ *  Get the currently defined symmetry operations. If no symmetry operation is defined,
+ *  returns an empty array.
+ */
+static VALUE
+s_Molecule_Symmetry(VALUE self)
+{
+    Molecule *mol;
+       VALUE val;
+       int i;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->nsyms <= 0)
+               return rb_ary_new();
+       val = rb_ary_new2(mol->nsyms);
+       for (i = 0; i < mol->nsyms; i++) {
+               rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
+       }
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.nsymmetries -> integer
+ *
+ *  Get the number of currently defined symmetry operations.
+ */
+static VALUE
+s_Molecule_Nsymmetries(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->nsyms);
+}
+
+/*
+ *  call-seq:
+ *     molecule.add_symmetry(Transform) -> integer (number of total transforms)
+ *
+ *  Add a new symmetry operation. If no symmetry operation is defined, then add an identity
+ *  transform as the index 0, then the new symmetry operation is appended.
+ */
+static VALUE
+s_Molecule_AddSymmetry(VALUE self, VALUE trans)
+{
+    Molecule *mol;
+       Transform tr;
+    Data_Get_Struct(self, Molecule, mol);
+       TransformFromValue(trans, &tr);
+       MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
+       return INT2NUM(mol->nsyms);
+}
+
+/*
+ *  call-seq:
+ *     molecule.remove_symmetry(count = nil) -> integer (number of total transforms)
+ *
+ *  Remove the specified number of symmetry operations. The last added ones are removed
+ *  first. If count is nil, then all symmetry operations are removed.
+ */
+static VALUE
+s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE cval;
+       int i, n;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &cval);
+       if (cval == Qnil)
+               n = mol->nsyms - 1;
+       else {
+               n = NUM2INT(rb_Integer(cval));
+               if (n < 0 || n > mol->nsyms)
+                       rb_raise(rb_eMolbyError, "the given count of symops is out of range");
+               if (n == mol->nsyms)
+                       n = mol->nsyms - 1;
+       }
+       for (i = 0; i < n; i++)
+               MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
+       return self;
+}
+
+static VALUE
+s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
+{
+       Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
+       IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
+       int idx = s_Molecule_AtomIndexFromValue(mol, arg);
+       IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
+       return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     molecule.atom_group
+ *     molecule.atom_group { block }
+ *     molecule.atom_group(arg1, arg2, ...)
+ *     molecule.atom_group(arg1, arg2, ...) { block }
+ *         argN is either integer, string, intGroup, or array-like object
+ *
+ *  Specify a group of atoms. If no arguments are given, IntGroup[0..natoms] is the result.
+ *  If arguments are given, then the atoms reprensented by the arguments are combined (the
+ *  arguments are not scanned recursively; i.e. if arg1 is an array of intGroups, the intGroups
+ *  are not scanned for component integers and exception will fire because intGroups cannot
+ *  be coerced into an integer). For a conversion of a string to an atom index, see description
+ *  of <code>Molecule#atom_index<code>.
+ *  If block is given, the block is evaluated with an AtomRef (not atom index integers!)
+ *  representing each atom, and the atoms are removed from the result if the block returns false.
+ *
+ */
+static VALUE
+s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
+{
+       IntGroup *ig1, *ig2;
+    Molecule *mol;
+       Int i, startPt, interval;
+       VALUE retval = IntGroup_Alloc(rb_cIntGroup);
+       Data_Get_Struct(retval, IntGroup, ig1);
+    Data_Get_Struct(self, Molecule, mol);
+       if (argc == 0) {
+               IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
+       } else {
+               while (argc > 0) {
+                       if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
+                               i = s_Molecule_AtomIndexFromValue(mol, *argv);
+                               IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
+                       } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
+                               ig2 = IntGroupFromValue(*argv);
+                               for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
+                                       interval = IntGroupGetInterval(ig2, i);
+                                       IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
+                               }
+                               IntGroupRelease(ig2);
+                       } else if (rb_respond_to(*argv, rb_intern("each"))) {
+                               VALUE values[2];
+                               values[0] = (VALUE)mol;
+                               values[1] = (VALUE)ig1;
+                               rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
+                       } else
+                               IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
+                       argc--;
+                       argv++;
+               }
+       }
+       if (rb_block_given_p()) {
+               /*  Evaluate the given block with an AtomRef as the argument, and delete
+                       the index if the block returns false  */
+               AtomRef *aref = AtomRefNew(mol, 0);
+               VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
+               ig2 = IntGroupNew();
+               IntGroupCopy(ig2, ig1);
+               for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
+                       VALUE resval;
+                       if (startPt >= mol->natoms)
+                               break;
+                       aref->idx = startPt;
+                       resval = rb_yield(arval);
+                       if (!RTEST(resval))
+                               IntGroupRemove(ig1, startPt, 1);
+               }
+               IntGroupRelease(ig2);
+       }
+       
+       /*  Remove points that are out of bounds */
+       IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
+
+       return retval;                  
+}
+
+/*
+ *  call-seq:
+ *     molecule.atom_index(val)       -> int
+ *
+ *  Returns the atom index represented by val. val can be either a non-negative integer
+ *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
+ *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
+ *  where resname, resid, name are the residue name, residue id, and atom name respectively.
+ *  If val is a string and multiple atoms match the description, the atom with the lowest index
+ *  is returned.
+ */
+static VALUE
+s_Molecule_AtomIndex(VALUE self, VALUE val)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
+}
+
+/*
+ *  call-seq:
+ *     molecule.extract(group, dummy_flag = nil)       -> molecule
+ *
+ *  Extract the atoms given by group and return as a new molecule object.
+ *  If dummy_flag is true, then the atoms that are not included in the group but are connected
+ *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
+ *  names beginning with an underscore) and included in the new molecule object.
+ */
+static VALUE
+s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol1, *mol2;
+       IntGroup *ig;
+       VALUE group, dummy_flag, retval;
+    Data_Get_Struct(self, Molecule, mol1);
+       rb_scan_args(argc, argv, "11", &group, &dummy_flag);
+       ig = s_Molecule_AtomGroupFromValue(self, group);
+       if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
+               retval = Qnil;
+       } else {
+               retval = s_Molecule_Alloc(rb_cMolecule);
+               Data_Get_Struct(retval, Molecule, mol1);
+               MoleculeExchange(mol1, mol2);
+               MoleculeRelease(mol2);
+       }
+       IntGroupRelease(ig);
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.add(molecule2)       -> molecule
+ *
+ *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
+    conflicts.
+    This operation is undoable.
+ */
+static VALUE
+s_Molecule_Add(VALUE self, VALUE val)
+{
+    Molecule *mol1, *mol2;
+    Data_Get_Struct(self, Molecule, mol1);
+       mol2 = MoleculeFromValue(val);
+//     MoleculeMerge(mol1, mol2, NULL, mol1->nresidues - 1);
+       MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
+       return self; 
+}
+
+static VALUE
+s_Molecule_Duplicate(VALUE self)
+{
+       VALUE val = s_Molecule_Alloc(rb_cMolecule);
+/*     s_Molecule_Init(val); */
+       return s_Molecule_Add(val, self);
+}
+
+/*
+ *  call-seq:
+ *     molecule.remove(group)       -> molecule
+ *
+ *  The atoms designated by the given group are removed from the molecule.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_Remove(VALUE self, VALUE group)
+{
+    Molecule *mol1;
+       IntGroup *ig;
+       Int *bonds, nbonds, i, temp[2];
+       IntGroupIterator iter;
+
+    Data_Get_Struct(self, Molecule, mol1);
+       group = rb_funcall(self, rb_intern("atom_group"), 1, group);
+       if (!rb_obj_is_kind_of(group, rb_cIntGroup))
+               rb_raise(rb_eMolbyError, "IntGroup instance is expected");
+       Data_Get_Struct(group, IntGroup, ig);
+
+       /*  Remove the bonds between the two fragments  */
+       /*  (This is necessary for undo to work correctly)  */
+       nbonds = 0;
+       bonds = NULL;
+       IntGroupIteratorInit(ig, &iter);
+       while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+               Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
+               int j;
+               for (j = 0; j < ap->nconnects; j++) {
+                       int n = ap->connects[j];
+                       if (!IntGroupLookup(ig, n, NULL)) {
+                               /*  bond i-n, i is in ig and n is not  */
+                               temp[0] = i;
+                               temp[1] = n;
+                               AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, temp);
+                       }
+               }
+       }
+       IntGroupIteratorRelease(&iter);
+       if (nbonds > 0) {
+               /*  Remove bonds  */
+       /*      temp[0] = kInvalidIndex;
+               AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, temp); */
+               MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, nbonds * 2, bonds);
+               free(bonds);
+       }
+       /*  Remove atoms  */
+       if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
+               return Qnil;
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.create_atom(name, pos = -1)  -> AtomRef
+ *
+ *  Create a new atom with the specified name (may contain residue 
+ *  information) and position (if position is out of range, the atom is appended at
+ *  the end). Returns the reference to the new atom.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Int i, pos;
+       VALUE name, ival;
+    Atom arec;
+    AtomRef *aref;
+       char *p, resName[6], atomName[6];
+       int resSeq;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &name, &ival);
+       if (ival != Qnil)
+               pos = NUM2INT(rb_Integer(ival));
+       else pos = -1;
+    p = StringValuePtr(name);
+    i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
+    if (atomName[0] == 0)
+      rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
+    memset(&arec, 0, sizeof(arec));
+    strncpy(arec.aname, atomName, 4);
+    if (resSeq >= 0) {
+      strncpy(arec.resName, resName, 4);
+      arec.resSeq = resSeq;
+    }
+       arec.occupancy = 1.0;
+//    i = MoleculeCreateAnAtom(mol, &arec);
+       if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
+               return Qnil;
+    aref = AtomRefNew(mol, pos);
+    return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
+}
+
+/*
+ *  call-seq:
+ *     molecule.duplicate_atom(atomref, pos = -1)  -> AtomRef
+ *
+ *  Create a new atom with the same attributes (but no bonding information)
+ *  with the specified atom. Returns the reference to the new atom.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       const Atom *apsrc;
+    Atom arec;
+       AtomRef *aref;
+       VALUE retval, aval, ival;
+       Int pos;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &aval, &ival);
+       if (FIXNUM_P(aval)) {
+               int idx = NUM2INT(aval);
+               if (idx < 0 || idx >= mol->natoms)
+                       rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
+               apsrc = ATOM_AT_INDEX(mol->atoms, idx);
+       } else {
+               apsrc = s_AtomFromValue(aval);
+       }
+       if (apsrc == NULL)
+               rb_raise(rb_eMolbyError, "bad atom specification");
+       if (ival != Qnil)
+               pos = NUM2INT(rb_Integer(ival));
+       else pos = -1;
+       AtomDuplicate(&arec, apsrc);
+       arec.nconnects = 0;
+       if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
+               retval = Qnil;
+       else {
+               aref = AtomRefNew(mol, pos);
+               retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
+       }
+       AtomClean(&arec);
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.create_bond(n1, n2, ...)       -> molecule
+ *
+ *  Create bonds between atoms n1 and n2, n3 and n4, and so on. Returns self.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       Int i, *ip;
+       if (argc == 0)
+               rb_raise(rb_eMolbyError, "missing arguments");
+       if (argc % 2 != 0)
+               rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
+    Data_Get_Struct(self, Molecule, mol);
+       ip = ALLOC_N(Int, argc + 1);
+       for (i = 0; i < argc; i++)
+               ip[i] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
+       ip[argc] = kInvalidIndex;
+//     i = MoleculeAddBonds(mol, ip, NULL);
+       i = MolActionCreateAndPerform(mol, gMolActionAddBonds, argc, ip);
+       if (i == -1)
+               rb_raise(rb_eMolbyError, "atom index out of range");
+       else if (i == -2)
+               rb_raise(rb_eMolbyError, "too many bonds");
+       else if (i == -3)
+               rb_raise(rb_eMolbyError, "duplicate bonds");
+       else if (i != 0)
+               rb_raise(rb_eMolbyError, "error in creating bonds");
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.add_angle(n1, n2, n3)       -> molecule
+ *
+ *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
+ *  when a bond is created, so it is rarely necessary to use this method explicitly.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
+{
+       Int n[4];
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+       n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+       n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+       if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
+               rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
+       n[3] = kInvalidIndex;
+       MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.remove_angle(n1, n2, n3)       -> molecule
+ *
+ *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
+ *  when a bond is removed, so it is rarely necessary to use this method explicitly.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
+{
+       Int n[4];
+    Molecule *mol;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+       n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+       n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+       if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
+               rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
+       ig = IntGroupNewWithPoints(n[3], 1, -1);
+       MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
+       IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.add_dihedral(n1, n2, n3, n4)       -> molecule
+ *
+ *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
+ *  when a bond is created, so it is rarely necessary to use this method explicitly.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
+{
+       Int n[5];
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+       n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+       n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+       n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+       if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
+               rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
+       n[4] = kInvalidIndex;
+       MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.remove_dihedral(n1, n2, n3, n4)       -> molecule
+ *
+ *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
+ *  when a bond is removed, so it is rarely necessary to use this method explicitly.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
+{
+       Int n[5];
+    Molecule *mol;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+       n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+       n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+       n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+       if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
+               rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
+       ig = IntGroupNewWithPoints(n[4], 1, -1);
+       MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
+       IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.add_improper(n1, n2, n3, n4)       -> molecule
+ *
+ *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
+ *  not automatically added when a new bond is created, so this method is more useful than
+ *  the angle/dihedral counterpart.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
+{
+       Int n[5];
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+       n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+       n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+       n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+       if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
+               rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
+       n[4] = kInvalidIndex;
+       MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.remove_improper(n1, n2, n3, n4)       -> molecule
+ *
+ *  Remove improper n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
+ *  not automatically added when a new bond is created, so this method is more useful than
+ *  the angle/dihedral counterpart.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_RemoveImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
+{
+       Int n[5];
+    Molecule *mol;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+       n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+       n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+       n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+       if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
+               rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
+       ig = IntGroupNewWithPoints(n[4], 1, -1);
+       MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
+       IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.assign_residue(group, res)       -> molecule
+ *
+ *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
+ *  or "resname.resno". When the residue number is not specified, the residue number of
+ *  the first atom in the group is used.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
+{
+    Molecule *mol;
+       IntGroup *ig;
+       char *p, *pp, buf[16];
+       Int resid, n;
+       Atom *ap;
+    Data_Get_Struct(self, Molecule, mol);
+       
+       /*  Parse the argument res  */
+       if (FIXNUM_P(res)) {
+               /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
+               resid = NUM2INT(res);
+               buf[0] = 0;
+       } else {
+               p = StringValuePtr(res);
+               pp = strchr(p, '.');
+               if (pp != NULL) {
+                       resid = atoi(pp + 1);
+                       n = pp - p;
+               } else {
+                       resid = -1;
+                       n = strlen(p);
+               }
+               if (n > sizeof buf - 1)
+                       n = sizeof buf - 1;
+               strncpy(buf, p, n);
+               buf[n] = 0;
+       }
+       ig = s_Molecule_AtomGroupFromValue(self, range);
+       if (ig == NULL || IntGroupGetCount(ig) == 0)
+               return Qnil;
+
+       if (resid < 0) {
+               /*  Use the residue number of the first specified atom  */
+               n = IntGroupGetNthPoint(ig, 0);
+               if (n >= mol->natoms)
+                       rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
+               ap = ATOM_AT_INDEX(mol->atoms, n);
+               resid = ap->resSeq;
+       }
+       /*  Change the residue number  */
+       MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
+       /*  Change the residue name if necessary  */
+       if (buf[0] != 0) {
+       /*      Int seqs[2];
+               seqs[0] = resid;
+               seqs[1] = kInvalidIndex; */
+               MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
+       }
+       IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.offset_residue(group, offset)       -> molecule
+ *
+ *  Offset the residue number of the specified atoms. If any of the residue number gets
+ *  negative, then exception is thrown.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
+{
+    Molecule *mol;
+       IntGroup *ig;
+       int ofs, result;
+    Data_Get_Struct(self, Molecule, mol);
+       ig = s_Molecule_AtomGroupFromValue(self, range);
+       ofs = NUM2INT(offset);
+       result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
+       if (result > 0)
+               rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
+       IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.reorder_atoms(array)       -> intGroup
+ *
+ *  Change the order of atoms so that the atoms specified in the array argument appear
+ *  in this order from the top of the molecule. The atoms that are not included in array
+ *  are placed after these atoms, and these atoms are returned as an intGroup.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_ReorderAtoms(VALUE self, VALUE array)
+{
+    Molecule *mol;
+       Int *new2old;
+       IntGroup *ig;
+       int i, n;
+       VALUE *valp, retval;
+    Data_Get_Struct(self, Molecule, mol);
+       if (TYPE(array) != T_ARRAY)
+               array = rb_funcall(array, rb_intern("to_a"), 0);
+       n = RARRAY_LEN(array);
+       valp = RARRAY_PTR(array);
+       new2old = ALLOC_N(Int, n + 1);
+       for (i = 0; i < n; i++)
+               new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
+       new2old[i] = kInvalidIndex;
+       i = MolActionCreateAndPerform(mol, gMolActionReorderAtoms, i, new2old);
+/*     i = MoleculeReorderAtoms(mol, new2old, n); */
+       if (i == 1)
+               rb_raise(rb_eMolbyError, "Atom index out of range");
+       else if (i == 2)
+               rb_raise(rb_eMolbyError, "Duplicate entry");
+       else if (i == 3)
+               rb_raise(rb_eMolbyError, "Internal inconsistency during atom reordering");
+       retval = IntGroup_Alloc(rb_cIntGroup);
+       Data_Get_Struct(retval, IntGroup, ig);
+       if (mol->natoms > n)
+               IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
+       free(new2old);
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.guess_bonds(limit = 1.2)       -> Array
+ *
+ *  Create bonds between atoms that are 'close enough', i.e. the interatomic distance is
+ *  smaller than the sum of the vdw radii times the argument 'limit'. If limit is not
+ *  given, a default value of 1.2 is used.
+ *  The newly created bonds are returned as an array of integer.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE limval, retval;
+       double limit;
+       Int i, nbonds, *bonds;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &limval);
+       if (limval == Qnil)
+               limit = 1.2;
+       else
+               limit = NUM2DBL(rb_Float(limval));
+       MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
+       if (nbonds > 0) {
+               MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
+               retval = rb_ary_new2(nbonds * 2);
+               for (i = 0; i < nbonds * 2; i++)
+                       rb_ary_push(retval, INT2NUM(bonds[i]));
+               free(bonds);
+       } else {
+               retval = rb_ary_new();
+       }
+       return retval;
+}
+       
+/*
+ *  call-seq:
+ *     molecule.register_undo(script, *args)
+ *
+ *  Register an undo operation with the current molecule.
+ */
+static VALUE
+s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       VALUE script, args;
+       MolAction *act;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1*", &script, &args);
+       act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
+       MolActionCallback_registerUndo(mol, act);
+       return script;
+}
+
+/*
+ *  call-seq:
+ *     molecule.undo_enabled? => true/false
+ *
+ *  Returns true if undo is enabled for this molecule; otherwise no.
+ */
+static VALUE
+s_Molecule_UndoEnabled(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (MolActionCallback_isUndoRegistrationEnabled(mol))
+               return Qtrue;
+       else return Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     molecule.undo_enabled = true/false
+ *
+ *  Enable or disable undo.
+ */
+static VALUE
+s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.selection       -> intGroup
+ *
+ *  Returns the current selection. The returned value is frozen.
+ */
+static VALUE
+s_Molecule_Selection(VALUE self)
+{
+    Molecule *mol;
+       IntGroup *ig;
+       VALUE val;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
+               val = ValueFromIntGroup(ig);
+       } else {
+               val = IntGroup_Alloc(rb_cIntGroup);
+       }
+       rb_obj_freeze(val);
+       return val;
+}
+
+static VALUE
+s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
+{
+    Molecule *mol;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       if (val == Qnil)
+               ig = NULL;
+       else
+               ig = s_Molecule_AtomGroupFromValue(self, val);
+       if (undoable)
+               MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
+       else
+               MoleculeSetSelection(mol, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.selection = intGroup
+ *
+ *  Set the current selection. The right-hand operand may be nil.
+ *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
+ */
+static VALUE
+s_Molecule_SetSelection(VALUE self, VALUE val)
+{
+       return s_Molecule_SetSelectionSub(self, val, 0);
+}
+
+/*
+ *  call-seq:
+ *     molecule.set_undoable_selection(intGroup)
+ *
+ *  Set the current selection with undo registration. The right-hand operand may be nil.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
+{
+       return s_Molecule_SetSelectionSub(self, val, 1);
+}
+
+/*
+ *  call-seq:
+ *     molecule.select_frame(index)
+ *     molecule.frame = index
+ *
+ *  Select the specified frame. If successful, returns true, otherwise returns false.
+ */
+static VALUE
+s_Molecule_SelectFrame(VALUE self, VALUE val)
+{
+    Molecule *mol;
+       int ival = NUM2INT(val);
+    Data_Get_Struct(self, Molecule, mol);
+       ival = MoleculeSelectFrame(mol, ival, 1);
+       if (ival >= 0)
+               return Qtrue;
+       else return Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     molecule.frame => integer
+ *
+ *  Get the current frame.
+ */
+static VALUE
+s_Molecule_Frame(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->cframe);
+}
+
+/*
+ *  call-seq:
+ *     molecule.nframes => integer
+ *
+ *  Get the number of frames.
+ */
+static VALUE
+s_Molecule_Nframes(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(MoleculeGetNumberOfFrames(mol));
+}
+
+/*
+ *  call-seq:
+ *     molecule.insert_frame(index, coordinates = nil) => integer
+ *
+ *  Insert a new frame at index. If index is negative or greater than the number of 
+ *  frames, a new frame is inserted at the last. If coordinates is given as an array
+ *  of Vector3Ds, then those coordinates are set to the new frame. Otherwise, the
+ *  coordinates of current molecule are copied to the new frame.
+ *  Returns the index of the new frame if successful, -1 if not.
+ */
+/*
+static VALUE
+s_Molecule_InsertFrame(int argc, VALUE *argv, VALUE self)
+{
+       VALUE val, coords;
+    Molecule *mol;
+       int ival;
+       Vector *vp;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &val, &coords);
+       ival = NUM2INT(val);
+       if (coords != Qnil) {
+               int i, len;
+               VALUE *ptr;
+               if (TYPE(coords) != T_ARRAY)
+                       rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
+               len = RARRAY_LEN(coords);
+               if (len < mol->natoms)
+                       rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d vectors", mol->natoms);
+               len = mol->natoms;
+               ptr = RARRAY_PTR(coords);
+               vp = ALLOC_N(Vector, len);
+               for (i = 0; i < len; i++) {
+                       VectorFromValue(ptr[i], &vp[i]);
+               }
+       } else vp = NULL;
+       ival = MoleculeInsertFrame(mol, ival, vp);
+       if (vp != NULL)
+               free(vp);
+       val = INT2NUM(ival);
+       rb_funcall(self, rb_intern("register_undo"), 2, rb_str_new2("remove_frame"), val);
+       return val;
+}
+*/
+
+/*
+ *  call-seq:
+ *     molecule.remove_frame(index)
+ *
+ *  Remove the frame at index. If successful, an array of the coordinates in the
+ *  removed frame is returned. Otherwise, nil is returned.
+ */
+/*
+static VALUE
+s_Molecule_RemoveFrame(VALUE self, VALUE val)
+{
+       VALUE coords;
+    Molecule *mol;
+       int ival;
+       Vector *vp;
+    Data_Get_Struct(self, Molecule, mol);
+       ival = NUM2INT(val);
+       vp = ALLOC_N(Vector, mol->natoms);
+       ival = MoleculeRemoveFrame(mol, ival, vp);
+       if (ival >= 0) {
+               int i;
+               coords = rb_ary_new2(mol->natoms);
+               for (i = 0; i < mol->natoms; i++) {
+                       rb_ary_push(coords, ValueFromVector(&vp[i]));
+               }
+               rb_funcall(self, rb_intern("register_undo"), 3, rb_str_new2("insert_frame"), val, coords);
+       } else coords = Qnil;
+       free(vp);
+       return coords;
+}
+*/
+
+/*
+ *  call-seq:
+ *     molecule.insert_frame(integer, coordinates = nil) => boolean
+ *     molecule.insert_frames(intGroup = nil, coordinates = nil) => boolean
+ *
+ *  Insert new frames at the indices specified by the intGroup. If the first argument is
+ *  an integer, a single new frame is inserted at that index. If the first argument is 
+ *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
+ *  should be an array of arrays of Vector3Ds, then those coordinates are set 
+ *  to the new frame. Otherwise, the coordinates of current molecule are copied 
+ *  to the new frame.
+ *  Returns an intGroup representing the inserted frames if successful, nil if not.
+ */
+static VALUE
+s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
+{
+       VALUE val, coords;
+    Molecule *mol;
+       IntGroup *ig;
+       int count, ival, i, j, len, nframes;
+       Vector *vp;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &val, &coords);
+       if (coords != Qnil) {
+               if (TYPE(coords) != T_ARRAY)
+                       rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
+               len = RARRAY_LEN(coords);
+       } else len = 0;
+       nframes = MoleculeGetNumberOfFrames(mol);
+       if (val == Qnil) {
+               ig = IntGroupNewWithPoints(nframes, (len > 0 ? len : 1), -1);
+               val = ValueFromIntGroup(ig);
+       } else {
+               ig = IntGroupFromValue(val);
+       }
+       count = IntGroupGetCount(ig);
+       vp = ALLOC_N(Vector, mol->natoms * count);
+       if (len > 0) {
+               VALUE *ptr;
+               if (len < count)
+                       rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
+               ptr = RARRAY_PTR(coords);
+               for (i = 0; i < len; i++) {
+                       VALUE *ptr2;
+                       int j, len2;
+                       if (TYPE(ptr[i]) != T_ARRAY)
+                               rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
+                       len2 = RARRAY_LEN(ptr[i]);
+                       if (len2 < mol->natoms)
+                               rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
+                       ptr2 = RARRAY_PTR(ptr[i]);
+                       for (j = 0; j < mol->natoms; j++)
+                               VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
+               }
+       } else {
+               Atom *ap;
+               for (i = 0; i < count; i++) {
+                       for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
+                               vp[i * mol->natoms + j] = ap->r;
+                       }
+               }
+       }
+#if 1
+       ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp);
+       IntGroupRelease(ig);
+       free(vp);
+       return (ival >= 0 ? val : Qnil);
+#else
+       ival = MoleculeInsertFrames(mol, ig, vp);
+       if (vp != NULL)
+               free(vp);
+       if (val == Qnil)
+               val = ValueFromIntGroup(ig);
+       IntGroupRelease(ig);
+       i = MoleculeGetNumberOfFrames(mol);
+       if (nframes + count < i) {
+               /*  Register undo operation to remove "extra" frames that were automatically inserted  */
+               ig = IntGroupNewWithPoints(nframes, i - (nframes + count), -1);
+               rb_funcall(self, rb_intern("register_undo"), 2, rb_str_new2("remove_frames"), ValueFromIntGroup(ig));
+               IntGroupRelease(ig);
+       }
+       rb_funcall(self, rb_intern("register_undo"), 2, rb_str_new2("remove_frames"), val);
+       return (ival >= 0 ? val : Qnil);
+#endif
+}
+
+/*
+ *  call-seq:
+ *     molecule.create_frame(coordinates = nil) => integer
+ *     molecule.create_frames(coordinates = nil) => integer
+ *
+ *  Same as molecule.insert_frames(nil, coordinates).
+ */
+static VALUE
+s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
+{
+       VALUE vals[2];
+       rb_scan_args(argc, argv, "01", &vals[1]);
+       vals[0] = Qnil;
+       return s_Molecule_InsertFrames(2, vals, self);
+}
+
+/*
+ *  call-seq:
+ *     molecule.remove_frames(intGroup, wantCoordinates = false)
+ *
+ *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
+ *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
+ *  removed frames is returned if operation is successful.
+ */
+static VALUE
+s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
+{
+       VALUE val, flag;
+       VALUE retval;
+    Molecule *mol;
+       IntGroup *ig;
+       int count;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &val, &flag);
+       ig = IntGroupFromValue(val);
+       count = IntGroupGetCount(ig);
+       if (RTEST(flag)) {
+               /*  Create return value before removing frames  */
+               VALUE coords;
+               int i, j, n;
+               Atom *ap;
+               Vector v;
+               retval = rb_ary_new2(count);
+               for (i = 0; i < count; i++) {
+                       n = IntGroupGetNthPoint(ig, i);
+                       coords = rb_ary_new2(mol->natoms);
+                       for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
+                               if (n < ap->nframes && n != mol->cframe)
+                                       v = ap->frames[n];
+                               else v = ap->r;
+                               rb_ary_push(coords, ValueFromVector(&v));
+                       }
+                       rb_ary_push(retval, coords);
+               }
+       } else retval = Qtrue;
+       if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
+               return retval;
+       else return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     molecule.each_frame block
+ *
+ *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
+ *  the frame number. After completion, the original frame number is restored.
+ */
+static VALUE
+s_Molecule_EachFrame(VALUE self)
+{
+       int i, cframe, nframes;
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       cframe = mol->cframe;
+       nframes = MoleculeGetNumberOfFrames(mol);
+       if (nframes > 0) {
+               for (i = 0; i < nframes; i++) {
+                       MoleculeSelectFrame(mol, i, 1);
+                       rb_yield(INT2NUM(i));
+               }
+               MoleculeSelectFrame(mol, cframe, 1);
+       }
+    return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.set_atom_attr(index, key, value)
+ *
+ *  Set the atom attribute for the specified atom.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
+{
+       Molecule *mol;
+       VALUE aref, oldval;
+    Data_Get_Struct(self, Molecule, mol);
+       aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
+       oldval = s_AtomRef_GetAttr(aref, key);
+       if (val == Qundef)
+               return oldval;
+       s_AtomRef_SetAttr(aref, key, val);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.get_atom_attr(index, key)
+ *
+ *  Get the atom attribute for the specified atom.
+ */
+static VALUE
+s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
+{
+       return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
+}
+
+/*
+ *  call-seq:
+ *     molecule.fragment(n1, *exatoms)  -> molecule
+ *     molecule.fragment(group, *exatoms)  -> molecule
+ *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
+ *  those atoms will not be counted during the search.
+ */
+static VALUE
+s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       IntGroup *baseg, *ig, *exatoms;
+       int n;
+       volatile VALUE nval, exval;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "1*", &nval, &exval);
+       if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
+               baseg = NULL;
+               n = NUM2INT(s_Molecule_AtomIndex(self, nval));
+       } else {
+               baseg = s_Molecule_AtomGroupFromValue(self, nval);
+       }
+       if (RARRAY_LEN(exval) == 0) {
+               exatoms = NULL;
+       } else {
+               exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
+               Data_Get_Struct(exval, IntGroup, exatoms);
+       }
+       if (baseg == NULL) {
+               ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
+       } else {
+               IntGroupIterator iter;
+               IntGroupIteratorInit(baseg, &iter);
+               if ((n = IntGroupIteratorNext(&iter)) < 0) {
+                       ig = IntGroupNew();
+               } else {
+                       ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
+                       if (ig != NULL) {
+                               while ((n = IntGroupIteratorNext(&iter)) >= 0) {
+                                       IntGroup *subg;
+                                       subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
+                                       if (subg != NULL) {
+                                               IntGroupAddIntGroup(ig, subg);
+                                               IntGroupRelease(subg);
+                                       }
+                               }
+                       }
+               }
+               IntGroupIteratorRelease(&iter);
+               IntGroupRelease(baseg);
+       }
+       if (ig == NULL)
+               rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
+       nval = ValueFromIntGroup(ig);
+       IntGroupRelease(ig);
+       return nval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.each_fragment block
+ *
+ *  Execute the block, with the IntGroup object for each fragment as the argument.
+ *  Atoms or bonds should not be added or removed during the execution of the block.
+ */
+static VALUE
+s_Molecule_EachFragment(VALUE self)
+{
+    Molecule *mol;
+       IntGroup *ag, *fg;
+       VALUE gval;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol == NULL || mol->natoms == 0)
+               return self;
+       ag = IntGroupNewWithPoints(0, mol->natoms, -1);
+       while (IntGroupGetCount(ag) > 0) {
+               int n = IntGroupGetNthPoint(ag, 0);
+               fg = MoleculeFragmentExcludingAtomGroup(mol, n, NULL);
+               if (fg == NULL)
+                       rb_raise(rb_eMolbyError, "internal error during each_fragment");
+               gval = ValueFromIntGroup(fg);
+               rb_yield(gval);
+               IntGroupRemoveIntGroup(ag, fg);
+               IntGroupRelease(fg);
+       }
+       IntGroupRelease(ag);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.detachable?(n1, group)  -> array of two atoms
+ *
+ *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
+ *  of the molecule via only one bond. If it is, then the indices of the atoms
+ *  belonging to the bond is returned, the first element being the atom included
+ *  in the fragment. Otherwise, Qnil is returned.
+ */
+static VALUE
+s_Molecule_Detachable_P(VALUE self, VALUE gval)
+{
+       Molecule *mol;
+       IntGroup *ig;
+       int n1, n2;
+       VALUE retval;
+    Data_Get_Struct(self, Molecule, mol);
+       ig = s_Molecule_AtomGroupFromValue(self, gval);
+       if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
+               retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
+       } else retval = Qnil;
+       IntGroupRelease(ig);
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.bonds_on_border(group = selection)  -> array of array of two atoms
+ *
+ *  Returns an array of bonds that connect an atom in the group and an atom out
+ *  of the group. The first atom in the bond always belongs to the group. If no
+ *  such bonds are present, an empty array is returned.
+ */
+static VALUE
+s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       IntGroup *ig, *bg;
+       VALUE gval, retval;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &gval);
+       if (gval == Qnil) {
+               ig = MoleculeGetSelection(mol);
+               if (ig != NULL)
+                       IntGroupRetain(ig);
+       } else {
+               ig = s_Molecule_AtomGroupFromValue(self, gval);
+       }
+       retval = rb_ary_new();
+       if (ig == NULL)
+               return retval;
+       bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
+       if (bg != NULL) {
+               IntGroupIterator iter;
+               Int i;
+               IntGroupIteratorInit(bg, &iter);
+               while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+                       /*  The atoms at the border  */
+                       Int n1, n2;
+                       n1 = mol->bonds[i * 2];
+                       n2 = mol->bonds[i * 2 + 1];
+                       if (IntGroupLookupPoint(ig, n1) < 0) {
+                               int w = n1;
+                               n1 = n2;
+                               n2 = w;
+                               if (IntGroupLookupPoint(ig, n1) < 0)
+                                       continue;  /*  Actually this is an internal error  */
+                       }
+                       rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
+               }
+               IntGroupIteratorRelease(&iter);
+       }
+       IntGroupRelease(bg);
+       IntGroupRelease(ig);
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.translate(vec, group = nil)       -> molecule
+ *
+ *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE vec, group;
+       Vector v;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &vec, &group);
+       ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+       VectorFromValue(vec, &v);
+//     MoleculeTranslate(mol, &v, ig);
+       MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.rotate(axis, angle, center = [0,0,0], group = nil)       -> molecule
+ *
+ *  Rotate the molecule. The axis must not a zero vector. angle is given in radian.
+ *  If group is given, only atoms in the group are moved.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       volatile VALUE aval, anval, cval, gval;
+       Double angle;
+       Vector av, cv;
+       Transform tr;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
+       ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+       angle = NUM2DBL(rb_Float(anval));
+       VectorFromValue(aval, &av);
+       if (NIL_P(cval))
+               cv.x = cv.y = cv.z = 0.0;
+       else
+               VectorFromValue(cval, &cv);
+       if (TransformForRotation(tr, &av, angle, &cv))
+               rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
+       MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.reflect(axis, center = [0,0,0], group = nil)       -> molecule
+ *
+ *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
+ *  axis must not be a zero vector.
+ *  If group is given, only atoms in the group are moved.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       volatile VALUE aval, cval, gval;
+       Vector av, cv;
+       Transform tr;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
+       ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+       VectorFromValue(aval, &av);
+       if (NIL_P(cval))
+               cv.x = cv.y = cv.z = 0.0;
+       else
+               VectorFromValue(cval, &cv);
+       if (TransformForReflection(tr, &av, &cv))
+               rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
+       MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.invert(center = [0,0,0], group = nil)       -> molecule
+ *
+ *  Invert the molecule with the given center.
+ *  If group is given, only atoms in the group are moved.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       volatile VALUE cval, gval;
+       Vector cv;
+       Transform tr;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "02", &cval, &gval);
+       ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+       if (NIL_P(cval))
+               cv.x = cv.y = cv.z = 0.0;
+       else
+               VectorFromValue(cval, &cv);
+       TransformForInversion(tr, &cv);
+       MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.transform(transform, group = nil)       -> molecule
+ *
+ *  Transform the molecule by the given Transform object.
+ *  If group is given, only atoms in the group are moved.
+ *  This operation is undoable.
+ */
+static VALUE
+s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE trans, group;
+       Transform tr;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "11", &trans, &group);
+       ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+       TransformFromValue(trans, &tr);
+/*     MoleculeTransform(mol, tr, ig); */
+       MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       return self;
+}
+
+static void
+s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
+{
+       switch (MoleculeCenterOfMass(mol, outv, ig)) {
+               case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
+               case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
+               case 0: break;
+               default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
+       }
+}
+
+/*
+ *  call-seq:
+ *     molecule.center_of_mass(group = nil)       -> vector3d
+ *
+ *  Calculate the center of mass for the given set of atoms. The argument
+ *  group is null, then all atoms are considered.
+ */
+static VALUE
+s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE group;
+       IntGroup *ig;
+       Vector v;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &group);
+       ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+       s_Molecule_DoCenterOfMass(mol, &v, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       return ValueFromVector(&v);
+}
+
+/*
+ *  call-seq:
+ *     molecule.centralize(group = nil)       -> self
+ *
+ *  Translate the molecule so that the center of mass of the given group is located
+ *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
+ */
+static VALUE
+s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE group;
+       IntGroup *ig;
+       Vector v;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &group);
+       ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+       s_Molecule_DoCenterOfMass(mol, &v, ig);
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       v.x = -v.x;
+       v.y = -v.y;
+       v.z = -v.z;
+       MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
+       return self;
+}
+
+/*
+ *  call-seq:
+ *     molecule.bounds(group = nil)       -> [min, max]
+ *
+ *  Calculate the boundary. The return value is an array of two Vector3D objects.
+ */
+static VALUE
+s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE group;
+       IntGroup *ig;
+       Vector vmin, vmax;
+       int n;
+       Atom *ap;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "01", &group);
+       ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+       if (ig != NULL && IntGroupGetCount(ig) == 0)
+               rb_raise(rb_eMolbyError, "atom group is empty");
+       vmin.x = vmin.y = vmin.z = 1e30;
+       vmax.x = vmax.y = vmax.z = -1e30;
+       for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
+               Vector r;
+               if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
+                       continue;
+               r = ap->r;
+               if (r.x < vmin.x)
+                       vmin.x = r.x;
+               if (r.y < vmin.y)
+                       vmin.y = r.y;
+               if (r.z < vmin.z)
+                       vmin.z = r.z;
+               if (r.x > vmax.x)
+                       vmax.x = r.x;
+               if (r.y > vmax.y)
+                       vmax.y = r.y;
+               if (r.z > vmax.z)
+                       vmax.z = r.z;
+       }
+       return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
+}
+
+/*  Get atom position or a vector  */
+static void
+s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
+{
+       if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
+               int n1 = s_Molecule_AtomIndexFromValue(mol, val);
+               *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
+       } else {
+               VectorFromValue(val, vp);
+       }
+}
+
+/*
+ *  call-seq:
+ *     molecule.measure_bond(n1, n2)       -> Float
+ *
+ *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
+ *  or Vector3D values.
+ *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
+ */
+static VALUE
+s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
+{
+    Molecule *mol;
+       Vector v1, v2;
+    Data_Get_Struct(self, Molecule, mol);
+       s_Molecule_GetVectorFromArg(mol, nval1, &v1);
+       s_Molecule_GetVectorFromArg(mol, nval2, &v2);
+       return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
+}
+
+/*
+ *  call-seq:
+ *     molecule.measure_angle(n1, n2, n3)       -> Float
+ *
+ *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
+ *  or Vector3D values. The return value is in degree.
+ *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
+ */
+static VALUE
+s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
+{
+    Molecule *mol;
+       Vector v1, v2, v3;
+       Double d;
+    Data_Get_Struct(self, Molecule, mol);
+       s_Molecule_GetVectorFromArg(mol, nval1, &v1);
+       s_Molecule_GetVectorFromArg(mol, nval2, &v2);
+       s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
+       d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
+       if (isnan(d))
+               return Qnil;  /*  Cannot define  */
+       else return rb_float_new(d);
+}
+
+/*
+ *  call-seq:
+ *     molecule.measure_dihedral(n1, n2, n3, n4)       -> Float
+ *
+ *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
+ *  or Vector3D values. The return value is in degree.
+ *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
+ */
+static VALUE
+s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
+{
+    Molecule *mol;
+       Vector v1, v2, v3, v4;
+       Double d;
+    Data_Get_Struct(self, Molecule, mol);
+       s_Molecule_GetVectorFromArg(mol, nval1, &v1);
+       s_Molecule_GetVectorFromArg(mol, nval2, &v2);
+       s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
+       s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
+       d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
+       if (isnan(d))
+               return Qnil;  /*  Cannot define  */
+       else return rb_float_new(d);
+}
+
+/*
+ *  call-seq:
+ *     molecule.expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> IntGroup
+ *
+ *  Expand the specified part of the molecule by the given symmetry operation.
+ *  Returns an IntGroup containing the added atoms.
+ */
+static VALUE
+s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE gval, sval, xval, yval, zval, rval;
+       IntGroup *ig;
+       int n[4];
+       int natoms;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
+       n[0] = NUM2INT(rb_Integer(sval));
+       n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
+       n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
+       n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
+       ig = s_Molecule_AtomGroupFromValue(self, gval);
+       if (n[0] < 0 || n[0] >= mol->nsyms)
+               rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
+       natoms = mol->natoms;
+       
+       MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0]);
+
+       if (natoms == mol->natoms)
+               rval = Qnil;
+       else {
+               rval = IntGroup_Alloc(rb_cIntGroup);
+               Data_Get_Struct(rval, IntGroup, ig);
+               IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
+       }
+       return rval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.wrap_unit_cell(group) -> Vector3D
+ *
+ *  Move the specified group so that the center of mass of the group is within the
+ *  unit cell. The offset vector is returned. If no periodic box is defined, 
+ *  exception is raised.
+ */
+static VALUE
+s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
+{
+    Molecule *mol;
+       IntGroup *ig;
+       Vector v, cv, dv;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->cell == NULL)
+               rb_raise(rb_eMolbyError, "no unit cell is defined");
+       ig = s_Molecule_AtomGroupFromValue(self, gval);
+       s_Molecule_DoCenterOfMass(mol, &cv, ig);
+       TransformVec(&v, mol->cell->rtr, &cv);
+       if (mol->cell->flags[0])
+               v.x -= floor(v.x + 0.5);
+       if (mol->cell->flags[1])
+               v.y -= floor(v.y + 0.5);
+       if (mol->cell->flags[2])
+               v.z -= floor(v.z + 0.5);
+       TransformVec(&dv, mol->cell->tr, &v);
+       VecDec(dv, cv);
+       MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
+       IntGroupRelease(ig);
+       return ValueFromVector(&dv);
+}
+
+/*
+ *  call-seq:
+ *     molecule.find_conflicts(limit[, group1[, group2]]) -> [[n1, n2], [n3, n4], ...]
+ *
+ *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
+ *  first and second atom in the pair should belong to group1 and group2, respectively.
+ */
+static VALUE
+s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE limval, gval1, gval2, rval;
+       IntGroup *ig1, *ig2;
+       IntGroupIterator iter1, iter2;
+       Int npairs, *pairs;
+       Int n[2], i;
+       Double lim;
+       Vector r1;
+       Atom *ap1, *ap2;
+    Data_Get_Struct(self, Molecule, mol);
+       rb_scan_args(argc, argv, "12", &limval, &gval1, &gval2);
+       lim = NUM2DBL(rb_Float(limval));
+       if (lim <= 0.0)
+               rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
+       if (gval1 != Qnil)
+               ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
+       else
+               ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
+       if (gval2 != Qnil)
+               ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
+       else
+               ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
+       IntGroupIteratorInit(ig1, &iter1);
+       IntGroupIteratorInit(ig2, &iter2);
+       npairs = 0;
+       pairs = NULL;
+       while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
+               ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
+               r1 = ap1->r;
+               IntGroupIteratorReset(&iter2);
+               while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
+                       ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
+                       if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
+                               /*  Is this pair already registered?  */
+                               Int *ip;
+                               for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
+                                       if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
+                                               break;
+                               }
+                               if (i >= npairs) {
+                                       /*  Not registered yet  */
+                                       AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
+                               }
+                       }
+               }
+       }
+       IntGroupIteratorRelease(&iter2);
+       IntGroupIteratorRelease(&iter1);
+       IntGroupRelease(ig2);
+       IntGroupRelease(ig1);
+       rval = rb_ary_new2(npairs);
+       if (pairs != NULL) {
+               for (i = 0; i < npairs; i++) {
+                       rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
+               }
+               free(pairs);
+       }
+       return rval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.display()
+ *
+ *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
+ */
+static VALUE
+s_Molecule_Display(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview != NULL)
+               MainViewCallback_display(mol->mview);
+       return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     molecule.update_enabled? => true/false
+ *
+ *  Returns true if screen update is enabled; otherwise no.
+ */
+static VALUE
+s_Molecule_UpdateEnabled(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview != NULL && !mol->mview->freezeScreen)
+               return Qtrue;
+       else return Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     molecule.update_enabled = true/false
+ *
+ *  Enable or disable screen update. This is effective for automatic update on modification.
+ *  Explicit call to molecule.display() always updates the screen.
+ */
+static VALUE
+s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
+       if (mol->mview != NULL)
+               mol->mview->freezeScreen = (val == Qfalse);
+       else val = Qfalse;
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_unitcell
+ *     molecule.show_unitcell true|false
+ *     molecule.show_unitcell = true|false
+ *
+ *  Set the flag whether to show the unit cell. If no argument is given, the
+ *  current flag is returned.
+ */
+static VALUE
+s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       if (argc > 0) {
+               mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       return (mol->mview->showUnitCell ? Qtrue : Qfalse);
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_hydrogens
+ *     molecule.show_hydrogens true|false
+ *     molecule.show_hydrogens = true|false
+ *
+ *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
+ *  current flag is returned.
+ */
+static VALUE
+s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       if (argc > 0) {
+               mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       return (mol->mview->showHydrogens ? Qtrue : Qfalse);
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_dummy_atoms
+ *     molecule.show_dummy_atoms true|false
+ *     molecule.show_dummy_atoms = true|false
+ *
+ *  Set the flag whether to show the dummy atoms. If no argument is given, the
+ *  current flag is returned.
+ */
+static VALUE
+s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       if (argc > 0) {
+               mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_expanded
+ *     molecule.show_expanded true|false
+ *     molecule.show_expanded = true|false
+ *
+ *  Set the flag whether to show the expanded atoms. If no argument is given, the
+ *  current flag is returned.
+ */
+static VALUE
+s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       if (argc > 0) {
+               mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_ellipsoids
+ *     molecule.show_ellipsoids true|false
+ *     molecule.show_ellipsoids = true|false
+ *
+ *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
+ *  current flag is returned.
+ */
+static VALUE
+s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       if (argc > 0) {
+               mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_graphite
+ *     molecule.show_graphite = integer
+ *
+ *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
+ *  number of rings to display for each direction.
+ */
+static VALUE
+s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       if (argc > 0) {
+               int n = NUM2INT(rb_Integer(argv[0]));
+               if (n < 0)
+                       rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
+               mol->mview->showGraphite = n;
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       return INT2NUM(mol->mview->showGraphite);
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
+ *
+ *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
+ *  set but no visual effects are observed.
+ */
+static VALUE
+s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       VALUE val;
+       int ival[6];
+       int i;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       rb_scan_args(argc, argv, "01", &val);
+       if (val != Qnil) {
+               val = rb_ary_to_ary(val);
+               for (i = 0; i < 6; i++) {
+                       if (i < RARRAY_LEN(val))
+                               ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
+               }
+               if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
+                       rb_raise(rb_eMolbyError, "bad arguments");
+               for (i = 0; i < 6; i++)
+                       mol->mview->showPeriodicImage[i] = ival[i];
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       val = rb_ary_new();
+       for (i = 0; i < 6; i++)
+               rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
+       return val;
+}
+
+
+/*
+ *  call-seq:
+ *     molecule.line_mode
+ *     molecule.line_mode true|false
+ *     molecule.line_mode = true|false
+ *
+ *  Set the flag whether to draw the model in line mode. If no argument is given, the
+ *  current flag is returned.
+ */
+static VALUE
+s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       if (argc > 0) {
+               mol->mview->lineMode = (RTEST(argv[0]) != 0);
+               MainViewCallback_setNeedsDisplay(mol->mview, 1);
+       }
+       return (mol->mview->lineMode ? Qtrue : Qfalse);
+}
+
+/*
+ *  call-seq:
+ *     molecule.show_text(string)
+ *
+ *  Show the string in the info text box.
+ */
+static VALUE
+s_Molecule_ShowText(VALUE self, VALUE arg)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview != NULL)
+               MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
+       return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     molecule.md_arena -> MDArena
+ *
+ *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
+ *  this molecule, a new arena is created.
+ */
+static VALUE
+s_Molecule_MDArena(VALUE self)
+{
+    Molecule *mol;
+       VALUE retval;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->arena == NULL)
+               md_arena_new(mol);
+       retval = ValueFromMDArena(mol->arena);
+/*     if (!mol->arena->is_initialized || mol->needsMDRebuild)
+               rb_funcall(retval, rb_intern("prepare"), 0); */
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.set_parameter_attr(type, index, key, value, src) -> value
+ *
+ *  This method is used only internally.
+ */
+static VALUE
+s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
+{
+       /*  This method is called from MolAction to change a MM parameter attribute.  */
+    Molecule *mol;
+       VALUE pval;
+       ParameterRef *pref;
+       UnionPar *up;
+    Data_Get_Struct(self, Molecule, mol);
+       pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
+       vval = s_ParameterRef_SetAttr(pval, kval, vval);
+       
+       /*  This is the special part of this method; it allows modification of the src field. */
+       /*  (ParameterRef#set_attr sets 0 to the src field)  */
+       Data_Get_Struct(pval, ParameterRef, pref);
+       up = ParameterRefGetPar(pref);
+       up->bond.src = FIX2INT(sval);
+       
+       return vval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.parameter -> Parameter
+ *
+ *  Get the local parameter of this molecule. If not defined, returns nil.
+ */
+static VALUE
+s_Molecule_Parameter(VALUE self)
+{
+    Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->par == NULL)
+               return Qnil;
+       return s_NewParameterValueFromValue(self);
+}
+
+/*
+ *  call-seq:
+ *     molecule.selectedMO -> IntGroup
+ *
+ *  Returns a group of selected mo in the "MO Info" table. If the MO info table
+ *  is not selected, returns nil. If the MO info table is selected but no MOs 
+ *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
+ */
+static VALUE
+s_Molecule_SelectedMO(VALUE self)
+{
+    Molecule *mol;
+       IntGroup *ig;
+       VALUE val;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               return Qnil;
+       ig = MainView_selectedMO(mol->mview);
+       if (ig == NULL)
+               return Qnil;
+       IntGroupOffset(ig, 1);
+       val = ValueFromIntGroup(ig);
+       IntGroupRelease(ig);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     molecule.default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
+ *
+ *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
+ *  If the molecule does not contain a basis set information, then returns nil.
+ */
+static VALUE
+s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
+{
+    Molecule *mol;
+       Vector o, dx, dy, dz;
+       Int nx, ny, nz;
+       VALUE nval;
+       Int npoints = 80 * 80 * 80;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->bset == NULL)
+               return Qnil;
+       rb_scan_args(argc, argv, "01", &nval);
+       if (nval != Qnil)
+               npoints = NUM2INT(rb_Integer(nval));
+       if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
+               return Qnil;
+       return rb_ary_new3(7, ValueFromVector(&o), rb_float_new(VecLength(dx)), rb_float_new(VecLength(dy)), rb_float_new(VecLength(dz)), INT2NUM(nx), INT2NUM(ny), INT2NUM(nz));
+}
+
+static int
+s_Cubegen_callback(double progress, void *ref)
+{
+       MyAppCallback_setProgressValue(progress);
+       if (MyAppCallback_checkInterrupt())
+               return 1;
+       else return 0;
+}
+
+/*
+ *  call-seq:
+ *     molecule.cubegen(fname, mo, npoints=1000000 [, iflag])
+ *     molecule.cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag])
+ *
+ *  Calculate the molecular orbital with number mo and create a 'cube' file.
+ *  In the first form, the cube size is estimated from the atomic coordinates. In the
+ *  second form, the cube dimension is explicitly given.
+ *  Returns fname when successful, nil otherwise.
+ *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
+ *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
+ */
+static VALUE
+s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
+{
+       VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival;
+    Molecule *mol;
+       Int mono, nx, ny, nz, npoints;
+       Vector o, dx, dy, dz;
+       int index, n;
+       char buf[1024];
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->bset == NULL)
+               rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
+       rb_scan_args(argc, argv, "28", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival);
+       
+       /*  Set up parameters  */
+       mono = NUM2INT(rb_Integer(mval));
+       if (mono <= 0 || mono > mol->bset->nmos)
+               rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->nmos);
+       if (oval == Qnil || dxval == Qnil) {
+               /*  Automatic grid formation  */
+               if (oval != Qnil)
+                       npoints = NUM2INT(rb_Integer(oval));
+               else npoints = 0;
+               if (npoints == 0)
+                       npoints = 1000000;
+               else if (npoints < 8)
+                       rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
+               if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
+                       rb_raise(rb_eMolbyError, "Cannot determine cube grids");
+               ival = dxval;
+       } else {
+               VectorFromValue(oval, &o);
+               if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
+                       VectorFromValue(dxval, &dx);
+               else {
+                       dx.x = NUM2DBL(rb_Float(dxval));
+                       dx.y = dx.z = 0.0;
+               }
+               if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
+                       VectorFromValue(dyval, &dy);
+               else {
+                       dy.y = NUM2DBL(rb_Float(dyval));
+                       dy.x = dy.z = 0.0;
+               }
+               if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
+                       VectorFromValue(dzval, &dz);
+               else {
+                       dz.z = NUM2DBL(rb_Float(dzval));
+                       dz.x = dz.y = 0.0;
+               }
+               nx = NUM2INT(rb_Integer(nxval));
+               ny = NUM2INT(rb_Integer(nyval));
+               nz = NUM2INT(rb_Integer(nzval));
+               if (nx <= 0 || ny <= 0 || nz <= 0)
+                       rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
+               else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
+                       rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) seem to be too large; please keep them less than 10000 and nx*ny*nz < 100000000", nx, ny, nz);
+       }
+       
+       /*  Calc MO  */
+       index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
+       if (index == -2)
+               rb_interrupt();
+       else if (index < 0)
+               rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
+       
+       /*  Output to file  */
+       MoleculeCallback_displayName(mol, buf, sizeof buf);
+       n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
+       if (n != 0)
+               rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
+       
+       /*  Discard the cube  */
+       MoleculeClearCubeAtIndex(mol, index);
+       return fval;
+}
+
+/*
+ *  call-seq:
+ *     molecule.nelpots
+ *
+ *  Get the number of electrostatic potential info.
+ */
+static VALUE
+s_Molecule_NElpots(VALUE self)
+{
+       Molecule *mol;
+    Data_Get_Struct(self, Molecule, mol);
+       return INT2NUM(mol->nelpots);
+}
+
+/*
+ *  call-seq:
+ *     molecule.elpot(idx)
+ *
+ *  Get the electrostatic potential info at the given index. If present, then the
+ *  return value is [Vector, Float] (position and potential). If not present, then
+ *  returns nil.
+ */
+static VALUE
+s_Molecule_Elpot(VALUE self, VALUE ival)
+{
+       Molecule *mol;
+       int idx;
+    Data_Get_Struct(self, Molecule, mol);
+       idx = NUM2INT(rb_Integer(ival));
+       if (idx < 0 || idx >= mol->nelpots)
+               return Qnil;
+       return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
+}
+
+/*
+ *  call-seq:
+ *     molecule.search_equivalent_atoms(ig = nil)
+ *
+ *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
+ */
+static VALUE
+s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
+{
+       Molecule *mol;
+       Int *result, i;
+       VALUE val;
+       IntGroup *ig;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->natoms == 0)
+               return Qnil;
+       rb_scan_args(argc, argv, "01", &val);
+       if (val != Qnil)
+               ig = IntGroupFromValue(val);
+       else ig = NULL;
+       result = MoleculeSearchEquivalentAtoms(mol, ig);
+       if (result == NULL)
+               rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
+       if (ig != NULL)
+               IntGroupRelease(ig);
+       val = rb_ary_new2(mol->natoms);
+       for (i = 0; i < mol->natoms; i++)
+               rb_ary_push(val, INT2NUM(result[i]));
+       free(result);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     Molecule.current       -> Molecule
+ *
+ *  Get the currently "active" molecule.
+ */
+static VALUE
+s_Molecule_Current(VALUE klass)
+{
+       return ValueFromMolecule(MoleculeCallback_currentMolecule());
+}
+
+/*
+ *  call-seq:
+ *     Molecule[]          -> Molecule
+ *     Molecule[n]         -> Molecule
+ *     Molecule[name]      -> Molecule
+ *     Molecule[name, k]   -> Molecule
+ *     Molecule[regex]     -> Molecule
+ *     Molecule[regex, k]  -> Molecule
+ *
+ *  Molecule[] is equivalent to Molecule.current.
+ *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
+ *  Molecule[name] gives the first document (in the order of creation time) that has
+ *  the given name. If a second argument (k) is given, the n-th document that has the
+ *  given name is returned.
+ *  Molecule[regex] gives the first document (in the order of creation time) that
+ *  has a name matching the regular expression. If a second argument (k) is given, 
+ *  the n-th document that has a name matching the re is returned.
+ */
+static VALUE
+s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
+{
+       VALUE val, kval;
+       int idx, k;
+       Molecule *mol;
+       char buf[1024];
+       rb_scan_args(argc, argv, "02", &val, &kval);
+       if (val == Qnil)
+               return s_Molecule_Current(klass);
+       if (rb_obj_is_kind_of(val, rb_cInteger)) {
+               idx = NUM2INT(val);
+               mol = MoleculeCallback_moleculeAtIndex(idx);
+       } else if (rb_obj_is_kind_of(val, rb_cString)) {
+               char *p = StringValuePtr(val);
+               k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
+               for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
+                       MoleculeCallback_displayName(mol, buf, sizeof buf);
+                       if (strcmp(buf, p) == 0 && --k == 0)
+                               break;
+               }
+       } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
+               k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
+               for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
+                       VALUE name;
+                       MoleculeCallback_displayName(mol, buf, sizeof buf);
+                       name = rb_str_new2(buf);
+                       if (rb_reg_match(val, name) != Qnil && --k == 0)
+                               break;
+               }       
+       } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
+       
+       if (mol == NULL)
+               return Qnil;
+       else return ValueFromMolecule(mol);
+}
+
+/*
+ *  call-seq:
+ *     Molecule.list         -> array of Molecules
+ *
+ *  Get the list of molecules associated to the documents, in the order of creation
+ *  time of the document. If no document is open, returns an empry array.
+ */
+static VALUE
+s_Molecule_List(VALUE klass)
+{
+       Molecule *mol;
+       int i;
+       VALUE ary;
+       i = 0;
+       ary = rb_ary_new();
+       while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
+               rb_ary_push(ary, ValueFromMolecule(mol));
+               i++;
+       }
+       return ary;
+}
+
+/*
+ *  call-seq:
+ *     Molecule.ordered_list         -> array of Molecules
+ *
+ *  Get the list of molecules associated to the documents, in the order of front-to-back
+ *  ordering of the associated window. If no document is open, returns an empry array.
+ */
+static VALUE
+s_Molecule_OrderedList(VALUE klass)
+{
+       Molecule *mol;
+       int i;
+       VALUE ary;
+       i = 0;
+       ary = rb_ary_new();
+       while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
+               rb_ary_push(ary, ValueFromMolecule(mol));
+               i++;
+       }
+       return ary;
+}
+
+void
+Init_Molby(void)
+{
+       int i;
+       
+       /*  Define Vector3D, Transform, IntGroup  */
+       Init_MolbyTypes();
+       
+       /*  Define MDArena  */
+       Init_MolbyMDTypes();
+
+       /*  class Molecule  */
+       rb_cMolecule = rb_define_class("Molecule", rb_cObject);
+       rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
+    rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
+    rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
+    rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
+    rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
+    rb_define_method(rb_cMolecule, "loadpsfx", s_Molecule_Loadpsf, -1);
+    rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
+    rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
+    rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
+    rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
+    rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
+    rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
+    rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
+    rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
+       rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
+    rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
+    rb_define_method(rb_cMolecule, "savepsfx", s_Molecule_Savepsf, 1);
+    rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
+    rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
+    rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1);
+    rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
+    rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
+    rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
+    rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
+    rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
+    rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
+    rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
+    rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
+    rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
+    rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
+       rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
+       rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
+       rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
+       rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
+       rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
+
+       rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
+       rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
+       rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
+       rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
+       rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
+       rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
+       
+       rb_define_method(rb_cMolecule, "find_angles", s_Molecule_FindAngles, 0);
+       rb_define_method(rb_cMolecule, "find_dihedrals", s_Molecule_FindDihedrals, 0);
+       rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
+       rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
+       rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
+       rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
+       rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, 0);
+       rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
+       rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
+       rb_define_method(rb_cMolecule, "set_cell", s_Molecule_SetCell, -1);
+       rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
+       rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
+       rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -1);
+/*     rb_define_method(rb_cMolecule, "box_transform", s_Molecule_BoxTransform, 0); */
+       rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
+       rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
+       rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
+       rb_define_method(rb_cMolecule, "remove_symmetries", s_Molecule_RemoveSymmetry, -1);
+       rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
+       rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
+    rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
+       rb_define_alias(rb_cMolecule, "+", "add");
+    rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
+       rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
+       rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
+       rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
+       rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
+       rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
+       rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
+       rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
+       rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
+       rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
+       rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
+       rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, 4);
+       rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
+       rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
+       rb_define_method(rb_cMolecule, "reorder_atoms", s_Molecule_ReorderAtoms, 1);
+       rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
+       rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
+       rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
+       rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
+       rb_define_method(rb_cMolecule, "select_frame", s_Molecule_SelectFrame, 1);
+       rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
+       rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
+       rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
+       rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
+       rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
+       rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, 1);
+       rb_define_method(rb_cMolecule, "create_frames", s_Molecule_CreateFrames, -1);
+       rb_define_method(rb_cMolecule, "insert_frames", s_Molecule_InsertFrames, -1);
+       rb_define_method(rb_cMolecule, "remove_frames", s_Molecule_RemoveFrames, -1);
+       rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
+       rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
+       rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
+       rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
+       rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
+       rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
+       rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
+       rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, 0);
+       rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
+       rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
+       rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
+       rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
+       rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
+       rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
+       rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
+       rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
+       rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
+       rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
+       rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
+       rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
+       rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
+       rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
+       rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
+       rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
+       rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
+       rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
+       rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
+       rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
+       rb_define_method(rb_cMolecule, "show_unitcell=", s_Molecule_ShowUnitCell, -1);
+       rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
+       rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
+       rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
+       rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
+       rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
+       rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
+       rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
+       rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
+       rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
+       rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
+       rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
+       rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
+       rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
+       rb_define_method(rb_cMolecule, "line_mode=", s_Molecule_LineMode, -1);
+       rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
+       rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
+       rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
+       rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
+       rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
+       rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
+       rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
+       rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
+       rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
+       rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
+       
+       rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
+       rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
+       rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, 1);
+       rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
+       rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
+       
+       /*  class MolEnumerable  */
+       rb_cMolEnumerable = rb_define_class("MolEnumerable", rb_cObject);
+    rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
+       rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
+       rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
+    rb_define_alias(rb_cMolEnumerable, "size", "length");
+       rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
+       
+       /*  class AtomRef  */
+       rb_cAtomRef = rb_define_class("AtomRef", rb_cObject);
+       for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
+               char buf[64];
+               snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
+               rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
+               s_AtomAttrDefTable[i].id = rb_intern(buf);
+               *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
+               strcat(buf, "=");
+               rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
+       }
+       rb_define_method(rb_cAtomRef, "set_attr", s_AtomRef_SetAttr, 2);
+       rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
+       rb_define_method(rb_cAtomRef, "get_attr", s_AtomRef_GetAttr, 1);
+       rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
+       s_SetAtomAttrString = rb_str_new2("set_atom_attr");
+       rb_global_variable(&s_SetAtomAttrString);
+       
+       /*  class Parameter  */
+       rb_cParameter = rb_define_class("Parameter", rb_cObject);
+       rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
+       rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, -1);
+       rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, -1);
+       rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, -1);
+       rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, -1);
+       rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, -1);
+       rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, -1);
+       rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, -1);
+       rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, -1);
+       rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, -1);
+       rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, -1);
+       rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, -1);
+       rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, -1);
+       rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, -1);
+       rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, -1);
+       rb_define_method(rb_cParameter, "atom", s_Parameter_Atom, -1);
+       rb_define_singleton_method(rb_cParameter, "atom", s_Parameter_Atom, -1);
+       rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
+       rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
+       rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
+       rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
+       rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
+       rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
+       rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
+       rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
+       rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
+       rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
+       rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
+       rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
+       rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
+       rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
+       rb_define_method(rb_cParameter, "natoms", s_Parameter_Natoms, 0);
+       rb_define_singleton_method(rb_cParameter, "natoms", s_Parameter_Natoms, 0);
+       rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
+       rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
+       rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
+       rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
+       rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
+       rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
+       rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
+       rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
+       rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
+       rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
+       rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
+       rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
+       rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
+       rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
+       rb_define_method(rb_cParameter, "atoms", s_Parameter_Atoms, 0);
+       rb_define_singleton_method(rb_cParameter, "atoms", s_Parameter_Atoms, 0);
+
+       /*  class ParEnumerable  */
+       rb_cParEnumerable = rb_define_class("ParEnumerable", rb_cObject);
+    rb_include_module(rb_cParEnumerable, rb_mEnumerable);
+       rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, -1);
+       rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
+       rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
+       rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
+       rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
+       rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
+       rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
+       
+       /*  class ParameterRef  */
+       rb_cParameterRef = rb_define_class("ParameterRef", rb_cObject);
+       for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
+               char buf[64];
+               snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
+               rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
+               s_ParameterAttrDefTable[i].id = rb_intern(buf);
+               if (s_ParameterAttrDefTable[i].symref != NULL)
+                       *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
+               if (s_ParameterAttrDefTable[i].setter != NULL) {
+                       strcat(buf, "=");
+                       rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
+               }
+       }
+       rb_define_method(rb_cParameterRef, "set_attr", s_ParameterRef_SetAttr, 2);
+       rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
+       rb_define_method(rb_cParameterRef, "get_attr", s_ParameterRef_GetAttr, 1);
+       rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
+       rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
+       rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
+       rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
+       
+       /*  class MolbyError  */
+       rb_eMolbyError = rb_define_class("MolbyError", rb_eStandardError);
+
+       /*  class MessageOutput  */
+       rb_cMessageOutput = rb_define_class("MessageOutput", rb_cObject);
+       rb_define_method(rb_cMessageOutput, "write", s_MessageOutput_Write, 1);
+       
+       /*  module Kernel  */
+       rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
+/*     rb_define_method(rb_mKernel, "idle", s_Kernel_Idle, 1); */
+       rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
+       rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
+       rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
+       rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
+       rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
+       rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
+       rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
+       rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
+       rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
+       rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
+       rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
+       rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
+       rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
+       rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
+       
+       s_ID_equal = rb_intern("==");
+}
+
+#pragma mark ====== External functions ======
+
+static VALUE
+s_evalRubyScriptOnMoleculeSub(VALUE val)
+{
+       void **ptr = (void **)val;
+       const char *script = (const char *)ptr[0];
+       Molecule *mol = (Molecule *)ptr[1];
+       if (mol == NULL) {
+               return rb_eval_string(script);
+       } else {
+               VALUE mval = ValueFromMolecule(mol);
+               VALUE sval = rb_str_new2(script);
+               return rb_obj_instance_eval(1, &sval, mval);
+       }
+}
+
+RubyValue
+Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, int *status)
+{
+       RubyValue retval;
+       void *args[2];
+       VALUE save_interrupt_flag;
+       if (gMolbyIsCheckingInterrupt) {
+               MolActionAlertRubyIsRunning();
+               *status = -1;
+               return (RubyValue)Qnil;
+       }
+/*     gMolbyBacktrace = Qnil; */
+       gMolbyRunLevel++;
+       args[0] = (void *)script;
+       args[1] = (void *)mol;
+       save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
+       retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
+       s_SetInterruptFlag(Qnil, save_interrupt_flag);
+       gMolbyRunLevel--;
+       return retval;
+}
+
+/*
+RubyValue
+Molby_evalRubyScript(const char *script, int *status)
+{
+       RubyValue retval;
+       if (gMolbyIsCheckingInterrupt) {
+               MolActionAlertRubyIsRunning();
+               return NULL;
+       }
+       gMolbyRunLevel = 1;
+       retval = (RubyValue)rb_eval_string_protect(script, status);
+       gMolbyRunLevel = 0;
+       return retval;
+}
+
+RubyValue
+Molby_evalRubyScriptOnActiveMoleculeWithInterrupt(const char *script, int *status)
+{
+       VALUE sval, mval;
+       RubyValue retval;
+       if (gMolbyIsCheckingInterrupt) {
+               MolActionAlertRubyIsRunning();
+               return NULL;
+       }
+       gMolbyRunLevel = 1;
+       sval = rb_str_new2(script);
+       mval = s_Molecule_Current(rb_cMolecule);
+       retval = (RubyValue)Ruby_CallMethodWithInterrupt(mval, 0, sval, status);
+       gMolbyRunLevel = 0;
+       return retval;
+}
+*/
+
+void
+Molby_showRubyValue(RubyValue value)
+{
+       VALUE val = (VALUE)value;
+       if (gMolbyIsCheckingInterrupt) {
+               MolActionAlertRubyIsRunning();
+               return;
+       }
+       if (val != Qnil) {
+               int status;
+               gMolbyRunLevel++;
+               val = rb_protect(rb_inspect, val, &status);
+               gMolbyRunLevel--;
+               MyAppCallback_showScriptMessage("%s", StringValuePtr(val));
+       }
+}
+
+void
+Molby_showError(int status)
+{
+       static const int tag_raise = 6;
+       char *msg = NULL;
+       VALUE val, backtrace;
+       int interrupted = 0;
+/*     char *amsg; */
+       if (status == tag_raise) {
+               VALUE eclass = CLASS_OF(ruby_errinfo);
+               if (eclass == rb_eInterrupt) {
+                       msg = "Interrupt";
+                       interrupted = 1;
+               }
+       }
+       gMolbyRunLevel++;
+       backtrace = rb_eval_string_protect("$backtrace = $!.backtrace", &status);
+       if (msg == NULL) {
+               val = rb_eval_string_protect("$!.to_s", &status);
+               if (status == 0)
+                       msg = RSTRING_PTR(val);
+               else msg = "(message not available)";
+       }
+/*     asprintf(&amsg, "%s: %s\n", msg, (status == 0 ? RSTRING_PTR(val) : "(no message)")); */
+       
+       MyAppCallback_messageBox(msg, "Molby script error", 0, 3);
+
+       /*      MyAppCallback_setConsoleColor(1);
+       MyAppCallback_showScriptMessage(amsg); */
+       /*      if (!interrupted)
+        NSRunAlertPanel(@"MolRuby Error", [NSString stringWithUTF8String: amsg], @"OK", nil, nil); */
+/*     free(amsg); */
+
+/*     if (TYPE(backtrace) == T_ARRAY) {
+               int i;
+               int n = RARRAY_LEN(backtrace);
+               VALUE *ap = RARRAY_PTR(backtrace);
+               for (i = 0; i < n; i++) {
+                       val = rb_protect(rb_str_to_str, ap[i], &status);
+                       MyAppCallback_showScriptMessage("%s\n", RSTRING_PTR(val));
+               }
+               MyAppCallback_showRubyPrompt();
+       } */
+
+       gMolbyRunLevel--;
+}
+
+void
+Molby_startup(const char *script, const char *dir)
+{
+       VALUE val;
+       int status;
+       char *libpath;
+
+       /*  Initialize Ruby interpreter  */
+       ruby_init();
+       
+       /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
+/*     ruby_init_loadpath(); */
+       ruby_incpush(".");
+#if __WXMSW__
+       asprintf(&libpath, "%s\\lib", dir);
+#else
+       asprintf(&libpath, "%s/lib", dir);
+#endif
+       ruby_incpush(libpath);
+       free(libpath);
+       ruby_incpush(dir);
+
+       ruby_script("Molby");
+       
+       /*  Define Molby classes  */
+       Init_Molby();
+       RubyDialogInitClass();
+       
+       /*  Create a MessageOutput instance and assign it to $stdout and $stderr  */
+       val = rb_funcall(rb_cMessageOutput, rb_intern("new"), 0);
+       rb_gv_set("$stdout", val);
+       rb_gv_set("$stderr", val);
+       
+       /*  Global variable to hold backtrace  */
+       rb_define_variable("$backtrace", &gMolbyBacktrace);
+       
+       /*  Register interrupt check code  */
+       rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL);
+       
+       /*  Get version/copyright string from Ruby interpreter  */
+       {
+               gRubyVersion = strdup(ruby_version);
+               asprintf(&gRubyCopyright, "  Copyright (C) %d-%d %s",
+                                RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
+       }
+       
+       /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
+       s_SetIntervalTimer(0, 50);
+
+       /*  Read the startup script  */
+       if (script != NULL && script[0] != 0) {
+               MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
+               gMolbyRunLevel++;
+               rb_load_protect(rb_str_new2(script), 0, &status);
+               gMolbyRunLevel--;
+               if (status != 0)
+                       Molby_showError(status);
+               else
+                       MyAppCallback_showScriptMessage("Done.\n");
+       }
+}
diff --git a/MolLib/Ruby_bind/ruby_dialog.c b/MolLib/Ruby_bind/ruby_dialog.c
new file mode 100644 (file)
index 0000000..d653f61
--- /dev/null
@@ -0,0 +1,1144 @@
+/*
+ *  ruby_dialog.c
+ *
+ *  Created by Toshi Nagata on 08/04/13.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+*/
+
+#include "ruby_dialog.h"
+#include "Molby.h"
+
+static VALUE
+       sTextSymbol, sTextFieldSymbol, sRadioSymbol, sButtonSymbol,
+       sCheckBoxSymbol, sPopUpSymbol, sTextViewSymbol, sViewSymbol,
+       sLineSymbol, sTagSymbol, sTypeSymbol, sTitleSymbol, sRadioGroupSymbol, 
+       sXSymbol, sYSymbol, sWidthSymbol, sHeightSymbol, 
+       sOriginSymbol, sSizeSymbol, sFrameSymbol,
+       sEnabledSymbol, sEditableSymbol, sHiddenSymbol, sValueSymbol,
+       sBlockSymbol, sRangeSymbol, sActionSymbol,
+       sAlignSymbol, sRightSymbol, sCenterSymbol,
+       sVerticalAlignSymbol, sBottomSymbol, 
+       sMarginSymbol, sPaddingSymbol, sSubItemsSymbol,
+       sHFillSymbol, sVFillSymbol;
+
+VALUE cRubyDialog = Qfalse;
+
+const RDPoint gZeroPoint = {0, 0};
+const RDSize gZeroSize = {0, 0};
+const RDRect gZeroRect = {{0, 0}, {0, 0}};
+
+/*  True if y-coordinate grows from bottom to top (like Cocoa)  */
+int gRubyDialogIsFlipped = 0;
+
+#pragma mark ====== RubyDialog alloc/init/release ======
+
+typedef struct RubyDialogInfo {
+       RubyDialog *dref;  /*  Reference to a platform-dependent "controller" object  */
+} RubyDialogInfo;
+
+static RubyDialog *
+s_RubyDialog_GetController(VALUE self)
+{
+       RubyDialogInfo *di;
+       Data_Get_Struct(self, RubyDialogInfo, di);
+       if (di != NULL)
+               return di->dref;
+       else return NULL;
+}
+
+static void
+s_RubyDialog_Release(void *p)
+{
+       if (p != NULL) {
+               RubyDialog *dref = ((RubyDialogInfo *)p)->dref;
+               if (dref != NULL) {
+                       RubyDialogCallback_setRubyObject(dref, Qfalse); /* Stop access to the Ruby object (in case the RubyDialogController is not dealloc'ed in the following line) */
+                       RubyDialogCallback_release(dref);
+                       ((RubyDialogInfo *)p)->dref = NULL;
+               }
+               free(p);
+       }
+}
+
+static VALUE
+s_RubyDialog_Alloc(VALUE klass)
+{
+       VALUE val;
+       RubyDialogInfo *di;
+       RubyDialog *dref = RubyDialogCallback_new();
+       val = Data_Make_Struct(klass, RubyDialogInfo, 0, s_RubyDialog_Release, di);
+       di->dref = dref;
+       RubyDialogCallback_setRubyObject(dref, (RubyValue)val);
+       return val;
+}
+
+static VALUE
+s_RubyDialog_Initialize(int argc, VALUE *argv, VALUE self)
+{
+       int i, j;
+       VALUE val1, val2, val3;
+       VALUE items;
+       char *title1, *title2;
+       RubyDialog *dref = s_RubyDialog_GetController(self);
+       
+       rb_scan_args(argc, argv, "03", &val1, &val2, &val3);
+       if (!NIL_P(val1)) {
+               char *p = StringValuePtr(val1);
+               RubyDialogCallback_setWindowTitle(dref, p);
+       }
+       if (val2 != Qnil)
+               title1 = StringValuePtr(val2);
+       else title1 = NULL;
+       if (val3 != Qnil)
+               title2 = StringValuePtr(val3);
+       else title2 = NULL;
+
+       //  Array of item informations
+       items = rb_ary_new();
+       
+       if (val2 == Qnil && argc < 2) {
+               /*  The 2nd argument is omitted (nil is not explicitly given)  */
+               title1 = "OK";  /*  Default title  */
+       }
+       if (val3 == Qnil && argc < 3) {
+               /*  The 3rd argument is omitted (nil is not explicitly given)  */
+               title2 = "Cancel";  /*  Default title  */
+       }
+       
+       /*  Create standard buttons  */
+       /*  (When title{1,2} == NULL, the buttons are still created but set to hidden)  */
+       RubyDialogCallback_createStandardButtons(dref, title1, title2);
+       for (i = 0; i < 2; i++) {
+               VALUE item, vals[14];
+               RDItem *button = RubyDialogCallback_dialogItemAtIndex(dref, i);
+               char *title = RubyDialogCallback_titleOfItem(button);
+               RDRect frame = RubyDialogCallback_frameOfItem(button);
+               vals[0] = sTagSymbol;
+               vals[1] = rb_str_new2(i == 0 ? "ok" : "cancel");
+               vals[2] = sTypeSymbol;
+               vals[3] = sButtonSymbol;
+               vals[4] = sTitleSymbol;
+               vals[5] = rb_str_new2(title);
+               vals[6] = sXSymbol;
+               vals[7] = rb_float_new(frame.origin.x);
+               vals[8] = sYSymbol;
+               vals[9] = rb_float_new(frame.origin.y);
+               vals[10] = sWidthSymbol;
+               vals[11] = rb_float_new(frame.size.width);
+               vals[12] = sHeightSymbol;
+               vals[13] = rb_float_new(frame.size.height);
+               item = rb_hash_new();
+               for (j = 0; j < 7; j++) {
+                       rb_hash_aset(item, vals[j * 2], vals[j * 2 + 1]);
+               }
+               rb_ary_push(items, item);
+               free(title);
+       }
+       
+       rb_iv_set(self, "_items", items);
+       
+       return Qnil;
+}
+
+#pragma mark ====== Ruby methods ======
+
+static int
+s_RubyDialog_ItemIndexForTag(VALUE self, VALUE tag)
+{
+       VALUE items = rb_iv_get(self, "_items");
+       int len = RARRAY_LEN(items);
+       VALUE *ptr = RARRAY_PTR(items);
+       int i;
+       if (FIXNUM_P(tag) && (i = NUM2INT(tag)) >= 0 && i < len)
+               return i;
+       for (i = 0; i < len; i++) {
+               if (rb_equal(tag, rb_hash_aref(ptr[i], sTagSymbol)) == Qtrue)
+                       return i;
+       }
+       rb_raise(rb_eStandardError, "RubyDialog has no item with tag %s", StringValuePtr(tag));
+       return -1; /* Not reached */
+}
+
+/*
+ *  call-seq:
+ *     dialog.set_attr(tag, hash)
+ *
+ *  Set the attributes given in the hash.
+ */
+static VALUE
+s_RubyDialog_SetAttr(VALUE self, VALUE tag, VALUE hash)
+{
+       int i;
+       VALUE items = rb_iv_get(self, "_items");
+       VALUE *ptr = RARRAY_PTR(items);
+       int itag = s_RubyDialog_ItemIndexForTag(self, tag);
+       VALUE item = ptr[itag];
+       VALUE type = rb_hash_aref(item, sTypeSymbol);
+       RubyDialog *dref = s_RubyDialog_GetController(self);
+       RDItem *view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
+       VALUE keys = rb_funcall(hash, rb_intern("keys"), 0);
+       int klen = RARRAY_LEN(keys);
+       VALUE *kptr = RARRAY_PTR(keys);
+       char *p;
+
+       for (i = 0; i < klen; i++) {
+               VALUE key = kptr[i];
+               VALUE val = rb_hash_aref(hash, key);
+               int flag;
+               if (key == sRangeSymbol) {
+                       /*  Range of value (must be an array of two integers or two floats)  */
+                       VALUE val1, val2;
+                       double d1, d2;
+                       if (TYPE(val) != T_ARRAY || RARRAY_LEN(val) != 2)
+                               rb_raise(rb_eTypeError, "the attribute 'range' should specify an array of two numbers");
+                       val1 = (RARRAY_PTR(val))[0];
+                       val2 = (RARRAY_PTR(val))[1];
+                       d1 = NUM2DBL(rb_Float(val1));
+                       d2 = NUM2DBL(rb_Float(val2));
+                       if (!FIXNUM_P(val1) || !FIXNUM_P(val2)) {
+                               /*  Convert to a range of floats  */
+                               if (TYPE(val1) != T_FLOAT || TYPE(val2) != T_FLOAT) {
+                                       val1 = rb_float_new(NUM2DBL(val1));
+                                       val2 = rb_float_new(NUM2DBL(val2));
+                                       val = rb_ary_new3(2, val1, val2);
+                               }
+                       }
+                       if (d1 > d2)
+                               rb_raise(rb_eArgError, "invalid number range [%g,%g]", d1, d2);
+                       rb_hash_aset(item, key, val);
+               } else if (key == sValueSymbol) {
+                       /*  Value  */
+                       if (type == sTextFieldSymbol || type == sTextViewSymbol) {
+                               RubyDialogCallback_setStringToItem(view, StringValuePtr(val));
+                       } else if (type == sPopUpSymbol) {
+                               RubyDialogCallback_setSelectedSubItem(view, NUM2INT(rb_Integer(val)));
+                       } else if (type == sCheckBoxSymbol || type == sRadioSymbol) {
+                               RubyDialogCallback_setStateForItem(view, NUM2INT(rb_Integer(val)));
+                       }
+               } else if (key == sTitleSymbol) {
+                       /*  Title  */
+                       p = StringValuePtr(val);
+                       if (type == sTextSymbol)
+                               RubyDialogCallback_setStringToItem(view, p);
+                       else RubyDialogCallback_setTitleToItem(view, p);
+               } else if (key == sEnabledSymbol) {
+                       /*  Enabled  */
+                       flag = (val != Qnil && val != Qfalse);
+                       RubyDialogCallback_setEnabledForItem(view, flag);
+               } else if (key == sEditableSymbol) {
+                       /*  Editable  */
+                       flag = (val != Qnil && val != Qfalse);
+                       RubyDialogCallback_setEditableForItem(view, flag);
+               } else if (key == sHiddenSymbol) {
+                       /*  Hidden  */
+                       flag = (val != Qnil && val != Qfalse);
+                       RubyDialogCallback_setHiddenForItem(view, flag);
+               } else if (key == sSubItemsSymbol) {
+                       /*  SubItems  */
+                       if (type == sPopUpSymbol) {
+                               int j, len2;
+                               VALUE *ptr2;
+                               val = rb_ary_to_ary(val);
+                               len2 = RARRAY_LEN(val);
+                               ptr2 = RARRAY_PTR(val);
+                               while (RubyDialogCallback_deleteSubItem(view, 0) >= 0);
+                               for (j = 0; j < len2; j++) {
+                                       VALUE val2 = ptr2[j];
+                                       RubyDialogCallback_appendSubItem(view, StringValuePtr(val2));
+                               }
+                               RubyDialogCallback_resizeToBest(view);
+                               RubyDialogCallback_setSelectedSubItem(view, 0);
+                       }                       
+               } else if (key == sXSymbol || key == sYSymbol || key == sWidthSymbol || key == sHeightSymbol) {
+                       /*  Frame components  */
+                       RDRect frame;
+                       float f = NUM2DBL(rb_Float(val));
+                       frame = RubyDialogCallback_frameOfItem(view);
+                       if (key == sXSymbol)
+                               frame.origin.x = f;
+                       else if (key == sYSymbol)
+                               frame.origin.y = f;
+                       else if (key == sWidthSymbol)
+                               frame.size.width = f;
+                       else
+                               frame.size.height = f;
+                       RubyDialogCallback_setFrameOfItem(view, frame);
+               } else if (key == sOriginSymbol || key == sSizeSymbol) {
+                       /*  Frame components  */
+                       RDRect frame;
+                       float f0 = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 0)));
+                       float f1 = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 1)));
+                       frame = RubyDialogCallback_frameOfItem(view);
+                       if (key == sOriginSymbol) {
+                               frame.origin.x = f0;
+                               frame.origin.y = f1;
+                       } else {
+                               frame.size.width = f0;
+                               frame.size.height = f1;
+                       }
+                       RubyDialogCallback_setFrameOfItem(view, frame);
+               } else if (key == sFrameSymbol) {
+                       /*  Frame (x, y, width, height)  */
+                       RDRect frame;
+                       frame.origin.x = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 0)));
+                       frame.origin.y = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 1)));
+                       frame.size.width = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 2)));
+                       frame.size.height = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 3)));
+                       RubyDialogCallback_setFrameOfItem(view, frame);
+               } else {
+                       rb_hash_aset(item, key, val);
+               }
+       }
+       return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     dialog.attr(tag, key)
+ *
+ *  Get the attribute for the key.
+ */
+static VALUE
+s_RubyDialog_Attr(VALUE self, VALUE tag, VALUE key)
+{
+       int flag;
+       VALUE items = rb_iv_get(self, "_items");
+       VALUE *ptr = RARRAY_PTR(items);
+       int itag = s_RubyDialog_ItemIndexForTag(self, tag);
+       VALUE item = ptr[itag];
+       VALUE type = rb_hash_aref(item, sTypeSymbol);
+       RubyDialog *dref = s_RubyDialog_GetController(self);
+       RDItem *view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
+       
+       VALUE val = Qnil;
+       char *cp;
+
+       if (key == sValueSymbol) {
+               /*  Value  */
+               if (type == sTextFieldSymbol) {
+                       /*  Is range specified?  */
+                       VALUE range = rb_hash_aref(item, sRangeSymbol);
+                       cp = RubyDialogCallback_getStringPtrFromItem(view);
+                       if (cp != NULL) {
+                               if (TYPE(range) == T_ARRAY) {
+                                       if (FIXNUM_P((RARRAY_PTR(range))[0]))
+                                               val = INT2NUM(atoi(cp));
+                                       else
+                                               val = rb_float_new(atof(cp));
+                               } else val = rb_str_new2(cp);
+                               free(cp);
+                       }
+               } else if (type == sTextViewSymbol) {
+                       cp = RubyDialogCallback_getStringPtrFromItem(view);
+                       if (cp != NULL) {
+                               val = rb_str_new2(cp);
+                               free(cp);
+                       }
+               } else if (type == sPopUpSymbol) {
+                       int n = RubyDialogCallback_selectedSubItem(view);
+                       if (n >= 0)
+                               val = INT2NUM(n);
+               } else if (type == sCheckBoxSymbol || type == sRadioSymbol) {
+                       val = INT2NUM(RubyDialogCallback_getStateForItem(view));
+               }
+       } else if (key == sTitleSymbol) {
+               cp = RubyDialogCallback_titleOfItem(view);
+               if (cp != NULL) {
+                       val = rb_str_new2(cp);
+                       free(cp);
+               }
+       } else if (key == sEnabledSymbol) {
+               /*  Enabled  */
+               flag = RubyDialogCallback_isItemEnabled(view);
+               val = (flag ? Qtrue : Qfalse);
+       } else if (key == sEditableSymbol) {
+               /*  Editable  */
+               flag = RubyDialogCallback_isItemEditable(view);
+               val = (flag ? Qtrue : Qfalse);
+       } else if (key == sHiddenSymbol) {
+               /*  Hidden  */
+               flag = RubyDialogCallback_isItemHidden(view);
+               val = (flag ? Qtrue : Qfalse);
+       } else if (key == sSubItemsSymbol) {
+               int i;
+               val = rb_ary_new();
+               for (i = 0; (cp = RubyDialogCallback_titleOfSubItem(view, i)) != NULL; i++) {
+                       rb_ary_push(val, rb_str_new2(cp));
+                       free(cp);
+               }
+       } else if (key == sXSymbol || key == sYSymbol || key == sWidthSymbol || key == sHeightSymbol) {
+               /*  Frame components  */
+               RDRect frame;
+               float f;
+               frame = RubyDialogCallback_frameOfItem(view);
+               if (key == sXSymbol)
+                       f = frame.origin.x;
+               else if (key == sYSymbol)
+                       f = frame.origin.y;
+               else if (key == sWidthSymbol)
+                       f = frame.size.width;
+               else
+                       f = frame.size.height;
+               val = rb_float_new(f);
+       } else if (key == sOriginSymbol || key == sSizeSymbol) {
+               /*  Frame components  */
+               RDRect frame;
+               float f0, f1;
+               frame = RubyDialogCallback_frameOfItem(view);
+               if (key == sOriginSymbol) {
+                       f0 = frame.origin.x;
+                       f1 = frame.origin.y;
+               } else {
+                       f0 = frame.size.width;
+                       f1 = frame.size.height;
+               }
+               val = rb_ary_new3(2, rb_float_new(f0), rb_float_new(f1));
+               rb_obj_freeze(val);
+       } else if (key == sFrameSymbol) {
+               /*  Frame (x, y, width, height)  */
+               RDRect frame = RubyDialogCallback_frameOfItem(view);
+               val = rb_ary_new3(4, rb_float_new(frame.origin.x), rb_float_new(frame.origin.y), rb_float_new(frame.size.width), rb_float_new(frame.size.height));
+               rb_obj_freeze(val);
+       } else {
+               val = rb_hash_aref(item, key);
+       }
+
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     dialog.run
+ *
+ *  Run the modal session for this dialog.
+ */
+static VALUE
+s_RubyDialog_Run(VALUE self)
+{
+       int retval;
+       VALUE iflag;
+       RubyDialog *dref = s_RubyDialog_GetController(self);
+
+       iflag = Ruby_SetInterruptFlag(Qfalse);
+       retval = RubyDialogCallback_runModal(dref);
+       Ruby_SetInterruptFlag(iflag);
+       RubyDialogCallback_close(dref);
+       if (retval == 0) {
+               VALUE items = rb_iv_get(self, "_items");
+               int len = RARRAY_LEN(items);
+               VALUE *ptr = RARRAY_PTR(items);
+               VALUE hash = rb_hash_new();
+               int i;
+               /*  Get values for controls with defined tags  */
+               for (i = 2; i < len; i++) {
+                       /*  Items 0, 1 are OK/Cancel buttons  */
+               /*      VALUE type = rb_hash_aref(ptr[i], sTypeSymbol); */
+                       VALUE tag = rb_hash_aref(ptr[i], sTagSymbol);
+                       if (tag != Qnil) {
+                               VALUE val;
+                               val = s_RubyDialog_Attr(self, tag, sValueSymbol);
+                               rb_hash_aset(hash, tag, val);
+                       }
+               }
+               return hash;
+       } else
+               return Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     dialog.layout(columns, i11, ..., i1c, i21, ..., i2c, ..., ir1, ..., irc [, options]) => integer
+ *
+ *  Layout items in a table. The first argument is the number of columns, and must be a positive integer.
+ *  If the last argument is a hash, then it contains the layout options.
+ *  The ixy is the item identifier (a non-negative integer) or [identifier, hash], where the hash
+ *  contains the specific options for the item.
+ *  Returns an integer that represents the NSView that wraps the items.
+ */
+static VALUE
+s_RubyDialog_Layout(int argc, VALUE *argv, VALUE self)
+{
+       VALUE items, oval, *opts;
+       int row, col, i, j, n, itag, nitems, *itags;
+       RubyDialog *dref;
+       float *widths, *heights;
+       float f, fmin;
+       RDSize *sizes;
+       RDItem *layoutView, *ditem;
+       RDSize contentMinSize;
+       RDRect layoutFrame;
+       float col_padding = 8.0;  /*  Padding between columns  */
+       float row_padding = 8.0;  /*  Padding between rows  */
+       float margin = 10.0;
+
+       dref = s_RubyDialog_GetController(self);
+       contentMinSize = RubyDialogCallback_windowMinSize(dref);
+       items = rb_iv_get(self, "_items");
+       nitems = RARRAY_LEN(items);
+       
+       if (argc > 0 && rb_obj_is_kind_of(argv[argc - 1], rb_cHash)) {
+               VALUE oval1;
+               oval = argv[argc - 1];
+               argc--;
+               oval1 = rb_hash_aref(oval, sPaddingSymbol);
+               if (rb_obj_is_kind_of(oval1, rb_cNumeric))
+                       col_padding = row_padding = NUM2DBL(oval1);
+               oval1 = rb_hash_aref(oval, sMarginSymbol);
+               if (rb_obj_is_kind_of(oval1, rb_cNumeric))
+                       margin = NUM2DBL(oval1);
+       } else {
+               oval = Qnil;
+       }
+       
+       if (--argc < 0 || (col = NUM2INT(rb_Integer(argv[0]))) <= 0 || argc < col)
+               rb_raise(rb_eArgError, "wrong arguments; the first argument (col) must be a positive integer, and at least col arguments must follow");
+       row = (argc + col - 1) / col;  /*  It actually means (int)(ceil((argc - 1.0) / col))  */
+       argv++;
+
+       /*  Allocate temporary storage  */
+       itags = (int *)calloc(sizeof(int), row * col);
+       opts = (VALUE *)calloc(sizeof(VALUE), row * col);
+       sizes = (RDSize *)calloc(sizeof(RDSize), row * col);
+       widths = (float *)calloc(sizeof(float), col);
+       heights = (float *)calloc(sizeof(float), row);
+       if (itags == NULL || sizes == NULL || opts == NULL || widths == NULL || heights == NULL)
+               rb_raise(rb_eNoMemError, "out of memory during layout");
+       
+       /*  Get frame sizes  */
+       for (i = 0; i < row; i++) {
+               for (j = 0; j < col; j++) {
+                       n = i * col + j;
+                       if (n >= argc)
+                               break;
+                       if (TYPE(argv[n]) == T_ARRAY && RARRAY_LEN(argv[n]) == 2) {
+                               itag = FIX2INT(RARRAY_PTR(argv[n])[0]);
+                               opts[n] = RARRAY_PTR(argv[n])[1];
+                               if (TYPE(opts[n]) != T_HASH)
+                                       rb_raise(rb_eTypeError, "The layout options should be given as a hash");
+                       } else {
+                               itag = FIX2INT(argv[n]);
+                       }
+                       if (itag >= nitems)
+                               rb_raise(rb_eRangeError, "item tag (%d) is out of range (should be 0..%d)", itag, nitems - 1);
+                       if (itag >= 0 && (ditem = RubyDialogCallback_dialogItemAtIndex(dref, itag)) != NULL) {
+                               sizes[n] = RubyDialogCallback_frameOfItem(ditem).size;
+                       }
+                       itags[n] = itag;
+               /*      printf("sizes(%d,%d) = [%f,%f]\n", i, j, sizes[n-2].width, sizes[n-2].height); */
+               }
+       }
+       
+       /*  Calculate required widths  */
+       for (j = 0; j < col; j++) {
+               fmin = 0.0;
+               for (i = 0; i < row; i++) {
+                       for (n = j; n >= 0; n--) {
+                               f = sizes[i * col + n].width;
+                               if (f > 0.0) {
+                                       f += (n > 0 ? widths[n - 1] : 0.0);
+                                       break;
+                               }
+                       }
+                       if (j < col - 1 && sizes[i * col + j + 1].width == 0.0)
+                               continue;  /*  The next right item is empty  */
+                       if (fmin < f)
+                               fmin = f;
+               }
+               fmin += col_padding;
+               widths[j] = fmin;
+       /*      printf("widths[%d]=%f\n", j, fmin); */
+       }
+
+       /*  Calculate required heights  */
+       fmin = 0.0;
+       for (i = 0; i < row; i++) {
+               for (j = 0; j < col; j++) {
+                       for (n = i; n >= 0; n--) {
+                               f = sizes[n * col + j].height;
+                               if (f > 0.0) {
+                                       f += (n > 0 ? heights[n - 1] : 0.0);
+                                       break;
+                               }
+                       }
+                       if (fmin < f)
+                               fmin = f;
+               }
+               fmin += row_padding;
+               heights[i] = fmin;
+       /*      printf("heights[%d]=%f\n", i, fmin); */
+       }
+       
+       /*  Calculate layout view size  */
+       layoutFrame.size.width = widths[col - 1];
+       layoutFrame.size.height = heights[row - 1];
+       layoutFrame.origin.x = margin;
+       layoutFrame.origin.y = margin;
+/*     printf("layoutFrame = [%f,%f,%f,%f]\n", layoutFrame.origin.x, layoutFrame.origin.y, layoutFrame.size.width, layoutFrame.size.height); */
+
+#if 0
+       /*  Resize the window  */
+       /*  Not necessary for wxWidgets, because contentSizer and buttonSizer will take care of the window resize
+           automatically  */
+       {
+               RDSize winSize;
+               RDRect bframe[2];
+               winSize.width = layoutFrame.size.width + margin * 2;
+               if (winSize.width < contentMinSize.width)
+                       winSize.width = contentMinSize.width;
+               winSize.height = layoutFrame.size.height + margin * 2;
+               for (i = 0; i < 2; i++) {
+                       /*  OK(0), Cancel(1) buttons  */
+                       RDItem *button = RubyDialogCallback_dialogItemAtIndex(dref, i);
+                       if (RubyDialogCallback_indexOfItem(dref, RubyDialogCallback_superview(button)) < 0 && !RubyDialogCallback_isItemHidden(button)) {
+                               bframe[i] = RubyDialogCallback_frameOfItem(button);
+                               if (layoutFrame.origin.y == margin) {
+                                       //  Move the layoutView up
+                                       layoutFrame.origin.y += margin + bframe[i].size.height;
+                               //      RubyDialogCallback_setFrameOfItem(layoutView, layoutFrame);
+                                       winSize.height += margin + bframe[i].size.height;
+                               }
+                               bframe[i].origin.y = winSize.height - margin - bframe[i].size.height;
+                               bframe[i].origin.x = (i == 0 ? winSize.width - bframe[i].size.width - margin : margin);
+                       } else {
+                               static RDRect zeroRect = {0, 0, 0, 0};
+                               bframe[i] = zeroRect;
+                       }
+               }
+               RubyDialogCallback_setWindowSize(dref, winSize);
+               for (i = 0; i < 2; i++) {
+                       if (bframe[i].size.width > 0) {
+                               RDItem *button = RubyDialogCallback_dialogItemAtIndex(dref, i);
+                               RubyDialogCallback_setFrameOfItem(button, bframe[i]);
+                       }
+               }
+       }
+#endif
+       
+       /*  Create a layout view  */
+       layoutView = RubyDialogCallback_createItem(dref, "view", "", layoutFrame);
+
+       /*  Move the subviews into the layout view  */
+       for (i = 0; i < row; i++) {
+               for (j = 0; j < col; j++) {
+                       n = i * col + j;
+                       if (n < argc && (itag = itags[n]) > 0 && itag < nitems) {
+                               RDPoint pt;
+                               float offset;
+                               RDRect cell;
+                               VALUE type, item;
+                               int k;
+                               ditem = RubyDialogCallback_dialogItemAtIndex(dref, itag);
+                               item = (RARRAY_PTR(items))[itag];
+                               type = rb_hash_aref(item, sTypeSymbol);
+                               if (type == sTextSymbol)
+                                       offset = 3.0;
+                               else offset = 0.0;
+                               cell.origin.x = (j > 0 ? widths[j - 1] : 0.0);
+                               cell.origin.y = (i > 0 ? heights[i - 1] : 0.0);
+                               for (k = j + 1; k < col; k++) {
+                                       if (itags[i * col + k] >= 0)
+                                               break;
+                               }
+                               cell.size.width = widths[k - 1] - cell.origin.x;
+                               for (k = i + 1; k < row; k++) {
+                                       if (itags[k * col + j] >= 0)
+                                               break;
+                               }
+                               cell.size.height = heights[k - 1] - cell.origin.y;
+                               pt.x = cell.origin.x + col_padding * 0.5;
+                               pt.y = cell.origin.y + row_padding * 0.5 + offset;
+                               {
+                                       /*  Handle item-specific options  */
+                                       VALUE oval1;
+                                       int resize = 0;
+                                       /*  HFill/VFill options can be specified as an item attribute or a layout option  */
+                                       if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sHFillSymbol)) == Qnil)
+                                               oval1 = rb_hash_aref(item, sHFillSymbol);
+                                       if (RTEST(oval1)) {
+                                               sizes[n].width = cell.size.width - col_padding;
+                                               resize = 1;
+                                       }
+                                       if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sVFillSymbol)) == Qnil)
+                                               oval1 = rb_hash_aref(item, sVFillSymbol);
+                                       if (RTEST(oval1)) {
+                                               sizes[n].height = cell.size.height - row_padding;
+                                               resize = 1;
+                                       }
+                                       if (resize) {
+                                               RDRect newFrameRect = RubyDialogCallback_frameOfItem(ditem);
+                                               newFrameRect.size.width = sizes[n].width;
+                                               newFrameRect.size.height = sizes[n].height;
+                                               RubyDialogCallback_setFrameOfItem(ditem, newFrameRect);
+                                       }
+                                       /*  align/vertical_align can only be specified as a layout option  */
+                                       if (RTEST(opts[n]) && (oval1 = rb_hash_aref(opts[n], sAlignSymbol)) != Qnil) {
+                                               if (oval1 == sCenterSymbol)
+                                                       pt.x += (cell.size.width - sizes[n].width - col_padding) * 0.5;
+                                               else if (oval1 == sRightSymbol)
+                                                       pt.x += (cell.size.width - sizes[n].width) - col_padding;
+                                       }
+                                       if (RTEST(opts[n]) && (oval1 = rb_hash_aref(opts[n], sVerticalAlignSymbol)) != Qnil) {
+                                               if (oval1 == sCenterSymbol)
+                                                       pt.y += (cell.size.height - sizes[n].height - row_padding) * 0.5;
+                                               else if (oval1 == sBottomSymbol)
+                                                       pt.y += (cell.size.height - sizes[n].height) - row_padding;
+                                       }
+                               }
+                               RubyDialogCallback_moveItemUnderView(ditem, layoutView, pt);
+                       }
+               }
+       }
+       
+       free(sizes);
+       free(widths);
+       free(heights);
+       free(opts);
+       free(itags);
+       
+       /*  Create a new hash for the layout view and push to _items */
+       {
+               VALUE nhash = rb_hash_new();
+               rb_hash_aset(nhash, sTypeSymbol, sViewSymbol);
+               rb_ary_push(items, nhash);
+       }
+       
+       /*  Tag for the layout view  */
+       itag = RARRAY_LEN(items) - 1;
+
+       /*  Returns the integer tag  */
+       return INT2NUM(itag);
+}
+
+/*
+ *  call-seq:
+ *     dialog.item(type, hash) => integer
+ *
+ *  Create a dialog item.
+ *    type: one of the following symbols; :text, :textfield, :radio, :checkbox, :popup
+ *    hash: attributes that can be set by set_attr
+ *  Returns an integer that represents the item. (0 and 1 are reserved for "OK" and "Cancel")
+ */
+static VALUE
+s_RubyDialog_Item(int argc, VALUE *argv, VALUE self)
+{
+       int itag;  /*  Integer tag for NSControl  */
+       RDRect rect;
+       const char *title;
+       double dval;
+//     NSDictionary *attr;
+//     NSFont *font;
+       VALUE type, hash, val, nhash, items;
+       RubyDialog *dref;
+
+       dref = s_RubyDialog_GetController(self);
+       rb_scan_args(argc, argv, "11", &type, &hash);
+       if (NIL_P(hash))
+               hash = rb_hash_new();
+       rect.size.width = rect.size.height = 1.0;
+       rect.origin.x = rect.origin.y = 0.0;
+
+       val = rb_hash_aref(hash, sTitleSymbol);
+       if (!NIL_P(val)) {
+               title = StringValuePtr(val);
+       } else {
+               title = "";
+       }
+
+       Check_Type(type, T_SYMBOL);
+       
+/*     if (type == sTextViewSymbol)
+               font = [NSFont userFixedPitchFontOfSize: 0];
+       else
+               font = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
+       attr = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, nil];
+       brect.origin.x = brect.origin.y = 0.0;
+       brect.size = [title sizeWithAttributes: attr];
+       brect.size.width += 8;
+*/
+       /*  Set rect if specified  */
+       rect.origin.x = rect.origin.y = 0.0;
+       rect.size.width = rect.size.height = 0.0;
+       val = rb_hash_aref(hash, sXSymbol);
+       if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
+               rect.origin.x = dval;
+       val = rb_hash_aref(hash, sYSymbol);
+       if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
+               rect.origin.y = dval;
+       val = rb_hash_aref(hash, sWidthSymbol);
+       if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
+               rect.size.width = dval;
+       val = rb_hash_aref(hash, sHeightSymbol);
+       if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
+               rect.size.height = dval;
+
+       /*  Create a new hash for this item  */
+       nhash = rb_hash_new();
+       rb_hash_aset(nhash, sTypeSymbol, type);
+
+       /*  Direction for the separator line  */
+       /*  The direction can be specified either by specifying non-square frame size or "vertical" flag  */
+       if (type == sLineSymbol) {
+               VALUE val1 = rb_hash_aref(hash, ID2SYM(rb_intern("vertical")));
+               if (rect.size.width == 0)
+                       rect.size.width = 1;
+               if (rect.size.height == 0)
+                       rect.size.height = 1;
+               if (rect.size.width == rect.size.height) {
+                       if (RTEST(val1))
+                               rect.size.height++;  /*  vertical  */
+                       else rect.size.width++;  /*  horizontal  */
+               }
+       }
+       
+       if (RubyDialogCallback_createItem(dref, rb_id2name(SYM2ID(type)), title, rect) == NULL)
+               rb_raise(rb_eStandardError, "item type :%s is not implemented", rb_id2name(SYM2ID(type)));
+
+       /*  Push to _items  */
+       items = rb_iv_get(self, "_items");
+       rb_ary_push(items, nhash);
+       itag = RARRAY_LEN(items) - 1;
+
+       /*  Tag as a Ruby integer  */
+       val = INT2NUM(itag);
+
+       /*  Set attributes  */
+       s_RubyDialog_SetAttr(self, val, hash);
+
+       /*  Type-specific attributes  */
+       if (type == sLineSymbol) {
+               if (rect.size.width > rect.size.height && rect.size.width == 2)
+                       rb_hash_aset(nhash, sHFillSymbol, Qtrue);
+               else if (rect.size.width < rect.size.height && rect.size.height == 2)
+                       rb_hash_aset(nhash, sVFillSymbol, Qtrue);
+       }
+
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     dialog._items => an array of hash
+ *
+ *  Returns an internal array of items. For debugging use only.
+ */
+static VALUE
+s_RubyDialog_Items(VALUE self)
+{
+       return rb_iv_get(self, "_items");
+}
+
+/*
+ *  call-seq:
+ *     dialog.nitems => integer
+ *
+ *  Returns the number of items.
+ */
+static VALUE
+s_RubyDialog_Nitems(VALUE self)
+{
+       VALUE items = rb_iv_get(self, "_items");
+       int nitems = RARRAY_LEN(items);
+       return INT2NUM(nitems);
+}
+
+/*
+ *  call-seq:
+ *     dialog.each_item block
+ *
+ *  Iterate the given block with the item number as the argument.
+ */
+static VALUE
+s_RubyDialog_EachItem(VALUE self)
+{
+       VALUE items = rb_iv_get(self, "_items");
+       int nitems = RARRAY_LEN(items);
+       int i;
+       for (i = 0; i < nitems; i++) {
+               rb_yield(INT2NUM(i));
+       }
+    return self;
+}
+
+/*
+ *  call-seq:
+ *     dialog.radio_group(ary)
+ *
+ *  Group radio buttons as a mutually exclusive group.
+ */
+static VALUE
+s_RubyDialog_RadioGroup(VALUE self, VALUE aval)
+{
+       int i, j, n;
+       VALUE gval;
+       VALUE items = rb_iv_get(self, "_items");
+       int nitems = RARRAY_LEN(items);
+       aval = rb_ary_to_ary(aval);
+       n = RARRAY_LEN(aval);
+
+       /*  Build a new array with checked arguments  */
+       gval = rb_ary_new2(n);
+       for (i = 0; i < n; i++) {
+               j = NUM2INT(RARRAY_PTR(aval)[i]);
+               if (j < 0 || j >= nitems)
+                       break;
+               if (rb_hash_aref(RARRAY_PTR(items)[j], sTypeSymbol) != sRadioSymbol)
+                       break;
+               rb_ary_push(gval, INT2NUM(j));
+       }
+       if (i < n)
+               rb_raise(rb_eStandardError, "the item %d (at index %d) does not represent a radio button", j, i);
+       
+       /*  Set the radio group array to the specified items. If the item already belongs to a radio group,
+           then it is removed from that group. */
+       /*  All items share the common array (gval in the above). This allows removing easy.  */
+       for (i = 0; i < n; i++) {
+               VALUE gval2;
+               j = NUM2INT(RARRAY_PTR(gval)[i]);
+               gval2 = rb_hash_aref(RARRAY_PTR(items)[j], sRadioGroupSymbol);
+               if (gval2 != Qnil)
+                       rb_ary_delete(gval2, INT2NUM(j));  /*  Remove j from gval2  */
+               rb_hash_aset(RARRAY_PTR(items)[j], sRadioGroupSymbol, gval);
+       }
+       return gval;
+}
+
+/*
+ *  call-seq:
+ *     dialog.action(index)
+ *
+ *  Do the default action for the dialog item with the given index.
+ *  If index == 0 (OK) or 1 (Cancel), RubyDialogCallback_endModal() is called.
+ *  Otherwise, the "action" attribute is looked for the item, and if found
+ *  it is called with the given index as the argument (the attribute must be
+ *  either a symbol (method name) or a Proc object).
+ *  If the "action" attribute is not found, do nothing.
+ */
+static VALUE
+s_RubyDialog_Action(VALUE self, VALUE idx)
+{
+       VALUE aval;
+       int ival = NUM2INT(idx);
+       if (ival == 0 || ival == 1) {
+               RubyDialogCallback_endModal(s_RubyDialog_GetController(self), ival);
+               return Qnil;
+       }
+       aval = s_RubyDialog_Attr(self, idx, sActionSymbol);
+       if (aval == Qnil)
+               return Qnil;
+       if (TYPE(aval) == T_SYMBOL) {
+               return rb_funcall(self, SYM2ID(aval), 1, idx);
+       } else if (rb_obj_is_kind_of(aval, rb_cProc)) {
+               return rb_funcall(aval, rb_intern("call"), 1, idx);
+       } else {
+               VALUE insval = rb_inspect(aval);
+               rb_raise(rb_eTypeError, "Cannot call action method '%s'", StringValuePtr(insval));
+       }
+       return Qnil;  /*  Not reached  */
+}
+
+/*
+ *  call-seq:
+ *     RubyDialog.save_panel(message = nil, directory = nil, default_filename = nil, wildcard = nil)
+ *
+ *  Display the "save as" dialog and returns the fullpath filename.
+ */
+static VALUE
+s_RubyDialog_SavePanel(int argc, VALUE *argv, VALUE klass)
+{
+       VALUE mval, dval, fval, wval, iflag;
+       const char *mp, *dp, *wp;
+       int n;
+       char buf[1024];
+       rb_scan_args(argc, argv, "04", &mval, &dval, &fval, &wval);
+       if (mval == Qnil)
+               mp = NULL;
+       else mp = StringValuePtr(mval);
+       if (dval == Qnil)
+               dp = NULL;
+       else dp = FileStringValuePtr(dval);
+       if (fval == Qnil)
+               buf[0] = 0;
+       else {
+               strncpy(buf, FileStringValuePtr(fval), 1023);
+               buf[1023] = 0;
+       }
+       if (wval == Qnil)
+               wp = NULL;
+       else wp = FileStringValuePtr(wval);
+       iflag = Ruby_SetInterruptFlag(Qfalse);
+       n = RubyDialogCallback_savePanel(mp, dp, wp, buf, sizeof buf);
+       Ruby_SetInterruptFlag(iflag);
+       if (n > 0)
+               return Ruby_NewFileStringValue(buf);
+       else return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     RubyDialog.open_panel(message = nil, directory = nil, wildcard = nil, for_directories = false, multiple_selection = false)
+ *
+ *  Display the "open" dialog and returns the fullpath filename.
+ */
+static VALUE
+s_RubyDialog_OpenPanel(int argc, VALUE *argv, VALUE klass)
+{
+       VALUE mval, dval, fval, mulval, wval, iflag;
+       const char *mp, *dp, *wp;
+       char **ary;
+       int for_directories = 0, multiple_selection = 0;
+       int n;
+       rb_scan_args(argc, argv, "05", &mval, &dval, &wval, &fval, &mulval);
+       if (mval == Qnil)
+               mp = NULL;
+       else mp = StringValuePtr(mval);
+       if (dval == Qnil)
+               dp = NULL;
+       else dp = FileStringValuePtr(dval);
+       if (wval == Qnil)
+               wp = NULL;
+       else wp = FileStringValuePtr(wval);
+       if (fval != Qnil && fval != Qfalse)
+               for_directories = 1;
+       if (mulval != Qnil && mulval != Qfalse) {
+               multiple_selection = 1;
+               if (for_directories && multiple_selection)
+                       rb_raise(rb_eStandardError, "open_panel for directories allows only single selection");
+       }
+       iflag = Ruby_SetInterruptFlag(Qfalse);
+       n = RubyDialogCallback_openPanel(mp, dp, wp, &ary, for_directories, multiple_selection);
+       Ruby_SetInterruptFlag(iflag);
+       if (n > 0) {
+               if (multiple_selection) {
+                       int i;
+                       VALUE retval = rb_ary_new();
+                       for (i = 0; i < n; i++) {
+                               rb_ary_push(retval, Ruby_NewFileStringValue(ary[i]));
+                               free(ary[i]);
+                       }
+                       free(ary);
+                       return retval;
+               } else return Ruby_NewFileStringValue(ary[0]);
+       } else return Qnil;
+}
+
+#pragma mark ====== Utility function ======
+
+int
+RubyDialog_validateItemContent(RubyValue self, RDItem *ip, const char *s)
+{
+       VALUE items, item, val, val_min, val_max;
+       int nitems, itag;
+       RubyDialog *dref = s_RubyDialog_GetController((VALUE)self);
+       char buf[80];
+       
+       items = rb_iv_get(((VALUE)self), "_items");
+       nitems = RARRAY_LEN(items);
+       itag = RubyDialogCallback_indexOfItem(dref, ip);
+       if (itag < 0 || itag >= nitems)
+               return 1;  /*  Accept anything  */
+       
+       item = (RARRAY_PTR(items))[itag];
+       val = rb_hash_aref(item, sRangeSymbol);
+       if (NIL_P(val))
+               return 1;  /*  Accept anything  */
+       
+       val_min = Ruby_ObjectAtIndex(val, 0);
+       val_max = Ruby_ObjectAtIndex(val, 1);
+       if (FIXNUM_P(val_min) && FIXNUM_P(val_max)) {
+               int ival = atoi(s);
+               int imin = NUM2INT(val_min);
+               int imax = NUM2INT(val_max);
+               if (ival < imin || ival > imax)
+                       return 0;
+               snprintf(buf, sizeof buf, "%d", ival);
+               RubyDialogCallback_setStringToItem(ip, buf);
+       } else {
+               double d = atof(s);
+               double dmin = NUM2DBL(rb_Float(val_min));
+               double dmax = NUM2DBL(rb_Float(val_max));
+               if (d < dmin || d > dmax)
+                       return 0;
+       }
+       return 1;
+}
+
+/*  Action for dialog items.
+ Get the item number, and call "action" method of the RubyDialog object with
+ the item number (integer) as the argument. The default "action" method is
+ defined as s_RubyDialog_action.  */
+void
+RubyDialog_doItemAction(RubyValue self, RDItem *ip)
+{
+       int status;
+       VALUE ival;
+       RubyDialog *dref = s_RubyDialog_GetController((VALUE)self);
+       VALUE items = rb_iv_get(((VALUE)self), "_items");
+       int nitems = RARRAY_LEN(items);
+       int idx = RubyDialogCallback_indexOfItem(dref, ip);
+       if (idx < 0)
+               return;
+       ival = INT2NUM(idx);
+       
+       /*  Handle radio group  */
+       {
+               VALUE gval = s_RubyDialog_Attr((VALUE)self, ival, sRadioGroupSymbol);
+               if (gval != Qnil && TYPE(gval) == T_ARRAY) {
+                       int i, j, n;
+                       n = RARRAY_LEN(gval);
+                       for (i = 0; i < n; i++) {
+                               j = NUM2INT(RARRAY_PTR(gval)[i]);
+                               if (j >= 0 && j < nitems && j != idx) {
+                                       RDItem *iptr = RubyDialogCallback_dialogItemAtIndex(dref, j);
+                                       RubyDialogCallback_setStateForItem(iptr, 0);  /*  Deselect  */
+                               }
+                       }
+               }
+       }
+       
+       Ruby_funcall2_protect((VALUE)self, rb_intern("action"), 1, &ival, &status);
+       if (status != 0)
+               Molby_showError(status);
+}
+
+#pragma mark ====== Initialize class ======
+
+void
+RubyDialogInitClass(void)
+{
+       if (cRubyDialog != Qfalse)
+               return;
+
+       cRubyDialog = rb_define_class("RubyDialog", rb_cObject);
+       rb_define_alloc_func(cRubyDialog, s_RubyDialog_Alloc);
+       rb_define_private_method(cRubyDialog, "initialize", s_RubyDialog_Initialize, -1);
+       rb_define_method(cRubyDialog, "run", s_RubyDialog_Run, 0);
+       rb_define_method(cRubyDialog, "item", s_RubyDialog_Item, -1);
+       rb_define_method(cRubyDialog, "layout", s_RubyDialog_Layout, -1);
+       rb_define_method(cRubyDialog, "_items", s_RubyDialog_Items, 0);
+       rb_define_method(cRubyDialog, "nitems", s_RubyDialog_Nitems, 0);
+       rb_define_method(cRubyDialog, "each_item", s_RubyDialog_EachItem, 0);
+       rb_define_method(cRubyDialog, "set_attr", s_RubyDialog_SetAttr, 2);
+       rb_define_method(cRubyDialog, "attr", s_RubyDialog_Attr, 2);
+       rb_define_method(cRubyDialog, "radio_group", s_RubyDialog_RadioGroup, 1);
+       rb_define_method(cRubyDialog, "action", s_RubyDialog_Action, 1);
+       rb_define_singleton_method(cRubyDialog, "save_panel", s_RubyDialog_SavePanel, -1);
+       rb_define_singleton_method(cRubyDialog, "open_panel", s_RubyDialog_OpenPanel, -1);
+
+       {
+               static VALUE *sTable1[] = { &sTextSymbol, &sTextFieldSymbol, &sRadioSymbol, &sButtonSymbol, &sCheckBoxSymbol, &sPopUpSymbol, &sTextViewSymbol, &sViewSymbol, &sLineSymbol, &sTagSymbol, &sTypeSymbol, &sTitleSymbol, &sXSymbol, &sYSymbol, &sWidthSymbol, &sHeightSymbol, &sOriginSymbol, &sSizeSymbol, &sFrameSymbol, &sEnabledSymbol, &sEditableSymbol, &sHiddenSymbol, &sValueSymbol, &sRadioGroupSymbol, &sBlockSymbol, &sRangeSymbol, &sActionSymbol, &sAlignSymbol, &sRightSymbol, &sCenterSymbol, &sVerticalAlignSymbol, &sBottomSymbol, &sMarginSymbol, &sPaddingSymbol, &sSubItemsSymbol, &sHFillSymbol, &sVFillSymbol };
+               static const char *sTable2[] = { "text", "textfield", "radio", "button", "checkbox", "popup", "textview", "view", "line", "tag", "type", "title", "x", "y", "width", "height", "origin", "size", "frame", "enabled", "editable", "hidden", "value", "radio_group", "block", "range", "action", "align", "right", "center", "vertical_align", "bottom", "margin", "padding", "subitems", "hfill", "vfill" };
+               int i;
+               for (i = 0; i < sizeof(sTable1) / sizeof(sTable1[0]); i++)
+                       *(sTable1[i]) = ID2SYM(rb_intern(sTable2[i]));
+       }
+}
diff --git a/MolLib/Ruby_bind/ruby_dialog.h b/MolLib/Ruby_bind/ruby_dialog.h
new file mode 100644 (file)
index 0000000..591428e
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  RubyDialog.h
+ *
+ *  Created by Toshi Nagata on 08/04/13.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __ruby_dialog_h__
+#define __ruby_dialog_h__
+
+#include "Molby_extern.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+/*  RubyDialog class  */
+/* extern VALUE cRubyDialog; */
+
+/*  True if y-coordinate grows from bottom to top (like Cocoa)  */
+extern int gRubyDialogIsFlipped;
+       
+/*  Opaque structures (actually they are used only as pointers)  */
+typedef struct RubyDialog RubyDialog;
+typedef struct RDItem RDItem;
+
+/*  Non-opaque structures  */
+typedef struct RDPoint { float x, y; } RDPoint;
+typedef struct RDSize  { float width, height; } RDSize;
+typedef struct RDRect  { RDPoint origin; RDSize size; } RDRect;
+
+extern const RDPoint gZeroPoint;
+extern const RDSize gZeroSize;
+extern const RDRect gZeroRect;
+       
+/*  Utility function  */
+extern int RubyDialog_validateItemContent(RubyValue self, RDItem *ip, const char *s);
+extern void RubyDialog_doItemAction(RubyValue self, RDItem *ip);
+extern void RubyDialogInitClass(void);
+
+/*  Stub routines  */
+STUB RubyDialog *RubyDialogCallback_new(void);
+STUB void RubyDialogCallback_release(RubyDialog *dref);
+STUB void RubyDialogCallback_setRubyObject(RubyDialog *dref, RubyValue val);
+STUB void RubyDialogCallback_setWindowTitle(RubyDialog *dref, const char *title);
+STUB int RubyDialogCallback_runModal(RubyDialog *dref);
+STUB void RubyDialogCallback_endModal(RubyDialog *dref, int status);
+STUB void RubyDialogCallback_close(RubyDialog *dref);
+STUB RDSize RubyDialogCallback_windowMinSize(RubyDialog *dref);
+STUB void RubyDialogCallback_setWindowSize(RubyDialog *dref, RDSize size);
+STUB void RubyDialogCallback_createStandardButtons(RubyDialog *dref, const char *oktitle, const char *canceltitle);
+STUB RDItem *RubyDialogCallback_createItem(RubyDialog *dref, const char *type, const char *title, RDRect frame);
+STUB RDItem *RubyDialogCallback_dialogItemAtIndex(RubyDialog *dref, int idx);
+//STUB RDItem *RubyDialogCallback_itemWithTag(RubyDialog *dref, int tag);
+STUB int RubyDialogCallback_indexOfItem(RubyDialog *dref, RDItem *item);
+//STUB int RubyDialogCallback_tagOfItem(RDItem *item);
+STUB void RubyDialogCallback_moveItemUnderView(RDItem *item, RDItem *superView, RDPoint origin);
+STUB RDItem *RubyDialogCallback_superview(RDItem *item);
+STUB char *RubyDialogCallback_titleOfItem(RDItem *item);
+STUB RDRect RubyDialogCallback_frameOfItem(RDItem *item);
+STUB void RubyDialogCallback_setFrameOfItem(RDItem *item, RDRect rect);
+STUB void RubyDialogCallback_setStringToItem(RDItem *item, const char *s);
+STUB void RubyDialogCallback_getStringFromItem(RDItem *item, char *buf, int bufsize);
+STUB char *RubyDialogCallback_getStringPtrFromItem(RDItem *item);
+STUB void RubyDialogCallback_setTitleToItem(RDItem *item, const char *s);
+STUB void RubyDialogCallback_setEnabledForItem(RDItem *item, int flag);
+STUB int RubyDialogCallback_isItemEnabled(RDItem *item);
+STUB void RubyDialogCallback_setEditableForItem(RDItem *item, int flag);
+STUB int RubyDialogCallback_isItemEditable(RDItem *item);
+STUB void RubyDialogCallback_setStateForItem(RDItem *item, int state);
+STUB int RubyDialogCallback_getStateForItem(RDItem *item);
+STUB void RubyDialogCallback_setHiddenForItem(RDItem *item, int flag);
+STUB int RubyDialogCallback_isItemHidden(RDItem *item);
+       
+STUB int RubyDialogCallback_countSubItems(RDItem *item);
+STUB int RubyDialogCallback_appendSubItem(RDItem *item, const char *s);
+STUB int RubyDialogCallback_insertSubItem(RDItem *item, const char *s, int pos);
+STUB int RubyDialogCallback_deleteSubItem(RDItem *item, int pos);
+STUB char *RubyDialogCallback_titleOfSubItem(RDItem *item, int pos);
+STUB void RubyDialogCallback_setSelectedSubItem(RDItem *item, int pos);        
+STUB int RubyDialogCallback_selectedSubItem(RDItem *item);
+       
+STUB RDSize RubyDialogCallback_sizeOfString(RDItem *item, const char *s);
+STUB RDSize RubyDialogCallback_resizeToBest(RDItem *item);
+
+STUB int RubyDialogCallback_savePanel(const char *title, const char *dirname, const char *wildcard, char *buf, int bufsize);
+STUB int RubyDialogCallback_openPanel(const char *title, const char *dirname, const char *wildcard, char ***array, int for_directories, int multiple_selection);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __ruby_dialog_h__ */
diff --git a/MolLib/Ruby_bind/ruby_md.c b/MolLib/Ruby_bind/ruby_md.c
new file mode 100644 (file)
index 0000000..5008de9
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ *  ruby_md.c
+ *  Ruby binding
+ *
+ *  Created by Toshi Nagata on 09/08/11.
+ *  Copyright 2007-2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+*/
+
+#include "Molby.h"
+#include "../MD/MDCore.h"
+#include "../MD/MDSurface.h"
+#include "../MD/MDPressure.h"
+
+#include "env.h"  /*  For ruby_frame  */
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#pragma mark ====== Global Values ======
+
+VALUE rb_cMDArena;
+
+#pragma mark ====== MDArena class ======
+
+struct MDArena *
+MDArenaFromValue(VALUE val)
+{
+       if (rb_obj_is_kind_of(val, rb_cMDArena)) {
+               MDArena *arena;
+               Data_Get_Struct(val, MDArena, arena);
+               return arena;
+       } else return NULL;
+}
+
+VALUE
+ValueFromMDArena(struct MDArena *arena)
+{
+       if (arena != NULL)
+               md_arena_retain(arena);
+       return Data_Wrap_Struct(rb_cMDArena, 0, (void (*)(void *))md_arena_release, arena);
+}
+
+static void
+s_MDArena_panic_func(MDArena *arena, const char *msg)
+{
+       rb_raise(rb_eMolbyError, "MD Error: %s", msg);
+}
+
+static VALUE
+s_MDArena_Run_or_minimize(VALUE self, VALUE arg, int minimize)
+{
+       MDArena *arena;
+       int nsteps, retval, start_step;
+       IntGroup *ig;
+       Data_Get_Struct(self, MDArena, arena);
+       nsteps = NUM2INT(rb_Integer(arg));
+       if (arena->is_running)
+               rb_raise(rb_eMolbyError, "the simulation is already running. You cannot do simulation recursively.");
+       if (nsteps < 0)
+               rb_raise(rb_eMolbyError, "the number of steps should be non-negative integer");
+       if (!arena->is_initialized || arena->xmol->needsMDRebuild) {
+               const char *msg = md_prepare(arena, 0);
+               if (msg != NULL)
+                       rb_raise(rb_eMolbyError, "cannot initialize for MD: %s", msg);
+       }
+       arena->end_step = arena->start_step + nsteps;
+       md_log(arena, "%s for %d steps\n", (minimize ? "Minimizing" : "Running"), nsteps);
+       start_step = arena->start_step;
+
+       /*  Create a new frame with current coordinates  */
+/*     if (nsteps > 0 && MoleculeGetNumberOfFrames(arena->xmol) == 0) {
+               ig = IntGroupNewWithPoints(0, 1, -1);
+               MolActionCreateAndPerform(arena->xmol, gMolActionInsertFrames, ig, 0, NULL);
+               IntGroupRelease(ig);
+       } */
+
+       /*  Run simulation  */
+/*     arena->md_panic_func = s_MDArena_panic_func; */
+       retval = md_main(arena, minimize);
+       if (retval != 0)
+               rb_raise(rb_eMolbyError, "MD Error: %s", arena->errmsg);
+
+/*     arena->md_panic_func = NULL; */
+       
+       if (arena->step > start_step) {
+               /*  Create a new frame and copy new coordinates  */
+               ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(arena->xmol), 1, -1);
+               MolActionCreateAndPerform(arena->xmol, gMolActionInsertFrames, ig, 0, NULL);
+               IntGroupRelease(ig);
+               md_copy_coordinates_from_internal(arena);
+       }
+       
+/*     if (retval != 0)
+               rb_raise(rb_eMolbyError, "Calculation aborted with status %d", retval); */
+       
+       if (minimize && arena->minimize_complete && rb_block_given_p())
+               rb_yield(self);
+
+       return self;
+}      
+
+/*
+ *  call-seq:
+ *     mdarena.run(n)       -> self
+ *
+ *  Run the simulation for n steps. 
+ */
+static VALUE
+s_MDArena_Run(VALUE self, VALUE arg)
+{
+       return s_MDArena_Run_or_minimize(self, arg, 0);
+}
+
+/*
+ *  call-seq:
+ *     mdarena.minimize(n)       -> self
+ *     mdarena.minimize(n) { ... } -> self
+ *
+ *  Minimize the energy for n steps. If a block is given, it is executed when minimization is complete.
+ */
+static VALUE
+s_MDArena_Minimize(VALUE self, VALUE arg)
+{
+       return s_MDArena_Run_or_minimize(self, arg, 1);
+}
+
+/*
+ *  call-seq:
+ *     mdarena.prepare(check_only = false)         -> self or nil
+ *
+ *  Prepare for the MD calculation; refresh the internal information even if initialization is
+ *  already done.
+ *  If check_only is true, then only the parameter checking is done.
+ *  Returns self when initialization is complete; returns nil if some MM parameters are missing;
+ *  otherwise, an exception is raised.
+ */
+static VALUE
+s_MDArena_Prepare(int argc, VALUE *argv, VALUE self)
+{
+       MDArena *arena;
+       Molecule *mol;
+       const char *msg;
+       Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
+       Int missing = 0;
+       Int check_only = 0;
+       IntGroup *ig1, *ig2, *ig3;
+       VALUE fval;
+
+       Data_Get_Struct(self, MDArena, arena);
+       rb_scan_args(argc, argv, "01", &fval);
+       if (RTEST(fval))
+               check_only = 1;
+
+       arena->is_initialized = 0;
+       mol = arena->xmol;
+       
+       /*  Rebuild the tables  */
+       ig1 = ig2 = ig3 = NULL;
+       nangles = MoleculeFindMissingAngles(mol, &angles);
+       ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
+       nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
+       if (nangles > 0) {
+               ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
+               MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
+               free(angles);
+               IntGroupRelease(ig1);
+       }
+       if (ndihedrals > 0) {
+               ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
+               MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
+               free(dihedrals);
+               IntGroupRelease(ig2);
+       }
+       if (nimpropers > 0) {
+               ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
+               MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
+               free(impropers);
+               IntGroupRelease(ig3);
+       }
+
+       /*  Prepare parameters and internal information  */
+       msg = md_prepare(arena, check_only);
+
+       /*  Some parameters are missing?  */
+       if (msg != NULL) {
+               if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
+                       missing = 1;
+               else
+                       rb_raise(rb_eMolbyError, "cannot initialize for MD: %s", msg);
+       }
+
+       /*  The local parameter list is updated  */
+       {
+               Int parType, idx;
+               if (mol->par == NULL)
+                       mol->par = ParameterNew();
+               for (parType = kFirstParType; parType <= kLastParType; parType++) {
+                       /*  Delete global and undefined parameters  */
+                       UnionPar *up, *upbuf;
+                       Int nparams, count;
+                       ig1 = IntGroupNew();
+                       for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
+                               if (up->bond.src != 0)
+                                       IntGroupAdd(ig1, idx, 1);
+                       }
+                       if (IntGroupGetCount(ig1) > 0)
+                               MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
+                       IntGroupRelease(ig1);
+                       /*  Copy global and undefined parameters from arena and insert to mol->par  */
+                       nparams = ParameterGetCountForType(arena->par, parType);
+                       if (nparams == 0)
+                               continue;
+                       upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
+                       ig1 = IntGroupNew();
+                       ig2 = IntGroupNew();
+                       for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
+                               if (up->bond.src > 0)
+                                       IntGroupAdd(ig1, idx, 1); /* Global parameter */
+                               else if (up->bond.src < 0)
+                                       IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
+                       }
+                       if ((count = IntGroupGetCount(ig1)) > 0) {
+                               /*  Insert global parameters (at the top)  */
+                               ParameterCopy(arena->par, parType, upbuf, ig1);
+                               ig3 = IntGroupNewWithPoints(0, count, -1);
+                               MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
+                               IntGroupRelease(ig3);
+                       }
+                       if ((count = IntGroupGetCount(ig2)) > 0) {
+                               /*  Insert undefined parameters (at the bottom)  */
+                               ParameterCopy(arena->par, parType, upbuf, ig2);
+                               idx = ParameterGetCountForType(mol->par, parType);
+                               ig3 = IntGroupNewWithPoints(idx, count, -1);
+                               MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
+                               IntGroupRelease(ig3);
+                       }
+                       IntGroupRelease(ig2);
+                       IntGroupRelease(ig1);
+                       free(upbuf);
+               }
+       }
+
+       if (missing)
+               return Qnil;
+       else return self;
+}
+
+/*
+ *  call-seq:
+ *     arena.energies -> [total, bond, angle, dihedral, improper, vdw, electrostatic, auxiliary, surface, kinetic, net]
+ *
+ *  Get the current energies.
+ */
+static VALUE
+s_MDArena_Energies(VALUE self)
+{
+       MDArena *arena;
+       VALUE val;
+       int i;
+       Data_Get_Struct(self, MDArena, arena);
+       val = rb_ary_new();
+       rb_ary_push(val, rb_float_new(arena->total_energy * INTERNAL2KCAL));
+       for (i = 0; i < kEndIndex; i++)
+               rb_ary_push(val, rb_float_new(arena->energies[i] * INTERNAL2KCAL));
+       rb_ary_push(val, rb_float_new((arena->energies[kKineticIndex] + arena->total_energy) * INTERNAL2KCAL));
+       return val;
+}
+
+static VALUE s_LogFileSym, s_CoordFileSym, s_VelFileSym, s_ForceFileSym, 
+s_DebugFileSym, s_DebugOutputLevelSym, s_StepSym, s_CoordOutputFreqSym, 
+s_EnergyOutputFreqSym, s_CoordFrameSym, s_TimestepSym, s_CutoffSym, 
+s_ElectroCutoffSym, s_PairlistDistanceSym, s_TemperatureSym, s_AndersenFreqSym, 
+s_AndersenCouplingSym, s_RandomSeedSym, s_DielectricSym, s_GradientConvergenceSym, 
+s_CoordinateConvergenceSym, s_UseXplorShiftSym, s_Scale14VdwSym, s_Scale14ElectSym, 
+s_RelocateCenterSym, s_SurfaceProbeRadiusSym, s_SurfaceTensionSym, s_SurfacePotentialFreqSym,
+s_UseGraphiteSym;
+
+struct s_MDArenaAttrDef {
+       char *name;
+       VALUE *symref;  /*  Address of s_LogFileSym etc. */
+       ID id;                  /*  Ruby ID of the symbol; will be set within Init_MolbyMDTypes()  */
+       ID sid;         /*  Ruby ID of the symbol plus '='; will be set within Init_MolbyMDTypes()  */
+       char type;      /*  s: string (const char *), i: Int, f: Double. Uppercase: read-only.  */
+       int  offset;    /*  Offset in the MDArena structure.  */
+};
+static struct s_MDArenaAttrDef s_MDArenaAttrDefTable[] = {
+       {"log_file",          &s_LogFileSym,          0, 0, 's', offsetof(MDArena, log_result_name)},
+       {"coord_file",        &s_CoordFileSym,        0, 0, 's', offsetof(MDArena, coord_result_name)},
+       {"vel_file",          &s_VelFileSym,          0, 0, 's', offsetof(MDArena, vel_result_name)}, 
+       {"force_file",        &s_ForceFileSym,        0, 0, 's', offsetof(MDArena, force_result_name)},
+       {"debug_file",        &s_DebugFileSym,        0, 0, 's', offsetof(MDArena, debug_result_name)},
+       {"debug_output_level", &s_DebugOutputLevelSym, 0, 0, 'i', offsetof(MDArena, debug_output_level)},
+       {"step",              &s_StepSym,             0, 0, 'I', offsetof(MDArena, step)},
+       {"coord_output_freq", &s_CoordOutputFreqSym,  0, 0, 'i', offsetof(MDArena, coord_output_freq)},
+       {"energy_output_freq", &s_EnergyOutputFreqSym, 0, 0, 'i', offsetof(MDArena, energy_output_freq)},
+       {"coord_frame",       &s_CoordFrameSym,       0, 0, 'I', offsetof(MDArena, coord_result_frame)},
+       {"timestep",          &s_TimestepSym,         0, 0, 'f', offsetof(MDArena, timestep)},
+       {"cutoff",            &s_CutoffSym,           0, 0, 'f', offsetof(MDArena, cutoff)},
+       {"electro_cutoff",    &s_ElectroCutoffSym,    0, 0, 'f', offsetof(MDArena, electro_cutoff)},
+       {"pairlist_distance", &s_PairlistDistanceSym, 0, 0, 'f', offsetof(MDArena, pairlist_distance)},
+       {"temperature",       &s_TemperatureSym,      0, 0, 'f', offsetof(MDArena, temperature)},
+       {"andersen_freq",     &s_AndersenFreqSym,     0, 0, 'i', offsetof(MDArena, andersen_thermo_freq)},
+       {"andersen_coupling", &s_AndersenCouplingSym, 0, 0, 'f', offsetof(MDArena, andersen_thermo_coupling)},
+       {"random_seed",       &s_RandomSeedSym,       0, 0, 'i', offsetof(MDArena, random_seed)},
+       {"dielectric",        &s_DielectricSym,       0, 0, 'f', offsetof(MDArena, dielectric)},
+       {"gradient_convergence", &s_GradientConvergenceSym, 0, 0, 'f', offsetof(MDArena, gradient_convergence)},
+       {"coordinate_convergence", &s_CoordinateConvergenceSym, 0, 0, 'f', offsetof(MDArena, coordinate_convergence)},
+       {"use_xplor_shift",   &s_UseXplorShiftSym,    0, 0, 'i', offsetof(MDArena, use_xplor_shift)},
+       {"scale14_vdw",       &s_Scale14VdwSym,       0, 0, 'f', offsetof(MDArena, scale14_vdw)},
+       {"scale14_elect",     &s_Scale14ElectSym,     0, 0, 'f', offsetof(MDArena, scale14_elect)},
+       {"relocate_center",   &s_RelocateCenterSym,   0, 0, 'i', offsetof(MDArena, relocate_center)},
+       {"surface_probe_radius", &s_SurfaceProbeRadiusSym, 0, 0, 'f', offsetof(MDArena, probe_radius)},
+       {"surface_tension",   &s_SurfaceTensionSym,   0, 0, 'f', offsetof(MDArena, surface_tension)},
+       {"surface_potential_freq", &s_SurfacePotentialFreqSym, 0, 0, 'i', offsetof(MDArena, surface_potential_freq)},
+       {"use_graphite",      &s_UseGraphiteSym,      0, 0, 'i', offsetof(MDArena, use_graphite)},
+       {NULL} /* Sentinel */
+};
+
+static VALUE s_PresFreqSym, s_PresCouplingSym, s_PresSym, s_PresCellFlexSym, s_PresFluctCellOriginSym, s_PresFluctCellOrientSym;
+static struct s_MDArenaAttrDef s_MDPressureAttrDefTable[] = {
+       {"pressure_freq",     &s_PresFreqSym,         0, 0, 'i', offsetof(MDPressureArena, freq)},
+       {"pressure_coupling", &s_PresCouplingSym,     0, 0, 'f', offsetof(MDPressureArena, coupling)},
+       {"pressure",          &s_PresSym,             0, 0, 'X', offsetof(MDPressureArena, apply)},
+       {"pressure_cell_flexibility", &s_PresCellFlexSym, 0, 0, 'Y', offsetof(MDPressureArena, cell_flexibility)},
+       {"pressure_fluctuate_cell_origin", &s_PresFluctCellOriginSym, 0, 0, 'f', offsetof(MDPressureArena, fluctuate_cell_origin)},
+       {"pressure_fluctuate_cell_orientation", &s_PresFluctCellOrientSym, 0, 0, 'f', offsetof(MDPressureArena, fluctuate_cell_orientation)},
+       {NULL} /* Sentinel */
+};
+
+/*
+ *  call-seq:
+ *     arena[attr]
+ *
+ *  Get the attribute value.
+ */
+static VALUE
+s_MDArena_Get(VALUE self, VALUE attr)
+{
+       MDArena *arena;
+       int i;
+       struct s_MDArenaAttrDef *dp;
+       ID aid = rb_to_id(attr);
+       Data_Get_Struct(self, MDArena, arena);
+       for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
+               if (dp->id == aid) {
+                       char *p = (char *)arena + dp->offset;
+                       switch (dp->type) {
+                               case 's':
+                               case 'S': {
+                                       const char *cp = *((const char **)p);
+                                       if (cp == NULL)
+                                               return Qnil;
+                                       else
+                                               return Ruby_NewFileStringValue(cp);
+                               }
+                               case 'i':
+                               case 'I':
+                                       return INT2NUM(*((Int *)p));
+                               case 'f':
+                               case 'F':
+                                       return rb_float_new(*((Double *)p));
+                               default:
+                                       rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
+                       }
+               }
+       }
+       for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
+               if (dp->id == aid) {
+                       char *pp;
+                       MDPressureArena *pres = arena->pressure;
+                       if (pres == NULL)
+                               return Qnil;
+                       pp = (char *)pres + dp->offset;
+                       switch (dp->type) {
+                               case 'i':
+                               case 'I':
+                                       return INT2NUM(*((Int *)pp));
+                               case 'f':
+                               case 'F':
+                                       return rb_float_new(*((Double *)pp));
+                               case 'X':
+                                       /*  Isotropic pressure only  */
+                                       return rb_ary_new3(3, rb_float_new(pres->apply[0]), rb_float_new(pres->apply[4]), rb_float_new(pres->apply[8]));
+                               case 'Y': {
+                                       VALUE aval = rb_ary_new();
+                                       int j;
+                                       for (j = 0; j < 8; j++)
+                                               rb_ary_push(aval, rb_float_new(pres->cell_flexibility[j]));
+                                       return aval;
+                               }
+                               default:
+                                       rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
+                       }
+               }
+       }
+       rb_raise(rb_eMolbyError, "unknown attribute name (%s)", rb_id2name(aid));
+       return Qnil;  /*  Not reached  */
+}
+
+/*
+ *  call-seq:
+ *     arena.(attrname)
+ *
+ *  Get the attribute value. The name of the attribute is taken from ruby_frame->last_func.
+ */
+static VALUE
+s_MDArena_GetAttr(VALUE self)
+{
+       return s_MDArena_Get(self, ID2SYM(ruby_frame->last_func));
+}
+
+/*
+ *  call-seq:
+ *     arena[attr]=
+ *
+ *  Set the attribute value.
+ */
+static VALUE
+s_MDArena_Set(VALUE self, VALUE attr, VALUE val)
+{
+       MDArena *arena;
+       int i, j;
+       struct s_MDArenaAttrDef *dp;
+       ID aid = rb_to_id(attr);
+       attr = ID2SYM(aid);  /*  May be used later  */
+       Data_Get_Struct(self, MDArena, arena);
+       for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
+               if (dp->id == aid) {
+                       char *p = (char *)arena + dp->offset;
+                       switch (dp->type) {
+                               case 's': {
+                                       const char *cp = (val == Qnil ? NULL : (const char *)strdup(FileStringValuePtr(val)));
+                                       const char **cpp = (const char **)p;
+                                       FILE **fpp;
+                                       if (*cpp == cp || (*cpp != NULL && cp != NULL && strcmp(*cpp, cp) == 0))
+                                               return val;  /*  No need to change  */
+                                       if (*cpp != NULL)
+                                               free((void *)*cpp);
+                                       if (cp != NULL && cp[0] == 0) {
+                                               free((void *)cp);
+                                               cp = NULL;
+                                       }
+                                       /*  Close the corresponding FILE if necessary  */
+                                       if (attr == s_LogFileSym)
+                                               fpp = &(arena->log_result);
+                                       else if (attr == s_CoordFileSym)
+                                               fpp = &(arena->coord_result);
+                                       else if (attr == s_VelFileSym)
+                                               fpp = &(arena->vel_result);
+                                       else if (attr == s_ForceFileSym)
+                                               fpp = &(arena->force_result);
+                                       else if (attr == s_DebugFileSym)
+                                               fpp = &(arena->debug_result);
+                                       else fpp = NULL;
+                                       if (fpp != NULL && *fpp != NULL) {
+                                               fclose(*fpp);
+                                               *fpp = NULL;
+                                       }
+                                       *cpp = cp;
+                                       return val;
+                               }
+                               case 'i':
+                                       *((Int *)p) = NUM2INT(rb_Integer(val));
+                                       return val;
+                               case 'f':
+                                       *((Double *)p) = NUM2DBL(rb_Float(val));
+                                       return val;
+                               case 'S': case 'I': case 'F':
+                                       rb_raise(rb_eMolbyError, "The attribute is read-only");
+                               default:
+                                       rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
+                       }
+               }
+       }
+       for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
+               if (dp->id == aid) {
+                       char *pp;
+                       MDPressureArena *pres = arena->pressure;
+                       if (pres == NULL)
+                               arena->pressure = pres = pressure_new();
+                       pp = (char *)pres + dp->offset;
+                       switch (dp->type) {
+                               case 'i':
+                                       *((Int *)pp) = NUM2INT(rb_Integer(val));
+                                       return val;
+                               case 'f':
+                                       *((Double *)pp) = NUM2DBL(rb_Float(val));
+                                       return val;
+                               case 'X':
+                                       /*  Isotropic pressure only  */
+                                       val = rb_ary_to_ary(val);
+                                       memset(pres->apply, 0, sizeof(Mat33));
+                                       for (j = 0; j < 3 && j < RARRAY_LEN(val); j++)
+                                               pres->apply[j * 4] = NUM2DBL(rb_Float(RARRAY_PTR(val)[j]));
+                                       return val;
+                               case 'Y':
+                                       val = rb_ary_to_ary(val);
+                                       for (j = 0; j < 8; j++) {
+                                               if (j < RARRAY_LEN(val))
+                                                       pres->cell_flexibility[j] = NUM2DBL(rb_Float(RARRAY_PTR(val)[j]));
+                                               else pres->cell_flexibility[j] = 0.0;
+                                       }
+                                       return val;
+                               case 'S': case 'I': case 'F':
+                                       rb_raise(rb_eMolbyError, "The attribute is read-only");
+                               default:
+                                       rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
+                       }
+               }
+       }
+       rb_raise(rb_eMolbyError, "unknown attribute name (%s)", rb_id2name(aid));
+       return Qnil;  /*  Not reached  */
+}
+
+/*
+ *  call-seq:
+ *     arena.(attrname)=
+ *
+ *  Set the attribute value. The name of the attribute is taken from ruby_frame->last_func.
+ */
+static VALUE
+s_MDArena_SetAttr(VALUE self, VALUE val)
+{
+       int i;
+       struct s_MDArenaAttrDef *dp;
+       ID aid = ruby_frame->last_func;
+       for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
+               if (dp->sid == aid)
+                       return s_MDArena_Set(self, *(dp->symref), val);
+       }
+       for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
+               if (dp->sid == aid)
+                       return s_MDArena_Set(self, *(dp->symref), val);
+       }
+       rb_raise(rb_eMolbyError, "unknown attribute name (%s)", rb_id2name(aid));
+       return Qnil;  /*  Not reached  */
+}
+
+/*
+ *  call-seq:
+ *     arena.to_hash
+ *
+ *  Returns a (frozen) hash that contains the current value for all attribute keys.
+ */
+static VALUE
+s_MDArena_ToHash(VALUE self)
+{
+       int i;
+       VALUE hash;
+       struct s_MDArenaAttrDef *dp;
+       hash = rb_hash_new();
+       for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
+               VALUE attr = ID2SYM(dp->id);
+               rb_hash_aset(hash, attr, s_MDArena_Get(self, attr));
+       }
+       rb_obj_freeze(hash);
+       return hash;
+}
+
+/*
+ *  call-seq:
+ *     arena.print_surface_area
+ *
+ *  Print the surface area information to standard output. (for debug)
+ */
+static VALUE
+s_MDArena_PrintSurfaceArea(VALUE self)
+{
+       MDArena *arena;
+       Int i, natoms;
+       VALUE retval, outval;
+       Data_Get_Struct(self, MDArena, arena);
+       if (arena->sp_arena == NULL)
+               rb_raise(rb_eMolbyError, "surface potential is not available");
+       natoms = arena->mol->natoms;
+       retval = rb_str_new2("Atom     area    energy         forcex1000\n");
+       for (i = 0; i < natoms; i++) {
+               char buf[256];
+               Vector f = arena->forces[kSurfaceIndex * natoms + i];
+               Double area = arena->sp_arena->atom_area[i];
+               Double energy = area * arena->sp_arena->atom_pot[i];
+               VecScaleSelf(f, INTERNAL2KCAL * 1000);
+               energy *= INTERNAL2KCAL;
+               snprintf(buf, sizeof buf, "%5d %11.5f %11.8f %11.5f %11.5f %11.5f\n",
+                          i+1, area, energy, f.x, f.y, f.z);
+               rb_str_cat(retval, buf, strlen(buf));
+       }
+       outval = rb_gv_get("$stdout");
+       rb_funcall(outval, rb_intern("write"), 1, retval);
+       return self;
+}
+
+void
+Init_MolbyMDTypes(void)
+{
+       int i;
+       struct s_MDArenaAttrDef *dp;
+       char name[40];
+
+       /*  class MDArena  */
+       rb_cMDArena = rb_define_class("MDArena", rb_cObject);
+/*     rb_define_alloc_func(rb_cMDArena, s_MDArena_Alloc);
+    rb_define_private_method(rb_cMDArena, "initialize", s_MDArena_Initialize, 1); */
+       rb_define_method(rb_cMDArena, "run", s_MDArena_Run, 1);
+    rb_define_method(rb_cMDArena, "minimize", s_MDArena_Minimize, 1);
+    rb_define_method(rb_cMDArena, "prepare", s_MDArena_Prepare, -1);
+    rb_define_method(rb_cMDArena, "energies", s_MDArena_Energies, 0);
+       rb_define_method(rb_cMDArena, "[]", s_MDArena_Get, 1);
+       rb_define_method(rb_cMDArena, "[]=", s_MDArena_Set, 2);
+       rb_define_method(rb_cMDArena, "to_hash", s_MDArena_ToHash, 0);
+       rb_define_method(rb_cMDArena, "print_surface_area", s_MDArena_PrintSurfaceArea, 0);
+
+       /*  All setter and getter are handled with the same C function (attribute name is taken
+           from ruby_frame)  */
+       for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
+               rb_define_method(rb_cMDArena, dp->name, s_MDArena_GetAttr, 0);
+               strncpy(name, dp->name, 38);
+               strcat(name, "=");
+               rb_define_method(rb_cMDArena, name, s_MDArena_SetAttr, 1);
+               dp->id = rb_intern(dp->name);
+               dp->sid = rb_intern(name);
+               *(dp->symref) = ID2SYM(dp->id);
+       }
+       for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
+               rb_define_method(rb_cMDArena, dp->name, s_MDArena_GetAttr, 0);
+               strncpy(name, dp->name, 38);
+               strcat(name, "=");
+               rb_define_method(rb_cMDArena, name, s_MDArena_SetAttr, 1);
+               dp->id = rb_intern(dp->name);
+               dp->sid = rb_intern(name);
+               *(dp->symref) = ID2SYM(dp->id);
+       }
+}
diff --git a/MolLib/Ruby_bind/ruby_types.c b/MolLib/Ruby_bind/ruby_types.c
new file mode 100644 (file)
index 0000000..1ca1095
--- /dev/null
@@ -0,0 +1,1659 @@
+/*
+ *  ruby_types.c
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/01/24.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "Molby.h"
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#pragma mark ====== Global Values ======
+
+VALUE rb_cVector3D, rb_cTransform, rb_cIntGroup;
+
+#pragma mark ====== Utility functions (Vector/Matrix) ======
+
+/*
+static VALUE
+s_VectorClass(void)
+{
+       if (rb_cVector == 0)
+               rb_cVector = rb_const_get(rb_cObject, rb_intern("Vector"));
+       return rb_cVector;
+}
+*/
+
+/*
+VALUE
+ValueFromVector(const Vector *vp)
+{
+       static ID mname = 0;
+       if (mname == 0)
+               mname = rb_intern("[]");
+       return rb_funcall(rb_cVector3D, mname, 3, rb_float_new(vp->x), rb_float_new(vp->y), rb_float_new(vp->z));
+}
+*/
+
+#pragma mark ====== Vector3D Class ======
+
+void
+VectorFromValue(VALUE val, Vector *vp)
+{
+       Vector *vp1;
+       if (rb_obj_is_kind_of(val, rb_cVector3D)) {
+               Data_Get_Struct(val, Vector, vp1);
+               *vp = *vp1;
+       } else {
+               static ID mname = 0;
+               if (mname == 0)
+                       mname = rb_intern("[]");
+               vp->x = NUM2DBL(rb_Float(rb_funcall(val, mname, 1, INT2FIX(0))));
+               vp->y = NUM2DBL(rb_Float(rb_funcall(val, mname, 1, INT2FIX(1))));
+               vp->z = NUM2DBL(rb_Float(rb_funcall(val, mname, 1, INT2FIX(2))));
+       }
+}
+
+static VALUE
+s_Vector3D_Alloc(VALUE klass)
+{
+       Vector *vp = ALLOC(Vector);
+       vp->x = vp->y = vp->z = 0.0;
+       return Data_Wrap_Struct(klass, 0, -1, vp);
+}
+
+VALUE
+ValueFromVector(const Vector *vp)
+{
+       Vector *vp1;
+       VALUE retval = s_Vector3D_Alloc(rb_cVector3D);
+       Data_Get_Struct(retval, Vector, vp1);
+       *vp1 = *vp;
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     Vector3D.new
+ *     Vector3D.new(vector3d)
+ *     Vector3D.new(ary)
+ *
+ *  Returns a new Vector3D object. In the first form, a zero vector
+ *  is returned. In the second form, the given vector3d is duplicated.
+ *  In the third form, a vector [ary[0], ary[1], ary[2]] is returned.
+ *  The argument ary can be anything that responds to '[]' method.
+ */
+static VALUE
+s_Vector3D_Initialize(int argc, VALUE *argv, VALUE self)
+{
+       VALUE val;
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       rb_scan_args(argc, argv, "01", &val);
+       if (!NIL_P(val))
+               VectorFromValue(val, vp);
+       return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     vector3d.size -> int
+ *  
+ *  Returns 3. This method is present only to be consistent with classes like
+ *  Array or Vector.
+ */
+static VALUE
+s_Vector3D_Size(VALUE self)
+{
+       return INT2FIX(3);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d[index]             -> float
+ *
+ *  Element Reference---Returns the element at _index_. If _index_ is
+ *  less than 0 or more than 2, an exception is thrown.
+ */
+static VALUE
+s_Vector3D_ElementAtIndex(VALUE self, VALUE val)
+{
+       Vector *vp;
+       Double w;
+       int n = NUM2INT(val);
+       Data_Get_Struct(self, Vector, vp);
+       if (n < 0 || n >= 3)
+               rb_raise(rb_eMolbyError, "index to Vector3D out of range");
+       w = (n == 0 ? vp->x : (n == 1 ? vp->y : vp->z));
+       return rb_float_new(w);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d[index] = float
+ *
+ *  Element Assignment---Set the element at _index_. If _index_ is
+ *  less than 0 or more than 2, an exception is thrown.
+ */
+static VALUE
+s_Vector3D_SetElementAtIndex(VALUE self, VALUE idx, VALUE val)
+{
+       Vector *vp;
+       Double w = NUM2DBL(rb_Float(val));
+       int n = NUM2INT(idx);
+       Data_Get_Struct(self, Vector, vp);
+       if (n < 0 || n >= 3)
+               rb_raise(rb_eMolbyError, "index to Vector3D out of range");
+       if (n == 0)
+               vp->x = w;
+       else if (n == 1)
+               vp->y = w;
+       else
+               vp->z = w;
+       return rb_float_new(w);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d == other_vector3d   ->   bool
+ *
+ *  Equality---Two vector3ds are equal if their elements are all equal.
+ *  Usual caution about comparison between floating point numbers should be
+ *  paid. Also consider using something like <code>vector3d.length < 1e-10</code>.
+ */
+static VALUE
+s_Vector3D_IsEqual(VALUE self, VALUE val)
+{
+       Vector *vp1, v2;
+       Data_Get_Struct(self, Vector, vp1);
+       VectorFromValue(val, &v2);
+       if (vp1->x == v2.x && vp1->y == v2.y && vp1->z == v2.z)
+               return Qtrue;
+       else return Qfalse;
+}
+
+/* 
+ *  call-seq:
+ *     vector3d + other_vector3d   ->   (new) vector3d
+ *
+ *  Add two vectors element by element.
+ */
+static VALUE
+s_Vector3D_Add(VALUE self, VALUE val)
+{
+       Vector *vp1, v2;
+       VALUE retval;
+       Data_Get_Struct(self, Vector, vp1);
+       VectorFromValue(val, &v2);
+       retval = s_Vector3D_Alloc(rb_cVector3D);
+       v2.x += vp1->x;
+       v2.y += vp1->y;
+       v2.z += vp1->z;
+       return ValueFromVector(&v2);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d - other_vector3d   ->   (new) vector3d
+ *
+ *  Subtract two vectors element by element.
+ */
+static VALUE
+s_Vector3D_Subtract(VALUE self, VALUE val)
+{
+       Vector *vp1, v2;
+       VALUE retval;
+       Data_Get_Struct(self, Vector, vp1);
+       VectorFromValue(val, &v2);
+       retval = s_Vector3D_Alloc(rb_cVector3D);
+       v2.x = vp1->x - v2.x;
+       v2.y = vp1->y - v2.y;
+       v2.z = vp1->z - v2.z;
+       return ValueFromVector(&v2);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.dot(other_vector3d) ->  float
+ *
+ *  Calculate the dot (inner) product of the two vectors. See also <code>vector3d.*</code>.
+ */
+static VALUE
+s_Vector3D_Dot(VALUE self, VALUE val)
+{
+       Vector *vp1, v2;
+       Data_Get_Struct(self, Vector, vp1);
+       VectorFromValue(val, &v2);
+       return rb_float_new(vp1->x * v2.x + vp1->y * v2.y + vp1->z * v2.z);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d * numeric           ->  (new) vector3d
+ *     vector3d * other_vector3d    ->  float (the dot product)
+ *
+ *  In the first form, the vector is scaled by the numeric. In the second
+ *  form, the dot (inner) product of the two vectors are returned (equivalent to
+ *  <code>vector3d.dot(other_vector3d)</code>).
+ */
+static VALUE
+s_Vector3D_Multiply(VALUE self, VALUE val)
+{
+       Vector *vp1, v2;
+       Data_Get_Struct(self, Vector, vp1);
+       if (rb_obj_is_kind_of(val, rb_cNumeric)) {
+               double w = NUM2DBL(rb_Float(val));
+               v2.x = vp1->x * w;
+               v2.y = vp1->y * w;
+               v2.z = vp1->z * w;
+               return ValueFromVector(&v2);
+       } else return s_Vector3D_Dot(self, val);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d / numeric           ->  (new) vector3d
+ *
+ *  The vector is scaled by the inverse of the given numeric.
+ */
+static VALUE
+s_Vector3D_Divide(VALUE self, VALUE val)
+{
+       Vector *vp1, v2;
+       double w = NUM2DBL(rb_Float(val));
+       Data_Get_Struct(self, Vector, vp1);
+       v2.x = vp1->x / w;
+       v2.y = vp1->y / w;
+       v2.z = vp1->z / w;
+       return ValueFromVector(&v2);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.cross(other_vector3d) ->  (new) vector3d
+ *
+ *  Calculate the cross (outer) product of the two vectors.
+ */
+static VALUE
+s_Vector3D_Cross(VALUE self, VALUE val)
+{
+       Vector *vp1, v2, v3;
+       Data_Get_Struct(self, Vector, vp1);
+       VectorFromValue(val, &v2);
+       v3.x = vp1->y * v2.z - vp1->z * v2.y;
+       v3.y = vp1->z * v2.x - vp1->x * v2.z;
+       v3.z = vp1->x * v2.y - vp1->y * v2.x;
+       return ValueFromVector(&v3);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.-@                ->  vector3d
+ *
+ *  Calculate the opposite vector. 
+ */
+static VALUE
+s_Vector3D_UnaryMinus(VALUE self)
+{
+       Vector *vp, v1;
+       Data_Get_Struct(self, Vector, vp);
+       v1.x = -vp->x;
+       v1.y = -vp->y;
+       v1.z = -vp->z;
+       return ValueFromVector(&v1);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.length                ->  float
+ *
+ *  Calculate the Pythagorean length of the vector. 
+ *  Note that this method is <em>not</em> an alias of <code>vector3d.size</code>.
+ */
+static VALUE
+s_Vector3D_Length(VALUE self)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       return rb_float_new(sqrt(vp->x * vp->x + vp->y * vp->y + vp->z * vp->z));
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.length2                ->  float
+ *
+ *  Calculate the square of the Pythagorean length of the vector.
+ */
+static VALUE
+s_Vector3D_Length2(VALUE self)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       return rb_float_new(vp->x * vp->x + vp->y * vp->y + vp->z * vp->z);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.normalize              ->  (new) vector3d
+ *
+ *  Returns a unit vector with the same direction. Raises an exception when the
+ *  vector is a zero vector.
+ */
+static VALUE
+s_Vector3D_Normalize(VALUE self)
+{
+       Vector *vp, v;
+       double w;
+       Data_Get_Struct(self, Vector, vp);
+       w = 1.0 / sqrt(vp->x * vp->x + vp->y * vp->y + vp->z * vp->z);
+       if (!isfinite(w))
+               rb_raise(rb_eMolbyError, "trying to normalize a (nearly) zero vector");
+       v.x = vp->x * w;
+       v.y = vp->y * w;
+       v.z = vp->z * w;
+       return ValueFromVector(&v);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.to_a                    ->  Array
+ *
+ *  Returns <code>[self.x, self.y, self.z]</code>.
+ */
+static VALUE
+s_Vector3D_ToArray(VALUE self)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);      
+       return rb_ary_new3(3, rb_float_new(vp->x), rb_float_new(vp->y), rb_float_new(vp->z));
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.each {|item| block }  -> vector3d (self)
+ *
+ *  Calls <i>block</i> once for x, y, z elements, passing that element as a parameter.
+ */
+static VALUE
+s_Vector3D_Each(VALUE self)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       rb_yield(rb_float_new(vp->x));
+       rb_yield(rb_float_new(vp->y));
+       rb_yield(rb_float_new(vp->z));
+       return self;
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.x       -> float
+ *
+ *  Get the x element of the vector.
+ */
+static VALUE
+s_Vector3D_GetX(VALUE self)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       return rb_float_new(vp->x);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.y       -> float
+ *
+ *  Get the y element of the vector.
+ */
+static VALUE
+s_Vector3D_GetY(VALUE self)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       return rb_float_new(vp->y);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.z       -> float
+ *
+ *  Get the z element of the vector.
+ */
+static VALUE
+s_Vector3D_GetZ(VALUE self)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       return rb_float_new(vp->z);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.x = float       -> float
+ *
+ *  Set the x element of the vector.
+ */
+static VALUE
+s_Vector3D_SetX(VALUE self, VALUE val)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       vp->x = NUM2DBL(rb_Float(val));
+       return rb_float_new(vp->x);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.y = float       -> float
+ *
+ *  Set the y element of the vector.
+ */
+static VALUE
+s_Vector3D_SetY(VALUE self, VALUE val)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       vp->y = NUM2DBL(rb_Float(val));
+       return rb_float_new(vp->y);
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.z = float       -> float
+ *
+ *  Set the z element of the vector.
+ */
+static VALUE
+s_Vector3D_SetZ(VALUE self, VALUE val)
+{
+       Vector *vp;
+       Data_Get_Struct(self, Vector, vp);
+       vp->z = NUM2DBL(rb_Float(val));
+       return rb_float_new(vp->z);
+}
+
+/* 
+ *  call-seq:
+ *     Vector3d[fx, fy, fz]   -> (new) vector3d
+ *
+ *  Create a new vector3d object. Equivalent to <code>Vector3d.new([fx, fy, fz])</code>.
+ */
+static VALUE
+s_Vector3D_Create(VALUE klass, VALUE args)
+{
+       VALUE val = s_Vector3D_Alloc(klass);
+       s_Vector3D_Initialize(1, &args, val);
+       return val;
+}
+
+/* 
+ *  call-seq:
+ *     vector3d.inspect        -> string
+ *
+ *  Create a readable string like "Vector3d[fx, fy, fz]".
+ */
+static VALUE
+s_Vector3D_Inspect(VALUE self)
+{
+       /*  self.class.name << self.to_a.inspect  */
+       VALUE klass = CLASS_OF(self);
+       VALUE val = rb_funcall(klass, rb_intern("name"), 0);
+       self = s_Vector3D_ToArray(self);
+       return rb_funcall(val, rb_intern("<<"), 1, rb_funcall(self, rb_intern("inspect"), 0));
+}
+
+#pragma mark ====== Transform Class ======
+
+static int s_index_order[16] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
+
+void
+TransformFromValue(VALUE val, Transform *tp)
+{
+       int i, j;
+       Transform *tp1;
+       if (rb_obj_is_kind_of(val, rb_cTransform)) {
+               Data_Get_Struct(val, Transform, tp1);
+               memmove(tp, tp1, sizeof(Transform));
+       } else if (TYPE(val) == T_ARRAY) {
+               /*  Array must be 
+                (1) [[a11 a21 a31] [a12 a22 a32] [a13 a23 a33] [a14 a24 a34]] or
+                (2) [a11 a21 a31 a12 a22 a32 a13 a23 a33 a14 a24 a34] */
+               int len = RARRAY_LEN(val);
+               VALUE *valp = RARRAY_PTR(val);
+               if (len == 12) {
+                       for (i = 0; i < 12; i++)
+                               (*tp)[s_index_order[i]] = NUM2DBL(rb_Float(valp[i]));
+                       return;
+               } else if (len == 4) {
+                       VALUE val2, *valp2;
+                       for (i = 0; i < 4; i++) {
+                               val2 = valp[i];
+                               if (TYPE(val2) != T_ARRAY)
+                                       val2 = rb_funcall(val2, rb_intern("to_a"), 0);
+                               if (TYPE(val2) != T_ARRAY || RARRAY_LEN(val2) != 3)
+                                       goto array_format_error;
+                               valp2 = RARRAY_PTR(val2);
+                               for (j = 0; j < 3; j++)
+                                       (*tp)[s_index_order[3 * i + j]] = NUM2DBL(rb_Float(valp2[j]));
+                       }
+                       return;
+               }
+       array_format_error:
+               rb_raise(rb_eMolbyError, "wrong array format; must be an array of either (1) four 3D column vectors or (2) 12 numerics");
+       } else {
+               static ID index_mid = 0, row_size_mid, column_size_mid;
+               if (index_mid == 0) {
+                       index_mid = rb_intern("[]");
+                       row_size_mid = rb_intern("row_size");
+                       column_size_mid = rb_intern("column_size");
+               }
+               if (rb_respond_to(val, row_size_mid) && rb_respond_to(val, column_size_mid)) {
+                       /*  Matrix-type object  */
+                       for (i = 0; i < 4; i++) {
+                               for (j = 0; j < 3; j++)
+                                       (*tp)[s_index_order[i * 3 + j]] = NUM2DBL(rb_Float(rb_funcall(val, index_mid, 2, INT2FIX(i), INT2FIX(j))));
+                       }
+               } else {
+                       /*  Other "array-like" object  */
+                       for (i = 0; i < 12; i++)
+                               (*tp)[s_index_order[i]] = NUM2DBL(rb_Float(rb_funcall(val, index_mid, 1, INT2FIX(i))));
+               }
+       }
+}
+
+static VALUE
+s_Transform_Alloc(VALUE klass)
+{
+       Transform *tp = ALLOC(Transform);
+       memset(tp, 0, sizeof(Transform));
+       (*tp)[0] = (*tp)[4] = (*tp)[8] = 1.0;
+       return Data_Wrap_Struct(klass, 0, -1, tp);
+}
+
+VALUE
+ValueFromTransform(Transform *tp)
+{
+       Transform *tp1;
+       VALUE val = s_Transform_Alloc(rb_cTransform);
+       Data_Get_Struct(val, Transform, tp1);
+       memmove(tp1, tp, sizeof(Transform));
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     Transform.new
+ *     Transform.new(array)
+ *     Transform.new(matrix)
+ *
+ *  Returns a new Transform object.
+ *
+ *  In the first form, an identity transform is returned.
+ *
+ *  In the second form, the array must be either of the following
+ *  two forms:
+ *  (1) [[a11 a21 a31] [a12 a22 a32] [a13 a23 a33] [a14 a24 a34]]
+ *  (2) [a11 a21 a31 a12 a22 a32 a13 a23 a33 a14 a24 a34]
+ *  where [a11..a33] denotes the rotation part and [a14 a24 a34] denotes
+ *  the translation part. All vectors in (1) are column vectors.
+ *  
+ *  In the third form, a new transform is built from a 3x4 matrix. The argument
+ *  <code>matrix</code> must respond to a method call <code>matrix[col, row]</code>
+ *  where <code>row</code> is in <code>0..2</code> and <code>col</code> in <code>0..3</code>. 
+ */
+static VALUE
+s_Transform_Initialize(int argc, VALUE *argv, VALUE self)
+{
+       VALUE val;
+       Transform *tp;
+       Data_Get_Struct(self, Transform, tp);
+       rb_scan_args(argc, argv, "01", &val);
+       if (!NIL_P(val))
+               TransformFromValue(val, tp);
+       return Qnil;
+}
+
+static VALUE
+s_Transform_NewFromTransform(Transform *tp)
+{
+       Transform *tp1;
+       VALUE retval = s_Transform_Alloc(rb_cTransform);
+       Data_Get_Struct(retval, Transform, tp1);
+       memmove(tp1, tp, sizeof(Transform));
+       return retval;
+}
+
+/*
+ *  call-seq:
+ *     Transform.from_columns(c1, c2, c3, c4)
+ *
+ *  Returns a new Transform object built from four column vectors. The arguments
+ *  <code>c1..c4</code> are vectors of (at least) three-dimension. This is equivalent
+ *  to <code>Transform.new([c1, c2, c3, c4])</code>.
+ */
+static VALUE
+s_Transform_NewFromColumns(VALUE klass, VALUE val)
+{
+       Transform tr;
+       int i, j, n;
+       VALUE *valp;
+       static ID to_a_mid = 0;
+       if (to_a_mid == 0)
+               to_a_mid = rb_intern("to_a");
+       if (TYPE(val) != T_ARRAY)
+               val = rb_funcall(val, to_a_mid, 0);
+       memset(tr, 0, sizeof(tr));
+       n = RARRAY_LEN(val);
+       valp = RARRAY_PTR(val);
+       for (i = 0; i < 4; i++) {
+               int nn;
+               VALUE *valpp;
+               double w[3];
+               w[0] = w[1] = w[2] = 0.0;
+               if (i < n) {
+                       val = valp[i];
+                       if (TYPE(val) != T_ARRAY)
+                               val = rb_funcall(val, to_a_mid, 0);
+                       nn = RARRAY_LEN(val);
+                       valpp = RARRAY_PTR(val);
+                       for (j = 0; j < 3 && j < nn; j++)
+                               w[j] = NUM2DBL(rb_Float(valpp[j]));
+               }
+               for (j = 0; j < 3; j++)
+                       tr[s_index_order[i * 3 + j]] = w[j];
+       }
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     Transform.from_rows(r1, r2, r3)
+ *
+ *  Returns a new Transform object built from three row vectors. The arguments
+ *  <code>r1, r2, r3</code> are vectors of (at least) four-dimension.
+ */
+static VALUE
+s_Transform_NewFromRows(VALUE klass, VALUE val)
+{
+       Transform tr;
+       int i, j, n;
+       VALUE *valp;
+       static ID to_a_mid = 0;
+       if (to_a_mid == 0)
+               to_a_mid = rb_intern("to_a");
+       if (TYPE(val) != T_ARRAY)
+               val = rb_funcall(val, to_a_mid, 0);
+       memset(tr, 0, sizeof(tr));
+       n = RARRAY_LEN(val);
+       valp = RARRAY_PTR(val);
+       for (i = 0; i < 3; i++) {
+               int nn;
+               VALUE *valpp;
+               double w[4];
+               w[0] = w[1] = w[2] = w[3] = 0.0;
+               if (i < n) {
+                       val = valp[i];
+                       if (TYPE(val) != T_ARRAY)
+                               val = rb_funcall(val, to_a_mid, 0);
+                       nn = RARRAY_LEN(val);
+                       valpp = RARRAY_PTR(val);
+                       for (j = 0; j < 4 && j < nn; j++)
+                               w[j] = NUM2DBL(rb_Float(valpp[j]));
+               }
+               for (j = 0; j < 4; j++)
+                       tr[s_index_order[j * 3 + i]] = w[j];
+       }
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     transform[i, j]  -> float
+ *
+ *  Get the element (+i+,+j+) of the transform matrix, i.e. column +i+, row +j+.
+ *  Be careful about the order of the arguments. It follows convention of multi-dimensional arrays
+ *  rather than mathematical notation.
+ */
+static VALUE
+s_Transform_ElementAtIndex(VALUE self, VALUE val1, VALUE val2)
+{
+       Transform *tp;
+       double w;
+       int n1 = NUM2INT(val1);
+       int n2 = NUM2INT(val2);
+       Data_Get_Struct(self, Transform, tp);
+       if (n1 < 0 || n1 >= 4 || n2 < 0 || n2 >= 3)
+               rb_raise(rb_eMolbyError, "index to Transform out of range");
+       w = (*tp)[s_index_order[n1 * 3 + n2]];
+       return rb_float_new(w);
+}
+
+/*
+ *  call-seq:
+ *     transform[i, j] = float  -> float
+ *
+ *  Set the element (+i+,+j+) of the transform matrix, i.e. column +i+, row +j+.
+ *  Be careful about the order of the arguments. It follows convention of multi-dimensional arrays
+ *  rather than mathematical notation.
+ */
+static VALUE
+s_Transform_SetElementAtIndex(VALUE self, VALUE idx1, VALUE idx2, VALUE val)
+{
+       Transform *tp;
+       double w;
+       int n1 = NUM2INT(idx1);
+       int n2 = NUM2INT(idx2);
+       Data_Get_Struct(self, Transform, tp);
+       if (n1 < 0 || n1 >= 4 || n2 < 0 || n2 >= 3)
+               rb_raise(rb_eMolbyError, "index to Transform out of range");
+       w = NUM2DBL(rb_Float(val));
+       (*tp)[s_index_order[n1 * 3 + n2]] = w;
+       return rb_float_new(w);
+}
+
+/*
+ *  call-seq:
+ *     transform == other_transform  -> bool
+ *
+ *  Returns +true+ if and only if all the corresponding elements are equal.
+ *  Usual caution about the comparison of floating-point numbers should be paid.
+ */
+static VALUE
+s_Transform_IsEqual(VALUE self, VALUE val)
+{
+       Transform *tp1, tr;
+       int i;
+       Data_Get_Struct(self, Transform, tp1);
+       TransformFromValue(val, &tr);
+       for (i = 0; i < 12; i++) {
+               if ((*tp1)[i] != tr[i])
+                       return Qfalse;
+       }
+       return Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     transform + other_transform  -> (new) transform
+ *
+ *  Returns a new transform corresponding to the sum of the two transform matrix.
+ */
+static VALUE
+s_Transform_Add(VALUE self, VALUE val)
+{
+       Transform *tp1, tr;
+       int i;
+       Data_Get_Struct(self, Transform, tp1);
+       TransformFromValue(val, &tr);
+       for (i = 0; i < 12; i++)
+               tr[i] += (*tp1)[i];
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     transform - other_transform  -> (new) transform
+ *
+ *  Returns a new transform corresponding to the difference of the two transform matrix.
+ */
+static VALUE
+s_Transform_Subtract(VALUE self, VALUE val)
+{
+       Transform *tp1, tr;
+       int i;
+       Data_Get_Struct(self, Transform, tp1);
+       TransformFromValue(val, &tr);
+       for (i = 0; i < 12; i++)
+               tr[i] = (*tp1)[i] - tr[i];
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     transform * numeric          -> (new) transform
+ *     transform * vector3d         -> (new) vector3d
+ *     transform * other_transform  -> (new) transform
+ *
+ *  Perform the matrix multiplication. In the first form, a new matrix with scaled elements
+ *  is returned. In the second, the transformed vector is returned. In the third form,
+ *  the multiple of the two matrices is returned.
+ */
+static VALUE
+s_Transform_Multiply(VALUE self, VALUE val)
+{
+       Transform *tp1, tr;
+       int i;
+       Data_Get_Struct(self, Transform, tp1);
+       if (rb_obj_is_kind_of(val, rb_cNumeric)) {
+               double w = NUM2DBL(rb_Float(val));
+               for (i = 0; i < 12; i++)
+                       tr[i] = (*tp1)[i] * w;
+               return s_Transform_NewFromTransform(&tr);
+       } else {
+               static ID size_mid = 0;
+               if (size_mid == 0)
+                       size_mid = rb_intern("size");
+               if (rb_respond_to(val, size_mid) && NUM2INT(rb_funcall(val, size_mid, 0)) == 3) {
+                       /*  A 3D vector  */
+                       Vector v;
+                       VectorFromValue(val, &v);
+                       TransformVec(&v, *tp1, &v);
+                       return ValueFromVector(&v);
+               } else {
+                       /*  Transform  */
+                       TransformFromValue(val, &tr);
+                       TransformMul(tr, *tp1, tr);
+                       return s_Transform_NewFromTransform(&tr);
+               }
+       }
+}
+
+/*
+ *  call-seq:
+ *     Transform.identity  -> transform
+ *
+ *  Returns an identity transform, <code>[[1,0,0], [0,1,0], [0,0,1], [0,0,0]]</code>.
+ */
+static VALUE
+s_Transform_Identity(VALUE klass)
+{
+       Transform tr;
+       memset(tr, 0, sizeof(tr));
+       tr[0] = tr[4] = tr[8] = 1.0;
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     Transform.zero  -> transform
+ *
+ *  Returns a zero transform, <code>[[0,0,0], [0,0,0], [0,0,0], [0,0,0]]</code>.
+ */
+static VALUE
+s_Transform_Zero(VALUE klass)
+{
+       Transform tr;
+       memset(tr, 0, sizeof(tr));
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     Transform.diagonal(array)
+ *     Transform.diagonal(f1, f2 = nil, f3 = nil)
+ *
+ *  Returns a diagonal transform (the translational componets are all zero).
+ *  In the first form, <code>array[0], array[1], array[2]</code> are for the
+ *  x, y, z components, respectively. In the second form, <code>f1, f2, f3</code>
+ *  are the x, y, z components. If <code>f3</code> is not given, the <code>f2</code>
+ *  is used for the z components. If <code>f2</code> is not given, the <code>f1</code>
+ *  is used for the y and z components.
+ */
+static VALUE
+s_Transform_Diagonal(int argc, VALUE *argv, VALUE klass)
+{
+       Transform tr;
+       VALUE arg1, arg2, arg3;
+       memset(tr, 0, sizeof(tr));
+       rb_scan_args(argc, argv, "12", &arg1, &arg2, &arg3);
+       if (TYPE(arg1) == T_ARRAY) {
+               /*  [d1, d2, d3]  */
+               VALUE *valp;
+               int len;
+               len = RARRAY_LEN(arg1);
+               valp = RARRAY_PTR(arg1);
+               if (len >= 1)
+                       tr[0] = tr[4] = tr[8] = NUM2DBL(rb_Float(valp[0]));
+               if (len >= 2)
+                       tr[4] = tr[8] = NUM2DBL(rb_Float(valp[1]));
+               if (len >= 3)
+                       tr[8] = NUM2DBL(rb_Float(valp[2]));
+       } else {
+               tr[0] = tr[4] = tr[8] = NUM2DBL(rb_Float(arg1));
+               if (!NIL_P(arg2)) {
+                       tr[4] = tr[8] = NUM2DBL(rb_Float(arg2));
+                       if (!NIL_P(arg3))
+                               tr[8] = NUM2DBL(rb_Float(arg3));
+               }
+       }
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     transform.inverse  -> (new) transform
+ *
+ *  Returns the inverse transform. If the matrix is not regular, an exception is raised.
+ */
+static VALUE
+s_Transform_Inverse(VALUE self)
+{
+       Transform *tp1, tr;
+       Data_Get_Struct(self, Transform, tp1);
+       if (TransformInvert(tr, *tp1))
+               rb_raise(rb_eMolbyError, "the transform matrix is not regular");
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     transform / other_transform -> (new) transform
+ *
+ *  Returns transform * other_transform.invert. If other_transform is not regular, 
+ *  an exception is raised.
+ */
+static VALUE
+s_Transform_Divide(VALUE self, VALUE val)
+{
+       Transform *tp1, tr;
+       Data_Get_Struct(self, Transform, tp1);
+       TransformFromValue(val, &tr);
+       if (TransformInvert(tr, tr))
+               rb_raise(rb_eMolbyError, "the transform matrix is not regular");
+       TransformMul(tr, *tp1, tr);
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     transform.transpose -> (new) transform
+ *
+ *  Returns a new transform in which the rotation component is transposed from the original.
+ */
+static VALUE
+s_Transform_Transpose(VALUE self)
+{
+       Transform *tp1, tr;
+       Data_Get_Struct(self, Transform, tp1);
+       TransformTranspose(tr, *tp1);
+       return s_Transform_NewFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     transform.determinant -> float
+ *
+ *  Returns the determinant of the transform.
+ */
+static VALUE
+s_Transform_Determinant(VALUE self)
+{
+       Transform *tp1;
+       Data_Get_Struct(self, Transform, tp1);
+       return rb_float_new(TransformDeterminant(*tp1));
+}
+
+/*
+ *  call-seq:
+ *     transform.trace -> float
+ *
+ *  Returns the trace (sum of the diagonal elements) of the transform.
+ */
+static VALUE
+s_Transform_Trace(VALUE self)
+{
+       Transform *tp1;
+       Data_Get_Struct(self, Transform, tp1);
+       return rb_float_new((*tp1)[0] + (*tp1)[4] + (*tp1)[8]);
+}
+
+/*
+ *  call-seq:
+ *     transform.column(index) -> vector3d
+ *
+ *  Returns the index-th (0..3) column vector.
+ */
+static VALUE
+s_Transform_Column(VALUE self, VALUE val)
+{
+       Transform *tp1;
+       Vector v;
+       int n = NUM2INT(val);
+       Data_Get_Struct(self, Transform, tp1);
+       if (n < 0 || n >= 4)
+               rb_raise(rb_eMolbyError, "row index out of range");
+       v.x = (*tp1)[n];
+       v.y = (*tp1)[n + 3];
+       v.z = (*tp1)[n + 6];
+       return ValueFromVector(&v);
+}
+
+/*
+ *  call-seq:
+ *     transform.eigenvalues -> [[k1, k2, k3], v1, v2, v3]
+ *
+ *  Calculate the eigenvalues and eigenvectors. The matrix must be symmetric.
+ */
+static VALUE
+s_Transform_Eigenvalues(VALUE self)
+{
+       Transform *tp1;
+       Vector v[3];
+       Double d[3];
+       int info;
+       Data_Get_Struct(self, Transform, tp1);
+       if ((info = MatrixSymDiagonalize(*((Mat33 *)tp1), d, v)) != 0)
+               rb_raise(rb_eMolbyError, "cannot diagonalize the given matrix: info = %d", info);
+       return rb_ary_new3(4, rb_ary_new3(3, rb_float_new(d[0]), rb_float_new(d[1]), rb_float_new(d[2])), ValueFromVector(v), ValueFromVector(v + 1), ValueFromVector(v + 2));
+}
+
+/*
+ *  call-seq:
+ *     Transform[*args]
+ *
+ *  Create a new transform. Equivalent to Transform.new(args).
+ */
+static VALUE
+s_Transform_Create(VALUE klass, VALUE args)
+{
+       VALUE val = s_Transform_Alloc(klass);
+       s_Transform_Initialize(1, &args, val);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     transform.to_a  -> array
+ *
+ *  Convert a transform to an array of 12 float numbers.
+ */
+static VALUE
+s_Transform_ToArray(VALUE self)
+{
+       Transform *tp1;
+       VALUE val[12];
+       int i;
+       Data_Get_Struct(self, Transform, tp1);
+       for (i = 0; i < 12; i++)
+               val[i] = rb_float_new((*tp1)[s_index_order[i]]);
+       return rb_ary_new4(12, val);
+}
+
+/*
+ *  call-seq:
+ *     transform.inspect  -> string
+ *
+ *  Convert a transform to a string like 
+ *  "Transform[[a11,a21,a31],[a12,a22,a32],[a13,a23,a33],[a14,a24,a34]]".
+ */
+static VALUE
+s_Transform_Inspect(VALUE self)
+{
+       Transform *tp;
+       int i, j;
+       VALUE klass = CLASS_OF(self);
+       VALUE val = rb_funcall(klass, rb_intern("name"), 0);
+       ID mid = rb_intern("<<");
+       ID mid2 = rb_intern("inspect");
+       rb_str_cat(val, "[", 1);
+       Data_Get_Struct(self, Transform, tp);
+       for (i = 0; i < 4; i++) {
+               rb_str_cat(val, "[", 1);
+               for (j = 0; j < 3; j++) {
+                       double f;
+                       f = (*tp)[s_index_order[i * 3 + j]];
+                       rb_funcall(val, mid, 1, rb_funcall(rb_float_new(f), mid2, 0));
+                       if (j < 2)
+                               rb_str_cat(val, ",", 1);
+               }
+               rb_str_cat(val, "]", 1);
+               if (i < 3)
+                       rb_str_cat(val, ",", 1);
+       }
+       rb_str_cat(val, "]", 1);
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     Transform.translation(vec)
+ *
+ *  Returns a transform corresponding to translation along the given vector. Equivalent
+ *  to <code>Transform[[1,0,0],[0,1,0],[0,0,1],vec]</code>.
+ */
+static VALUE
+s_Transform_Translation(VALUE klass, VALUE vec)
+{
+       Transform *tp;
+       Vector v;
+       VALUE val = s_Transform_Alloc(klass);
+       Data_Get_Struct(val, Transform, tp);
+       VectorFromValue(vec, &v);
+       (*tp)[9] = v.x;
+       (*tp)[10] = v.y;
+       (*tp)[11] = v.z;
+       return val;
+}
+
+/*
+ *  call-seq:
+ *     Transform.rotation(axis, angle, center = [0,0,0])
+ *
+ *  Returns a transform corresponding to the rotation along the given axis and angle. If 
+ *  center is also given, that point will be the center of rotation.
+ */
+static VALUE
+s_Transform_Rotation(int argc, VALUE *argv, VALUE klass)
+{
+       Transform tr;
+       VALUE axis, angle, center;
+       Vector av, cv;
+       double ang;
+       rb_scan_args(argc, argv, "21", &axis, &angle, &center);
+       VectorFromValue(axis, &av);
+       if (NIL_P(center))
+               cv.x = cv.y = cv.z = 0.0;
+       else
+               VectorFromValue(center, &cv);
+       ang = NUM2DBL(rb_Float(angle));
+       if (TransformForRotation(tr, &av, ang, &cv))
+               rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
+       return ValueFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     Transform.reflection(axis, center = [0,0,0])
+ *
+ *  Returns a transform corresponding to the reflection along the given axis. If 
+ *  center is also given, that point will be fixed.
+ */
+static VALUE
+s_Transform_Reflection(int argc, VALUE *argv, VALUE klass)
+{
+       VALUE axis, center;
+       Vector av, cv;
+       Transform tr;
+       rb_scan_args(argc, argv, "11", &axis, &center);
+       VectorFromValue(axis, &av);
+       if (NIL_P(center))
+               cv.x = cv.y = cv.z = 0.0;
+       else
+               VectorFromValue(center, &cv);
+       if (TransformForReflection(tr, &av, &cv))
+               rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
+       return ValueFromTransform(&tr);
+}
+
+/*
+ *  call-seq:
+ *     Transform.inversion(center = [0,0,0])
+ *
+ *  Returns a transform corresponding to the inversion along the given point.
+ */
+static VALUE
+s_Transform_Inversion(int argc, VALUE *argv, VALUE klass)
+{
+       VALUE center;
+       Vector cv;
+       Transform tr;
+       rb_scan_args(argc, argv, "01", &center);
+       if (NIL_P(center))
+               cv.x = cv.y = cv.z = 0.0;
+       else
+               VectorFromValue(center, &cv);
+       TransformForInversion(tr, &cv);
+       return ValueFromTransform(&tr);
+}
+
+#pragma mark ====== IntGroup Class ======
+
+IntGroup *
+IntGroupFromValue(VALUE val)
+{
+       IntGroup *ig;
+       if (!rb_obj_is_kind_of(val, rb_cIntGroup))
+               val = rb_funcall(rb_cIntGroup, rb_intern("new"), 1, val);
+       Data_Get_Struct(val, IntGroup, ig);
+       IntGroupRetain(ig);
+       return ig;
+}
+
+VALUE
+ValueFromIntGroup(IntGroup *ig)
+{
+       if (ig == NULL)
+               return Qnil;
+       IntGroupRetain(ig);
+    return Data_Wrap_Struct(rb_cIntGroup, 0, (void (*)(void *))IntGroupRelease, ig);
+}
+
+void
+IntGroup_RaiseIfError(int err)
+{
+       if (err != 0) {
+               const char *s;
+               switch (err) {
+                       case kIntGroupStatusOutOfMemory: s = "out of memory"; break;
+                       case kIntGroupStatusOutOfRange: s = "out of range"; break;
+                       default: s = ""; break;
+               }
+               rb_raise(rb_eMolbyError, "%s error occurred during IntGroup operation", s);
+       }
+}
+
+/*  Allocator  */
+VALUE
+IntGroup_Alloc(VALUE klass)
+{
+       IntGroup *ig = IntGroupNew();
+    return Data_Wrap_Struct(klass, 0, (void (*)(void *))IntGroupRelease, ig);
+}
+
+/*  Iterator block for initializer  */
+static VALUE
+s_IntGroup_Initialize_i(VALUE val, VALUE ig1)
+{
+       IntGroup_RaiseIfError(IntGroupAdd((IntGroup *)ig1, NUM2INT(val), 1));
+       return Qnil;
+}
+
+static VALUE
+s_IntGroup_Initialize(int argc, VALUE *argv, VALUE self)
+{
+       IntGroup *ig1;
+       Data_Get_Struct(self, IntGroup, ig1);
+       while (argc-- > 0) {
+               VALUE arg = *argv++;
+               int type = TYPE(arg);
+               if (rb_obj_is_kind_of(arg, rb_cIntGroup))
+                       rb_funcall(rb_cIntGroup, rb_intern("merge"), 1, arg);
+               else if (rb_obj_is_kind_of(arg, rb_cRange)) {
+                       int sp, ep;
+                       sp = NUM2INT(rb_funcall(arg, rb_intern("begin"), 0));
+                       ep = NUM2INT(rb_funcall(arg, rb_intern("end"), 0));
+                       if (RTEST(rb_funcall(arg, rb_intern("exclude_end?"), 0)))
+                               ep--;
+                       if (ep >= sp)
+                               IntGroup_RaiseIfError(IntGroupAdd(ig1, sp, ep - sp + 1));
+               } else if (rb_respond_to(arg, rb_intern("each")) && type != T_STRING)
+                       rb_iterate(rb_each, arg, s_IntGroup_Initialize_i, (VALUE)ig1);
+               else
+                       IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(arg), 1));
+       }
+       if (rb_block_given_p()) {
+               IntGroup *ig2 = IntGroupNew();
+               int i, n;
+               for (i = 0; (n = IntGroupGetNthPoint(ig1, i)) >= 0; i++) {
+                       n = NUM2INT(rb_yield(INT2NUM(n)));
+                       if (n >= 0)
+                               IntGroup_RaiseIfError(IntGroupAdd(ig2, n, 1));
+               }
+               IntGroup_RaiseIfError(IntGroupCopy(ig1, ig2));
+       }
+       return Qnil;
+}
+
+static VALUE
+s_IntGroup_Clear(VALUE self)
+{
+       IntGroup *ig;
+       Data_Get_Struct(self, IntGroup, ig);
+       IntGroupClear(ig);
+       return self;
+}
+
+static VALUE
+s_IntGroup_InitializeCopy(VALUE self, VALUE val)
+{
+       IntGroup *ig1, *ig2;
+       Data_Get_Struct(self, IntGroup, ig1);
+       if (!rb_obj_is_kind_of(val, rb_cIntGroup))
+               rb_raise(rb_eMolbyError, "IntGroup instance is expected");
+    Data_Get_Struct(val, IntGroup, ig2);
+       IntGroupCopy(ig1, ig2);
+       return self;
+}
+
+static VALUE
+s_IntGroup_Length(VALUE self)
+{
+       IntGroup *ig;
+       Data_Get_Struct(self, IntGroup, ig);
+       return INT2NUM(IntGroupGetCount(ig));
+}
+
+static VALUE
+s_IntGroup_MemberP(VALUE self, VALUE val)
+{
+       IntGroup *ig;
+       int n = NUM2INT(val);
+       Data_Get_Struct(self, IntGroup, ig);
+       return (IntGroupLookup(ig, n, NULL) ? Qtrue : Qfalse);
+}
+
+static VALUE
+s_IntGroup_ElementAtIndex(VALUE self, VALUE val)
+{
+       IntGroup *ig;
+       int n;
+       int index = NUM2INT(rb_Integer(val));
+       Data_Get_Struct(self, IntGroup, ig);
+       n = IntGroupGetNthPoint(ig, index);
+       return (n >= 0 ? INT2NUM(n) : Qnil);
+}
+
+static VALUE
+s_IntGroup_Each(VALUE self)
+{
+       IntGroup *ig;
+       int i, j, sp, ep;
+       Data_Get_Struct(self, IntGroup, ig);
+       for (i = 0; (sp = IntGroupGetStartPoint(ig, i)) >= 0; i++) {
+               ep = IntGroupGetEndPoint(ig, i);
+               for (j = sp; j < ep; j++) {
+                       rb_yield(INT2NUM(j));
+               }
+       }
+       return self;
+}
+
+static VALUE
+s_IntGroup_Add(VALUE self, VALUE val)
+{
+       IntGroup *ig, *ig2;
+    if (OBJ_FROZEN(self))
+               rb_error_frozen("IntGroup");
+       Data_Get_Struct(self, IntGroup, ig);
+       if (rb_obj_is_kind_of(val, rb_cNumeric)) {
+               int n = NUM2INT(rb_Integer(val));
+               if (n < 0)
+                       rb_raise(rb_eMolbyError, "the integer group can contain only non-negative values");
+               IntGroupAdd(ig, n, 1);
+       } else {
+               ig2 = IntGroupFromValue(val);
+               IntGroupAddIntGroup(ig, ig2);
+               IntGroupRelease(ig2);
+       }
+       return self;
+}
+
+static VALUE
+s_IntGroup_Delete(VALUE self, VALUE val)
+{
+       IntGroup *ig, *ig2;
+    if (OBJ_FROZEN(self))
+               rb_error_frozen("IntGroup");
+       Data_Get_Struct(self, IntGroup, ig);
+       if (rb_obj_is_kind_of(val, rb_cNumeric)) {
+               int n = NUM2INT(rb_Integer(val));
+               if (n >= 0 && IntGroupLookup(ig, n, NULL))
+                       IntGroupRemove(ig, n, 1);
+       } else {
+               ig2 = IntGroupFromValue(val);
+               IntGroupRemoveIntGroup(ig, ig2);
+               IntGroupRelease(ig2);
+       }
+       return self;
+}
+
+static VALUE
+s_IntGroup_Binary(VALUE self, VALUE val, int (*func)(const IntGroup *, const IntGroup *, IntGroup *))
+{
+       IntGroup *ig1, *ig2, *ig3;
+       VALUE retval;
+       Data_Get_Struct(self, IntGroup, ig1);
+       ig2 = IntGroupFromValue(val);
+       retval = IntGroup_Alloc(rb_cIntGroup);
+       Data_Get_Struct(retval, IntGroup, ig3);
+       IntGroup_RaiseIfError(func(ig1, ig2, ig3));
+       IntGroupRelease(ig2);
+       return retval;
+}
+
+static VALUE
+s_IntGroup_Union(VALUE self, VALUE val)
+{
+       return s_IntGroup_Binary(self, val, IntGroupUnion);
+}
+
+static VALUE
+s_IntGroup_Intersection(VALUE self, VALUE val)
+{
+       return s_IntGroup_Binary(self, val, IntGroupIntersect);
+}
+
+static VALUE
+s_IntGroup_Difference(VALUE self, VALUE val)
+{
+       return s_IntGroup_Binary(self, val, IntGroupDifference);
+}
+
+static VALUE
+s_IntGroup_SymDifference(VALUE self, VALUE val)
+{
+       return s_IntGroup_Binary(self, val, IntGroupXor);
+}
+
+static VALUE
+s_IntGroup_Convolute(VALUE self, VALUE val)
+{
+       return s_IntGroup_Binary(self, val, IntGroupConvolute);
+}
+
+static VALUE
+s_IntGroup_Deconvolute(VALUE self, VALUE val)
+{
+       return s_IntGroup_Binary(self, val, IntGroupDeconvolute);
+}
+
+static VALUE
+s_IntGroup_RangeAt(VALUE self, VALUE val)
+{
+       IntGroup *ig;
+       int n = NUM2INT(val);
+       int sp, ep;
+       Data_Get_Struct(self, IntGroup, ig);
+       sp = IntGroupGetStartPoint(ig, n);
+       if (sp < 0)
+               return Qnil;
+       ep = IntGroupGetEndPoint(ig, n) - 1;
+       return rb_funcall(rb_cRange, rb_intern("new"), 2, INT2NUM(sp), INT2NUM(ep));
+}
+
+static VALUE
+s_IntGroup_Merge(VALUE self, VALUE val)
+{
+       IntGroup *ig1, *ig2;
+       int i, sp, interval;
+    if (OBJ_FROZEN(self))
+               rb_error_frozen("IntGroup");
+       Data_Get_Struct(self, IntGroup, ig1);
+       ig2 = IntGroupFromValue(val);
+       for (i = 0; (sp = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
+               interval = IntGroupGetInterval(ig2, i);
+               IntGroup_RaiseIfError(IntGroupAdd(ig1, sp, interval));
+       }
+       IntGroupRelease(ig2);
+       return self;
+}
+
+static VALUE
+s_IntGroup_Subtract(VALUE self, VALUE val)
+{
+       IntGroup *ig1, *ig2;
+       int i, sp, interval;
+    if (OBJ_FROZEN(self))
+               rb_error_frozen("IntGroup");
+       Data_Get_Struct(self, IntGroup, ig1);
+       ig2 = IntGroupFromValue(val);
+       for (i = 0; (sp = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
+               interval = IntGroupGetInterval(ig2, i);
+               IntGroup_RaiseIfError(IntGroupRemove(ig1, sp, interval));
+       }
+       IntGroupRelease(ig2);
+       return self;
+}
+
+static VALUE
+s_IntGroup_Offset(VALUE self, VALUE ofs)
+{
+       IntGroup *ig1, *ig2;
+       int iofs;
+       VALUE val;
+       Data_Get_Struct(self, IntGroup, ig1);
+       ig2 = IntGroupNewFromIntGroup(ig1);
+       if (ig2 == NULL)
+               rb_raise(rb_eMolbyError, "Cannot duplicate IntGroup");
+       iofs = NUM2INT(ofs);
+       if (IntGroupOffset(ig2, iofs) != 0)
+               rb_raise(rb_eMolbyError, "Bad offset %d", iofs);
+       val = ValueFromIntGroup(ig2);
+       IntGroupRelease(ig2);
+       return val;
+}
+
+static VALUE
+s_IntGroup_Create(int argc, VALUE *argv, VALUE klass)
+{
+       VALUE val = IntGroup_Alloc(klass);
+       s_IntGroup_Initialize(argc, argv, val);
+       return val;
+}
+
+static VALUE
+s_IntGroup_Inspect(VALUE self)
+{
+       int i, sp, ep;
+       IntGroup *ig;
+       char buf[64];
+       VALUE klass = CLASS_OF(self);
+       VALUE val = rb_funcall(klass, rb_intern("name"), 0);
+       Data_Get_Struct(self, IntGroup, ig);
+       rb_str_cat(val, "[", 1);
+       for (i = 0; (sp = IntGroupGetStartPoint(ig, i)) >= 0; i++) {
+               if (i > 0)
+                       rb_str_cat(val, ", ", 2);
+               ep = IntGroupGetEndPoint(ig, i);
+               if (ep > sp + 1)
+                       snprintf(buf, sizeof buf, "%d..%d", sp, ep - 1);
+               else
+                       snprintf(buf, sizeof buf, "%d", sp);
+               rb_str_cat(val, buf, strlen(buf));
+       }
+       rb_str_cat(val, "]", 1);
+       return val;
+}
+
+void
+Init_MolbyTypes(void)
+{
+       /*  class Vector3D  */
+       rb_cVector3D = rb_define_class("Vector3D", rb_cObject);
+       rb_define_alloc_func(rb_cVector3D, s_Vector3D_Alloc);
+       rb_define_method(rb_cVector3D, "initialize", s_Vector3D_Initialize, -1);
+       rb_define_method(rb_cVector3D, "size", s_Vector3D_Size, 0);
+       rb_define_method(rb_cVector3D, "[]", s_Vector3D_ElementAtIndex, 1);
+       rb_define_method(rb_cVector3D, "[]=", s_Vector3D_SetElementAtIndex, 2);
+       rb_define_method(rb_cVector3D, "==", s_Vector3D_IsEqual, 1);
+       rb_define_method(rb_cVector3D, "+", s_Vector3D_Add, 1);
+       rb_define_method(rb_cVector3D, "-", s_Vector3D_Subtract, 1);
+       rb_define_method(rb_cVector3D, "*", s_Vector3D_Multiply, 1);
+       rb_define_method(rb_cVector3D, "/", s_Vector3D_Divide, 1);
+       rb_define_method(rb_cVector3D, "dot", s_Vector3D_Dot, 1);
+       rb_define_method(rb_cVector3D, "cross", s_Vector3D_Cross, 1);
+       rb_define_method(rb_cVector3D, "-@", s_Vector3D_UnaryMinus, 0);
+       rb_define_method(rb_cVector3D, "length", s_Vector3D_Length, 0);
+       rb_define_alias(rb_cVector3D, "r", "length"); /* size and length are not synonym!  */
+       rb_define_method(rb_cVector3D, "length2", s_Vector3D_Length2, 0);
+       rb_define_alias(rb_cVector3D, "r2", "length2");
+       rb_define_method(rb_cVector3D, "normalize", s_Vector3D_Normalize, 0);
+       rb_define_method(rb_cVector3D, "to_a", s_Vector3D_ToArray, 0);
+       rb_define_method(rb_cVector3D, "each", s_Vector3D_Each, 0);
+       rb_define_method(rb_cVector3D, "x", s_Vector3D_GetX, 0);
+       rb_define_method(rb_cVector3D, "y", s_Vector3D_GetY, 0);
+       rb_define_method(rb_cVector3D, "z", s_Vector3D_GetZ, 0);
+       rb_define_method(rb_cVector3D, "x=", s_Vector3D_SetX, 1);
+       rb_define_method(rb_cVector3D, "y=", s_Vector3D_SetY, 1);
+       rb_define_method(rb_cVector3D, "z=", s_Vector3D_SetZ, 1);
+       rb_define_method(rb_cVector3D, "inspect", s_Vector3D_Inspect, 0);
+       rb_define_alias(rb_cVector3D, "to_s", "inspect");
+       rb_define_singleton_method(rb_cVector3D, "[]", s_Vector3D_Create, -2);
+
+       /*  class Transform  */
+       rb_cTransform = rb_define_class("Transform", rb_cObject);
+       rb_define_alloc_func(rb_cTransform, s_Transform_Alloc);
+       rb_define_method(rb_cTransform, "initialize", s_Transform_Initialize, -1);
+       rb_define_method(rb_cTransform, "[]", s_Transform_ElementAtIndex, 2);
+       rb_define_method(rb_cTransform, "[]=", s_Transform_SetElementAtIndex, 3);
+       rb_define_method(rb_cTransform, "==", s_Transform_IsEqual, 1);
+       rb_define_method(rb_cTransform, "+", s_Transform_Add, 1);
+       rb_define_method(rb_cTransform, "-", s_Transform_Subtract, 1);
+       rb_define_method(rb_cTransform, "*", s_Transform_Multiply, 1);
+       rb_define_method(rb_cTransform, "/", s_Transform_Divide, 1);
+       rb_define_method(rb_cTransform, "inverse", s_Transform_Inverse, 0);
+       rb_define_method(rb_cTransform, "transpose", s_Transform_Transpose, 0);
+       rb_define_method(rb_cTransform, "determinant", s_Transform_Determinant, 0);
+       rb_define_method(rb_cTransform, "trace", s_Transform_Trace, 0);
+       rb_define_method(rb_cTransform, "column", s_Transform_Column, 1);
+       rb_define_method(rb_cTransform, "eigenvalues", s_Transform_Eigenvalues, 0);
+       rb_define_method(rb_cTransform, "to_a", s_Transform_ToArray, 0);
+       rb_define_method(rb_cTransform, "inspect", s_Transform_Inspect, 0);
+       rb_define_alias(rb_cTransform, "to_s", "inspect");
+       rb_define_singleton_method(rb_cTransform, "diagonal", s_Transform_Diagonal, -1);
+       rb_define_singleton_method(rb_cTransform, "[]", s_Transform_Create, -2);
+       rb_define_singleton_method(rb_cTransform, "from_columns", s_Transform_NewFromColumns, -2);
+       rb_define_singleton_method(rb_cTransform, "from_rows", s_Transform_NewFromRows, -2);
+       rb_define_singleton_method(rb_cTransform, "identity", s_Transform_Identity, 0);
+       rb_define_singleton_method(rb_cTransform, "zero", s_Transform_Zero, 0);
+       rb_define_singleton_method(rb_cTransform, "translation", s_Transform_Translation, 1);
+       rb_define_singleton_method(rb_cTransform, "rotation", s_Transform_Rotation, -1);
+       rb_define_singleton_method(rb_cTransform, "reflection", s_Transform_Reflection, -1);
+       rb_define_singleton_method(rb_cTransform, "inversion", s_Transform_Inversion, -1);
+
+       /*  class IntGroup  */
+       rb_cIntGroup = rb_define_class("IntGroup", rb_cObject);
+       rb_include_module(rb_cIntGroup, rb_mEnumerable);
+       rb_define_alloc_func(rb_cIntGroup, IntGroup_Alloc);
+       rb_define_method(rb_cIntGroup, "clear", s_IntGroup_Clear, 0);
+       rb_define_method(rb_cIntGroup, "initialize", s_IntGroup_Initialize, -1);
+       rb_define_method(rb_cIntGroup, "initialize_copy", s_IntGroup_InitializeCopy, 1);
+       rb_define_method(rb_cIntGroup, "length", s_IntGroup_Length, 0);
+       rb_define_alias(rb_cIntGroup, "size", "length");
+       rb_define_method(rb_cIntGroup, "member?", s_IntGroup_MemberP, 1);
+       rb_define_alias(rb_cIntGroup, "include?", "member?");
+       rb_define_method(rb_cIntGroup, "each", s_IntGroup_Each, 0);
+       rb_define_method(rb_cIntGroup, "[]", s_IntGroup_ElementAtIndex, 1);
+       rb_define_method(rb_cIntGroup, "add", s_IntGroup_Add, 1);
+       rb_define_alias(rb_cIntGroup, "<<", "add");
+       rb_define_method(rb_cIntGroup, "delete", s_IntGroup_Delete, 1);
+       rb_define_method(rb_cIntGroup, "union", s_IntGroup_Union, 1);
+       rb_define_method(rb_cIntGroup, "difference", s_IntGroup_Difference, 1);
+       rb_define_method(rb_cIntGroup, "intersection", s_IntGroup_Intersection, 1);
+       rb_define_method(rb_cIntGroup, "sym_difference", s_IntGroup_SymDifference, 1);
+       rb_define_method(rb_cIntGroup, "convolute", s_IntGroup_Convolute, 1);
+       rb_define_method(rb_cIntGroup, "deconvolute", s_IntGroup_Deconvolute, 1);
+       rb_define_method(rb_cIntGroup, "offset", s_IntGroup_Offset, 1);
+       rb_define_alias(rb_cIntGroup, "+", "union");
+       rb_define_alias(rb_cIntGroup, "|", "union");
+       rb_define_alias(rb_cIntGroup, "-", "difference");
+       rb_define_alias(rb_cIntGroup, "&", "intersection");
+       rb_define_alias(rb_cIntGroup, "^", "sym_difference");
+       rb_define_method(rb_cIntGroup, "range_at", s_IntGroup_RangeAt, 1);
+       rb_define_method(rb_cIntGroup, "merge", s_IntGroup_Merge, -1);
+       rb_define_method(rb_cIntGroup, "subtract", s_IntGroup_Subtract, -1);
+       rb_define_method(rb_cIntGroup, "inspect", s_IntGroup_Inspect, 0);
+       rb_define_alias(rb_cIntGroup, "to_s", "inspect");
+       rb_define_singleton_method(rb_cIntGroup, "[]", s_IntGroup_Create, -1);
+       {
+               VALUE igval = IntGroup_Alloc(rb_cIntGroup);
+               IntGroup *ig;
+               Data_Get_Struct(igval, IntGroup, ig);
+               IntGroupAdd(ig, 0, ATOMS_MAX_NUMBER);
+               rb_define_global_const("All", igval);
+       }
+}
diff --git a/MolLib/Trackball.c b/MolLib/Trackball.c
new file mode 100644 (file)
index 0000000..4a0c79b
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ *  Trackball.c
+ *
+ *  Created by Toshi Nagata on 2005/09/17.
+ *  Copyright 2005-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "Trackball.h"
+#include "Types.h"
+#include <string.h>
+
+#pragma mark ====== Internal functions ======
+
+static void
+multiplyQuat(const float *a, const float *b, float *c)
+{
+    c[0] = a[0] * b[0] - (a[1] * b[1] + a[2] * b[2] + a[3] * b[3]);
+    c[1] = b[0] * a[1] + a[0] * b[1] + (a[2] * b[3] - a[3] * b[2]);
+    c[2] = b[0] * a[2] + a[0] * b[2] + (a[3] * b[1] - a[1] * b[3]);
+    c[3] = b[0] * a[3] + a[0] * b[3] + (a[1] * b[2] - a[2] * b[1]);
+}
+
+static void
+normalizeQuat(float *a)
+{
+    float f;
+    f = 1.0 / sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]);
+    a[0] *= f;
+    a[1] *= f;
+    a[2] *= f;
+    a[3] *= f;
+}
+
+static void
+getRotationToCenter(float x, float y, float radius, float *quat)
+{
+    const float sqrt2 = 1.41421356 / 2;
+    float r, r2;
+    r2 = radius * radius;
+    r = x * x + y * y;
+    if (r >= r2) {
+        /*  Rotation of 90 degree along (y, -x, 0) */
+        r = 1.0 / sqrt(r);
+        x *= r;
+        y *= r;
+        quat[0] = sqrt2;
+        quat[1] = sqrt2 * y;
+        quat[2] = -sqrt2 * x;
+        quat[3] = 0.0;
+    } else {
+        float z, cosAng, sinAng;
+        z = sqrt(r2 - r);      /* z coordinate of the point on the sphere */
+        /*  Rotation of arccos(z/radius) along (y, -x, 0)  */
+        if (r < 1e-6) {
+            quat[0] = 1.0;
+            quat[1] = quat[2] = quat[3] = 0.0;
+        } else {
+            cosAng = sqrt(0.5 * (1.0 + z / radius));
+            sinAng = sqrt(0.5 * (1.0 - z / radius));
+            quat[0] = cosAng;
+            r = 1.0 / sqrt(r);
+            quat[1] = sinAng * y * r;
+            quat[2] = -sinAng * x * r;
+            quat[3] = 0.0;
+        }
+    }
+}
+
+static void
+rotation2Quat(const float *A, float *q)
+{
+    float ang2;  /* The half-angle */
+    float sinAng2; /* sin(half-angle) */
+    
+    /* Convert a GL-style rotation to a quaternion.  The GL rotation looks like this:
+     * {angle, x, y, z}, the corresponding quaternion looks like this:
+     * {{v}, cos(angle/2)}, where {v} is {x, y, z} / sin(angle/2).  */
+    
+    ang2 = A[0] * kDeg2Rad * 0.5;  /* Convert from degrees to radians, get the half-angle. */
+    sinAng2 = sin(ang2);
+    q[0] = A[1] * sinAng2; q[1] = A[2] * sinAng2; q[2] = A[3] * sinAng2;
+    q[3] = cos(ang2);
+}
+
+#pragma mark ====== New/Retain/Release ======
+
+Trackball *
+TrackballNew(void)
+{
+       Trackball *track = (Trackball *)calloc(sizeof(Trackball), 1);
+       MALLOC_CHECK(track, "allocating a trackball record");
+    track->quat[0] = track->tempQuat[0] = 1.0;
+       return track;
+}
+
+void
+TrackballRetain(Trackball *track)
+{
+       if (track != NULL)
+               track->refCount++;
+}
+
+void
+TrackballRelease(Trackball *track)
+{
+       if (track != NULL && --(track->refCount) == 0) {
+               free(track);
+       }
+}
+
+
+/*  Get the current rotation (in GL format)  */
+//- (void)getRotate:(float *)a
+void
+TrackballGetRotate(const Trackball *track, float *a)
+{
+    float w[4], t, f;
+       NULL_CHECK(track, "TrackballGetRotate");        
+    /*  Rotate: multiply the two quaternions and convert it to a GL rotater  */
+    multiplyQuat(track->tempQuat, track->quat, w);
+    normalizeQuat(w);
+    if (fabs(fabs(w[0]) - 1.0) < 1e-7) {
+        /*  Identity rotation  */
+        a[0] = 0.0;
+        a[1] = 1.0;
+        a[2] = a[3] = 0.0;
+    } else {
+        /*  Quaternion: (cos(theta/2), sin(theta/2)*(x,y,z))  */
+        /*  GL rotater: (theta, x, y, z)  */
+        t = acos(w[0]);
+        a[0] = t * 2.0 * kRad2Deg;
+        f = 1.0 / sin(t);
+        a[1] = w[1] * f;
+        a[2] = w[2] * f;
+        a[3] = w[3] * f;
+    }
+}
+
+/*  Get the current translation.  The result values should be multiplied with
+ * the dimension of the model.  */
+void
+TrackballGetTranslate(const Trackball *track, float *a)
+//- (void)getTranslate:(float *)a
+{
+       NULL_CHECK(track, "TrackballGetTranslate");
+    a[0] = track->tempTrans[0] + track->trans[0];
+    a[1] = track->tempTrans[1] + track->trans[1];
+    a[2] = track->tempTrans[2] + track->trans[2];
+/*    f = a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
+    if (f > 1.0) {
+        f = 1.0 / sqrt(f);
+        a[0] *= f;
+        a[1] *= f;
+        a[2] *= f;
+    } */
+}
+
+/*  Get the perspective parameter (fovy and distance)
+ *  Fovy (a[0]) is in degree.
+ *  Distance (a[1]) should be multiplied with the dimension of the model. */
+void
+TrackballGetPerspective(const Trackball *track, float *a)
+//- (void)getPerspective:(float *)a
+{
+    float f;
+       NULL_CHECK(track, "TrackballGetPerspective");
+    /*  Get the current scale factor (-1.0 to 1.0 for x1/10 to x10)  */
+    f = track->tempScale + track->scale;
+    if (f < 0) {
+        /*  Shrink: fovy is fixed at 30 degree, and distance is enlarged  */
+        if (f < -5.0)
+            f = -5.0;
+        a[1] = kCot15Deg * exp(-kLog10 * f);
+        a[0] = 30.0;
+    } else {
+        /*  Expand: fovy is reduced while distance fixed at kCot15Deg  */
+        if (f > 5.0)
+            f = 5.0;
+        a[0] = 30.0 * exp(-kLog10 * f);
+        a[1] = kCot15Deg;
+    }
+}
+
+void
+TrackballReset(Trackball *track)
+{
+       NULL_CHECK(track, "TrackballReset");
+       memset(track->trans, 0, sizeof(track->trans));
+       memset(track->tempTrans, 0, sizeof(track->trans));
+       memset(track->quat, 0, sizeof(track->trans));
+       memset(track->tempQuat, 0, sizeof(track->trans));
+    track->quat[0] = track->tempQuat[0] = 1.0;
+       track->tempScale = 0;
+       track->scale = 0;
+}
+
+void
+TrackballSetScale(Trackball *track, float scale)
+{
+       NULL_CHECK(track, "TrackballSetScale");
+       track->tempScale = 0;
+       track->scale = scale;
+}
+
+void
+TrackballSetTranslate(Trackball *track, const float *a)
+{
+       NULL_CHECK(track, "TrackballSetTranslate");
+       track->tempTrans[0] = track->tempTrans[1] = track->tempTrans[2] = 0;
+       track->trans[0] = a[0];
+       track->trans[1] = a[1];
+       track->trans[2] = a[2];
+}
+
+#pragma mark ====== Mouse operations ======
+
+void
+TrackballStartDragging(Trackball *track, const float *mousePos, TrackballMode mode)
+//- (void)startAt:(NSPoint)pt sender:(NSView *)sender mode:(int)inMode
+{
+       NULL_CHECK(track, "TrackballStartDragging");
+    
+    /*  1: rotate, 2: translate, 3: scale  */
+    track->mode = mode;
+
+    /*  The start point vector  */
+       if (mousePos != NULL) {
+               track->start[0] = mousePos[0];
+               track->start[1] = mousePos[1];
+       } else {
+               track->start[0] = track->start[1] = 0.0;
+       }
+    
+    /*  The rotation from the start point to the center, for trackball operation  */
+    getRotationToCenter(track->start[0], track->start[1], 1.0, track->startQuat);
+}
+
+void
+TrackballSetTemporaryRotation(Trackball *track, const float *q)
+{
+       memmove(track->tempQuat, q, sizeof(float) * 4);
+}
+
+void
+TrackballDrag(Trackball *track, const float *mousePos)
+//- (void)dragTo:(NSPoint)pt sender:(NSView *)sender
+{
+    float rot[4];
+    float w1[4], w2[4];
+    float w;
+       NULL_CHECK(track, "TrackballDrag");
+
+    if (track->mode == kTrackballRotateMode) {
+
+        /*  Rotation from the center to the end point  */
+        getRotationToCenter(mousePos[0], mousePos[1], 1.0, w1);
+        w1[1] = -w1[1];
+        w1[2] = -w1[2];
+        w1[3] = -w1[3];
+        
+        //  Accumulate 'start to center' and 'center to end' in this order
+        multiplyQuat(track->startQuat, w1, track->tempQuat);
+    
+    } else if (track->mode == kTrackballTranslateMode) {
+
+        /*  Make an (x, y, 0) vector in viewport coordinate  */
+        TrackballGetPerspective(track, rot);
+        w = (rot[1] * tan(rot[0] * 0.5 * kDeg2Rad) * 2.0);
+        rot[0] = 0.0;
+        rot[1] = (mousePos[0] - track->start[0]) * w;
+        rot[2] = (mousePos[1] - track->start[1]) * w;
+        rot[3] = 0.0;
+        
+        /*  Rotate with the viewport transform to give world coordinate
+         *  y = q* . x . q, where x = original vector, y = transformed vector,
+         *  q = quaternion, q* = conjugate quaternion  */
+        multiplyQuat(rot, track->quat, w1);
+        w2[0] = track->quat[0];
+        w2[1] = -track->quat[1];
+        w2[2] = -track->quat[2];
+        w2[3] = -track->quat[3];
+        multiplyQuat(w2, w1, rot);
+        track->tempTrans[0] = rot[1];
+        track->tempTrans[1] = rot[2];
+        track->tempTrans[2] = rot[3];
+    } else if (track->mode == kTrackballScaleMode) {
+        w = (mousePos[0] - track->start[0]);
+        if (w > 5.0)
+            w = 5.0;
+        else if (w < -5.0)
+            w = -5.0;
+        track->tempScale = w;
+    }
+}
+
+void
+TrackballEndDragging(Trackball *track, const float *mousePos)
+//- (void)endAt:(NSPoint)pt sender:(NSView *)sender
+{
+    float w[4];
+       NULL_CHECK(track, "TrackballEndDragging");
+       if (mousePos != NULL)
+               TrackballDrag(track, mousePos);
+    multiplyQuat(track->tempQuat, track->quat, w);
+    normalizeQuat(w);
+    track->quat[0] = w[0];
+    track->quat[1] = w[1];
+    track->quat[2] = w[2];
+    track->quat[3] = w[3];
+    track->tempQuat[0] = 1.0;
+    track->tempQuat[1] = track->tempQuat[2] = track->tempQuat[3] = 0.0;
+    track->trans[0] += track->tempTrans[0];
+    track->trans[1] += track->tempTrans[1];
+    track->trans[2] += track->tempTrans[2];
+/*    f = track->trans[0] * track->trans[0] + track->trans[1] * track->trans[1] + track->trans[2] * track->trans[2];
+    if (f > 1.0) {
+        f = 1.0 / sqrt(f);
+        track->trans[0] *= f;
+        track->trans[1] *= f;
+        track->trans[2] *= f;
+    } */
+    track->tempTrans[0] = track->tempTrans[1] = track->tempTrans[2] = 0.0;
+    track->scale += track->tempScale;
+    if (track->scale > 5.0)
+        track->scale = 5.0;
+    else if (track->scale < -5.0)
+        track->scale = -5.0;
+    track->tempScale = 0.0;
+}
+
diff --git a/MolLib/Trackball.h b/MolLib/Trackball.h
new file mode 100644 (file)
index 0000000..176b3d1
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *  Trackball.h
+ *
+ *  Created by Toshi Nagata on 2005/09/17.
+ *  Copyright 2005-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __Trackball_H__
+#define __Trackball_H__
+
+#include <math.h>
+
+#ifndef kRad2Deg
+#define kRad2Deg  (180./3.14159265358979)
+#endif
+#ifndef kDeg2Rad
+#define kDeg2Rad  (3.14159265358979 / 180.)
+#endif
+#ifndef kLog10
+#define kLog10 2.3025851
+#endif
+#ifndef kCot15Deg
+#define kCot15Deg 3.73205075
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+typedef enum TrackballMode {
+       kTrackballRotateMode = 1,
+       kTrackballTranslateMode,
+       kTrackballScaleMode,
+       /*  The following "modes" are not related to the trackball operation,
+           but maybe useful for use in molecular modeling applications  */
+       kTrackballSelectionMode,
+       kTrackballCreateMode,
+       kTrackballEraseMode
+} TrackballMode;
+
+/*  Simulate the trackball operation. The screen coordinates should be converted
+ *  so that the unit circle represents the trackball (i.e. the center of the
+ *  view is (0, 0), and the half of the screen height (or width) is 1.0)  */
+typedef struct Trackball {
+       int refCount;
+    float start[2];     /*  Drag started here  */
+    float startQuat[4];        /*  Rotation from the start point to the center (of the trackball) */
+    TrackballMode   mode;
+
+    float quat[4];             /*  Rotation (in quaternion)  */
+    float tempQuat[4]; /*  Temporary rotation during dragging of trackball  */
+    float trans[3];            /*  Translation  */
+    float tempTrans[3];        /*  Temporary translation  */
+    float scale;               /*  Scale  */
+    float tempScale;   /*  Temporary scale */
+} Trackball;
+
+Trackball *TrackballNew(void);
+void TrackballRetain(Trackball *track);
+void TrackballRelease(Trackball *track);
+
+void TrackballGetRotate(const Trackball *track, float *a);
+void TrackballGetTranslate(const Trackball *track, float *a);
+void TrackballGetPerspective(const Trackball *track, float *a);
+
+void TrackballReset(Trackball *track);
+void TrackballSetScale(Trackball *track, float scale);
+void TrackballSetTranslate(Trackball *track, const float *a);
+
+void TrackballStartDragging(Trackball *track, const float *mousePos, TrackballMode mode);
+void TrackballSetTemporaryRotation(Trackball *track, const float *q);
+void TrackballDrag(Trackball *track, const float *mousePos);
+void TrackballEndDragging(Trackball *track, const float *mousePos);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __Trackball_H__  */
diff --git a/MolLib/Types.c b/MolLib/Types.c
new file mode 100755 (executable)
index 0000000..1cc7b3c
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ *  Types.c
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "Types.h"
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+static void
+defaultPanic(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       abort();
+}
+
+static void
+defaultWarning(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+}
+
+void (*gPanicFunc)(const char *, ...) = defaultPanic;
+void (*gWarningFunc)(const char *, ...) = defaultWarning;
+
+void
+SetPanicFunc(void (*func)(const char *, ...))
+{
+       if (func == NULL)
+               gPanicFunc = defaultPanic;
+       else gPanicFunc = func;
+}
+
+void
+SetWarningFunc(void (*func)(const char *, ...))
+{
+       if (func == NULL)
+               gWarningFunc = defaultWarning;
+       else gWarningFunc = func;
+}
+
+void
+PanicByOutOfMemory(const char *msg)
+{
+       Panic("Out of memory while %s", msg);
+}
+
+void
+PanicByInternalError(const char *msg, const char *file, int line)
+{
+       Panic("Internal Error in %s; File %s line %d", msg, file, line);
+}
+
+
+#pragma mark ==== Vector and Matrix ====
+
+int
+NormalizeVec(Vector *vdst, const Vector *vsrc)
+{
+       double dval;
+       dval = 1.0 / VecLength(*vsrc);
+       VecScale(*vdst, *vsrc, dval);
+       if (!isfinite(vdst->x) || !isfinite(vdst->y) || !isfinite(vdst->z)) {
+               return 1;
+       }
+       return 0;
+}
+
+void
+MatrixRotation(Mat33 dst, const Vector *axis, Double angle)
+{
+       /*  Taken from "Matrix and Quaternion FAQ"
+               http://www.j3d.org/matrix_faq/matrfaq_latest.html  */
+       double rcos = cos((double)angle);
+       double rsin = sin((double)angle);
+       dst[0] =            rcos + axis->x*axis->x*(1-rcos);
+       dst[1] =  axis->z * rsin + axis->y*axis->x*(1-rcos);
+       dst[2] = -axis->y * rsin + axis->z*axis->x*(1-rcos);
+       dst[3] = -axis->z * rsin + axis->x*axis->y*(1-rcos);
+       dst[4] =            rcos + axis->y*axis->y*(1-rcos);
+       dst[5] =  axis->x * rsin + axis->z*axis->y*(1-rcos);
+       dst[6] =  axis->y * rsin + axis->x*axis->z*(1-rcos);
+       dst[7] = -axis->x * rsin + axis->y*axis->z*(1-rcos);
+       dst[8] =            rcos + axis->z*axis->z*(1-rcos);
+}
+
+void
+MatrixVec(Vector *dst, const Mat33 mat, const Vector *src)
+{
+       Vector temp;
+       temp.x = mat[0] * src->x + mat[1] * src->y + mat[2] * src->z;
+       temp.y = mat[3] * src->x + mat[4] * src->y + mat[5] * src->z;
+       temp.z = mat[6] * src->x + mat[7] * src->y + mat[8] * src->z;
+       *dst = temp;
+}
+
+void
+MatrixMul(Mat33 dst, const Mat33 src1, const Mat33 src2)
+{
+       Mat33 temp;
+       temp[0] = src1[0] * src2[0] + src1[1] * src2[3] + src1[2] * src2[6];
+       temp[1] = src1[0] * src2[1] + src1[1] * src2[4] + src1[2] * src2[7];
+       temp[2] = src1[0] * src2[2] + src1[1] * src2[5] + src1[2] * src2[8];
+       temp[3] = src1[3] * src2[0] + src1[4] * src2[3] + src1[5] * src2[6];
+       temp[4] = src1[3] * src2[1] + src1[4] * src2[4] + src1[5] * src2[7];
+       temp[5] = src1[3] * src2[2] + src1[4] * src2[5] + src1[5] * src2[8];
+       temp[6] = src1[6] * src2[0] + src1[7] * src2[3] + src1[8] * src2[6];
+       temp[7] = src1[6] * src2[1] + src1[7] * src2[4] + src1[8] * src2[7];
+       temp[8] = src1[6] * src2[2] + src1[7] * src2[5] + src1[8] * src2[8];
+       memmove(dst, temp, sizeof(Mat33));
+}
+
+void
+MatrixTranspose(Mat33 dst, const Mat33 src)
+{
+       Double w;
+       dst[0] = src[0];
+       dst[4] = src[4];
+       dst[8] = src[8];
+       w = src[3]; dst[3] = src[1]; dst[1] = w;
+       w = src[6]; dst[6] = src[2]; dst[2] = w;
+       w = src[7]; dst[7] = src[5]; dst[5] = w;
+}
+
+Double
+MatrixDeterminant(const Mat33 src)
+{
+       return src[0] * src[4] * src[8] + src[1] * src[5] * src[6] + src[2] * src[3] * src[7]
+               - src[0] * src[5] * src[7] - src[1] * src[3] * src[8] - src[2] * src[4] * src[6];
+}
+
+int
+MatrixInvert(Mat33 dst, const Mat33 src)
+{
+       Mat33 temp;
+       Double d = MatrixDeterminant(src);
+       if (d == 0.0)
+               return 1;
+       d = 1.0 / d;
+       temp[0] = d * ( src[4] * src[8] - src[5] * src[7]);
+       temp[1] = d * (-src[1] * src[8] + src[2] * src[7]);
+       temp[2] = d * ( src[1] * src[5] - src[2] * src[4]);
+       temp[3] = d * (-src[3] * src[8] + src[5] * src[6]);
+       temp[4] = d * ( src[0] * src[8] - src[2] * src[6]);
+       temp[5] = d * (-src[0] * src[5] + src[2] * src[3]);
+       temp[6] = d * ( src[3] * src[7] - src[4] * src[6]);
+       temp[7] = d * (-src[0] * src[7] + src[1] * src[6]);
+       temp[8] = d * ( src[0] * src[4] - src[1] * src[3]);
+       memmove(dst, temp, sizeof(Mat33));
+       return 0;
+}
+
+void
+MatrixScale(Mat33 dst, const Mat33 src, Double factor)
+{
+       dst[0] = src[0] * factor;
+       dst[1] = src[1] * factor;
+       dst[2] = src[2] * factor;
+       dst[3] = src[3] * factor;
+       dst[4] = src[4] * factor;
+       dst[5] = src[5] * factor;
+       dst[6] = src[6] * factor;
+       dst[7] = src[7] * factor;
+       dst[8] = src[8] * factor;
+}
+
+/*  Get the matrix to rotate (1,0,0)->v1, (0,1,0)->v2, (0,0,1)->v3  */
+void
+MatrixGeneralRotation(Mat33 dst, const Vector *v1, const Vector *v2, const Vector *v3)
+{
+       dst[0] = v1->x; dst[1] = v2->x; dst[2] = v3->x;
+       dst[3] = v1->y; dst[4] = v2->y; dst[5] = v3->y;
+       dst[6] = v1->z; dst[7] = v2->z; dst[8] = v3->z;
+}
+
+
+/*  Get the eigenvalue/eigenvector for a real symmetric matrix (3x3)  */
+#ifndef __WXMAC__
+typedef integer        __CLPK_integer;
+typedef logical        __CLPK_logical;
+typedef real           __CLPK_real;
+typedef doublereal     __CLPK_doublereal;
+#endif
+
+int
+MatrixSymDiagonalize(Mat33 mat, Double *out_values, Vector *out_vectors)
+{
+       __CLPK_integer n, lda, lwork, info;
+       __CLPK_real a[9], w[3], work[9];
+       int i, j;
+       for (i = 0; i < 3; i++) {
+               for (j = 0; j < 3; j++) {
+                       a[j * 3 + i] = mat[i * 3 + j];
+               }
+       }
+       n = lda = 3;
+       lwork = 9;
+       /*  For the meanings of the arguments, consult the LAPACK source; 
+               http://www.netlib.org/lapack/single/ssyev.f */
+       ssyev_("V", "U", &n, a, &lda, w, work, &lwork, &info);
+       if (info == 0) {
+               for (i = 0; i < 3; i++) {
+                       out_values[i] = w[i];
+                       out_vectors[i].x = a[i * 3];
+                       out_vectors[i].y = a[i * 3 + 1];
+                       out_vectors[i].z = a[i * 3 + 2];
+               }
+       }
+       return info;
+}
+
+void
+TransformVec(Vector *dst, const Transform tf, const Vector *src)
+{
+       Vector temp;
+       temp.x = tf[0] * src->x + tf[1] * src->y + tf[2] * src->z + tf[9];
+       temp.y = tf[3] * src->x + tf[4] * src->y + tf[5] * src->z + tf[10];
+       temp.z = tf[6] * src->x + tf[7] * src->y + tf[8] * src->z + tf[11];
+       *dst = temp;
+}
+
+void
+TransformMul(Transform dst, const Transform src1, const Transform src2)
+{
+       Transform temp;
+       temp[0] = src1[0] * src2[0] + src1[1] * src2[3] + src1[2] * src2[6];
+       temp[1] = src1[0] * src2[1] + src1[1] * src2[4] + src1[2] * src2[7];
+       temp[2] = src1[0] * src2[2] + src1[1] * src2[5] + src1[2] * src2[8];
+       temp[3] = src1[3] * src2[0] + src1[4] * src2[3] + src1[5] * src2[6];
+       temp[4] = src1[3] * src2[1] + src1[4] * src2[4] + src1[5] * src2[7];
+       temp[5] = src1[3] * src2[2] + src1[4] * src2[5] + src1[5] * src2[8];
+       temp[6] = src1[6] * src2[0] + src1[7] * src2[3] + src1[8] * src2[6];
+       temp[7] = src1[6] * src2[1] + src1[7] * src2[4] + src1[8] * src2[7];
+       temp[8] = src1[6] * src2[2] + src1[7] * src2[5] + src1[8] * src2[8];
+       temp[9] = src1[0] * src2[9] + src1[1] * src2[10] + src1[2] * src2[11] + src1[9];
+       temp[10] = src1[3] * src2[9] + src1[4] * src2[10] + src1[5] * src2[11] + src1[10];
+       temp[11] = src1[6] * src2[9] + src1[7] * src2[10] + src1[8] * src2[11] + src1[11];
+       memmove(dst, temp, sizeof(Transform));
+}
+
+Double
+TransformDeterminant(const Transform src)
+{
+       return MatrixDeterminant(src);
+}
+
+void
+TransformTranspose(Transform dst, const Transform src)
+{
+       Double w;
+       dst[0] = src[0];
+       dst[4] = src[4];
+       dst[8] = src[8];
+       w = src[3]; dst[3] = src[1]; dst[1] = w;
+       w = src[6]; dst[6] = src[2]; dst[2] = w;
+       w = src[7]; dst[7] = src[5]; dst[5] = w;
+       dst[9] = src[9];
+       dst[10] = src[10];
+       dst[11] = src[11];
+}
+
+int
+TransformInvert(Transform dst, const Transform src)
+{
+       Transform temp;
+       int n = MatrixInvert(temp, src);
+       if (n == 0) {
+               temp[9] = -temp[0] * src[9] - temp[1] * src[10] - temp[2] * src[11];
+               temp[10] = -temp[3] * src[9] - temp[4] * src[10] - temp[5] * src[11];
+               temp[11] = -temp[6] * src[9] - temp[7] * src[10] - temp[8] * src[11];
+               memmove(dst, temp, sizeof(Transform));
+               return 0;
+       } else return n;
+}
+
+void
+TransformForInversion(Transform dst, const Vector *center)
+{
+       dst[0] = dst[4] = dst[8] = -1.0;
+       dst[1] = dst[2] = dst[3] = dst[5] = dst[6] = dst[7] = 0.0;
+       dst[9] = center->x * 2.0;
+       dst[10] = center->y * 2.0;
+       dst[11] = center->z * 2.0;
+}
+
+int
+TransformForReflection(Transform dst, const Vector *axis, const Vector *center)
+{
+       Vector av = *axis;
+       Double w;
+       if ((w = VecLength2(av)) < 1e-15)
+               return 1;
+       w = 1.0 / sqrt(w);
+       VecScaleSelf(av, w);
+       /*  r' = r - 2 * VecDot(r-c, a) * a; a should be a unit vector */
+       /*  (x',y',z') = (x,y,z) - 2*((x-cx)*ax + (y-cy)*ay + (z-cz)*az)*(ax,ay,az) */
+       /*  = (1-2*ax*ax, -2*ax*ay, -2*ax*az)x + (-2*ax*ay, 1-2*ay*ay, -2*ay*az)y
+        + (-2*ax*az, -2*ay*az, 1-2*az*az)z + (ax*C, ay*C, az*C); C = 2*(ax*cx+ay*cy+az*cz)  */
+       dst[0] = 1.0 - 2.0 * av.x * av.x;
+       dst[1] = dst[3] = -2.0 * av.x * av.y;
+       dst[2] = dst[6] = -2.0 * av.x * av.z;
+       dst[4] = 1.0 - 2.0 * av.y * av.y;
+       dst[5] = dst[7] = -2.0 * av.y * av.z;
+       dst[8] = 1.0 - 2.0 * av.z * av.z;
+       w = 2.0 * VecDot(av, *center);
+       dst[9] = av.x * w;
+       dst[10] = av.y * w;
+       dst[11] = av.z * w;
+       return 0;
+}
+
+int
+TransformForRotation(Transform dst, const Vector *axis, Double angle, const Vector *center)
+{
+       Transform tf, temp1;
+       Double w;
+       Vector av = *axis;
+       if ((w = VecLength2(av)) < 1e-15)
+               return 1;
+       w = 1.0 / sqrt(w);
+       VecScaleSelf(av, w);
+       memset(tf, 0, sizeof(tf));
+       memset(temp1, 0, sizeof(tf));
+       tf[0] = tf[4] = tf[8] = 1.0;
+       tf[1] = tf[2] = tf[3] = tf[5] = tf[6] = tf[7] = 0.0;
+       tf[9] = -center->x;
+       tf[10] = -center->y;
+       tf[11] = -center->z;
+       MatrixRotation((Double *)temp1, &av, angle);
+       temp1[9] = center->x;
+       temp1[10] = center->y;
+       temp1[11] = center->z;
+       TransformMul(dst, temp1, tf);
+       return 0;
+}
+
+#pragma mark ==== Array ====
+
+/*  Assign a value to an array. An array is represented by two fields; count and base,
+ *  where base is a pointer to an array and count is the number of items.
+ *  The memory block of the array is allocated by 8*item_size. If the index exceeds
+ *  that limit, then a new memory block is allocated.  */
+void *
+AssignArray(void *base, Int *count, int item_size, int idx, const void *value)
+{
+       void **bp = (void **)base;
+       if (*count == 0 || idx / 8 > (*count - 1) / 8) {
+               int new_size = (idx / 8 + 1) * 8;
+               if (*bp == NULL)
+                       *bp = calloc(item_size, new_size);
+               else
+                       *bp = realloc(*bp, new_size * item_size);
+               if (*bp == NULL)
+                       return NULL;
+               memset((char *)*bp + *count * item_size, 0, (new_size - *count) * item_size);
+       }
+       if (idx >= *count)
+               *count = idx + 1;
+       if (value != NULL)
+               memcpy((char *)*bp + idx * item_size, value, item_size);
+       return (char *)*bp + idx * item_size;
+}
+
+/*  Allocate a new array. This works consistently with AssignArray().
+ *  Don't mix calloc()/malloc() with AssignArray(); that causes disasters!
+ *  (free() is OK though).  */
+void *
+NewArray(void *base, Int *count, int item_size, int nitems)
+{
+       void **bp = (void *)base;
+       *bp = NULL;
+       *count = 0;
+       return AssignArray(base, count, item_size, nitems - 1, NULL);
+}
+
+/*  Insert items to an array.  */
+void *
+InsertArray(void *base, Int *count, int item_size, int idx, int nitems, const void *value)
+{
+       void **bp = (void *)base;
+       void *p;
+       int ocount = *count;
+       if (nitems <= 0)
+               return NULL;
+       /*  Allocate storage  */
+       p = AssignArray(base, count, item_size, *count + nitems - 1, NULL);
+       if (p == NULL)
+               return NULL;
+       /*  Move items if necessary  */
+       if (idx < ocount)
+               memmove((char *)*bp + (idx + nitems) * item_size, (char *)*bp + idx * item_size, (ocount - idx) * item_size);
+       /*  Copy items  */
+       if (value != NULL)
+               memmove((char *)*bp + idx * item_size, value, nitems * item_size);
+       else
+               memset((char *)*bp + idx * item_size, 0, nitems * item_size);
+       return (char *)*bp + idx * item_size;
+}
+
+void *
+DeleteArray(void *base, Int *count, int item_size, int idx, int nitems, void *outValue)
+{
+       void **bp = (void *)base;
+       if (nitems <= 0 || idx < 0 || idx >= *count)
+               return NULL;
+       if (nitems > *count - idx)
+               nitems = *count - idx;
+       /*  Copy items  */
+       if (outValue != NULL)
+               memmove(outValue, (char *)*bp + idx * item_size, nitems * item_size);
+       /*  Move items  */
+       if (idx + nitems < *count)
+               memmove((char *)*bp + idx * item_size, (char *)*bp + (idx + nitems) * item_size, (*count - idx - nitems) * item_size);
+       *count -= nitems;
+       if (*count == 0) {
+               free(*bp);
+               *bp = NULL;
+       }
+}
+
+int
+ReadLine(char *buf, int size, FILE *stream, int *lineNumber)
+{
+       int i, c;
+       i = 0;
+       c = 0;
+       while (i < size - 1) {
+               c = getc(stream);
+               if (c == EOF)
+                       break;
+               buf[i++] = c;
+               if (c == '\n')
+                       break;
+               else if (c == '\r') {
+                       c = getc(stream);
+                       if (c != '\n')
+                               ungetc(c, stream);
+                       c = '\n';
+                       break;
+               }
+       }
+       buf[i] = 0;
+       if (c != '\n' && c != EOF) {
+               /*  Skip until the end of line  */
+               while (c != '\n' && c != '\r' && c != EOF)
+                       c = getc(stream);
+               if (c == '\r') {
+                       c = getc(stream);
+                       if (c != '\n')
+                               ungetc(c, stream);
+               }
+       }
+       if (lineNumber != NULL)
+               (*lineNumber)++;
+       return i;
+}
+
+int
+ReadFormat(const char *str, const char *fmt,...)
+{
+       va_list ap;
+       char buf[64];
+       int c, n, count, len;
+       Int *ip;
+       Double *fp;
+       char *sp;
+       va_start(ap, fmt);
+       count = 0;
+       len = strlen(str);
+       while (*fmt != 0 && *str != 0) {
+               if (isspace(*fmt)) {
+                       fmt++;
+                       continue;
+               }
+               c = tolower(*fmt++);
+               if (isdigit(*fmt)) {
+                       n = strtol(fmt, (char **)&fmt, 0);
+                       if (n > 63)
+                               n = 63;
+                       else if (n < 1)
+                               n = 1;
+               } else n = 1;
+               if (len < n)
+                       n = len;
+               strncpy(buf, str, n);
+               buf[n] = 0;
+               str += n;
+               len -= n;
+               switch (c) {
+                       case 'i':
+                               ip = va_arg(ap, Int *);
+                               *ip = atoi(buf);
+                               count++;
+                               break;
+                       case 'f':
+                               fp = va_arg(ap, Double *);
+                               *fp = atof(buf);
+                               count++;
+                               break;
+                       case 's':
+                               sp = va_arg(ap, char *);
+                               sscanf(buf, " %s", sp);
+                               count++;
+                               break;
+                       default:
+                               break;
+               }
+       }
+       va_end(ap);
+       return count;
+}
diff --git a/MolLib/Types.h b/MolLib/Types.h
new file mode 100755 (executable)
index 0000000..8e2de7e
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *  Types.h
+ *
+ *  Created by Toshi Nagata on 06/03/11.
+ *  Copyright 2006-2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __Types_h__
+#define __Types_h__
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#ifdef __WXMAC__
+/*  On Mac OS X, CLAPACK is in Accelerate.framework  */
+#include <vecLib/clapack.h>
+#else
+#include <f2c.h>
+#include <blaswrap.h>
+#include <clapack.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       
+#define STUB extern
+
+#ifndef kRad2Deg
+#define kRad2Deg  (180./3.14159265358979)
+#endif
+#ifndef kDeg2Rad
+#define kDeg2Rad  (3.14159265358979 / 180.)
+#endif
+#ifndef kLog10
+#define kLog10 2.3025851
+#endif
+#ifndef kArcCot15
+#define kArcCot15 3.73205075
+#endif
+
+#undef kBohr2Angstrom
+#define kBohr2Angstrom 0.529177249
+
+#undef kAngstrom2Bohr
+#define kAngstrom2Bohr 1.8897259885
+
+typedef double Double;
+typedef int Int;
+typedef unsigned char Byte;
+typedef unsigned int UInt;
+
+#define kInvalidIndex -99999999  /*  Used for terminating integer array  */
+
+typedef struct Vector { Double x, y, z; } Vector;
+typedef struct Quat { Double x, y, z, w; } Quat;   /*  A quaternion  */
+typedef Double Mat33[9];
+
+/*  Mat33 (rot) and Vector (translation)  */
+/*  Transform[0..8] are row-first, and Transform[9..11] are the fourth column. So that, the
+    matrix elements are in the following order;
+    {a11, a12, a13, a21, a22, a23, a31, a32, a33, a14, a24, a34}
+   Sorry for confusion!  */
+typedef Double Transform[12];
+/*typedef Double Matrix[4][4]; */
+
+#define VecAdd(v3, v1, v2) ((v3).x=(v1).x+(v2).x, (v3).y=(v1).y+(v2).y, (v3).z=(v1).z+(v2).z)
+#define VecInc(v1, v2) ((v1).x+=(v2).x, (v1).y+=(v2).y, (v1).z+=(v2).z)
+#define VecSub(v3, v1, v2) ((v3).x=(v1).x-(v2).x, (v3).y=(v1).y-(v2).y, (v3).z=(v1).z-(v2).z)
+#define VecDec(v1, v2) ((v1).x-=(v2).x, (v1).y-=(v2).y, (v1).z-=(v2).z)
+#define VecScale(v2, v1, r) ((v2).x=(v1).x*(r), (v2).y=(v1).y*(r), (v2).z=(v1).z*(r))
+#define VecScaleSelf(v1, r) ((v1).x*=(r), (v1).y*=(r), (v1).z*=(r))
+#define VecScaleInc(v2, v1, r) ((v2).x+=(v1).x*(r), (v2).y+=(v1).y*(r), (v2).z+=(v1).z*(r))
+#define VecLength2(v1) ((v1).x*(v1).x+(v1).y*(v1).y+(v1).z*(v1).z)
+#define VecLength(v1) (sqrt(VecLength2(v1)))
+#define VecDot(v1, v2) ((v1).x*(v2).x+(v1).y*(v2).y+(v1).z*(v2).z)
+#define VecCross(v3, v1, v2) ((v3).x=(v1).y*(v2).z-(v1).z*(v2).y, (v3).y=(v1).z*(v2).x-(v1).x*(v2).z, (v3).z=(v1).x*(v2).y-(v1).y*(v2).x)
+#define VecZero(v1) ((v1).x=(v1).y=(v1).z=0.0)
+#define VecIndex(vp, i) (((Double *)(vp))[i])
+       
+/*  Vector utility functions  */
+int NormalizeVec(Vector *vdst, const Vector *vsrc);  /*  Returns non-zero when zero-vector */
+
+void MatrixRotation(Mat33 dst, const Vector *axis, Double angle);
+void MatrixVec(Vector *dst, const Mat33 mat, const Vector *src);
+void MatrixMul(Mat33 dst, const Mat33 src1, const Mat33 src2);
+void MatrixTranspose(Mat33 dst, const Mat33 src);
+void MatrixScale(Mat33 dst, const Mat33 src, Double factor);
+Double MatrixDeterminant(const Mat33 src);
+int MatrixInvert(Mat33 dst, const Mat33 src);  /*  Return non-zero when determinant is zero */
+int MatrixSymDiagonalize(Mat33 mat, Double *out_values, Vector *out_vectors);
+void MatrixGeneralRotation(Mat33 dst, const Vector *v1, const Vector *v2, const Vector *v3);
+
+void TransformVec(Vector *dst, const Transform tf, const Vector *src);
+void TransformMul(Transform dst, const Transform src1, const Transform src2);
+Double TransformDeterminant(const Transform src);
+int TransformInvert(Transform dst, const Transform src);  /*  Return non-zero when determinant is zero */
+void TransformTranspose(Transform dst, const Transform src);
+
+void TransformForInversion(Transform dst, const Vector *center);
+int  TransformForReflection(Transform dst, const Vector *axis, const Vector *center);
+int  TransformForRotation(Transform dst, const Vector *axis, Double angle, const Vector *center);
+
+/*  Utility functions  */
+void SetPanicFunc(void (*func)(const char *, ...));
+void SetWarningFunc(void (*func)(const char *, ...));
+extern void (*gPanicFunc)(const char *, ...);
+extern void (*gWarningFunc)(const char *, ...);
+#define Panic (*gPanicFunc)
+#define Warning (*gWarningFunc)
+
+void PanicByOutOfMemory(const char *msg);
+void PanicByInternalError(const char *msg, const char *file, int line);
+
+#define MALLOC_CHECK(p, s) ((p) == NULL ? PanicByOutOfMemory(s) : (void)0)
+#define NULL_CHECK(p, s) ((p) == NULL ? PanicByInternalError((s), __FILE__, __LINE__) : (void)0)
+
+void *AssignArray(void *base, Int *count, int item_size, int idx, const void *value);
+void *NewArray(void *base, Int *count, int item_size, int nitems);
+void *InsertArray(void *base, Int *count, int item_size, int idx, int nitems, const void *value);
+void *DeleteArray(void *base, Int *count, int item_size, int idx, int nitems, void *outValue);
+int ReadLine(char *buf, int size, FILE *stream, int *lineNumber);
+int ReadFormat(const char *str, const char *fmt,...);
+
+#ifdef __cplusplus
+}
+#endif
+               
+#endif /* __Types_h__ */
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..0a9a816
--- /dev/null
+++ b/README
@@ -0,0 +1,184 @@
+========================================================
+
+                     Molby
+
+    An Interactive Molecular Modeling Software
+        with Integrated Ruby Interpreter
+  
+          Version 0.5.0 build 20100121
+
+                  Toshi Nagata
+
+========================================================
+
+1. What is it?
+
+Molby is an application to visualize and build molecular models interactively through 3D display. It provides the following capability:
+
+- Viewing molecular models in either ball-and-stick or "line" display.
+- Interactive modification of molecular models by mouse operation.
+- Viewing and editing atomic coordinates and other parameters in numeric tables.
+- Importing and exporting structure/coordinate data for other program packages such as Gaussian, GAMESS, NAMD and SHELX.
+- Embedded Ruby interpreter for automated processing of molecular models.
+
+2. Supported Platforms
+
+Molby runs on Mac OS X (10.4 and later) and Microsoft Windows (XP and later). Executable binaries are provided for both platforms (Molby.dmg for Mac, SetupMolby.exe for Windows).
+
+3. Copyright and License
+
+Molby is a copyrighted product of Toshi Nagata.
+
+       Copyright (C) 2009 Toshi Nagata
+
+Molby includes (more technically: is statically linked to) the following softwares, which are copyrighted products as described below:
+
+- wxWidgets 2.8.9: Copyright (C) 1992-2008 Julian Smart, Robert Roebling, Vadim Zeitlin and other members of the wxWidgets team. Portions (c) 1996 Artificial Intelligence Applications Institute
+- Ruby 1.8.7: Copyright (C) 1993-2009 Yukihiro Matsumoto
+- CLAPACK: Copyright (c) 1992-2008 The University of Tennessee.
+
+Molby is distributed under the GNU General Public License (version 2). See the file COPYING for more details.
+
+4. How to Use
+
+The document in HTML format is available. Try the tutorial to get familiar with Molby.
+
+5. How to Build
+
+The information in this section is only for those who want to build Molby from the source. If you use the binary distribution, you do not need to read this section.
+
+Building Molby is somewhat complicated because you need static libraries of wxWidgets and Ruby (and CLAPACK in Windows). The procedures are described below for each platform.
+
+(1) Mac OS X
+
+(i) Xcode
+
+Install Xcode, if you have not done so yet. The Xcode project included in the source distribution is for Xcode 3.0 and later, thus you need Mac OS 10.5. It may work with Xcode 2.5 on Mac OS 10.4, but this have not been tested.
+
+(ii) wxWidgets
+
+Get wxMac-2.8.9.tar.gz from the wxWidgets page in sourceforge.net (http://sourceforge.net/projects/wxwindows/files/). Newer versions may work, but they are not tested.
+
+Unpack wxMac-2.8.9.tar.gz. Move the resulting directory wxMac-2.8.9 to $HOME/Development, and rename it to wxMac (or make a symbolic link). The console commands will be as follows:
+$ mkdir -p $HOME/Development
+$ cd $HOME/Development
+$ mv somewhere/wxMac-2.8.9.tar.gz ./
+$ tar xvzf wxMac-2.8.9.tar.gz
+$ ln -s wxMac-2.8.9 wxMac
+
+If you want to place wxMac in another place (and/or another name) than $HOME/Development, you will need to change the build settings in Xcode.
+
+Build wxMac. The commands will be as follows. Take care of the configure options.
+$ cd $HOME/Development/wxMac
+$ mkdir osx-build
+$ cd osx-build
+$ ../configure --with-macos-sdk=/Developer/SDKs/MacOSX10.4u.sdk --with-macosx-version-min=10.4 --enable-universal_binary --disable-shared --with-opengl --enable-unicode --with-libjpeg=builtin --with-libpng=builtin --with-regex=builtin --with-libtiff=builtin --with-zlib=builtin --with-expat=builtin
+$ make
+(You do not need to do "sudo make install".)
+
+(iii) Ruby
+
+Get ruby-1.8.7-p160.tar.gz from the Molby source distribution site. Unpack it, and move the resulting directory ruby-1.8.7-p160 to $HOME/Development. Rename it to ruby-1.8.7-static (or make a symbolic link).
+$ cd $HOME/Development
+$ mv somewhere/ruby-1.8.7-p160.tar.gz ./
+$ tar xvzf ruby-1.8.7-p160.tar.gz
+$ ln -s ruby-1.8.7-p160 ruby-1.8.7-static
+
+If you want to place ruby-1.8.7-static in another place (and/or another name) than $HOME/Development, you will need to change the build settings in Xcode.
+
+Patch the Ruby source.
+$ cd $HOME/Development/ruby-1.8.7-static
+$ patch --backup -p1 < $MOLBY/ruby-1.8.7-p160-tn.patch
+($MOLBY is the location of the Molby source.)
+
+Build Ruby with --disable-shared option.
+$ CFLAGS='-isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -arch i386 -arch ppc -O2' ./configure --disable-shared --disable-thread
+$ make
+
+(iv) Build on Xcode
+
+Open xcode-build/Molby.xcodeproj, and build. Note that there are two configurations, Debug and Release. In the "Release" build, the Ruby scripts update_version.rb and Documents/makedoc.rb are executed before compilation; update_version.rb modifies the version strings in the source files (by looking up the file Version), and makedoc.rb creates the HTML docments in the doc directory.
+
+(2) Windows
+
+The Windows binary is built by using MinGW/MSYS. If you use VC++ or Cygwin, you need to help yourself. I use Windows only occasionally, so there may be errors and misunderstandings in the following descriptions. Feedback is welcome!
+
+(i) MinGW and MSYS
+
+Install MinGW and MSYS. The default configuration should be sufficient.
+
+(ii) wxWidgets
+
+Get wxMSW-2.8.9.tar.gz from the wxWidgets page in sourceforge.net (http://sourceforge.net/projects/wxwindows/files/). Newer versions may work, but they are not tested.
+
+Unpack wxMSW-2.8.9.tar.gz. Move the resulting directory wxMSW-2.8.9 to $HOME (the home directory of the MinGW The console commands will be as follows:
+$ cd $HOME
+$ mv somewhere/wxMSW-2.8.9.tar.gz ./
+$ tar xvzf wxMSW-2.8.9.tar.gz
+
+Build wxMSW. The commands will be as follows. Take care of the configure options.
+$ cd $HOME/wxMSW-2.8.9
+$ mkdir msw-build
+$ cd msw-build
+$ ../configure --with-msw --with-opengl --disable-shared --enable-unicode --with-libjpeg-builtin --with-zlib=builtin
+$ make
+(You do not need to do "sudo make install".)
+
+(iii) Ruby
+
+Get ruby-1.8.7-p160.tar.gz from the Molby source distribution site. Unpack it, and move the resulting directory ruby-1.8.7-p160 to $HOME. Rename it to ruby-1.8.7-static.
+$ cd $HOME
+$ mv somewhere/ruby-1.8.7-p160.tar.gz ./
+$ tar xvzf ruby-1.8.7-p160.tar.gz
+$ mv ruby-1.8.7-p160 ruby-1.8.7-static
+
+Patch the Ruby source.
+$ cd $HOME/ruby-1.8.7-static
+$ patch --backup -p1 < $MOLBY/ruby-1.8.7-p160-tn.patch
+($MOLBY is the location of the Molby source.)
+
+Build Ruby with --disable-shared option.
+$ ./configure --disable-shared --disable-thread
+$ make
+
+(iv) CLAPACK
+
+Get clapack-3.1.1.1.tar.gz from the CLAPACK official site (http://www.netlib.org/clapack/) and unpack it. (clapack-3.2.1 may cause trouble because it uses symbolic links.)
+$ cd $HOME
+$ mv somewhere/clapack-3.1.1.1.tgz ./
+$ tar xvzf clapack-3.1.1.1.tgz
+
+Patch the clapack source.
+$ patch -p0 <$MOLBY/clapack-3.1.1.1-mingw.patch
+
+Build CLAPACK.
+$ cd CLAPACK-3.1.1.1
+$ make
+
+Modify libf2c.a, so that the 'main' entry does not interfere the main program.
+$ cd F2CLIBS
+$ cp libf2c.a libf2c_nomain.a
+$ ar d libf2c_nomain.a main.o
+$ cd ..
+
+Copy the library and header files to /lib/clapack.
+$ mkdir /lib/clapack
+$ cp F2CLIBS/libf2c_nomain.a /lib/clapack
+$ cp blasMinGW.a /lib/clapack/libblas.a
+$ cp lapackMinGW.a /lib/clapack/liblapack.a
+$ cp INCLUDE/f2c.h INCLUDE/blaswrap.h INCLUDE/clapack.h /lib/clapack
+
+(v) Inno Setup 5
+
+Install Inno Setup 5 (http://www.jrsoftware.org/isinfo.php). This is necessary to create the Molby Installer.
+
+(vi) Build
+
+Now you can start building Molby! Go to the project directory and do make.
+$ cd somewhere/Molby/msw-build
+$ make
+$ make setup  #  To make the installer
+
+
+2010.1.21.
+Toshi Nagata
diff --git a/Scripts/6311Gdp.txt b/Scripts/6311Gdp.txt
new file mode 100644 (file)
index 0000000..82216a7
--- /dev/null
@@ -0,0 +1,96 @@
+!  6-311G(d,p)
+!  Generated by GAMESS (Jan122009R1)
+!  $BASIS  GBASIS=N311 NDFUNC=1 NPFUNC=1 NGAUSS=6 $END
+
+ $DATA  
+H 
+   N311    6
+   P       1
+     1              0.7500000000  1.00000000
+        
+HE
+   N311    6
+   P       1
+     1              0.7500000000  1.00000000
+        
+LI
+   N311    6
+   D       1
+     1              0.2000000000  1.00000000
+        
+BE
+   N311    6
+   D       1
+     1              0.2550000000  1.00000000
+        
+B 
+   N311    6
+   D       1
+     1              0.4010000000  1.00000000
+        
+C 
+   N311    6
+   D       1
+     1              0.6260000000  1.00000000
+        
+N 
+   N311    6
+   D       1
+     1              0.9130000000  1.00000000
+        
+O 
+   N311    6
+   D       1
+     1              1.2920000000  1.00000000
+        
+F 
+   N311    6
+   D       1
+     1              1.7500000000  1.00000000
+        
+NE
+   N311    6
+   D       1
+     1              2.3040000000  1.00000000
+        
+NA
+   MC      6
+   D       1
+     1              0.1750000000  1.00000000
+        
+MG
+   MC      6
+   D       1
+     1              0.1750000000  1.00000000
+        
+AL
+   MC      6
+   D       1
+     1              0.3250000000  1.00000000
+        
+SI
+   MC      6
+   D       1
+     1              0.3950000000  1.00000000
+        
+P 
+   MC      6
+   D       1
+     1              0.5500000000  1.00000000
+        
+S 
+   MC      6
+   D       1
+     1              0.6500000000  1.00000000
+        
+CL
+   MC      6
+   D       1
+     1              0.7500000000  1.00000000
+        
+AR
+   MC      6
+   D       1
+     1              0.8500000000  1.00000000
+        
+ $END      
diff --git a/Scripts/631Gd.txt b/Scripts/631Gd.txt
new file mode 100644 (file)
index 0000000..d9fdf9c
--- /dev/null
@@ -0,0 +1,182 @@
+!  6-31G(d)
+!  Generated by GAMESS (Jan122009R1)
+!  $BASIS  GBASIS=N31 NDFUNC=1 NGAUSS=6           $END
+
+ $DATA  
+H 
+   N31     6
+        
+HE
+   N31     6
+        
+LI
+   N31     6
+   D       1
+     1              0.2000000000  1.00000000
+        
+BE
+   N31     6
+   D       1
+     1              0.4000000000  1.00000000
+        
+B 
+   N31     6
+   D       1
+     1              0.6000000000  1.00000000
+        
+C 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+N 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+O 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+F 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+NE
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+NA
+   N31     6
+   D       1
+     1              0.1750000000  1.00000000
+        
+MG
+   N31     6
+   D       1
+     1              0.1750000000  1.00000000
+        
+AL
+   N31     6
+   D       1
+     1              0.3250000000  1.00000000
+        
+SI
+   N31     6
+   D       1
+     1              0.3950000000  1.00000000
+        
+P 
+   N31     6
+   D       1
+     1              0.5500000000  1.00000000
+        
+S 
+   N31     6
+   D       1
+     1              0.6500000000  1.00000000
+        
+CL
+   N31     6
+   D       1
+     1              0.7500000000  1.00000000
+        
+AR
+   N31     6
+   D       1
+     1              0.8500000000  1.00000000
+        
+K 
+   N31     6
+   D       1
+     1              0.0448500000  1.00000000
+        
+CA
+   N31     6
+   D       1
+     1              0.0502000000  1.00000000
+        
+SC
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+TI
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+V 
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+CR
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+MN
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+FE
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+CO
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+NI
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+CU
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+ZN
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+GA
+   N31     6
+   D       1
+     1              0.2289000000  1.00000000
+        
+GE
+   N31     6
+   D       1
+     1              0.2772000000  1.00000000
+        
+AS
+   N31     6
+   D       1
+     1              0.3277000000  1.00000000
+        
+SE
+   N31     6
+   D       1
+     1              0.3810000000  1.00000000
+        
+BR
+   N31     6
+   D       1
+     1              0.4366000000  1.00000000
+        
+KR
+   N31     6
+   D       1
+     1              0.4948000000  1.00000000
+        
+ $END      
diff --git a/Scripts/631Gdp.txt b/Scripts/631Gdp.txt
new file mode 100644 (file)
index 0000000..378ea05
--- /dev/null
@@ -0,0 +1,186 @@
+!  6-31G(d,p)
+!  Generated by GAMESS (Jan122009R1)
+!  $BASIS  GBASIS=N31 NDFUNC=1 NPFUNC=1 NGAUSS=6  $END
+
+ $DATA  
+H 
+   N31     6
+   P       1
+     1              1.1000000000  1.00000000
+        
+HE
+   N31     6
+   P       1
+     1              1.1000000000  1.00000000
+        
+LI
+   N31     6
+   D       1
+     1              0.2000000000  1.00000000
+        
+BE
+   N31     6
+   D       1
+     1              0.4000000000  1.00000000
+        
+B 
+   N31     6
+   D       1
+     1              0.6000000000  1.00000000
+        
+C 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+N 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+O 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+F 
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+NE
+   N31     6
+   D       1
+     1              0.8000000000  1.00000000
+        
+NA
+   N31     6
+   D       1
+     1              0.1750000000  1.00000000
+        
+MG
+   N31     6
+   D       1
+     1              0.1750000000  1.00000000
+        
+AL
+   N31     6
+   D       1
+     1              0.3250000000  1.00000000
+        
+SI
+   N31     6
+   D       1
+     1              0.3950000000  1.00000000
+        
+P 
+   N31     6
+   D       1
+     1              0.5500000000  1.00000000
+        
+S 
+   N31     6
+   D       1
+     1              0.6500000000  1.00000000
+        
+CL
+   N31     6
+   D       1
+     1              0.7500000000  1.00000000
+        
+AR
+   N31     6
+   D       1
+     1              0.8500000000  1.00000000
+        
+K 
+   N31     6
+   D       1
+     1              0.0448500000  1.00000000
+        
+CA
+   N31     6
+   D       1
+     1              0.0502000000  1.00000000
+        
+SC
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+TI
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+V 
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+CR
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+MN
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+FE
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+CO
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+NI
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+CU
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+ZN
+   N31     6
+   F       1
+     1              0.8000000000  1.00000000
+        
+GA
+   N31     6
+   D       1
+     1              0.2289000000  1.00000000
+        
+GE
+   N31     6
+   D       1
+     1              0.2772000000  1.00000000
+        
+AS
+   N31     6
+   D       1
+     1              0.3277000000  1.00000000
+        
+SE
+   N31     6
+   D       1
+     1              0.3810000000  1.00000000
+        
+BR
+   N31     6
+   D       1
+     1              0.4366000000  1.00000000
+        
+KR
+   N31     6
+   D       1
+     1              0.4948000000  1.00000000
+        
+ $END      
diff --git a/Scripts/LanL2DZ.txt b/Scripts/LanL2DZ.txt
new file mode 100644 (file)
index 0000000..9f3a6dd
--- /dev/null
@@ -0,0 +1,2974 @@
+ v1.2.2 
+
+!  LANL2DZ ECP  EMSL  Basis Set Exchange Library   11/21/09 7:40 PM
+! Elements                             References
+! --------                             ----------
+! H  - Ne: T. H. Dunning Jr. and P. J. Hay, in Methods of Electronic Structure
+!          Theory, Vol. 2, H. F. Schaefer III, ed., PLENUM PRESS (1977)
+! Na - Hg: P. J. Hay and W. R. Wadt, J. Chem. Phys. 82, 270 (1985).
+!          P. J. Hay and W. R. Wadt, J. Chem. Phys. 82, 284 (1985).
+!          P. J. Hay and W. R. Wadt, J. Chem. Phys. 82, 299 (1985).
+!   
+
+
+$DATA
+HYDROGEN
+S   3
+  1     19.2384000              0.0328280        
+  2      2.8987000              0.2312040        
+  3      0.6535000              0.8172260        
+S   1
+  1      0.1776000              1.0000000        
+
+LITHIUM
+S   7
+  1    921.3000000              0.0013670        
+  2    138.7000000              0.0104250        
+  3     31.9400000              0.0498590        
+  4      9.3530000              0.1607010        
+  5      3.1580000              0.3446040        
+  6      1.1570000              0.4251970        
+  7      0.4446000              0.1694680        
+S   2
+  1      0.4446000             -0.2223110        
+  2      0.0766600              1.1164770        
+S   1
+  1      0.0286400              1.0000000        
+P   3
+  1      1.4880000              0.0387700        
+  2      0.2667000              0.2362570        
+  3      0.0720100              0.8304480        
+P   1
+  1      0.0237000              1.0000000        
+
+BERYLLIUM
+S   7
+  1   1741.0000000              0.0013050        
+  2    262.1000000              0.0099550        
+  3     60.3300000              0.0480310        
+  4     17.6200000              0.1585770        
+  5      5.9330000              0.3513250        
+  6      2.1850000              0.4270060        
+  7      0.8590000              0.1604900        
+S   2
+  1      2.1850000             -0.1852940        
+  2      0.1806000              1.0570140        
+S   1
+  1      0.0583500              1.0000000        
+P   4
+  1      6.7100000              0.0163780        
+  2      1.4420000              0.0915530        
+  3      0.4103000              0.3414690        
+  4      0.1397000              0.6854280        
+P   1
+  1      0.0492200              1.0000000        
+
+BORON
+S   7
+  1   2788.0000000              0.0012880        
+  2    419.0000000              0.0098350        
+  3     96.4700000              0.0476480        
+  4     28.0700000              0.1600690        
+  5      9.3760000              0.3628940        
+  6      3.4060000              0.4335820        
+  7      1.3060000              0.1400820        
+S   2
+  1      3.4060000             -0.1793300        
+  2      0.3245000              1.0625940        
+S   1
+  1      0.1022000              1.0000000        
+P   4
+  1     11.3400000              0.0179880        
+  2      2.4360000              0.1103430        
+  3      0.6836000              0.3830720        
+  4      0.2134000              0.6478950        
+P   1
+  1      0.0701100              1.0000000        
+
+CARBON
+S   7
+  1   4233.0000000              0.0012200        
+  2    634.9000000              0.0093420        
+  3    146.1000000              0.0454520        
+  4     42.5000000              0.1546570        
+  5     14.1900000              0.3588660        
+  6      5.1480000              0.4386320        
+  7      1.9670000              0.1459180        
+S   2
+  1      5.1480000             -0.1683670        
+  2      0.4962000              1.0600910        
+S   1
+  1      0.1533000              1.0000000        
+P   4
+  1     18.1600000              0.0185390        
+  2      3.9860000              0.1154360        
+  3      1.1430000              0.3861880        
+  4      0.3594000              0.6401140        
+P   1
+  1      0.1146000              1.0000000        
+
+NITROGEN
+S   7
+  1   5909.0000000              0.0011900        
+  2    887.5000000              0.0090990        
+  3    204.7000000              0.0441450        
+  4     59.8400000              0.1504640        
+  5     20.0000000              0.3567410        
+  6      7.1930000              0.4465330        
+  7      2.6860000              0.1456030        
+S   2
+  1      7.1930000             -0.1604050        
+  2      0.7000000              1.0582150        
+S   1
+  1      0.2133000              1.0000000        
+P   4
+  1     26.7900000              0.0182540        
+  2      5.9560000              0.1164610        
+  3      1.7070000              0.3901780        
+  4      0.5314000              0.6371020        
+P   1
+  1      0.1654000              1.0000000        
+
+OXYGEN
+S   7
+  1   7817.0000000              0.0011760        
+  2   1176.0000000              0.0089680        
+  3    273.2000000              0.0428680        
+  4     81.1700000              0.1439300        
+  5     27.1800000              0.3556300        
+  6      9.5320000              0.4612480        
+  7      3.4140000              0.1402060        
+S   2
+  1      9.5320000             -0.1541530        
+  2      0.9398000              1.0569140        
+S   1
+  1      0.2846000              1.0000000        
+P   4
+  1     35.1800000              0.0195800        
+  2      7.9040000              0.1242000        
+  3      2.3050000              0.3947140        
+  4      0.7171000              0.6273760        
+P   1
+  1      0.2137000              1.0000000        
+
+FLUORINE
+S   7
+  1   9995.0000000              0.0011600        
+  2   1506.0000000              0.0088700        
+  3    350.3000000              0.0423800        
+  4    104.1000000              0.1429290        
+  5     34.8400000              0.3553720        
+  6     12.2200000              0.4620850        
+  7      4.3690000              0.1408480        
+S   2
+  1     12.2200000             -0.1484520        
+  2      1.2080000              1.0552700        
+S   1
+  1      0.3634000              1.0000000        
+P   4
+  1     44.3600000              0.0208760        
+  2     10.0800000              0.1301070        
+  3      2.9960000              0.3961660        
+  4      0.9383000              0.6204040        
+P   1
+  1      0.2733000              1.0000000        
+
+NEON
+S   7
+  1  12100.0000000              0.0012000        
+  2   1821.0000000              0.0090920        
+  3    432.8000000              0.0413050        
+  4    132.5000000              0.1378670        
+  5     43.7700000              0.3624330        
+  6     14.9100000              0.4722470        
+  7      5.1270000              0.1300350        
+S   2
+  1     14.9100000             -0.1408100        
+  2      1.4910000              1.0533270        
+S   1
+  1      0.4468000              1.0000000        
+P   4
+  1     56.4500000              0.0208750        
+  2     12.9200000              0.1300320        
+  3      3.8650000              0.3956790        
+  4      1.2030000              0.6214500        
+P   1
+  1      0.3444000              1.0000000        
+
+SODIUM
+S   2
+  1      0.4972000             -0.2753574        
+  2      0.0560000              1.0989969        
+S   1
+  1      0.0221000              1.0000000        
+P   2
+  1      0.6697000             -0.0683845        
+  2      0.0636000              1.0140550        
+P   1
+  1      0.0204000              1.0000000        
+
+MAGNESIUM
+S   2
+  1      0.7250000             -0.4058454        
+  2      0.1112000              1.1688704        
+S   1
+  1      0.0404000              1.0000000        
+P   2
+  1      1.2400000             -0.0749753        
+  2      0.1346000              1.0178183        
+P   1
+  1      0.0422000              1.0000000        
+
+ALUMINUM
+S   2
+  1      0.9615000             -0.5021546        
+  2      0.1819000              1.2342547        
+S   1
+  1      0.0657000              1.0000000        
+P   2
+  1      1.9280000             -0.0712584        
+  2      0.2013000              1.0162966        
+P   1
+  1      0.0580000              1.0000000        
+
+SILICON
+S   2
+  1      1.2220000             -0.5707339        
+  2      0.2595000              1.2823826        
+S   1
+  1      0.0931000              1.0000000        
+P   2
+  1      2.5800000             -0.0777250        
+  2      0.2984000              1.0197870        
+P   1
+  1      0.0885000              1.0000000        
+
+PHOSPHOROUS
+S   2
+  1      1.5160000             -0.5862089        
+  2      0.3369000              1.2994376        
+S   1
+  1      0.1211000              1.0000000        
+P   2
+  1      3.7050000             -0.0691472        
+  2      0.3934000              1.0161988        
+P   1
+  1      0.1190000              1.0000000        
+
+SULFUR
+S   2
+  1      1.8500000             -0.5324335        
+  2      0.4035000              1.2763801        
+S   1
+  1      0.1438000              1.0000000        
+P   2
+  1      4.9450000             -0.0608116        
+  2      0.4870000              1.0132686        
+P   1
+  1      0.1379000              1.0000000        
+
+CHLORINE
+S   2
+  1      2.2310000             -0.4900589        
+  2      0.4720000              1.2542684        
+S   1
+  1      0.1631000              1.0000000        
+P   2
+  1      6.2960000             -0.0635641        
+  2      0.6333000              1.0141355        
+P   1
+  1      0.1819000              1.0000000        
+
+ARGON
+S   2
+  1      2.6130000             -0.5110463        
+  2      0.5736000              1.2701236        
+S   1
+  1      0.2014000              1.0000000        
+P   2
+  1      7.8600000             -0.0555167        
+  2      0.7387000              1.0115982        
+P   1
+  1      0.2081000              1.0000000        
+
+POTASSIUM
+S   3
+  1      3.0720000             -0.3083067        
+  2      0.6752000              0.7820711        
+  3      0.2545000              0.4142883        
+S   4
+  1      3.0720000              0.1894087        
+  2      0.6752000             -0.5472332        
+  3      0.2545000             -0.7576458        
+  4      0.0529000              0.9952157        
+S   1
+  1      0.0209000              1.0000000        
+P   3
+  1      8.2330000             -0.0419919        
+  2      0.9526000              0.5776819        
+  3      0.3013000              0.5234611        
+P   1
+  1      0.0376000              1.0000000        
+P   1
+  1      0.0140000              1.0000000        
+
+CALCIUM
+S   3
+  1      3.4840000             -0.3337370        
+  2      0.8551000              0.7589831        
+  3      0.3192000              0.4606109        
+S   4
+  1      3.4840000              0.2606030        
+  2      0.8551000             -0.6839150        
+  3      0.3192000             -1.1987807        
+  4      0.1447000              1.0251221        
+S   1
+  1      0.0350000              1.0000000        
+P   3
+  1      9.4200000             -0.0436943        
+  2      1.1500000              0.6034747        
+  3      0.3688000              0.4961107        
+P   1
+  1      0.0705000              1.0000000        
+P   1
+  1      0.0263000              1.0000000        
+
+SCANDIUM
+S   3
+  1      3.7170000             -0.3926292        
+  2      1.0970000              0.7147339        
+  3      0.4164000              0.5509400        
+S   4
+  1      3.7170000              0.2197446        
+  2      1.0970000             -0.4546067        
+  3      0.4164000             -0.6938181        
+  4      0.0761000              1.1260172        
+S   1
+  1      0.0284000              1.0000000        
+P   3
+  1     10.4000000             -0.0492615        
+  2      1.3110000              0.6071965        
+  3      0.4266000              0.4917582        
+P   1
+  1      0.0470000              1.0000000        
+P   1
+  1      0.0140000              1.0000000        
+D   4
+  1     15.1300000              0.0379291        
+  2      4.2050000              0.1738360        
+  3      1.3030000              0.4268057        
+  4      0.3680000              0.6238539        
+D   1
+  1      0.0812000              1.0000000        
+
+TITANIUM
+S   3
+  1      4.3720000             -0.3637098        
+  2      1.0980000              0.8184533        
+  3      0.4178000              0.4184526        
+S   4
+  1      4.3720000              0.2049027        
+  2      1.0980000             -0.5575413        
+  3      0.4178000             -0.5893652        
+  4      0.0872000              1.1451661        
+S   1
+  1      0.0314000              1.0000000        
+P   3
+  1     12.5200000             -0.0456908        
+  2      1.4910000              0.6203313        
+  3      0.4859000              0.4765329        
+P   1
+  1      0.0530000              1.0000000        
+P   1
+  1      0.0160000              1.0000000        
+D   4
+  1     20.2100000              0.0341682        
+  2      5.4950000              0.1710006        
+  3      1.6990000              0.4405849        
+  4      0.4840000              0.6114246        
+D   1
+  1      0.1157000              1.0000000        
+
+VANADIUM
+S   3
+  1      4.5900000             -0.4277302        
+  2      1.4930000              0.7069691        
+  3      0.5570000              0.5895813        
+S   4
+  1      4.5900000              0.2501081        
+  2      1.4930000             -0.4697777        
+  3      0.5570000             -0.7129415        
+  4      0.0975000              1.1006569        
+S   1
+  1      0.0342000              1.0000000        
+P   3
+  1     13.7600000             -0.0482312        
+  2      1.7120000              0.6141161        
+  3      0.5587000              0.4838342        
+P   1
+  1      0.0590000              1.0000000        
+P   1
+  1      0.0180000              1.0000000        
+D   4
+  1     25.7000000              0.0331033        
+  2      6.5300000              0.1795753        
+  3      2.0780000              0.4373062        
+  4      0.6243000              0.5984860        
+D   1
+  1      0.1542000              1.0000000        
+
+CHROMIUM
+S   3
+  1      5.3610000             -0.3805689        
+  2      1.4490000              0.7795625        
+  3      0.5496000              0.4730777        
+S   4
+  1      5.3610000              0.2203111        
+  2      1.4490000             -0.5423391        
+  3      0.5496000             -0.6047716        
+  4      0.1052000              1.1136657        
+S   1
+  1      0.0364000              1.0000000        
+P   3
+  1     16.4200000             -0.0461397        
+  2      1.9140000              0.6109965        
+  3      0.6241000              0.4859635        
+P   1
+  1      0.0630000              1.0000000        
+P   1
+  1      0.0190000              1.0000000        
+D   4
+  1     28.9500000              0.0337787        
+  2      7.7080000              0.1780345        
+  3      2.4950000              0.4370008        
+  4      0.7655000              0.5941795        
+D   1
+  1      0.1889000              1.0000000        
+
+MANGANESE
+S   3
+  1      5.9140000             -0.3764508        
+  2      1.6050000              0.7724789        
+  3      0.6260000              0.4769346        
+S   4
+  1      5.9140000              0.2119966        
+  2      1.6050000             -0.5199472        
+  3      0.6260000             -0.5857681        
+  4      0.1115000              1.1003964        
+S   1
+  1      0.0380000              1.0000000        
+P   3
+  1     18.2000000             -0.0447901        
+  2      2.1410000              0.6260311        
+  3      0.7009000              0.4696329        
+P   1
+  1      0.0690000              1.0000000        
+P   1
+  1      0.0210000              1.0000000        
+D   4
+  1     32.2700000              0.0341580        
+  2      8.8750000              0.1761105        
+  3      2.8900000              0.4394298        
+  4      0.8761000              0.5943271        
+D   1
+  1      0.2120000              1.0000000        
+
+IRON
+S   3
+  1      6.4220000             -0.3927882        
+  2      1.8260000              0.7712643        
+  3      0.7135000              0.4920228        
+S   4
+  1      6.4220000              0.1786877        
+  2      1.8260000             -0.4194032        
+  3      0.7135000             -0.4568185        
+  4      0.1021000              1.1035048        
+S   1
+  1      0.0363000              1.0000000        
+P   3
+  1     19.4800000             -0.0470282        
+  2      2.3890000              0.6248841        
+  3      0.7795000              0.4722542        
+P   1
+  1      0.0740000              1.0000000        
+P   1
+  1      0.0220000              1.0000000        
+D   4
+  1     37.0800000              0.0329000        
+  2     10.1000000              0.1787418        
+  3      3.2200000              0.4487657        
+  4      0.9628000              0.5876361        
+D   1
+  1      0.2262000              1.0000000        
+
+COBALT
+S   3
+  1      7.1760000             -0.3856734        
+  2      2.0090000              0.7453116        
+  3      0.8055000              0.5091819        
+S   4
+  1      7.1760000              0.1736161        
+  2      2.0090000             -0.3970442        
+  3      0.8055000             -0.4630622        
+  4      0.1070000              1.0899654        
+S   1
+  1      0.0375000              1.0000000        
+P   3
+  1     21.3900000             -0.0480413        
+  2      2.6500000              0.6222337        
+  3      0.8619000              0.4758042        
+P   1
+  1      0.0800000              1.0000000        
+P   1
+  1      0.0230000              1.0000000        
+D   4
+  1     39.2500000              0.0361541        
+  2     10.7800000              0.1896744        
+  3      3.4960000              0.4524981        
+  4      1.0660000              0.5710427        
+D   1
+  1      0.2606000              1.0000000        
+
+NICKEL
+S   3
+  1      7.6200000             -0.4082550        
+  2      2.2940000              0.7455308        
+  3      0.8760000              0.5325721        
+S   4
+  1      7.6200000              0.1872591        
+  2      2.2940000             -0.3966964        
+  3      0.8760000             -0.4954003        
+  4      0.1153000              1.0844343        
+S   1
+  1      0.0396000              1.0000000        
+P   3
+  1     23.6600000             -0.0481558        
+  2      2.8930000              0.6258473        
+  3      0.9435000              0.4715158        
+P   1
+  1      0.0840000              1.0000000        
+P   1
+  1      0.0240000              1.0000000        
+D   4
+  1     42.7200000              0.0372699        
+  2     11.7600000              0.1956103        
+  3      3.8170000              0.4561273        
+  4      1.1690000              0.5621587        
+D   1
+  1      0.2836000              1.0000000        
+
+COPPER
+S   3
+  1      8.1760000             -0.4210260        
+  2      2.5680000              0.7385924        
+  3      0.9587000              0.5525692        
+S   4
+  1      8.1760000              0.1787665        
+  2      2.5680000             -0.3592273        
+  3      0.9587000             -0.4704825        
+  4      0.1153000              1.0807407        
+S   1
+  1      0.0396000              1.0000000        
+P   3
+  1     25.6300000             -0.0489173        
+  2      3.1660000              0.6272854        
+  3      1.0230000              0.4716188        
+P   1
+  1      0.0860000              1.0000000        
+P   1
+  1      0.0240000              1.0000000        
+D   4
+  1     41.3400000              0.0465424        
+  2     11.4200000              0.2227824        
+  3      3.8390000              0.4539059        
+  4      1.2300000              0.5314769        
+D   1
+  1      0.3102000              1.0000000        
+
+ZINC
+S   2
+  1      0.7997000             -0.6486112        
+  2      0.1752000              1.3138291        
+S   1
+  1      0.0556000              1.0000000        
+P   1
+  1      0.1202000              1.0000000        
+P   1
+  1      0.0351000              1.0000000        
+D   4
+  1     68.8500000              0.0258532        
+  2     18.3200000              0.1651195        
+  3      5.9220000              0.4468212        
+  4      1.9270000              0.5831080        
+D   1
+  1      0.5528000              1.0000000        
+
+GALLIUM
+S   2
+  1      0.8306000             -1.6759436        
+  2      0.3392000              1.9877108        
+S   1
+  1      0.0918000              1.0000000        
+P   2
+  1      1.6750000             -0.0856107        
+  2      0.2030000              1.0226850        
+P   1
+  1      0.0579000              1.0000000        
+
+GERMANIUM
+S   2
+  1      0.8935000             -2.1756591        
+  2      0.4424000              2.4493467        
+S   1
+  1      0.1162000              1.0000000        
+P   2
+  1      1.8770000             -0.1006779        
+  2      0.2623000              1.0306256        
+P   1
+  1      0.0798000              1.0000000        
+
+ARSENIC
+S   2
+  1      0.9635000             -2.6709549        
+  2      0.5427000              2.9387892        
+S   1
+  1      0.1407000              1.0000000        
+P   2
+  1      2.0840000             -0.1137100        
+  2      0.3224000              1.0380266        
+P   1
+  1      0.1020000              1.0000000        
+
+SELENIUM
+S   2
+  1      1.0330000             -3.3224095        
+  2      0.6521000              3.6003462        
+S   1
+  1      0.1660000              1.0000000        
+P   2
+  1      2.3660000             -0.1185522        
+  2      0.3833000              1.0414320        
+P   1
+  1      0.1186000              1.0000000        
+
+BROMINE
+S   2
+  1      1.1590000             -3.0378769        
+  2      0.7107000              3.3703735        
+S   1
+  1      0.1905000              1.0000000        
+P   2
+  1      2.6910000             -0.1189800        
+  2      0.4446000              1.0424471        
+P   1
+  1      0.1377000              1.0000000        
+
+KRYPTON
+S   2
+  1      1.2270000             -4.0317198        
+  2      0.8457000              4.3554125        
+S   1
+  1      0.2167000              1.0000000        
+P   2
+  1      2.9200000             -0.1330685        
+  2      0.5169000              1.0502807        
+P   1
+  1      0.1614000              1.0000000        
+
+RUBIDIUM
+S   3
+  1      1.4570000             -1.0617406        
+  2      0.9400000              1.1722125        
+  3      0.2706000              0.7437008        
+S   4
+  1      1.4570000              0.5052174        
+  2      0.9400000             -0.5718404        
+  3      0.2706000             -0.6835308        
+  4      0.0366000              1.1136477        
+S   1
+  1      0.0155000              1.0000000        
+P   3
+  1      3.3000000             -0.0729417        
+  2      0.5985000              0.6321782        
+  3      0.2067000              0.4707426        
+P   2
+  1      0.1947000             -0.1250552        
+  2      0.0318000              1.0438206        
+P   1
+  1      0.0124000              1.0000000        
+
+STRONTIUM
+S   3
+  1      1.6480000             -0.9519879        
+  2      1.0030000              1.0869857        
+  3      0.3106000              0.7203160        
+S   4
+  1      1.6480000              0.7914337        
+  2      1.0030000             -0.9434046        
+  3      0.3106000             -1.3043063        
+  4      0.1099000              0.9046869        
+S   1
+  1      0.0292000              1.0000000        
+P   3
+  1      3.5520000             -0.0886461        
+  2      0.6975000              0.6661857        
+  3      0.2480000              0.4390705        
+P   2
+  1      0.2735000             -0.1787470        
+  2      0.0570000              1.0766134        
+P   1
+  1      0.0222000              1.0000000        
+
+YTTRIUM
+S   3
+  1      1.7510000             -1.1617905        
+  2      1.1430000              1.2958877        
+  3      0.3581000              0.7115125        
+S   4
+  1      1.7510000              0.9683123        
+  2      1.1430000             -1.1346367        
+  3      0.3581000             -1.1914054        
+  4      0.1058000              0.9281063        
+S   1
+  1      0.0318000              1.0000000        
+P   3
+  1      3.8840000             -0.0820601        
+  2      0.7660000              0.6756413        
+  3      0.2890000              0.4195482        
+P   2
+  1      0.2896000             -0.1501080        
+  2      0.0629000              1.0686926        
+P   1
+  1      0.0223000              1.0000000        
+D   3
+  1      1.5230000              0.1074843        
+  2      0.5634000              0.4563954        
+  3      0.1834000              0.6039855        
+D   1
+  1      0.0569000              1.0000000        
+
+ZIRCONIUM
+S   3
+  1      1.9760000             -0.9206745        
+  2      1.1540000              1.0929033        
+  3      0.3910000              0.6795621        
+S   4
+  1      1.9760000              0.7506060        
+  2      1.1540000             -0.9810826        
+  3      0.3910000             -1.0225151        
+  4      0.1001000              1.0860038        
+S   1
+  1      0.0334000              1.0000000        
+P   3
+  1      4.1920000             -0.0942554        
+  2      0.8764000              0.6783675        
+  3      0.3263000              0.4246618        
+P   2
+  1      0.2972000             -0.1527401        
+  2      0.0724000              1.0777185        
+P   1
+  1      0.0243000              1.0000000        
+D   3
+  1      2.2690000              0.0599741        
+  2      0.7855000              0.4734158        
+  3      0.2615000              0.6117178        
+D   1
+  1      0.0802000              1.0000000        
+
+NIOBIUM
+S   3
+  1      2.1820000             -0.8846144        
+  2      1.2090000              1.1033775        
+  3      0.4165000              0.6298776        
+S   4
+  1      2.1820000              0.7790287        
+  2      1.2090000             -1.0752788        
+  3      0.4165000             -1.1506014        
+  4      0.1454000              0.9969155        
+S   1
+  1      0.0392000              1.0000000        
+P   3
+  1      4.5190000             -0.0817303        
+  2      0.9406000              0.6995115        
+  3      0.3492000              0.3980996        
+P   2
+  1      0.4106000             -0.1212176        
+  2      0.0752000              1.0480477        
+P   1
+  1      0.0247000              1.0000000        
+D   3
+  1      3.4660000              0.0315983        
+  2      0.9938000              0.4834306        
+  3      0.3350000              0.6164893        
+D   1
+  1      0.1024000              1.0000000        
+
+MOLYBDENUM
+S   3
+  1      2.3610000             -0.9121760        
+  2      1.3090000              1.1477453        
+  3      0.4500000              0.6097109        
+S   4
+  1      2.3610000              0.8139259        
+  2      1.3090000             -1.1360084        
+  3      0.4500000             -1.1611592        
+  4      0.1681000              1.0064786        
+S   1
+  1      0.0423000              1.0000000        
+P   3
+  1      4.8950000             -0.0908258        
+  2      1.0440000              0.7042899        
+  3      0.3877000              0.3973179        
+P   2
+  1      0.4995000             -0.1081945        
+  2      0.0780000              1.0368093        
+P   1
+  1      0.0247000              1.0000000        
+D   3
+  1      2.9930000              0.0527063        
+  2      1.0630000              0.5003907        
+  3      0.3721000              0.5794024        
+D   1
+  1      0.1178000              1.0000000        
+
+TECHNETIUM
+S   3
+  1      2.3420000             -1.4911782        
+  2      1.6340000              1.6749043        
+  3      0.5094000              0.6573006        
+S   4
+  1      2.3420000              1.3523997        
+  2      1.6340000             -1.6216301        
+  3      0.5094000             -1.1463770        
+  4      0.1706000              0.9859190        
+S   1
+  1      0.0435000              1.0000000        
+P   3
+  1      5.2780000             -0.0995419        
+  2      1.1560000              0.7081544        
+  3      0.4302000              0.3973571        
+P   2
+  1      0.4767000             -0.0973127        
+  2      0.0895000              1.0404862        
+P   1
+  1      0.0246000              1.0000000        
+D   3
+  1      4.6320000              0.0268724        
+  2      1.2790000              0.5073089        
+  3      0.4425000              0.5911381        
+D   1
+  1      0.1364000              1.0000000        
+
+RUTHENIUM
+S   3
+  1      2.5650000             -1.0431056        
+  2      1.5080000              1.3314786        
+  3      0.5129000              0.5613065        
+S   4
+  1      2.5650000              0.8770128        
+  2      1.5080000             -1.2634660        
+  3      0.5129000             -0.8384987        
+  4      0.1362000              1.0637773        
+S   1
+  1      0.0417000              1.0000000        
+P   3
+  1      4.8590000             -0.0945755        
+  2      1.2190000              0.7434798        
+  3      0.4413000              0.3668144        
+P   2
+  1      0.5725000             -0.0880864        
+  2      0.0830000              1.0283970        
+P   1
+  1      0.0250000              1.0000000        
+D   3
+  1      4.1950000              0.0485729        
+  2      1.3770000              0.5105223        
+  3      0.4828000              0.5730028        
+D   1
+  1      0.1501000              1.0000000        
+
+RHODIUM
+S   3
+  1      2.6460000             -1.3554084        
+  2      1.7510000              1.6112233        
+  3      0.5713000              0.5893814        
+S   4
+  1      2.6460000              1.1472137        
+  2      1.7510000             -1.4943525        
+  3      0.5713000             -0.8589704        
+  4      0.1438000              1.0297241        
+S   1
+  1      0.0428000              1.0000000        
+P   3
+  1      5.4400000             -0.0987699        
+  2      1.3290000              0.7433595        
+  3      0.4845000              0.3668462        
+P   2
+  1      0.6595000             -0.0838056        
+  2      0.0869000              1.0244841        
+P   1
+  1      0.0257000              1.0000000        
+D   3
+  1      3.6690000              0.0760059        
+  2      1.4230000              0.5158852        
+  3      0.5091000              0.5436585        
+D   1
+  1      0.1610000              1.0000000        
+
+PALLADIUM
+S   3
+  1      2.7870000             -1.6102393        
+  2      1.9650000              1.8489842        
+  3      0.6243000              0.6037492        
+S   4
+  1      2.7870000              1.3540775        
+  2      1.9650000             -1.6780848        
+  3      0.6243000             -0.8559381        
+  4      0.1496000              1.0200299        
+S   1
+  1      0.0436000              1.0000000        
+P   3
+  1      5.9990000             -0.1034910        
+  2      1.4430000              0.7456952        
+  3      0.5264000              0.3656494        
+P   2
+  1      0.7368000              0.0763285        
+  2      0.0899000              0.9740065        
+P   1
+  1      0.0262000              1.0000000        
+D   3
+  1      6.0910000              0.0376146        
+  2      1.7190000              0.5200479        
+  3      0.6056000              0.5706071        
+D   1
+  1      0.1883000              1.0000000        
+
+SILVER
+S   3
+  1      2.9500000             -1.7910564        
+  2      2.1490000              2.0244570        
+  3      0.6684000              0.6072839        
+S   4
+  1      2.9500000              1.0141125        
+  2      2.1490000             -1.2413971        
+  3      0.6684000             -0.4901427        
+  4      0.0997000              1.1128375        
+S   1
+  1      0.0347000              1.0000000        
+P   3
+  1      6.5530000             -0.1079117        
+  2      1.5650000              0.7403645        
+  3      0.5748000              0.3721008        
+P   2
+  1      0.9085000             -0.0418371        
+  2      0.0833000              1.0087586        
+P   1
+  1      0.0252000              1.0000000        
+D   3
+  1      3.3910000              0.1396938        
+  2      1.5990000              0.4744421        
+  3      0.6282000              0.5156311        
+D   1
+  1      0.2108000              1.0000000        
+
+CADMIUM
+S   2
+  1      0.5095000             -1.2713002        
+  2      0.1924000              1.8002112        
+S   1
+  1      0.0544000              1.0000000        
+P   2
+  1      0.8270000             -0.1083020        
+  2      0.1287000              1.0367049        
+P   1
+  1      0.0405000              1.0000000        
+D   3
+  1      5.1480000              0.0703071        
+  2      1.9660000              0.5138427        
+  3      0.7360000              0.5416758        
+D   1
+  1      0.2479000              1.0000000        
+
+INDIUM
+S   2
+  1      0.4915000             -4.2418681        
+  2      0.3404000              4.4784826        
+S   1
+  1      0.0774000              1.0000000        
+P   2
+  1      0.9755000             -0.1226473        
+  2      0.1550000              1.0417571        
+P   1
+  1      0.0474000              1.0000000        
+
+TIN
+S   2
+  1      0.5418000             -4.2089644        
+  2      0.3784000              4.5198368        
+S   1
+  1      0.0926000              1.0000000        
+P   2
+  1      1.0470000             -0.1417678        
+  2      0.1932000              1.0554488        
+P   1
+  1      0.0630000              1.0000000        
+
+ANTIMONY
+S   2
+  1      0.5863000             -4.7537662        
+  2      0.4293000              5.1096593        
+S   1
+  1      0.1078000              1.0000000        
+P   2
+  1      1.1110000             -0.1811707        
+  2      0.2365000              1.0791649        
+P   1
+  1      0.0800000              1.0000000        
+
+TELLURIUM
+S   2
+  1      0.6938000             -2.4115013        
+  2      0.4038000              2.9179976        
+S   1
+  1      0.1165000              1.0000000        
+P   2
+  1      1.2310000             -0.1923340        
+  2      0.2756000              1.0876382        
+P   1
+  1      0.0911000              1.0000000        
+
+IODINE
+S   2
+  1      0.7242000             -2.9731048        
+  2      0.4653000              3.4827643        
+S   1
+  1      0.1336000              1.0000000        
+P   2
+  1      1.2900000             -0.2092377        
+  2      0.3180000              1.1035347        
+P   1
+  1      0.1053000              1.0000000        
+
+XENON
+S   2
+  1      0.7646000             -3.6543172        
+  2      0.5322000              4.1674919        
+S   1
+  1      0.1491000              1.0000000        
+P   2
+  1      1.2110000             -0.2616924        
+  2      0.3808000              1.1570355        
+P   1
+  1      0.1259000              1.0000000        
+
+CESIUM
+S   3
+  1      0.8709000             -1.1604798        
+  2      0.5393000              1.4198515        
+  3      0.1724000              0.5872905        
+S   4
+  1      0.8709000              0.8237727        
+  2      0.5393000             -1.0709181        
+  3      0.1724000             -0.9085122        
+  4      0.0393000              1.0774049        
+S   1
+  1      0.0151000              1.0000000        
+P   3
+  1      1.4680000             -0.1321033        
+  2      0.4134000              0.6572410        
+  3      0.1536000              0.4756456        
+P   2
+  1      0.1458000             -0.1680556        
+  2      0.0279000              1.0665746        
+P   1
+  1      0.0113000              1.0000000        
+
+BARIUM
+S   3
+  1      0.8699000             -2.2549747        
+  2      0.6676000              2.5145786        
+  3      0.1982000              0.5775184        
+S   4
+  1      0.8699000              2.0391383        
+  2      0.6676000             -2.3717712        
+  3      0.1982000             -1.2758006        
+  4      0.0823000              1.1703346        
+S   1
+  1      0.0231000              1.0000000        
+P   3
+  1      1.6050000             -0.1626403        
+  2      0.4790000              0.6971289        
+  3      0.1818000              0.4505107        
+P   2
+  1      0.1804000             -0.2642537        
+  2      0.0476000              1.1347212        
+P   1
+  1      0.0192000              1.0000000        
+
+LANTHANUM
+S   3
+  1      0.9167000             -3.0240751        
+  2      0.7427000              3.2971476        
+  3      0.2237000              0.5513542        
+S   4
+  1      0.9167000              2.6910243        
+  2      0.7427000             -3.0474363        
+  3      0.2237000             -1.1030211        
+  4      0.0792000              1.1941863        
+S   1
+  1      0.0239000              1.0000000        
+P   3
+  1      1.5540000             -0.1817168        
+  2      0.5622000              0.6632226        
+  3      0.2239000              0.5029782        
+P   2
+  1      0.2125000             -0.1968810        
+  2      0.0483000              1.0907542        
+P   1
+  1      0.0179000              1.0000000        
+D   2
+  1      0.4524000              0.4468051        
+  2      0.1602000              0.6543493        
+D   1
+  1      0.0531000              1.0000000        
+
+HAFNIUM
+S   3
+  1      1.9500000             -1.2260850        
+  2      1.1830000              1.5695074        
+  3      0.3897000              0.4933412        
+S   4
+  1      1.9500000              1.0778382        
+  2      1.1830000             -1.5010862        
+  3      0.3897000             -1.1982175        
+  4      0.1656000              1.2290296        
+S   1
+  1      0.0424000              1.0000000        
+P   3
+  1      1.9720000             -0.6323467        
+  2      1.3540000              1.0465162        
+  3      0.4134000              0.5801670        
+P   2
+  1      0.3427000             -0.1739178        
+  2      0.0804000              1.0841644        
+P   1
+  1      0.0274000              1.0000000        
+D   2
+  1      0.8226000              0.4490405        
+  2      0.2585000              0.6723891        
+D   1
+  1      0.0762000              1.0000000        
+
+TANTALUM
+S   3
+  1      2.0440000             -1.3104779        
+  2      1.2670000              1.6579925        
+  3      0.4157000              0.4848337        
+S   4
+  1      2.0440000              1.1897315        
+  2      1.2670000             -1.6650462        
+  3      0.4157000             -1.0563335        
+  4      0.1671000              1.1840652        
+S   1
+  1      0.0482000              1.0000000        
+P   3
+  1      2.5650000             -0.2917238        
+  2      1.2290000              0.7570649        
+  3      0.4244000              0.5240081        
+P   2
+  1      0.4360000             -0.1513014        
+  2      0.0840000              1.0613123        
+P   1
+  1      0.0280000              1.0000000        
+D   2
+  1      0.8948000              0.4731072        
+  2      0.2989000              0.6399200        
+D   1
+  1      0.0935000              1.0000000        
+
+TUNGSTEN
+S   3
+  1      2.1370000             -1.3916151        
+  2      1.3470000              1.7510261        
+  3      0.4366000              0.4694647        
+S   4
+  1      2.1370000              1.2985088        
+  2      1.3470000             -1.8102429        
+  3      0.4366000             -1.0844531        
+  4      0.1883000              1.2580618        
+S   1
+  1      0.0518000              1.0000000        
+P   3
+  1      3.0050000             -0.2405563        
+  2      1.2280000              0.7364092        
+  3      0.4415000              0.4881487        
+P   2
+  1      0.4010000             -0.1497499        
+  2      0.0900000              1.0707463        
+P   1
+  1      0.0280000              1.0000000        
+D   2
+  1      0.9519000              0.4985265        
+  2      0.3270000              0.6111110        
+D   1
+  1      0.1054000              1.0000000        
+
+RHENIUM
+S   3
+  1      2.1850000             -1.6223730        
+  2      1.4510000              1.9938647        
+  3      0.4585000              0.4531166        
+S   4
+  1      2.1850000              1.5459752        
+  2      1.4510000             -2.0758927        
+  3      0.4585000             -1.1922396        
+  4      0.2314000              1.2272864        
+S   1
+  1      0.0566000              1.0000000        
+P   3
+  1      3.3580000             -0.2318667        
+  2      1.2710000              0.7458683        
+  3      0.4644000              0.4632686        
+P   2
+  1      0.4960000             -0.1311370        
+  2      0.0890000              1.0503671        
+P   1
+  1      0.0280000              1.0000000        
+D   2
+  1      1.1160000              0.4689888        
+  2      0.4267000              0.6209591        
+D   1
+  1      0.1378000              1.0000000        
+
+OSMIUM
+S   3
+  1      2.2220000             -1.6538036        
+  2      1.4960000              2.0670297        
+  3      0.4774000              0.4232017        
+S   4
+  1      2.2220000              1.6046968        
+  2      1.4960000             -2.2120386        
+  3      0.4774000             -1.1301209        
+  4      0.2437000              1.2654553        
+S   1
+  1      0.0583000              1.0000000        
+P   3
+  1      2.5180000             -0.4177345        
+  2      1.4600000              0.9434947        
+  3      0.4923000              0.4672976        
+P   2
+  1      0.5100000             -0.1490279        
+  2      0.0980000              1.0603616        
+P   1
+  1      0.0290000              1.0000000        
+D   2
+  1      1.1830000              0.4838470        
+  2      0.4492000              0.6079573        
+D   1
+  1      0.1463000              1.0000000        
+
+IRIDIUM
+S   3
+  1      2.3500000             -1.6784642        
+  2      1.5820000              2.0952553        
+  3      0.5018000              0.4162934        
+S   4
+  1      2.3500000              1.6464467        
+  2      1.5820000             -2.2748150        
+  3      0.5018000             -1.0494357        
+  4      0.2500000              1.2167791        
+S   1
+  1      0.0598000              1.0000000        
+P   3
+  1      2.7920000             -0.3889212        
+  2      1.5410000              0.9077516        
+  3      0.5285000              0.4691443        
+P   2
+  1      0.5100000             -0.1170669        
+  2      0.0980000              1.0489002        
+P   1
+  1      0.0290000              1.0000000        
+D   2
+  1      1.2400000              0.5087022        
+  2      0.4647000              0.5862102        
+D   1
+  1      0.1529000              1.0000000        
+
+PLATINUM
+S   3
+  1      2.5470000             -1.4739175        
+  2      1.6140000              1.9115719        
+  3      0.5167000              0.3922319        
+S   4
+  1      2.5470000              1.4388166        
+  2      1.6140000             -2.0911821        
+  3      0.5167000             -1.0921315        
+  4      0.2651000              1.3426596        
+S   1
+  1      0.0580000              1.0000000        
+P   3
+  1      2.9110000             -0.5247438        
+  2      1.8360000              0.9671884        
+  3      0.5982000              0.5438632        
+P   2
+  1      0.6048000             -0.1061438        
+  2      0.0996000              1.0383102        
+P   1
+  1      0.0290000              1.0000000        
+D   2
+  1      1.2430000              0.5598150        
+  2      0.4271000              0.5511090        
+D   1
+  1      0.1370000              1.0000000        
+
+GOLD
+S   3
+  1      2.8090000             -1.2021556        
+  2      1.5950000              1.6741578        
+  3      0.5327000              0.3526593        
+S   4
+  1      2.8090000              1.1608481        
+  2      1.5950000             -1.8642846        
+  3      0.5327000             -1.0356230        
+  4      0.2826000              1.3064399        
+S   1
+  1      0.0598000              1.0000000        
+P   3
+  1      3.6840000             -0.2802681        
+  2      1.6660000              0.7818398        
+  3      0.5989000              0.4804776        
+P   2
+  1      0.6838000             -0.0952078        
+  2      0.0977000              1.0299147        
+P   1
+  1      0.0279000              1.0000000        
+D   2
+  1      1.2870000              0.5844273        
+  2      0.4335000              0.5298161        
+D   1
+  1      0.1396000              1.0000000        
+
+LEAD
+S   2
+  1      0.5135000             -4.3675036        
+  2      0.3756000              4.8504656        
+S   1
+  1      0.0944000              1.0000000        
+P   2
+  1      0.8748000             -0.1793128        
+  2      0.1843000              1.0776505        
+P   1
+  1      0.0598000              1.0000000        
+
+BISMUTH
+S   2
+  1      0.5744000             -3.2278875        
+  2      0.3851000              3.7637689        
+S   1
+  1      0.1050000              1.0000000        
+P   2
+  1      0.9105000             -0.2164189        
+  2      0.2194000              1.1041867        
+P   1
+  1      0.0745000              1.0000000        
+
+URANIUM
+S   4
+  1      2.4440000              0.2802780        
+  2      1.4330000             -1.3231100        
+  3      0.6677000              1.3949990        
+  4      0.2556000              0.4531230        
+S   5
+  1      2.4440000             -0.0837540        
+  2      1.4330000              0.6082360        
+  3      0.6677000             -0.8184330        
+  4      0.2556000             -0.9057530        
+  5      0.1200000              1.2340200        
+S   1
+  1      0.0400000              1.0000000        
+P   3
+  1      1.1380000             -0.5581500        
+  2      0.6874000              0.9701720        
+  3      0.2506000              0.5419030        
+P   4
+  1      1.1380000              0.2257630        
+  2      0.6874000             -0.4204690        
+  3      0.2506000             -0.5459030        
+  4      0.1200000              0.7429360        
+P   1
+  1      0.0400000              1.0000000        
+D   1
+  1      0.3826000              1.0000000        
+D   1
+  1      0.1473000              1.0000000        
+F   2
+  1      4.1130000              0.2189930        
+  2      1.6970000              0.4595390        
+F   2
+  1      0.6774000              0.4216990        
+  2      0.2510000              0.1790660        
+
+NEPTUNIUM
+S   4
+  1      2.3780000              0.5719500        
+  2      1.5750000             -1.9571550        
+  3      0.8950000              1.4731800        
+  4      0.3180000              0.7126170        
+S   5
+  1      2.3780000             -0.2204320        
+  2      1.5750000              0.9324950        
+  3      0.8950000             -0.8246300        
+  4      0.3180000             -0.9022650        
+  5      0.1130000              1.1030570        
+S   1
+  1      0.0400000              1.0000000        
+P   3
+  1      1.3050000             -0.4680230        
+  2      0.7210000              0.8473720        
+  3      0.2690000              0.5628310        
+P   4
+  1      1.3050000              0.1995460        
+  2      0.7210000             -0.3949300        
+  3      0.2690000             -0.4360310        
+  4      0.1000000              0.7862530        
+P   1
+  1      0.0374000              1.0000000        
+D   1
+  1      0.3960000              1.0000000        
+D   1
+  1      0.1500000              1.0000000        
+F   2
+  1      4.7910000              0.1853610        
+  2      1.9830000              0.4568540        
+F   2
+  1      0.7890000              0.4369420        
+  2      0.2900000              0.1981980        
+
+PLUTONIUM
+S   5
+  1     11.5600000             -0.0364860        
+  2      2.4870000              1.6156990        
+  3      1.7990000             -3.6388560        
+  4      1.1760000              2.0314710        
+  5      0.3590000              0.8533040        
+S   6
+  1     11.5600000              0.0175310        
+  2      2.4870000             -0.7027910        
+  3      1.7990000              1.7339680        
+  4      1.1760000             -1.0902960        
+  5      0.3590000             -0.9450600        
+  6      0.1130000              1.1062660        
+S   1
+  1      0.0400000              1.0000000        
+P   4
+  1      2.5800000              0.2005000        
+  2      1.6220000             -0.5912260        
+  3      0.7482000              0.7974730        
+  4      0.2793000              0.5635550        
+P   5
+  1      2.5800000             -0.0697090        
+  2      1.6220000              0.2196110        
+  3      0.7482000             -0.3575470        
+  4      0.2793000             -0.4281660        
+  5      0.1000000              0.7932680        
+P   1
+  1      0.0370000              1.0000000        
+D   1
+  1      0.4090000              1.0000000        
+D   1
+  1      0.1520000              1.0000000        
+F   2
+  1      4.8570000              0.2066870        
+  2      2.0310000              0.4696250        
+F   2
+  1      0.8150000              0.4212150        
+  2      0.3020000              0.1725220        
+$END
+! Elements                             References
+! --------                             ----------
+! Na - Hg: P. J. Hay and W. R. Wadt, J. Chem. Phys. 82, 270 (1985).
+!          P. J. Hay and W. R. Wadt, J. Chem. Phys. 82, 284 (1985).
+!          P. J. Hay and W. R. Wadt, J. Chem. Phys. 82, 299 (1985).
+!   
+
+
+  $ECP 
+  NA-ECP GEN     10     2
+  5      ----- d potential     -----
+    -10.0000000        1    175.5502590        
+    -47.4902024        2     35.0516791        
+    -17.2283007        2      7.9060270        
+     -6.0637782        2      2.3365719        
+     -0.7299393        2      0.7799867        
+  5      ----- s-d potential     -----
+      3.0000000        0    243.3605846        
+     36.2847626        1     41.5764759        
+     72.9304880        2     13.2649167        
+     23.8401151        2      3.6797165        
+      6.0123861        2      0.9764209        
+  6      ----- p-d potential     -----
+      5.0000000        0   1257.2650682        
+    117.4495683        1    189.6248810        
+    423.3986704        2     54.5247759        
+    109.3247297        2     13.7449955        
+     31.3701656        2      3.6813579        
+      7.1241813        2      0.9461106        
+  MG-ECP GEN     10     2
+  5      ----- d potential     -----
+    -10.0000000        1    237.5484804        
+    -55.8993968        2     47.7520367        
+    -20.1391957        2     10.7837852        
+     -7.0679107        2      3.1992124        
+     -0.8133109        2      1.0636953        
+  5      ----- s-d potential     -----
+      3.0000000        0    348.3008631        
+     44.0075660        1     59.4680133        
+    107.3861344        2     19.0767582        
+     35.8289088        2      5.2965613        
+     10.1143435        2      1.3867373        
+  6      ----- p-d potential     -----
+      5.0000000        0   1256.8739085        
+    117.1053672        1    189.8608839        
+    420.5972073        2     54.6949631        
+    107.6122959        2     13.8990137        
+     29.1002576        2      3.9597181        
+      7.0875570        2      1.2552787        
+  AL-ECP GEN     10     2
+  5      ----- d   potential     -----
+    -10.0000000        1    304.7291926        
+    -63.8079837        2     61.5299768        
+    -22.8972174        2     13.9259006        
+     -8.0063232        2      4.1463626        
+     -0.8829345        2      1.3715443        
+  5      ----- s-d potential     -----
+      3.0000000        0    467.8437756        
+     50.9705682        1     79.3992216        
+    143.8716460        2     25.4035967        
+     48.0055753        2      6.9954696        
+     14.0114180        2      1.7860129        
+  6      ----- p-d potential     -----
+      5.0000000        0    776.2717190        
+     93.0595424        1    118.4992254        
+    266.7686329        2     34.4107276        
+     70.0725805        2      8.7859563        
+     17.2290839        2      2.3406228        
+      0.7105331        2      0.7398386        
+  SI-ECP GEN     10     2
+  5      ----- d   potential     -----
+    -10.0000000        1    505.3137693        
+    -84.9236087        2    103.2221026        
+    -30.3299410        2     23.4569248        
+    -12.1049046        2      6.7505693        
+     -1.8945408        2      2.1603140        
+  5      ----- s-d potential     -----
+      3.0000000        0    689.4910719        
+     60.5206833        1    114.1728510        
+    201.3086137        2     35.7424336        
+     65.9399745        2      9.4529586        
+     19.0300789        2      2.2543590        
+  6      ----- p-d potential     -----
+      5.0000000        0     88.9379355        
+      6.6413817        1     76.7773538        
+    247.5972416        2     56.1480987        
+    129.3715380        2     21.1874014        
+     47.4617107        2      6.8277276        
+     11.7376574        2      2.1001192        
+  P-ECP GEN     10     2
+  5      ----- d   potential     -----
+    -10.0000000        1    462.1211423        
+    -79.4864658        2     93.6863701        
+    -28.3668251        2     21.2349094        
+     -9.8577589        2      6.3388415        
+     -1.0163783        2      2.0620684        
+  5      ----- s-d potential     -----
+      3.0000000        0     78.0831823        
+     12.9104154        1     58.9576810        
+    150.0250298        2     36.0571255        
+     71.7083146        2     11.2464453        
+     23.0397012        2      2.6757561        
+  6      ----- p-d potential     -----
+      5.0000000        0     75.1617880        
+      6.3446507        1     57.4544041        
+    198.5585104        2     47.9481748        
+    111.1470820        2     18.4588360        
+     40.3944144        2      5.9414190        
+      6.4483233        2      1.8487507        
+  S-ECP GEN     10     2
+  5      ----- d   potential     -----
+    -10.0000000        1    532.6685222        
+    -85.3593846        2    108.1342248        
+    -30.4513290        2     24.5697664        
+    -10.3745886        2      7.3702438        
+     -0.9899295        2      2.3712569        
+  5      ----- s-d potential     -----
+      3.0000000        0    106.3176781        
+     10.6284036        1    100.8245833        
+    223.6360469        2     53.5858472        
+     93.6460845        2     15.3706332        
+     28.7609065        2      3.1778402        
+  6      ----- p-d potential     -----
+      5.0000000        0    101.9709185        
+      6.0969842        1     93.2808973        
+    285.4425500        2     65.1431772        
+    147.1448413        2     24.6347440        
+     53.6569778        2      7.8120535        
+      8.9249559        2      2.3112730        
+  CL-ECP GEN     10     2
+  5      ----- d   potential     -----
+    -10.0000000        1     94.8130000        
+     66.2729170        2    165.6440000        
+    -28.9685950        2     30.8317000        
+    -12.8663370        2     10.5841000        
+     -1.7102170        2      3.7704000        
+  5      ----- s-d potential     -----
+      3.0000000        0    128.8391000        
+     12.8528510        1    120.3786000        
+    275.6723980        2     63.5622000        
+    115.6777120        2     18.0695000        
+     35.0606090        2      3.8142000        
+  6      ----- p-d potential     -----
+      5.0000000        0    216.5263000        
+      7.4794860        1     46.5723000        
+    613.0320000        2    147.4685000        
+    280.8006850        2     48.9869000        
+    107.8788240        2     13.2096000        
+     15.3439560        2      3.1831000        
+  AR-ECP GEN     10     2
+  5      ----- d   potential     -----
+    -10.0000000        1    711.5242175        
+    -99.0606669        2    144.6708689        
+    -35.2711767        2     32.9246992        
+    -11.8151947        2      9.9103877        
+     -1.0453382        2      3.1328926        
+  5      ----- s-d potential     -----
+      3.0000000        0    152.8553033        
+     11.2465621        1     82.1424792        
+    250.7412812        2     83.7154800        
+    139.1606543        2     23.8557161        
+     41.2897981        2      4.3128823        
+  6      ----- p-d potential     -----
+      5.0000000        0    212.5573747        
+      5.2787358        1    266.4884712        
+    631.7135166        2    139.9222477        
+    305.1649809        2     50.1097659        
+    106.4807615        2     14.5537276        
+     17.0215765        2      3.4828623        
+  K-ECP GEN     10     2
+  5      ----- d potential     -----
+    -10.0000000        1   1067.1081575        
+   -124.3469306        2    218.4185142        
+    -43.9937468        2     50.0433502        
+    -16.5946158        2     14.9374500        
+     -1.8728686        2      5.0996937        
+  3      ----- s-d potential     -----
+      3.0000000        0     16.5503988        
+     10.4828609        1     19.1641591        
+     45.4561569        2      4.8910684        
+  5      ----- p-d potential     -----
+      5.0000000        0     95.3645855        
+      5.5748307        1    119.1209062        
+    309.6493189        2     57.0795815        
+    125.5951809        2     17.4450910        
+     23.1977649        2      4.4636131        
+  CA-ECP GEN     10     2
+  5      ----- d potential     -----
+    -10.0000000        1   1270.4543808        
+   -136.3684190        2    260.2495057        
+    -48.3403600        2     59.7068501        
+    -18.5141902        2     17.6818150        
+     -2.2114740        2      5.9668207        
+  3      ----- s-d potential     -----
+      3.0000000        0     18.6029912        
+     11.7228027        1     21.4138971        
+     54.2117561        2      5.6851879        
+  5      ----- p-d potential     -----
+      5.0000000        0    134.7687233        
+      5.1639340        1    181.8458004        
+    442.8686022        2     80.9240132        
+    175.0255989        2     23.3806993        
+     28.8217434        2      5.1868564        
+  SC-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    237.3166528        
+    -48.9695553        2     42.6489004        
+     -8.7066044        2     10.6211229        
+  5      ----- s-d potential     -----
+      3.0000000        0     69.2552324        
+     17.0584559        1     58.8953416        
+    167.9595412        2     27.2637459        
+    179.8382113        2      5.4737304        
+   -121.1523144        2      5.1523607        
+  6      ----- p-d potential     -----
+      5.0000000        0     77.2512970        
+      6.1088195        1     44.4527155        
+    192.9384914        2     53.6854752        
+    127.2274755        2     18.9421170        
+     68.0945974        2      4.7970573        
+    -41.6469108        2      4.4142925        
+  TI-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    265.3263909        
+    -51.8427816        2     47.7687815        
+     -9.1429145        2     11.8903334        
+  5      ----- s-d potential     -----
+      3.0000000        0     81.4730696        
+     19.4825579        1     72.6496724        
+    207.3349279        2     31.8128213        
+    235.6744501        2      6.1664468        
+   -166.8784387        2      5.8268347        
+  5      ----- p-d potential     -----
+      5.0000000        0     50.2966943        
+      5.5348822        1     63.5089754        
+    177.8419384        2     26.0996084        
+    107.4207153        2      5.6022573        
+    -71.9065902        2      5.2171069        
+  V-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    296.4487582        
+    -54.8909808        2     53.5015052        
+     -9.7010586        2     13.3204510        
+  5      ----- s-d potential     -----
+      3.0000000        0     93.2193654        
+     22.5345689        1     71.1029567        
+    216.5634009        2     33.8766993        
+    252.6822538        2      6.8691200        
+   -176.2671654        2      6.4804722        
+  5      ----- p-d potential     -----
+      5.0000000        0     52.0953977        
+      5.7003555        1     65.6778411        
+    182.9054274        2     27.4703764        
+    117.2756226        2      6.2352020        
+    -78.5829134        2      5.8139909        
+  CR-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    329.2720539        
+    -57.9409398        2     59.5487597        
+    -10.2623285        2     14.8265966        
+  5      ----- s-d potential     -----
+      3.0000000        0    100.0260653        
+     25.2534599        1     67.8468124        
+    210.6472366        2     34.6771006        
+    235.2819587        2      7.6757005        
+   -152.5343461        2      7.2077427        
+  5      ----- p-d potential     -----
+      5.0000000        0     47.9723321        
+      5.6390266        1     59.6224873        
+    164.8362198        2     26.0432600        
+    106.0168018        2      6.5582692        
+    -69.8585468        2      6.1268236        
+  MN-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    357.3914469        
+    -60.2990287        2     64.6477389        
+    -10.4217834        2     16.0960833        
+  5      ----- s-d potential     -----
+      3.0000000        0    107.4127215        
+     16.2591819        1    111.4958973        
+    276.9373928        2     46.5568346        
+    241.3174342        2      8.3688135        
+   -146.4635329        2      7.7237489        
+  5      ----- p-d potential     -----
+      5.0000000        0     80.0415103        
+      5.7589756        1    105.6043646        
+    285.2918654        2     40.8300466        
+    143.4222647        2      8.0098457        
+    -88.7031851        2      7.3390928        
+  FE-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    392.6149787        
+    -63.2667518        2     71.1756979        
+    -10.9613338        2     17.7320281        
+  5      ----- s-d potential     -----
+      3.0000000        0    126.0571895        
+     18.1729137        1    138.1264251        
+    339.1231164        2     54.2098858        
+    317.1068012        2      9.2837966        
+   -207.3421649        2      8.6289082        
+  5      ----- p-d potential     -----
+      5.0000000        0     83.1759490        
+      5.9535930        1    106.0559938        
+    294.2665527        2     42.8284937        
+    154.4244635        2      8.7701805        
+    -95.3164249        2      8.0397818        
+  CO-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    434.1076104        
+    -66.7240276        2     78.8402758        
+    -11.7192243        2     19.6037749        
+  4      ----- s-d potential     -----
+      3.0000000        0    151.2307582        
+     20.1553110        1    169.6790026        
+    413.3762244        2     64.9909423        
+    133.8727705        2     12.5076287        
+  4      ----- p-d potential     -----
+      5.0000000        0     92.7319924        
+      6.1892353        1    121.1910183        
+    326.3215919        2     48.6265909        
+     73.8788970        2     11.9966971        
+  NI-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    469.9324331        
+    -69.4084805        2     85.4236411        
+    -12.0951020        2     21.2674984        
+  4      ----- s-d potential     -----
+      3.0000000        0    162.1686097        
+     22.0253618        1    176.5333232        
+    443.0181088        2     68.9562010        
+    145.5696411        2     13.5792838        
+  4      ----- p-d potential     -----
+      5.0000000        0     69.0181506        
+      4.9882824        1    275.5955596        
+    256.6945853        2     47.1315453        
+     78.4754450        2     12.9874075        
+  CU-ECP GEN     10     2
+  3      ----- d potential     -----
+    -10.0000000        1    511.9951763        
+    -72.5548282        2     93.2801074        
+    -12.7450231        2     23.2206669        
+  4      ----- s-d potential     -----
+      3.0000000        0    173.1180854        
+     23.8351825        1    185.2419886        
+    473.8930488        2     73.1517847        
+    157.6345823        2     14.6884157        
+  4      ----- p-d potential     -----
+      5.0000000        0    100.7191369        
+      6.4990936        1    130.8345665        
+    351.4605395        2     53.8683720        
+     85.5016036        2     14.0989469        
+  ZN-ECP GEN     18     3
+  5      ----- f potential     -----
+    -18.0000000        1    386.7379660        
+   -124.3527403        2     72.8587359        
+    -30.6601822        2     15.9066170        
+    -10.6358989        2      4.3502340        
+     -0.7683623        2      1.2842199        
+  5      ----- s-f potential     -----
+      3.0000000        0     19.0867858        
+     22.5234225        1      5.0231080        
+     48.4465942        2      1.2701744        
+    -44.5560119        2      1.0671287        
+     12.9983958        2      0.9264190        
+  5      ----- p-f potential     -----
+      5.0000000        0     43.4927750        
+     20.7435589        1     20.8692669        
+     90.3027158        2     21.7118378        
+     74.6610316        2      6.3616915        
+      9.8894424        2      1.2291195        
+  3      ----- d-f potential     -----
+     -4.8490359        2     13.5851800        
+      3.6913379        2      9.8373050        
+     -0.5037319        2      0.8373113        
+  GA-ECP GEN     28     3
+  5      ----- f potential     -----
+    -28.0000000        1    260.9051184        
+   -161.6248634        2     50.4244564        
+    -49.2659751        2     10.7195520        
+    -16.9305942        2      3.1563995        
+     -1.5149893        2      0.9502385        
+  5      ----- s-f potential     -----
+      3.0000000        0    339.0575995        
+     93.8597449        1     95.9625227        
+    308.6495485        2     30.0989360        
+     94.7290607        2      7.8907039        
+     27.0931066        2      2.1154234        
+  5      ----- p-f potential     -----
+      5.0000000        0     30.1202708        
+     22.8495315        1     14.6143825        
+     43.3974907        2     12.4639433        
+     48.6203923        2      5.1039219        
+     14.7365483        2      1.6566033        
+  5      ----- d-f potential     -----
+      3.0000000        0     32.2704306        
+     24.2994834        1     10.9444004        
+     35.4415813        2      4.5111791        
+      9.1055575        2      1.5561710        
+      0.9601121        2      0.5550926        
+  GE-ECP GEN     28     3
+  5      ----- f potential     -----
+    -28.0000000        1    318.2167583        
+   -180.9891676        2     61.5370967        
+    -55.0043909        2     13.2986899        
+    -19.7906526        2      3.8985215        
+     -1.8533572        2      1.2137666        
+  5      ----- s-f potential     -----
+      3.0000000        0    205.1886932        
+     65.2262558        1     68.9790278        
+    225.2354522        2     27.9194879        
+     94.0125472        2      8.5481650        
+     29.9415005        2      2.3173734        
+  5      ----- p-f potential     -----
+      5.0000000        0     33.2488002        
+     23.4778157        1     15.7777247        
+     45.0980414        2     14.9260722        
+     56.3326957        2      5.8416394        
+     16.6058640        2      1.8349575        
+  5      ----- d-f potential     -----
+      3.0000000        0     42.0206343        
+     23.7371518        1     19.2096363        
+     56.4792249        2      9.4133917        
+     25.8901835        2      3.3282907        
+      3.0229836        2      0.8522331        
+  AS-ECP GEN     28     3
+  5      ----- f potential     -----
+    -28.0000000        1    375.4748803        
+   -198.3687357        2     72.6554769        
+    -60.4563798        2     15.9162677        
+    -22.3188274        2      4.6420251        
+     -2.1638290        2      1.4789182        
+  5      ----- s-f potential     -----
+      3.0000000        0    105.8853870        
+     31.8486777        1     62.5951846        
+    200.9958215        2     34.5017431        
+    102.6387604        2     10.3758594        
+     34.1845359        2      2.5379347        
+  5      ----- p-f potential     -----
+      5.0000000        0     37.6471440        
+     24.2505747        1     17.2034665        
+     48.9174963        2     18.9075709        
+     66.5332259        2      6.8153452        
+     19.6577666        2      2.0611853        
+  5      ----- d-f potential     -----
+      3.0000000        0     52.0731335        
+     23.3460712        1     28.0037824        
+     80.7854919        2     14.7051208        
+     40.2681347        2      4.7973194        
+      4.8332790        2      1.1062763        
+  SE-ECP GEN     28     3
+  5      ----- f potential     -----
+    -28.0000000        1    433.1931336        
+   -214.3841762        2     83.8952157        
+    -65.6918782        2     18.5839139        
+    -24.6153932        2      5.3955286        
+     -2.4481497        2      1.7474326        
+  5      ----- s-f potential     -----
+      3.0000000        0    202.8986193        
+     62.0295390        1     78.3820487        
+    258.8555984        2     35.0753037        
+    118.7800153        2     10.8769543        
+     38.2355279        2      2.8005941        
+  5      ----- p-f potential     -----
+      5.0000000        0     44.3011875        
+     24.7973458        1     20.3874206        
+     63.7575640        2     23.1889948        
+     79.0512831        2      7.9777664        
+     22.9520183        2      2.2988146        
+  5      ----- d-f potential     -----
+      3.0000000        0     73.3628263        
+     22.4705907        1     48.3835618        
+    140.5492887        2     25.6297211        
+     63.5781835        2      7.1705822        
+      7.0753614        2      1.3639538        
+  BR-ECP GEN     28     3
+  4      ----- f potential     -----
+    -28.0000000        1    213.6143969        
+   -134.9268852        2     41.0585380        
+    -41.9271913        2      8.7086530        
+     -5.9336420        2      2.6074661        
+  4      ----- s-f potential     -----
+      3.0000000        0     54.1980682        
+     27.3430642        1     32.9053558        
+    118.8028847        2     13.6744890        
+     43.4354876        2      3.0341152        
+  5      ----- p-f potential     -----
+      5.0000000        0     54.2563340        
+     25.0504252        1     26.0095593        
+     92.6157463        2     28.2012995        
+     95.8249016        2      9.4341061        
+     26.2684983        2      2.5321764        
+  5      ----- d-f potential     -----
+      3.0000000        0     87.6328721        
+     22.5533557        1     61.7373377        
+    178.1241988        2     32.4385104        
+     76.9924162        2      8.7537199        
+      9.4818270        2      1.6633189        
+  KR-ECP GEN     28     3
+  4      ----- f potential     -----
+    -28.0000000        1    237.5470853        
+   -143.4259955        2     45.7406754        
+    -44.3723838        2      9.7073237        
+     -6.2911252        2      2.9320738        
+  4      ----- s-f potential     -----
+      3.0000000        0     70.7606918        
+     37.1893647        1     33.6809745        
+    132.2973958        2     13.8075281        
+     47.9346985        2      3.3188254        
+  5      ----- p-f potential     -----
+      5.0000000        0     57.7676804        
+     26.1564008        1     27.6779453        
+     96.3176501        2     30.4757449        
+    103.6632794        2     10.3642639        
+     31.0779315        2      2.8470827        
+  5      ----- d-f potential     -----
+      3.0000000        0     85.3902277        
+     24.4356537        1     36.0781993        
+     85.7229402        2     38.0387668        
+     91.6970578        2     10.6069882        
+     11.8300709        2      1.9390148        
+  RB-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0364362        0    879.5983664        
+    -20.8033052        1    142.0188695        
+   -115.4661910        2     40.9346882        
+    -42.5061293        2      9.9084802        
+     -5.5247889        2      3.1556041        
+  4      ----- s-f potential     -----
+      2.9747910        0     70.9807215        
+     36.1170435        1     38.6949807        
+    152.2741068        2     15.9082035        
+     53.7151906        2      3.7152659        
+  5      ----- p-f potential     -----
+      4.9551026        0     93.3058684        
+     24.2726236        1     47.1221370        
+    215.2341411        2     48.8050640        
+    151.6687666        2     14.3784949        
+     33.8750280        2      3.0547807        
+  4      ----- d-f potential     -----
+      3.0465224        0     33.0477430        
+     29.4243624        1      9.9601880        
+     33.4431808        2      3.7403577        
+     11.1029460        2      0.8124208        
+  SR-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0384323        0    782.3804631        
+    -20.6174271        1    124.6542338        
+   -101.1737744        2     36.9874966        
+    -38.7743603        2      9.8828819        
+     -4.6479243        2      3.2829588        
+  4      ----- s-f potential     -----
+      2.9989022        0     59.3240631        
+     25.6552669        1     55.2038472        
+    183.1818533        2     20.4692092        
+     58.4384739        2      3.9588141        
+  5      ----- p-f potential     -----
+      4.9551189        0     92.1201991        
+     25.4472367        1     46.8132559        
+    203.8002780        2     48.6566432        
+    155.0518740        2     14.9503238        
+     39.3605192        2      3.4268785        
+  5      ----- d-f potential     -----
+      3.0056451        0     65.8291301        
+     26.7064119        1     32.7282621        
+     74.5756901        2     21.1146030        
+     63.1742121        2      9.1071292        
+     20.2961162        2      2.8110754        
+  Y-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0404817        0    578.4310349        
+    -20.6194344        1    152.7792004        
+   -116.7522279        2     44.9301524        
+    -43.7975806        2     11.4587918        
+     -5.4247609        2      3.7523267        
+  4      ----- s-f potential     -----
+      2.9801339        0     59.4715114        
+     34.7834676        1     17.2173553        
+     28.8453246        2     18.4797093        
+     64.7642088        2      4.3276192        
+  4      ----- p-f potential     -----
+      4.9885783        0     45.7271005        
+     19.6506564        1     49.4595886        
+    194.0943181        2     18.9952373        
+     43.1349769        2      3.6603193        
+  5      ----- d-f potential     -----
+      3.0066647        0     62.8268435        
+     25.9879250        1     31.8897904        
+     85.7172897        2     18.3646572        
+     48.7792568        2      7.3062400        
+     11.4535104        2      2.4051635        
+  ZR-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0425843        0    645.9321873        
+    -20.2222409        1    134.7547401        
+   -101.8695172        2     42.3074619        
+    -41.6195784        2     12.0003227        
+     -4.6986160        2      4.1260454        
+  3      ----- s-f potential     -----
+      2.7910559        0    117.6251862        
+     41.9489459        1     22.9646089        
+     67.7271866        2      4.5225298        
+  4      ----- p-f potential     -----
+      4.9911144        0     47.1953145        
+     20.7193172        1     48.0356033        
+    195.5867758        2     19.4541456        
+     48.2877176        2      4.0512875        
+  5      ----- d-f potential     -----
+      3.0049226        0     79.9073983        
+     25.9377989        1     45.8263798        
+    125.1244934        2     26.9903522        
+     70.7634022        2      9.6835718        
+     15.0492822        2      2.7995666        
+  NB-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0447401        0    342.9638405        
+    -20.0535100        1    139.9308014        
+   -103.7244021        2     43.3523405        
+    -41.0459165        2     12.4448533        
+     -4.2046219        2      4.3204837        
+  3      ----- s-f potential     -----
+      2.7964669        0    112.8630617        
+     42.8845786        1     22.9678676        
+     75.1876966        2      4.9340673        
+  4      ----- p-f potential     -----
+      4.9461846        0     63.6801963        
+     24.8316376        1     23.0912429        
+    137.9183292        2     24.4283647        
+     50.9778471        2      4.2310090        
+  5      ----- d-f potential     -----
+      3.0064059        0     99.3359636        
+     25.5347458        1     64.0586472        
+    178.9597452        2     37.0891011        
+     92.9577490        2     12.0708574        
+     18.4744256        2      3.1575323        
+  MO-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0469492        0    537.9667807        
+    -20.2080084        1    147.8982938        
+   -106.2116302        2     45.7358898        
+    -41.8107368        2     13.2911467        
+     -4.2054103        2      4.7059961        
+  3      ----- s-f potential     -----
+      2.8063717        0    110.2991760        
+     44.5162012        1     23.2014645        
+     82.7785227        2      5.3530131        
+  4      ----- p-f potential     -----
+      4.9420876        0     63.2901397        
+     25.8604976        1     23.3315302        
+    132.4708742        2     24.6759423        
+     57.3149794        2      4.6493040        
+  5      ----- d-f potential     -----
+      3.0054591        0    104.4839977        
+     26.3637851        1     66.2307245        
+    183.3849199        2     39.1283176        
+     98.4453068        2     13.1164437        
+     22.4901377        2      3.6280263        
+  TC-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0492115        0    501.1199409        
+    -20.1467308        1    150.2468844        
+   -105.5792880        2     46.7097914        
+    -41.5901197        2     13.9339014        
+     -3.9415111        2      4.9854069        
+  3      ----- s-f potential     -----
+      2.8222374        0    108.1775995        
+     46.7330708        1     23.6594351        
+     90.5318657        2      5.7814977        
+  4      ----- p-f potential     -----
+      4.9390782        0     63.3062014        
+     26.9229631        1     23.6935532        
+    128.5079796        2     25.0525854        
+     63.7490250        2      5.0717084        
+  5      ----- d-f potential     -----
+      2.9921902        0    102.7721397        
+     28.1821379        1     45.6713756        
+     89.0548465        2     48.2611997        
+    121.1894325        2     15.7089115        
+     25.5866973        2      3.9484576        
+  RU-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0515270        0    554.3796303        
+    -20.1816536        1    155.1066871        
+   -105.9966915        2     48.4976263        
+    -42.2166788        2     14.7701594        
+     -3.7675024        2      5.2077363        
+  5      ----- s-f potential     -----
+      2.9578344        0     66.7118060        
+     25.3748707        1     77.3503632        
+    536.1262372        2     18.3571445        
+   -651.2057221        2     11.8404727        
+    381.3816943        2      8.1179479        
+  5      ----- p-f potential     -----
+      4.9651557        0     54.9937915        
+     23.8861501        1     13.9399212        
+    464.4631344        2     15.2118246        
+   -714.4451788        2     10.5460691        
+    377.5503594        2      7.5539486        
+  4      ----- d-f potential     -----
+      3.0352988        0     60.3444595        
+     23.2901723        1     45.2100305        
+    146.0926620        2     19.1190074        
+     28.9129770        2      4.2712090        
+  RH-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0538958        0    600.3243032        
+    -20.1316282        1    157.6910176        
+   -105.3654121        2     49.8841995        
+    -42.3274370        2     15.5966895        
+     -3.6654043        2      5.5099296        
+  5      ----- s-f potential     -----
+      2.9753728        0     59.3442526        
+     25.1230306        1     83.7426061        
+    626.0926145        2     18.4530248        
+   -812.2549385        2     12.4194606        
+    467.3729340        2      8.8172913        
+  5      ----- p-f potential     -----
+      4.9537213        0     53.4309068        
+     20.4871116        1     65.6671843        
+    598.0120139        2     16.8369862        
+   -718.4059028        2     11.3042136        
+    382.8173151        2      8.0312444        
+  4      ----- d-f potential     -----
+      3.0279532        0     64.3993653        
+     24.7526516        1     43.4625053        
+    142.6844289        2     19.4020301        
+     32.1406857        2      4.6879328        
+  PD-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0563177        0    598.3336444        
+    -20.1288036        1    162.4298290        
+   -105.8197923        2     51.5714771        
+    -42.5733345        2     16.4888260        
+     -3.6165086        2      5.8287656        
+  5      ----- s-f potential     -----
+      3.0003651        0     73.3806304        
+     32.4350093        1     14.7550438        
+    459.0830383        2     17.8350204        
+   -868.0629029        2     12.7111477        
+    514.4726098        2      9.3292063        
+  5      ----- p-f potential     -----
+      4.9593099        0     55.6689877        
+     21.1711029        1     64.2337771        
+    605.0560092        2     17.6254952        
+   -726.9641846        2     11.9058155        
+    396.3274883        2      8.5100832        
+  5      ----- d-f potential     -----
+      3.0508745        0     49.9994728        
+     22.2506580        1     39.7477547        
+    674.8357698        2     11.4321366        
+  -1040.8554048        2      9.1790080        
+    505.9375147        2      7.5624429        
+  AG-ECP GEN     28     3
+  5      ----- f potential     -----
+     -0.0587930        0    568.7006237        
+    -20.1145146        1    162.3579066        
+   -104.2733114        2     51.1025755        
+    -40.4539787        2     16.9205822        
+     -3.4420009        2      6.1669596        
+  5      ----- s-f potential     -----
+      2.9861527        0     76.0974658        
+     35.1576460        1     15.3327359        
+    450.1809906        2     18.7715345        
+   -866.0248308        2     13.3663294        
+    523.1110176        2      9.8236948        
+  5      ----- p-f potential     -----
+      4.9640671        0     56.3318043        
+     21.5028219        1     69.0609098        
+    546.0275453        2     19.2717998        
+   -600.3822556        2     12.5770654        
+    348.2949289        2      8.7956670        
+  5      ----- d-f potential     -----
+      3.0467486        0     53.4641078        
+     23.3656705        1     40.1975457        
+    777.2540117        2     11.9086073        
+  -1238.8602423        2      9.7528183        
+    608.0677121        2      8.1788997        
+  CD-ECP GEN     36     3
+  7      ----- f potential     -----
+     -0.0613214        0    350.0762490        
+    -24.9355593        1     81.1757778        
+    -87.7728765        2     24.7747818        
+    -19.0665030        2     12.5760330        
+    -16.1901254        2      4.9889712        
+     -3.9441254        2      1.8449910        
+     -0.3097476        2      0.5309494        
+  5      ----- s-f potential     -----
+      2.9050102        0     54.5786193        
+     39.6289198        1     16.0082316        
+     57.4435790        2      5.2767462        
+     90.2351166        2      0.9731070        
+    -71.7139236        2      0.9305646        
+  4      ----- p-f potential     -----
+      1.9585463        0     41.3581766        
+     37.5669844        1     12.1214982        
+     44.0875104        2      3.4478806        
+     12.5678599        2      0.9362175        
+  6      ----- d-f potential     -----
+      2.9753499        0     85.3710499        
+     24.7054373        1     73.0500781        
+    218.2163143        2     28.4262887        
+    118.9945370        2      3.9450158        
+    -82.4999671        2      3.5349379        
+     -0.2413758        2      0.6482194        
+  IN-ECP GEN     46     3
+  5      ----- f potential     -----
+     -0.0639031        0      0.1802071        
+    -29.9082901        1     33.0176528        
+    -63.3446824        2      9.7219749        
+    -19.0058428        2      2.3481578        
+     -2.5161921        2      0.7226853        
+  5      ----- s-f potential     -----
+      3.2301607        0    173.1403228        
+     76.7809140        1     58.2752431        
+    245.4770760        2     21.1294304        
+     79.2892721        2      5.9581061        
+     26.4089460        2      1.4830272        
+  5      ----- p-f potential     -----
+      4.4690290        0     35.9438033        
+     43.6473399        1     11.2431083        
+     50.7565406        2      3.5340830        
+    -48.6730017        2      1.4120937        
+     58.7692653        2      1.2828947        
+  5      ----- d-f potential     -----
+      3.1020770        0     42.6276817        
+     36.5445090        1     14.5649330        
+     48.8041808        2      5.5505378        
+     19.4362361        2      1.8351485        
+      3.9855647        2      0.5288122        
+  SN-ECP GEN     46     3
+  5      ----- f potential     -----
+     -0.0665380        0      0.2169021        
+    -29.7469416        1     33.3097182        
+    -61.8307278        2      9.8846832        
+    -18.9410634        2      2.5019593        
+     -2.2384982        2      0.7833323        
+  5      ----- s-f potential     -----
+      2.9482777        0    155.6062293        
+     63.7614718        1     56.5145464        
+    230.5276788        2     22.6583648        
+     85.0713314        2      6.5837080        
+     28.8215644        2      1.5947702        
+  5      ----- p-f potential     -----
+      2.6941946        0     40.4666705        
+     44.8271079        1     13.6218440        
+     57.6223370        2      4.1092688        
+    -44.6853312        2      1.5046137        
+     57.1351815        2      1.3646762        
+  5      ----- d-f potential     -----
+      3.0259297        0     60.1915052        
+     39.5341071        1     20.4961142        
+     68.4686504        2      8.4438328        
+     29.1123764        2      2.6103790        
+      5.8428577        2      0.6501317        
+  SB-ECP GEN     46     3
+  6      ----- f potential     -----
+     -0.0692261        0    227.2723558        
+    -31.5434712        1     77.1429882        
+   -115.8407306        2     26.0116501        
+    -42.0224212        2      9.1421159        
+    -20.0463938        2      2.9281082        
+     -2.8796028        2      0.9231136        
+  5      ----- s-f potential     -----
+      2.9331303        0    116.0010888        
+     44.1104819        1     59.6755065        
+    228.0198050        2     27.6135849        
+     92.5610162        2      7.8045081        
+     31.2537244        2      1.6934418        
+  5      ----- p-f potential     -----
+      4.9947369        0     27.8050796        
+     38.0223860        1     10.3929685        
+     62.0520654        2      3.6381067        
+    -49.5489389        2      1.9839681        
+     51.3666000        2      1.5546562        
+  5      ----- d-f potential     -----
+      2.9738470        0     83.2183030        
+     41.6495825        1     30.3070501        
+    104.5919562        2     13.2014120        
+     42.3187857        2      3.6961611        
+      7.9413503        2      0.7711144        
+  TE-ECP GEN     46     3
+  6      ----- f potential     -----
+     -0.0719675        0    285.0323292        
+    -31.3740754        1     75.9105798        
+   -112.9711996        2     26.1827859        
+    -41.9599185        2      9.0635141        
+    -19.1274006        2      2.9543257        
+     -2.2917778        2      0.9169942        
+  5      ----- s-f potential     -----
+      2.9286639        0    188.3327504        
+     68.9626773        1     68.6306921        
+    276.9637063        2     27.4402195        
+    104.4701765        2      7.8835282        
+     34.8384805        2      1.8443362        
+  5      ----- p-f potential     -----
+      4.9732188        0     54.0822795        
+     38.8983634        1     28.5434714        
+    137.3139246        2     16.8660857        
+     69.7370323        2      5.5244176        
+     22.7487178        2      1.4625225        
+  5      ----- d-f potential     -----
+      3.1548778        0     34.5312648        
+     33.1153649        1     11.2699366        
+    210.3056318        2      2.2704361        
+   -321.1963746        2      1.7766992        
+    152.4853001        2      1.4251666        
+  I-ECP GEN     46     3
+  5      ----- f   potential     -----
+     -0.0747621        0      1.0715702        
+    -30.0811224        1     44.1936028        
+    -75.3722721        2     12.9367609        
+    -22.0563758        2      3.1956412        
+     -1.6979585        2      0.8589806        
+  5      ----- s-f potential     -----
+      2.9380036        0    127.9202670        
+     41.2471267        1     78.6211465        
+    287.8680095        2     36.5146237        
+    114.3758506        2      9.9065681        
+     37.6547714        2      1.9420086        
+  5      ----- p-f potential     -----
+      2.2222630        0     13.0035304        
+     39.4090831        1     76.0331404        
+    177.4075002        2     24.1961684        
+     77.9889462        2      6.4053433        
+     25.7547641        2      1.5851786        
+  5      ----- d-f potential     -----
+      7.0524360        0     40.4278108        
+     33.3041635        1     28.9084375        
+    186.9453875        2     15.6268936        
+     71.9688361        2      4.1442856        
+      9.3630657        2      0.9377235        
+  XE-ECP GEN     46     3
+  6      ----- f   potential     -----
+     -0.0776099        0    442.5138313        
+    -32.4332953        1    106.0005534        
+   -151.0853351        2     32.6123743        
+    -46.4977626        2      9.4739667        
+    -17.8080990        2      3.1147195        
+     -1.6481163        2      0.9194472        
+  5      ----- s-f potential     -----
+      2.9170308        0    148.7400724        
+     52.0620644        1     74.7687436        
+    293.2865672        2     34.1548413        
+    122.8610373        2      9.6899675        
+     41.6867277        2      2.1065947        
+  5      ----- p-f potential     -----
+      2.0968515        0      8.1495157        
+     30.0220424        1    113.9235640        
+    172.8757636        2     35.1266431        
+     67.7609841        2      8.1449579        
+     29.8970913        2      1.7187138        
+  5      ----- d-f potential     -----
+      7.0414935        0     49.4518384        
+     33.3391744        1     38.9480918        
+    249.2893077        2     20.0161598        
+     89.3576166        2      5.0614904        
+     11.6206324        2      1.0672963        
+  CS-ECP GEN     46     3
+  6      ----- f potential     -----
+     -0.0805109        0    636.7840130        
+    -33.4311448        1    147.8912446        
+   -193.9270887        2     42.5724911        
+    -56.8301433        2     11.2427883        
+    -18.6504975        2      3.5689707        
+     -2.5411399        2      1.3107462        
+  3      ----- s-f potential     -----
+      3.0499934        0     40.8648070        
+     37.9677514        1     10.9329329        
+     45.2340673        2      2.2294903        
+  4      ----- p-f potential     -----
+      4.6406951        0    118.8598171        
+     62.0313533        1     30.6847958        
+    129.5909481        2     10.1762999        
+     31.5186825        2      1.8272850        
+  4      ----- d-f potential     -----
+      7.3054473        0     16.0081704        
+     33.2785760        1      2.6488291        
+     53.1296948        2     14.5555672        
+     16.2118878        2      0.6703604        
+  BA-ECP GEN     46     3
+  6      ----- f potential     -----
+     -0.0834652        0    620.9690488        
+    -33.3257671        1    146.3648826        
+   -190.8607232        2     42.3207114        
+    -55.1984172        2     11.2135151        
+    -18.0236340        2      3.6963891        
+     -2.1978281        2      1.3169502        
+  4      ----- s-f potential     -----
+      2.8131160        0    140.3669200        
+     55.3050626        1     39.1436547        
+    149.5513402        2     12.9553493        
+     50.5078553        2      2.4308751        
+  5      ----- p-f potential     -----
+      4.9191812        0     99.4922261        
+     39.3872075        1     68.2711309        
+    335.0753584        2     36.1518181        
+    131.2535153        2     10.1051537        
+     36.3175025        2      2.0232648        
+  5      ----- d-f potential     -----
+      2.9764140        0     95.2752536        
+     46.0571141        1     34.6604608        
+    117.1658588        2     15.4891040        
+     54.0130815        2      5.0015895        
+     15.5784906        2      1.3236266        
+  LA-ECP GEN     46     3
+  6      ----- f potential     -----
+     -0.0864727        0    740.9166034        
+    -33.5168803        1    146.6571499        
+   -189.0709829        2     42.3585413        
+    -54.2464367        2     11.1647500        
+    -17.1351342        2      3.7556343        
+     -1.8092185        2      1.2871827        
+  4      ----- s-f potential     -----
+      2.8894943        0    125.6862665        
+     61.9521592        1     37.5768955        
+    164.8074490        2     12.6954869        
+     55.6954609        2      2.6276082        
+  4      ----- p-f potential     -----
+      4.5978412        0    179.1782247        
+     71.6719736        1     42.6195661        
+    175.8965896        2     13.4135957        
+     37.9136404        2      2.0950392        
+  5      ----- d-f potential     -----
+      2.9705496        0    104.7682064        
+     46.5239946        1     40.1930340        
+    137.4078176        2     18.5050798        
+     63.4945183        2      5.8051185        
+     18.0998654        2      1.4583220        
+  HF-ECP GEN     60     4
+  6      ----- g potential     -----
+    -60.0000000        1    685.5502931        
+   -588.4980664        2    151.8080485        
+   -199.7748820        2     38.0522099        
+    -77.2365121        2     10.2057544        
+    -14.6532596        2      3.4376287        
+     -0.6762204        2      0.8997539        
+  7      ----- s-g potential     -----
+      3.0000000        0    380.1096725        
+     31.3395532        1    724.6234505        
+   1251.6866138        2    281.4764603        
+    700.8900106        2     89.3590988        
+    266.0445845        2     22.9765773        
+    465.0196155        2      3.6700035        
+   -369.5404568        2      3.4703000        
+  5      ----- p-g potential     -----
+      2.0000000        0    228.3135771        
+     60.2776972        1     64.0878447        
+    192.2174983        2     20.5139289        
+    339.6460089        2      3.1643736        
+   -266.1066543        2      2.9880924        
+  5      ----- d-g potential     -----
+      3.0000000        0    143.7102399        
+     54.6576760        1     63.1859170        
+    217.5811427        2     32.0227449        
+    125.1363084        2     10.7674922        
+     37.6203195        2      3.1003260        
+  5      ----- f-g potential     -----
+      4.0000000        0     85.0395853        
+     49.6358393        1     41.8198820        
+    178.4657222        2     19.6524967        
+     82.0077173        2      6.0777120        
+      6.3063943        2      1.4776693        
+  TA-ECP GEN     60     4
+  6      ----- g potential     -----
+    -60.0000000        1    768.2267533        
+   -629.7129313        2    173.5940218        
+   -221.1750503        2     43.7042268        
+    -83.4769683        2     11.6181319        
+    -17.8370275        2      4.0314618        
+     -0.8160609        2      1.0905378        
+  7      ----- s-g potential     -----
+      3.0000000        0    365.4111559        
+     33.9798985        1    697.1300915        
+   1242.5776244        2    271.7810379        
+    706.1663884        2     87.3074263        
+    276.2464563        2     22.7492339        
+    513.2283132        2      3.8680082        
+   -412.9601916        2      3.6718891        
+  5      ----- p-g potential     -----
+      2.0000000        0    228.2107367        
+     61.8174163        1     62.6185462        
+    194.4975479        2     19.6465670        
+    307.8346671        2      3.3651559        
+   -230.6346381        2      3.1497236        
+  5      ----- d-g potential     -----
+      3.0000000        0    151.2755554        
+     55.2670978        1     67.9794911        
+    236.2957545        2     34.7474706        
+    134.4755072        2     11.5656425        
+     41.1401183        2      3.2930148        
+  5      ----- f-g potential     -----
+      4.0000000        0     82.8368029        
+     50.7533760        1     38.0719872        
+    162.0312877        2     17.4547034        
+     72.0619298        2      5.6785931        
+      6.0009895        2      1.5814041        
+  W-ECP GEN     60     4
+  6      ----- g potential     -----
+    -60.0000000        1    839.4489120        
+   -664.1987920        2    192.8532482        
+   -238.6143651        2     48.6651974        
+    -88.4192407        2     12.9221727        
+    -20.6062326        2      4.5748890        
+     -0.9283792        2      1.2681796        
+  7      ----- s-g potential     -----
+      3.0000000        0    313.4267518        
+     39.1192967        1    699.3155462        
+   1180.9692974        2    259.8923741        
+    728.9564210        2     85.4999980        
+    293.5591140        2     22.7635925        
+    562.6731493        2      4.0764317        
+   -457.3807185        2      3.8827162        
+  5      ----- p-g potential     -----
+      2.0000000        0    224.3926424        
+     63.8948393        1     61.6736931        
+    205.8901837        2     19.1469043        
+    312.1427153        2      3.5565710        
+   -231.3961281        2      3.3263210        
+  5      ----- d-g potential     -----
+      3.0000000        0    161.5278958        
+     55.3315256        1     75.5814607        
+    267.1976653        2     38.9115852        
+    146.8485578        2     12.5426271        
+     44.1055243        2      3.4615187        
+  5      ----- f-g potential     -----
+      4.0000000        0     91.2102727        
+     50.3065523        1     45.4152756        
+    190.7363098        2     22.0452967        
+     91.7605552        2      6.9810413        
+      8.4247312        2      1.8380480        
+  RE-ECP GEN     60     4
+  6      ----- g potential     -----
+     -0.1497104        1   1833.8143836        
+  -1669.2555781        2    414.0589456        
+   -346.6661129        2     59.0349540        
+    -96.6684892        2     11.9177094        
+    -11.0738567        2      3.6531764        
+     -0.5798552        2      1.2764184        
+  7      ----- s-g potential     -----
+      3.1497104        0    247.0023085        
+     45.2969969        1    631.7195767        
+   1116.4279585        2    249.2800441        
+    793.6419065        2     84.2947315        
+    318.2099193        2     23.0407912        
+    586.3105025        2      4.2997111        
+   -475.6904164        2      4.0974390        
+  5      ----- p-g potential     -----
+      2.1497104        0    193.6897726        
+     64.2295736        1     56.9308394        
+    218.8504607        2     18.7227602        
+    344.4189168        2      3.7340020        
+   -260.4727267        2      3.5127483        
+  5      ----- d-g potential     -----
+      3.1497104        0    126.2340824        
+     46.5714868        1     80.7090826        
+    304.3834595        2     42.5529714        
+    157.1589652        2     13.4074494        
+     48.5701211        2      3.6904228        
+  5      ----- f-g potential     -----
+      3.9569610        0    112.7786459        
+     52.3586781        1     56.7815968        
+    236.0340824        2     28.5831966        
+    116.5787266        2      8.5734158        
+     11.1050530        2      2.0892721        
+  OS-ECP GEN     60     4
+  5      ----- g potential     -----
+     -0.1537293        1    848.8743292        
+  -1642.9695542        2    400.4640004        
+   -336.7954567        2     58.0359466        
+    -94.9839027        2     12.0454682        
+    -10.3516218        2      3.4442512        
+  6      ----- s-g potential     -----
+      3.1537293        0    216.2672249        
+     25.9948233        1    343.8582098        
+    859.4865756        2    136.0523606        
+    380.1376504        2     35.0389457        
+    242.7010666        2      4.4724452        
+   -125.1753107        2      3.7675119        
+  5      ----- p-g potential     -----
+      2.1537293        0    326.0600134        
+     62.8684275        1     95.6330206        
+    293.1322987        2     32.5261043        
+    264.4648388        2      3.7699730        
+   -172.6420314        2      3.3895859        
+  5      ----- d-g potential     -----
+      3.1537293        0    131.0319929        
+     45.3399200        1     89.2464903        
+    335.8575032        2     46.3976980        
+    166.3037457        2     14.1866053        
+     49.0237825        2      3.7668843        
+  5      ----- f-g potential     -----
+      3.9557982        0    134.7368148        
+     51.9521957        1     73.8771318        
+    314.2555866        2     38.1030425        
+    147.1839894        2     10.5337695        
+     13.9622947        2      2.3338246        
+  IR-ECP GEN     60     4
+  5      ----- g potential     -----
+     -0.1578014        1    823.5880147        
+  -1517.5270446        2    364.6613336        
+   -316.5306529        2     55.7082801        
+    -91.8880941        2     12.0464544        
+     -9.2241773        2      3.5120610        
+  6      ----- s-g potential     -----
+      3.1578014        0    188.0490770        
+     26.8322577        1    340.4194712        
+    800.4250007        2    128.2373673        
+    369.4050683        2     33.8644961        
+    242.4171899        2      4.7560005        
+   -118.2173282        2      3.9649974        
+  5      ----- p-g potential     -----
+      2.1578014        0    289.7291139        
+     61.9678610        1     87.4633789        
+    269.0581986        2     30.4363766        
+    231.1654793        2      4.0553412        
+   -133.6952667        2      3.5525341        
+  5      ----- d-g potential     -----
+      3.1578014        0    136.4017106        
+     45.9349803        1     95.0776925        
+    359.0344668        2     49.2258410        
+    176.4740119        2     15.0874145        
+     54.5155286        2      4.0405764        
+  5      ----- f-g potential     -----
+      3.9546197        0    127.3507908        
+     52.9773655        1     66.2364374        
+    274.8643383        2     34.4299229        
+    137.2047338        2     10.1995721        
+     14.8633305        2      2.5409702        
+  PT-ECP GEN     60     4
+  5      ----- g potential     -----
+     -0.1619268        1    728.9394056        
+  -1320.2873852        2    320.6567800        
+   -298.3178135        2     52.8680174        
+    -87.5837065        2     12.0280128        
+     -8.1493274        2      3.5238913        
+  6      ----- s-g potential     -----
+      2.7334218        0    409.4437358        
+     59.7024329        1    274.5419231        
+    891.4589550        2    127.5658570        
+    368.4467656        2     32.9036631        
+    238.0263090        2      5.0593880        
+   -107.0556454        2      4.1506556        
+  4      ----- p-g potential     -----
+      1.8878568        0    466.1728892        
+     76.0138629        1    120.7888259        
+    343.5511116        2     36.4118791        
+    119.4911786        2      5.6985408        
+  5      ----- d-g potential     -----
+      2.9343678        0    249.5650763        
+     59.3306571        1    126.6678585        
+    452.4445194        2     63.1430586        
+    210.4769479        2     17.9059470        
+     58.6254112        2      4.2239373        
+  5      ----- f-g potential     -----
+      3.9534253        0    121.8158799        
+     53.8555182        1     60.8757030        
+    247.4305133        2     31.4767147        
+    127.8187976        2      9.8811751        
+     15.3772046        2      2.7319874        
+  AU-ECP GEN     60     4
+  5      ----- g potential     -----
+    -60.0000000        1    622.6287956        
+   -555.5292312        2    136.2843607        
+   -168.0019785        2     33.1549781        
+    -63.0399875        2      9.9894895        
+     -4.2516681        2      3.0481312        
+  6      ----- s-g potential     -----
+      3.0000000        0    194.7374304        
+     38.6020880        1    351.5327447        
+    864.8370727        2    122.3270402        
+    374.9935520        2     32.0914617        
+    289.7910100        2      5.2451812        
+   -152.4532773        2      4.4916223        
+  4      ----- p-g potential     -----
+      2.0000000        0    420.6158801        
+     73.8885625        1    109.4417815        
+    326.6729872        2     34.1714280        
+    126.5814591        2      5.9879750        
+  5      ----- d-g potential     -----
+      3.0000000        0    219.2666158        
+     55.6793149        1    122.7297786        
+    449.1987335        2     63.1063369        
+    215.0269091        2     18.3684520        
+     64.0840995        2      4.4972844        
+  5      ----- f-g potential     -----
+      4.0000000        0    108.5506037        
+     51.8065335        1     56.4795527        
+    231.2183113        2     29.2069159        
+    119.0047386        2      9.5440543        
+     15.3424188        2      2.8965118        
+  PB-ECP GEN     78     4
+  6      ----- g potential     -----
+     -0.1789605        0    376.5803786        
+    -54.3972337        1     86.4840014        
+   -199.7061759        2     26.6784276        
+    -79.1223941        2      9.4261986        
+    -24.9869020        2      2.7101719        
+     -4.4397939        2      0.8792031        
+  6      ----- s-g potential     -----
+      2.8115386        0    132.4248796        
+     65.0367205        1     47.2376044        
+    212.7868545        2     17.6312727        
+     72.1053175        2      5.4744712        
+     33.0140940        2      1.2634856        
+     -5.7708461        2      0.7651447        
+  6      ----- p-g potential     -----
+      4.8754911        0     67.8966454        
+     63.9148102        1     24.9898225        
+    148.1064358        2     10.7052939        
+     47.3106301        2      3.2792568        
+     21.0306702        2      0.8452522        
+     -7.0930772        2      0.6416245        
+  6      ----- d-g potential     -----
+      3.2161388        0     68.8336005        
+     55.7386086        1     24.2815874        
+    121.4168351        2      9.4532762        
+     19.3456064        2      2.4788185        
+     15.3675168        2      2.4789161        
+      6.1298724        2      0.5551738        
+  6      ----- f-g potential     -----
+      4.1353682        0    128.1021322        
+     67.5128446        1     54.8029154        
+    258.7373107        2     24.5529308        
+    113.2478264        2      8.1144792        
+     34.1680201        2      1.6931290        
+     -6.5531956        2      0.7670500        
+  BI-ECP GEN     78     4
+  6      ----- g potential     -----
+     -0.1833520        0    223.3091971        
+    -53.3389492        1     86.4699216        
+   -201.9117841        2     26.8492203        
+    -76.3115295        2      9.3823622        
+    -24.2578468        2      2.7614708        
+     -4.0319700        2      0.9423923        
+  6      ----- s-g potential     -----
+      2.7916510        0    155.9783914        
+     71.8553817        1     54.4591139        
+    252.4327444        2     20.1794558        
+     89.6045109        2      6.1273104        
+     61.1744136        2      1.1805983        
+    -33.0471906        2      0.9932464        
+  6      ----- p-g potential     -----
+      4.8462914        0     74.5091386        
+     65.6883396        1     28.0487647        
+    166.2619673        2     12.0168508        
+     54.1393329        2      3.6914327        
+     28.6204880        2      0.9080824        
+    -12.3452204        2      0.7394617        
+  6      ----- d-g potential     -----
+      3.2183832        0     68.0856679        
+     54.8952681        1     23.7250355        
+    116.5540708        2      9.2437053        
+     69.0760662        2      1.8452292        
+    -72.6410870        2      1.2209859        
+     38.9400055        2      0.8740861        
+  6      ----- f-g potential     -----
+      3.9864075        0    134.3538983        
+     63.0262078        1     51.5812797        
+     92.5259959        2     54.9307284        
+    240.9973672        2     17.0475346        
+     62.1844577        2      3.7702824        
+     -1.7251134        2      0.7670500        
+  U-ECP GEN     78     4
+  7      ----- g potential     -----
+     -0.2252710        0      4.5162725        
+    -53.7805701        1    368.6876952        
+   -516.3052053        2    112.7576708        
+   -202.6738222        2     30.2032915        
+    -62.5644049        2     10.2978355        
+    -21.5589559        2      3.4910515        
+     -1.5390545        2      1.2668391        
+  8      ----- s-g potential     -----
+      2.6118816        0    257.3529442        
+     81.2356657        1     83.1605428        
+    353.2465364        2     29.7200513        
+    311.9292234        2      6.2482132        
+   -423.5525795        2      4.1634672        
+    293.2160836        2      3.0001511        
+    -21.3442552        2      1.5600000        
+      1.6692548        2      0.8700000        
+  8      ----- p-g potential     -----
+      1.8414388        0    288.7298250        
+     75.7054755        1     76.7838420        
+    288.4601674        2     24.8252003        
+    250.7289429        2      5.1248735        
+   -318.8968061        2      3.7603300        
+    160.0844924        2      2.9761706        
+     33.7801891        2      1.9919017        
+     -2.0699997        2      0.9738832        
+  6      ----- d-g potential     -----
+      2.9082064        0    350.4699407        
+     74.3577628        1    166.7091301        
+    587.1140329        2     85.8206119        
+    317.2543679        2     26.9842255        
+    102.8422117        2      7.1565592        
+     22.4214278        2      1.2840855        
+  7      ----- f-g potential     -----
+      3.9350339        0    251.2670754        
+     64.9434120        1    108.0109218        
+    265.1804776        2    114.8021895        
+    389.4284090        2     39.9112545        
+    153.4767221        2     11.9039784        
+     12.0807444        2      3.3850772        
+     -1.3510138        2      1.2677607        
+  NP-ECP GEN     78     4
+  7      ----- g potential     -----
+     -0.2301948        0      1.0431273        
+    -45.5001281        1    317.0276068        
+   -476.5653727        2    105.9200340        
+   -195.8477182        2     29.2113328        
+    -57.8033664        2      9.9327688        
+    -19.7434600        2      3.5043448        
+     -1.0669649        2      1.2134041        
+  8      ----- s-g potential     -----
+      2.6018728        0    196.0114236        
+     85.3028322        1     73.6959968        
+    372.5884385        2     27.9519076        
+    355.0393107        2      6.0935022        
+   -432.4088966        2      4.2584097        
+    302.4498093        2      2.8448358        
+    -66.3372789        2      1.7818317        
+     12.2063863        2      1.3126743        
+  8      ----- p-g potential     -----
+      1.8377632        0    217.8757476        
+     86.7559344        1     66.6524691        
+    326.3012014        2     22.5472363        
+    387.4745274        2      4.5144361        
+   -616.3588385        2      3.4261582        
+    367.0476211        2      2.7548460        
+     -9.1550781        2      1.3259098        
+      1.6218141        2      0.9946893        
+  6      ----- d-g potential     -----
+      2.9061609        0    250.4999784        
+     64.0478688        1    130.4032828        
+    474.2855599        2     75.7534615        
+    299.0115220        2     25.4552546        
+     97.8146796        2      6.9093550        
+     25.0346351        2      1.3686266        
+  7      ----- f-g potential     -----
+      3.9473741        0    283.1561694        
+     52.4962633        1    135.8585924        
+    488.6480804        2    146.7224456        
+    462.0665382        2     51.0645382        
+    194.5802503        2     15.2903208        
+     21.9961143        2      4.1160496        
+     -1.8379283        2      1.5118960        
+  PU-ECP GEN     78     4
+  7      ----- g potential     -----
+     -0.2351718        0      1.1870180        
+    -45.4081287        1    320.1179395        
+   -476.8288295        2    107.2740348        
+   -197.0071237        2     29.6307950        
+    -56.9841048        2      9.9804742        
+    -19.1954418        2      3.5674691        
+     -0.9423357        2      1.1899211        
+  8      ----- s-g potential     -----
+      2.5916624        0    158.8492184        
+     62.4462904        1     89.9329314        
+    385.9249706        2     36.8569318        
+    404.6175823        2      7.1893383        
+   -541.9641571        2      4.8242287        
+    383.4766370        2      3.0576352        
+    -95.8282398        2      1.9204674        
+     17.9027180        2      1.4002527        
+  8      ----- p-g potential     -----
+      1.8340376        0    205.3583344        
+     64.7077751        1     73.8574796        
+    280.8660049        2     26.4937731        
+    385.3008049        2      4.9612440        
+   -769.1010536        2      3.6464185        
+    517.2637286        2      2.8852656        
+    -34.2942762        2      1.6913216        
+      2.9362540        2      1.1024245        
+  6      ----- d-g potential     -----
+      2.9040916        0    236.9374680        
+     65.4380888        1    121.7063684        
+    435.7288038        2     70.3224181        
+    287.9883569        2     24.5731063        
+     96.3047822        2      6.7775423        
+     26.8450498        2      1.4529015        
+  7      ----- f-g potential     -----
+      3.9474354        0    274.1162206        
+     52.9504481        1    131.2837669        
+    467.0766447        2    143.5982730        
+    454.3389567        2     49.6887665        
+    189.3942401        2     14.9990763        
+     21.1763500        2      4.2478349        
+     -1.8564639        2      1.5745337        
+  $END
diff --git a/Scripts/commands.rb b/Scripts/commands.rb
new file mode 100755 (executable)
index 0000000..920e949
--- /dev/null
@@ -0,0 +1,442 @@
+#
+#  commands.rb
+#
+#  Created by Toshi Nagata on 2008/06/28.
+#  Copyright 2008 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+class Molecule
+
+  def cmd_assign_residue
+    sel = self.selection
+       if sel.length == 0
+         sel = self.atom_group
+       end
+       atoms = sel.inspect.sub!("IntGroup[", "").sub!("]", "")
+    hash = RubyDialog.run {
+         layout(2,
+               item(:text, :title=>"New residue name/number\n(like \"RES.1\")\nfor atoms #{atoms}"),
+           item(:textfield, :width=>120, :tag=>"residue"))
+    }
+    if hash
+         residue = hash["residue"]
+         assign_residue(sel, residue)
+       end
+  end
+
+  def cmd_offset_residue
+    sel = self.selection
+       if sel.length == 0
+         sel = self.atom_group
+       end
+       atoms = sel.inspect.sub!("IntGroup[", "").sub!("]", "")
+    hash = RubyDialog.run {
+         layout(2,
+               item(:text, :title=>"Offset residue number:\nfor atoms #{atoms}"),
+           item(:textfield, :width=>120, :tag=>"offset"))
+    }
+       if hash
+         offset = hash["offset"].to_i
+         offset_residue(sel, offset)
+       end
+  end
+   
+  def cmd_sort_by_residue
+    sel = self.selection
+       if sel.length == 0
+         sel = self.atom_group
+       end
+       sorted = sel.sort_by { |i| [self.atoms[i].res_seq, i] }
+       ary = []
+       j = 0
+       (0...natoms).each { |i|
+         if sel.include?(i)
+           ary << sorted[j]
+               j += 1
+               break if j >= sorted.length
+         else
+           ary << i
+         end
+       }
+       self.reorder_atoms(ary)
+  end
+
+  def save_gamess_with_ecp(filename)
+    if natoms == 0
+      raise MolbyError, "cannot save GAMESS input; the molecule is empty"
+    end
+    fp = open(filename, "wb")
+       now = Time.now.to_s
+       fp.print <<end_of_header
+!  GAMESS input
+!  Generated by Molby at #{now}
+ $CONTRL COORD=UNIQUE EXETYP=RUN ICHARG=0
+         ICUT=20 INTTYP=HONDO ITOL=30
+         MAXIT=200 MOLPLT=.T. MPLEVL=0
+         MULT=1 QMTTOL=1e-08 RUNTYP=OPTIMIZE
+                ECP=READ 
+         SCFTYP=RHF UNITS=ANGS                  $END
+ $SCF    CONV=1.0E-06 DIRSCF=.T. FDIFF=.T. DAMP=.T. $END
+ $STATPT NSTEP=400 OPTTOL=1.0E-06               $END
+ $SYSTEM MEMDDI=0 MWORDS=16 TIMLIM=50000        $END
+ $BASIS  EXTFIL=.T. GBASIS=321LAN               $END
+ $GUESS  GUESS=HUCKEL                           $END
+!
+ $DATA
+ #{name}
+ C1
+end_of_header
+       ecp = Hash.new
+       each_atom { |ap|
+               fp.printf " %-6s %4d %10.6f %10.6f %10.6f\n", ap.name, ap.atomic_number, ap.r.x, ap.r.y, ap.r.z
+               if ap.atomic_number >= 11
+                       ecp[ap.element.upcase] = 1
+               end
+       }
+       fp.print " $END\n"
+       #  Read ECP from file
+       efp = open($startup_dir + "/gamess_ecp.txt", "rb")
+       elem = ""
+       ecplines = nil
+       while 1
+               line = efp.gets
+               if line == nil || line =~ /(\w\w)-ECP /
+                       if ecplines != nil
+                               print "#{line}"
+                               ecp[elem] = ecplines
+                       end
+                       break if line == nil
+                       elem = $1.upcase
+                       if ecp.member?(elem)
+                               ecplines = ""
+                       else
+                               ecplines = nil
+                       end
+                       print "#{line}: #{'->ignore' if ecplines == nil}\n"
+               end
+               if ecplines != nil
+                       ecplines += line
+               end
+       end
+       efp.close
+       #  Create ECP section
+       fp.print " $ECP\n"
+       each_atom { |ap|
+               elem = ap.element.upcase
+               if ecp.member?(elem)
+                       fp.print ecp[elem]
+                       ecp[elem] = "#{elem}-ECP\n"
+               else
+                       fp.print "#{elem}-ECP NONE\n"
+               end
+       }
+       fp.print " $END\n"      
+       fp.close
+  end
+  
+  def cmd_create_gamess
+    hash = RubyDialog.run {
+         layout(1,
+           item(:text, :title=>"Create GAMESS input with LANL2DZ effective core potentials"))
+    }
+    if hash
+      fname = File.basename(self.path, ".*") + ".inp"
+         fname = RubyDialog.save_panel(nil, self.dir, fname)
+         if fname
+               save_gamess_with_ecp(fname)
+         end
+       end
+  end
+
+  def cmd_create_cube
+    grid = default_MO_grid
+       if grid == nil
+         RubyDialog.run {
+           layout(1,
+                 item(:text, :title=>"This molecule does not contain MO information."))
+         }
+         return
+       end
+    mos = selected_MO
+       if mos == nil || mos.length == 0
+      RubyDialog.run {
+           layout(1,
+                 item(:text, :title=>"Please select MO(s) in the MO Info table."))
+      }
+         return
+       end
+       hash = RubyDialog.run {
+         layout(1,
+           item(:text, :title=>"Please specify cube dimensions (in bohr unit):"),
+           layout(4,
+                 item(:text, :title=>"Origin"),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"originx", :value=>sprintf("%.6f", grid[0].x)),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"originy", :value=>sprintf("%.6f", grid[0].y)),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"originz", :value=>sprintf("%.6f", grid[0].z)),
+                 item(:text, :title=>"Delta"),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"deltax", :value=>sprintf("%.6f", grid[1])),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"deltay", :value=>sprintf("%.6f", grid[2])),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"deltaz", :value=>sprintf("%.6f", grid[3])),
+                 item(:text, :title=>"Step"),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"stepx", :value=>grid[4].to_s),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"stepy", :value=>grid[5].to_s),
+                 item(:textfield, :width=>100, :height=>20, :tag=>"stepz", :value=>grid[6].to_s)))
+       }
+       if hash
+         origin = Vector3D[hash["originx"], hash["originy"], hash["originz"]]
+         dx = hash["deltax"]
+         dy = hash["deltay"]
+         dz = hash["deltaz"]
+         nx = hash["stepx"]
+         ny = hash["stepy"]
+         nz = hash["stepz"]
+         basename = File.basename(self.path, ".*")
+         filenames = []
+         mos.each { |n|
+           fname = RubyDialog.save_panel("Cube file name for MO #{n}", self.dir, basename + "_#{n}.cube", "Gaussian cube file (*.cube)|*.cube")
+               if !fname
+                 filenames.clear
+                 break
+               end
+               filenames.push([n, fname])
+         }
+         filenames.each { |pair|
+           n = pair[0]
+           show_progress_panel("Creating cube file for MO #{n}...")
+               cubegen(pair[1], n, origin, dx, dy, dz, nx, ny, nz, true)
+           hide_progress_panel
+         }
+       end
+  end
+
+  def RubyDialog.list_remote_files(host, directory)
+#    list = `ssh #{host} "env COLUMNS=40 ls -C #{directory}"`
+    list = `ssh #{host} "ls #{directory}"`
+  end
+  
+  def Molecule.cmd_load_remote(mol)  #  mol is not used
+    hash = RubyDialog.run {
+         def action(n)
+           if n == 0 || n == 1
+                 self.each_item { |i|
+                   if (type = attr(i, :type)) == :textfield || type == :textview
+                     val = value(i)
+                     tag = attr(i, :tag)
+                     set_global_settings("load_remote.#{tag}", val)
+                       end
+                 }
+               end
+               if n == 0  #  OK
+                 local = File.expand_path(value("local"))
+                 if value("local") != ""
+                   #  Check whether the local file already exists
+                       exist = []
+                       sfile = value("sfile")
+                       cfile = value("cfile")
+                       if sfile != "" && FileTest.exist?("#{local}/#{sfile}")
+                         exist.push(sfile)
+                       end
+                       if cfile != "" && FileTest.exist?("#{local}/#{cfile}")
+                         exist.push(cfile)
+                       end
+                       if exist.length > 0
+                         if exist.length == 1
+                           msg = "The file #{exist[0]} already exists"
+                         else
+                           msg = "The files " + exist.join(", ") + " already exist"
+                         end
+                         msg += " in directory #{local}. Overwrite?"
+                         hash = RubyDialog.run {
+                           layout(1, item(:text, :title=>msg, :width=>240, :height=>60))
+                         }
+                         return if !hash  #  No call of super -> dialog is not dismissed
+                       end
+                 end
+               elsif n != 0 && n != 1
+             if value("host") != "" && value("directory") != "" && value("sfile") != ""
+                   set_attr(0, :enabled=>true)
+                 else
+                   set_attr(0, :enabled=>false)
+                 end
+           end
+           super
+         end
+         layout(2,
+               item(:text, :title=>"Remote host"),
+               item(:textfield, :width=>280, :height=>20, :tag=>"host"),
+               item(:text, :title=>"Directory"),
+               item(:textfield, :width=>280, :height=>20, :tag=>"directory"),
+               item(:text, :title=>"Structure File"),
+               item(:textfield, :width=>280, :height=>20, :tag=>"sfile"),
+               item(:text, :title=>"Coordinate File"),
+               item(:textfield, :width=>280, :height=>20, :tag=>"cfile"),
+               item(:text, :title=>"File List"),
+               item(:textview, :width=>280, :height=>80, :tag=>"list", :editable=>false),
+               nil,
+               [ item(:button, :title=>"Update",
+                       :action=>proc { |n| 
+                         list = RubyDialog.list_remote_files(value("host"), value("directory"))
+                         set_value("list", list)
+                       }
+                 ), {:align=>:right} ],
+       #       item(:checkbox, :title=>"Copy files to a local directory"),
+       #       nil,
+               item(:text, :title=>"Local directory"),
+               item(:textfield, :width=>280, :height=>20, :tag=>"local"),
+               nil,
+               [ item(:button, :title=>"Choose...",
+                   :action=>proc { |n|
+                         dir = RubyDialog.open_panel(nil, nil, nil, true)
+                         if dir
+                           set_value("local", dir)
+                         end
+                   }
+                 ), {:align=>:right} ]
+       #       layout(1, 2, 1, 0)
+         )
+         set_attr(0, :enabled=>false)
+         self.each_item { |i|
+               tag = attr(i, :tag)
+           if (type = attr(i, :type)) == :textfield || type == :textview
+                 val = get_global_settings("load_remote.#{tag}")
+                 if (val != nil)
+                       set_value(tag, val)
+                 end
+               end
+         }
+    }
+       if hash
+         sfile = hash["sfile"]
+         cfile = hash["cfile"]
+         host = hash["host"]
+         directory = hash["directory"]
+         local = hash["local"]
+         if local == ""
+           local = `mktemp -d /tmp/MolbyRemoteLoad.XXXXXX`.chomp
+               if $? != 0
+                 raise "Cannot create a temporary directory"
+               end
+         end
+         local = File.expand_path(local)
+         if cfile == "" && sfile =~ /\.psf$/
+           cfile = sfile.sub(/\.psf$/, ".pdb")
+         end
+         if cfile == ""
+           files = sfile
+         else
+           files = "{#{sfile},#{cfile}}"
+         end
+         show_progress_panel("Fetching remote file(s)...")
+         if !system("scp '#{host}:#{directory}/#{files}' '#{local}'")
+           raise "Cannot copy remote files"
+         end
+         hide_progress_panel()
+         mol = Molecule.open("#{local}/#{sfile}")
+         if cfile != "" && FileTest.exist?("#{local}/#{cfile}")
+           mol.undo_enabled = false
+           mol.molload("#{local}/#{cfile}")
+               mol.undo_enabled = true
+         end
+       end
+  end
+
+  def cmd_delete_frames
+    n = nframes
+    return if n == 0
+       hash = RubyDialog.run {
+         layout(2,
+           item(:text, :title=>"Start"),
+           item(:textfield, :width=>120, :tag=>"start", :value=>"0"),
+               item(:text, :title=>"End"),
+               item(:textfield, :width=>120, :tag=>"end", :value=>(n - 1).to_s),
+               item(:text, :title=>"Keeping frames every..."),
+               -1,
+               item(:text, :title=>"Step"),
+               item(:textfield, :width=>120, :tag=>"step", :value=>"0"))
+       }
+       if hash
+         sframe = Integer(hash["start"])
+         eframe = Integer(hash["end"])
+         step = Integer(hash["step"])
+         return if sframe > eframe
+         eframe = n - 1 if eframe >= n
+         fgroup = IntGroup[sframe..eframe]
+         if step > 0
+           while sframe <= eframe
+                 fgroup.delete(sframe)
+                 sframe += step
+               end
+         end
+         remove_frames(fgroup)
+       end
+  end
+
+  def cmd_solvate
+    #  Find molecule with unit cell defined
+    solv = []
+       solvnames = []
+       Molecule.list.each { |m|
+         if m.cell != nil
+           solv.push m
+               solvnames.push m.name
+         end
+    }
+       if solvnames.length == 0
+         RubyDialog.run {
+           layout(1,
+                 item(:text, :title=>"Please open a molecule file containing a solvent box."))
+         }
+         return
+       end
+       hash = RubyDialog.run {
+         layout(1,
+           item(:text, :title=>"Choose solvent box:"),
+           item(:popup, :subitems=>solvnames, :tag=>"solvent"),
+               item(:text, :title=>"Box offset\n(Negative numbers for absolute sizes)"),
+               layout(2,
+                 item(:text, :title=>"x"),
+                 item(:textfield, :width=>"120", :tag=>"x", :value=>"10.0"),
+                 item(:text, :title=>"y"),
+                 item(:textfield, :width=>"120", :tag=>"y", :value=>"10.0"),
+                 item(:text, :title=>"z"),
+                 item(:textfield, :width=>"120", :tag=>"z", :value=>"10.0")),
+               item(:text, :title=>"Exclusion limit distance:"),
+               item(:textfield, :width=>"120", :tag=>"limit", :value=>"3.0"))
+       }
+       if hash
+         solvate(solv[hash["solvent"]], [hash["x"], hash["y"], hash["z"]], hash["limit"])
+       end
+  end
+     
+  def cmd_show_graphite
+    n = self.show_graphite
+       hash = RubyDialog.run("Show Graphite") {
+      layout(1,
+           item(:text, :title=>"Number of graphite rings for each direction:\n(0 to suppress display)"),
+           item(:textfield, :width=>120, :tag=>"graphite", :value=>n.to_s))
+       }
+       if hash
+         self.show_graphite(hash["graphite"])
+       end
+  end
+  
+end
+
+register_menu("Assign residue...", :cmd_assign_residue)
+register_menu("Offset residue...", :cmd_offset_residue)
+register_menu("Sort by residue", :cmd_sort_by_residue)
+register_menu("", "")
+register_menu("Load remote...", :cmd_load_remote)
+#register_menu("Create GAMESS input...", :cmd_create_gamess)
+#register_menu("Create Cube file...", :cmd_create_cube)
+register_menu("", "")
+register_menu("Delete Frames...", :cmd_delete_frames)
+register_menu("Solvate...", :cmd_solvate)
diff --git a/Scripts/default.par b/Scripts/default.par
new file mode 100644 (file)
index 0000000..8e607a1
--- /dev/null
@@ -0,0 +1,2 @@
+include "gaff.par"
+include "parm99.par"
diff --git a/Scripts/dialog.rb b/Scripts/dialog.rb
new file mode 100755 (executable)
index 0000000..aeb03d9
--- /dev/null
@@ -0,0 +1,39 @@
+#
+#  dialog.rb
+#
+#  Created by Toshi Nagata on 2008/06/27.
+#  Copyright 2008 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+class RubyDialog
+
+  def self.run(*args, &block)
+    obj = RubyDialog.new(*args)
+    obj.instance_eval(&block)
+    obj.run
+  end
+
+  alias initialize_orig initialize
+
+  def initialize(*args, &block)
+    initialize_orig(*args)
+       instance_eval(&block) if block
+  end
+
+  def value(tag)
+    attr(tag, :value)
+  end
+  
+  def set_value(tag, value)
+    set_attr(tag, :value=>value)
+  end
+  
+end
diff --git a/Scripts/dispatom.par b/Scripts/dispatom.par
new file mode 100644 (file)
index 0000000..34c425f
--- /dev/null
@@ -0,0 +1,111 @@
+;  Atom display parameters
+;  symbol, atomic number, radius, color (r,g,b), weight, fullname
+;  The atomic radii are taken from http://www.webelements.com/.
+;  The 'covalent' radii are taken where available; otherwise,
+;  the 'atomic' radii are used.
+
+dispatom Du   0   0.35  0.000  1.000  0.000  0.0000   dummy
+dispatom H    1   0.37  0.564  1.000  0.965  1.0079   hydrogen
+dispatom He   2   0.32  0.500  0.500  0.500  4.0026   helium
+dispatom Li   3   1.34  0.500  0.500  0.500  6.9410   lithium
+dispatom Be   4   0.90  0.500  0.500  0.500  9.0122   beryllium
+dispatom B    5   0.82  0.500  0.500  0.500  10.811   boron
+dispatom C    6   0.77  0.784  0.784  0.784  12.0107  carbon
+dispatom N    7   0.75  0.588  0.706  1.000  14.0067  nitrogen
+dispatom O    8   0.73  1.000  0.569  0.592  15.9994  oxygen
+dispatom F    9   0.71  0.900  1.000  0.850  18.9984  fluorine
+dispatom Ne  10   0.69  0.500  0.500  0.500  20.1797  neon
+dispatom Na  11   1.54  0.900  0.700  0.400  22.9898  sodium
+dispatom Mg  12   1.30  1.000  0.600  0.450  24.3050  magnesium
+dispatom Al  13   1.18  0.600  0.750  0.800  26.9815  aluminum
+dispatom Si  14   1.11  0.150  0.150  0.900  28.0855  silicon
+dispatom P   15   1.06  0.800  0.800  0.300  30.9738  phosphorous
+dispatom S   16   1.02  1.000  1.000  0.200  32.066   sulfur
+dispatom Cl  17   0.99  0.100  1.000  0.000  35.4527  chlorine
+dispatom Ar  18   0.97  0.500  0.500  0.500  39.948   argon
+dispatom K   19   1.96  0.800  0.450  0.800  39.0983  potassium
+dispatom Ca  20   1.74  0.620  0.660  0.800  40.078   calcium
+dispatom Sc  21   1.44  0.580  0.500  0.620  44.9559  scandium
+dispatom Ti  22   1.36  0.700  0.780  0.930  47.867   titanium
+dispatom V   23   1.25  0.950  0.400  0.360  50.9415  vanadium
+dispatom Cr  24   1.27  0.500  0.400  0.500  51.9961  chromium
+dispatom Mn  25   1.39  0.750  0.250  0.710  54.9380  manganese
+dispatom Fe  26   1.25  0.770  0.560  0.240  55.845   iron
+dispatom Co  27   1.26  0.120  0.080  0.670  58.9332  cobalt
+dispatom Ni  28   1.21  0.700  0.700  0.700  58.6934  nickel
+dispatom Cu  29   1.38  0.120  0.140  0.810  63.546   copper
+dispatom Zn  30   1.31  0.600  0.550  0.600  65.39    zinc
+dispatom Ga  31   1.26  0.620  0.930  0.650  69.723   gallium
+dispatom Ge  32   1.22  0.490  0.300  0.400  72.61    germanium
+dispatom As  33   1.19  0.460  0.760  0.500  74.9216  arsenic
+dispatom Se  34   1.16  0.700  0.950  0.440  78.96    selenium
+dispatom Br  35   1.14  0.680  0.460  0.250  79.904   bromine
+dispatom Kr  36   1.10  0.500  0.500  0.500  83.80    krypton
+dispatom Rb  37   2.11  1.000  0.230  0.260  85.4678  rubidium
+dispatom Sr  38   1.92  1.000  0.200  0.950  87.62    strontium
+dispatom Y   39   1.62  0.500  0.500  0.500  88.9059  yttrium
+dispatom Zr  40   1.48  0.500  0.500  0.500  91.224   zirconium
+dispatom Nb  41   1.37  0.500  0.500  0.500  92.9064  niobium
+dispatom Mo  42   1.45  0.500  0.500  0.500  95.94    molybdenum
+dispatom Tc  43   1.56  0.500  0.500  0.500  98       technetium
+dispatom Ru  44   1.26  0.500  0.500  0.500  101.07   ruthenium
+dispatom Rh  45   1.35  0.500  0.500  0.500  102.9055 rhodium
+dispatom Pd  46   1.31  0.500  0.500  0.500  106.42   palladium
+dispatom Ag  47   1.53  0.500  0.500  0.500  107.8682 silver
+dispatom Cd  48   1.48  0.500  0.500  0.500  112.411  cadmium
+dispatom In  49   1.44  0.500  0.500  0.500  114.818  indium
+dispatom Sn  50   1.41  0.500  0.500  0.500  118.710  tin
+dispatom Sb  51   1.38  0.500  0.500  0.500  121.765  antimony
+dispatom Te  52   1.35  0.500  0.500  0.500  127.60   tellurium
+dispatom I   53   1.33  0.500  0.500  0.500  126.9048 iodine
+dispatom Xe  54   1.30  0.500  0.500  0.500  131.29   xenon
+dispatom Cs  55   2.25  0.500  0.500  0.500  132.9055 cesium
+dispatom Ba  56   1.98  0.500  0.500  0.500  137.327  barium
+dispatom La  57   1.69  0.500  0.500  0.500  138.9044 lanthanum
+dispatom Ce  58   1.85  0.500  0.500  0.500  140.116  cerium
+dispatom Pr  59   1.85  0.500  0.500  0.500  140.9077 praseodymium
+dispatom Nd  60   1.85  0.500  0.500  0.500  144.24   neodymium
+dispatom Pm  61   1.85  0.500  0.500  0.500  145      promethium
+dispatom Sm  62   1.85  0.500  0.500  0.500  150.36   samarium
+dispatom Eu  63   1.85  0.500  0.500  0.500  151.964  europium
+dispatom Gd  64   1.80  0.500  0.500  0.500  157.25   gadolinium
+dispatom Tb  65   1.75  0.500  0.500  0.500  158.9253 terbium
+dispatom Dy  66   1.75  0.500  0.500  0.500  162.50   dysprosium
+dispatom Ho  67   1.75  0.500  0.500  0.500  164.9303 holmium
+dispatom Er  68   1.75  0.500  0.500  0.500  167.26   erbium
+dispatom Tm  69   1.75  0.500  0.500  0.500  168.9342 thulium
+dispatom Yb  70   1.75  0.500  0.500  0.500  173.04   ytterbium
+dispatom Lu  71   1.60  0.500  0.500  0.500  174.967  lutetium
+dispatom Hf  72   1.50  0.500  0.500  0.500  178.49   hafnium
+dispatom Ta  73   1.38  0.500  0.500  0.500  180.9479 tantalum
+dispatom W   74   1.46  0.500  0.500  0.500  183.84   tungsten
+dispatom Re  75   1.59  0.500  0.500  0.500  186.207  rhenium
+dispatom Os  76   1.28  0.500  0.500  0.500  190.23   osmium
+dispatom Ir  77   1.37  0.500  0.500  0.500  192.217  iridium
+dispatom Pt  78   1.28  0.500  0.500  0.500  195.078  platinum
+dispatom Au  79   1.44  0.500  0.500  0.500  196.9666 gold
+dispatom Hg  80   1.49  0.500  0.500  0.500  200.59   mercury
+dispatom Tl  81   1.48  0.500  0.500  0.500  204.3833 thallium
+dispatom Pb  82   1.47  0.500  0.500  0.500  207.2    lead
+dispatom Bi  83   1.46  0.500  0.500  0.500  208.9804 bismuth
+dispatom Po  84   1.90  0.500  0.500  0.500  209      polonium
+dispatom At  85   1.80  0.500  0.500  0.500  210      astatine
+dispatom Rn  86   1.45  0.500  0.500  0.500  222      radon
+dispatom Fr  87   1.80  0.500  0.500  0.500  223      francium
+dispatom Ra  88   2.15  0.500  0.500  0.500  226.025  radium
+dispatom Ac  89   1.95  0.500  0.500  0.500  227      actinium
+dispatom Th  90   1.80  0.500  0.500  0.500  232.3081 tholium
+dispatom Pa  91   1.80  0.500  0.500  0.500  231.0359 protactinium
+dispatom U   92   1.75  0.500  0.500  0.500  238.0289 uranium
+dispatom Np  93   1.75  0.500  0.500  0.500  237      neptunium
+dispatom Pu  94   1.75  0.500  0.500  0.500  244      plutonium
+dispatom Am  95   1.75  0.500  0.500  0.500  243      americium
+dispatom Cm  96   1.75  0.500  0.500  0.500  247      curium
+dispatom Bk  97   1.75  0.500  0.500  0.500  247      berkelium
+dispatom Cf  98   1.75  0.500  0.500  0.500  251      californium
+dispatom Es  99   1.75  0.500  0.500  0.500  252      einsteinium
+dispatom Fm 100   1.75  0.500  0.500  0.500  257      fermium
+dispatom Md 101   1.75  0.500  0.500  0.500  258      mendelevium
+dispatom No 102   1.75  0.500  0.500  0.500  259      nobelium
+dispatom Lr 103   1.75  0.500  0.500  0.500  262      lawrencium
+
diff --git a/Scripts/formula.rb b/Scripts/formula.rb
new file mode 100755 (executable)
index 0000000..a4b9892
--- /dev/null
@@ -0,0 +1,714 @@
+#
+#  main.rb
+#
+#  Created by Toshi Nagata.
+#  Copyright 2008 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+class Molecule
+
+  @@known_fragments = Hash.new
+  @@known_fragment_names = []
+  
+  #  The instance variable @dummies is used to retain the indices of the detachable
+  #  atoms in the fragment. For example, a methyl group "CH3" is internally 
+  #  represented by a 5-atom molecule "Du-CH3" where Du is a dummy atom. When
+  #  a molecule is returned from the method Molecule.from_formula, the dummy atom
+  #  is replaced with a hydrogen atom but the position of the dummy atom is retained
+  #  as @dummies[0].
+
+  attr_accessor :dummies
+
+  #  Find dummy atoms; the atoms whose element is "Du" and name begines with "_" 
+  #  are regarded as dummy atoms.
+  def find_dummy_atoms(how_many = nil)
+    d = []
+    atoms.each { |ap|
+         if ap.element == "Du" && ap.name[0] == ?_
+           d.push(ap.index)
+               if how_many != nil && d.length >= how_many
+                 break
+               end
+         end
+       }
+       d
+  end
+  
+  #  Returns the first atom connected to the specified atom.
+  def root_atom(dummy_atom)
+    return atoms[dummy_atom].connects[0]
+  end
+  
+  #  Register a fragment in @@known_fragment and rebuild @@known_fragment_names
+  def Molecule.register_fragment(name, fragment)
+    @@known_fragments[name] = fragment
+       idx = @@known_fragment_names.length
+       @@known_fragment_names.each_with_index { |n,i|
+         if n.length < name.length
+           idx = i
+               break
+         end
+       }
+       @@known_fragment_names[idx, 0] = name
+  end
+  
+  def Molecule.known_fragment(name)
+       @@known_fragments[name].dup
+  end
+  
+  def Molecule.lookup_fragment_from_string(str)
+       @@known_fragment_names.each { |name|
+         if str.rindex(name, 0) == 0
+               return name
+         end
+       }
+       nil
+  end
+
+  #  Create a fragment containing an atom and appropriate number of dummy atoms.
+  #  The argument valence specifies the number of dummy atoms. Length is the
+  #  bond length, and degree is the angle Du-X-Du (given in degree, not radian).
+  def Molecule.atom_fragment(name, valence, length = nil, degree = nil)
+    fragment = Molecule.new
+       fragment.add_atom(name, "", name)  #  Atom type will be guessed later
+       length = 1.0 if length == nil
+       if valence > 0
+         fragment.add_atom("_1", "", "Du", length, 0)
+         if valence > 1
+           if degree == nil
+                 degree = 180.0 if valence == 2
+                 degree = 120.0 if valence == 3
+                 degree = 109.2 if valence == 4
+               end
+               fragment.add_atom("_2", "", "Du", length, 0, degree, 1)
+               if valence > 2
+                 if valence == 3
+                   sn = Math.sin(3.1415927/180.0 * degree)
+                       cs = Math.cos(3.1415927/180.0 * degree)
+                       acs = cs * (1 - cs) / (sn * sn)
+                       if (acs < -1 || acs > 1)
+                         dihed = 180.0
+                       else
+                         dihed = 180.0/3.1415927 * Math.acos(acs)
+                       end
+                 elsif valence == 4
+                   dihed = 120.0
+                 end
+                 fragment.add_atom("_3", "", "Du", length, 0, degree, 1, dihed, 2)
+                 if valence > 3
+                   fragment.add_atom("_4", "", "Du", length, 0, degree, 1, -dihed, 2)
+                 end
+               end
+         end
+       end
+       fragment.dummies = fragment.find_dummy_atoms
+       fragment
+  end
+
+  #  Examine whether the given atom is a carbonyl carbon
+  def is_carbonyl(idx)
+    if atoms[idx].element == "C"
+         atoms[idx].connects.each { |idx2|
+           if atoms[idx2].element == "O" && atoms[idx2].connects.length == 1
+                 return true
+               end
+         }
+       end
+       return false
+  end
+  
+  def guess_bond_length(e1, e2, mult)
+    if e1 > e2
+         ee = e1
+         e1 = e2
+         e2 = ee
+       end
+       if (e1 == "C" && e2 == "C")
+         len = (mult == 3 ? 1.18 : (mult == 2 ? 1.39 : 1.54))
+       elsif (e1 == "C" && e2 == "H")
+         len = 1.08
+       elsif (e1 == "C" && e2 == "O")
+         len = (mult == 2 ? 1.23 : 1.41)
+       elsif (e1 == "C" && e2 == "N")
+         len = (mult == 2 ? 1.29 : 1.48)
+       elsif (e1 == "H" && e2 == "O")
+         len = 0.96
+       elsif (e1 == "H" && e2 == "N")
+         len = 1.01
+       elsif (e1 == "C" && e2 == "Cl")
+         len = 1.79
+       elsif (e1 == "C" && e2 == "S")
+         len = 1.82
+       elsif (e1 == "O" && e2 == "S")
+         len = (mult == 2 ? 1.60 : 1.80)
+       else
+         len = 1.5
+       end
+       return len
+  end
+  
+  #  Make the 'root' atom trigonal (sp2). The atoms in the dummies array are
+  #  considered as one dummy atom.
+  def make_sp2(root, dummies, vec)
+    nd = atoms[root].connects.select { |n| !dummies.include?(n) }
+       return if nd.length != 2
+       r0 = atoms[root].r
+       r1 = atoms[nd[0]].r - r0
+       ax = r1.cross(atoms[nd[1]].r - r0)
+       if (ax.length < 1e-4)
+         ax = r1.cross(atoms[dummies[0]].r - r0)
+         if (ax.length < 1e-4)
+           ax = r1.cross(Vector3D[1, 0, 0])
+               if (ax.length < 1e-4)
+                 ax = r1.cross(Vector3D[0, 1, 0])
+                 if (ax.length < 1e-4)
+                   raise("Cannot determine axis for rotating fragment")
+                 end
+               end
+         end
+       end
+       r2 = atoms[nd[1]].r - r0
+       cs = r1.dot(r2) / (r1.length * r2.length)
+       ang = Math.acos(cs)
+       rotate(ax, 3.1415927 * 2 / 3 - ang, r0, fragment(nd[0], root))
+       v = ((atoms[nd[0]].r - r0).normalize + (atoms[nd[1]].r - r0).normalize) * (-1.0)
+       vec.x = v.x
+       vec.y = v.y
+       vec.z = v.z
+       return vec
+  end
+  
+  #  Add a molecular fragment to self. The new connection is created at the position
+  #  of the dummy atoms. The 'first' dummy atom in self is kept untouched wherever possible.
+  #  Mult (multiplicity) is the number of dummy atoms removed from each fragment. 
+  #  If unspecified, it is the same as the number of the dummy atoms in the added fragment.
+  def add_formula(mol, mult = nil)
+    natoms_s = self.natoms
+       natoms_m = mol.natoms
+       #  Find list of dummy atoms
+    md = mol.find_dummy_atoms
+       len = md.length
+       if len == 0
+         raise "The given molecule has no dummy atoms"
+       end
+       if mult == nil
+         mult = len
+       elsif mult > len
+         raise "The given molecule has fewer dummy atoms (#{len}) than the bond order (#{mult})"
+       else
+         md.slice!(mult..-1)
+       end
+       sd = self.find_dummy_atoms
+       len2 = sd.length
+       if len2 < mult
+         raise "Self has too few dummy atoms (#{len2}) to connect the given molecule (#{mult} bond order)"
+       elsif len2 > mult
+         sd.shift  #  Keep the first dummy atom untouched
+         sd.slice!(mult..-1)
+       end
+       #  Find root atoms and the bond direction (which is defined by the average position of all dummy atoms)
+       mroot = nil
+       sroot = nil
+       mvec = Vector3D[0, 0, 0]
+       svec = Vector3D[0, 0, 0]
+       md.each_index { |i|
+         mr = mol.root_atom(md[i])
+         if mroot == nil
+           mroot = mr
+         elsif mroot != mr
+           raise "The given molecule has multiple dummy atoms bonded to different root atoms"
+         end
+         mvec += mol.atoms[md[i]].r
+         sr = self.root_atom(sd[i])
+         if sroot == nil
+           sroot = sr
+         elsif sroot != sr
+           raise "self has multiple dummy atoms bonded to different root atoms"
+         end
+         svec += self.atoms[sd[i]].r
+       }
+       mvec *= 1.0 / mult
+       svec *= 1.0 / mult
+       mvec -= mol.atoms[mroot].r
+       svec -= self.atoms[sroot].r
+       #  Specify the atoms to define dihedral angles while docking
+       #  (Preference: "untouched" dummy atoms > normal atoms > "removed" dummy atoms)
+       mdihed = mol.atoms[mroot].connects.sort_by { |n|
+         if md.index(n) != nil
+           n - natoms_m
+         elsif mol.atoms[n].element == "Du"
+           n + natoms_m
+         else
+           n
+         end
+       } [-1]
+       sdihed = self.atoms[sroot].connects.sort_by { |n|
+         if sd.index(n) != nil
+           n + natoms_s
+         elsif self.atoms[n].element == "Du"
+           n - natoms_s
+         else
+           n
+         end
+       } [0]
+
+       #  Determine the bond length
+       me = mol.atoms[mroot].element
+       se = self.atoms[sroot].element
+       len = guess_bond_length(me, se, mult)
+
+       #  Kludge: special treatment for the amide bond
+       if (mult == 1 && ((se == "N" && mol.is_carbonyl(mroot)) || (me == "N" && self.is_carbonyl(sroot))))
+         self.make_sp2(sroot, sd, svec)
+         mol.make_sp2(mroot, md, mvec)
+         len = 1.33
+       end
+       if (mult == 2 && se == "C")
+         self.make_sp2(sroot, sd, svec)
+       end
+       if (mult == 2 && me == "C")
+         mol.make_sp2(mroot, md, mvec)
+       end
+
+       #  Dock the two fragments
+       self.dock(mol, sroot, mroot, svec, mvec, len, 3.1415927, sdihed, mdihed)
+       #  Remove the dummy atoms
+       md.map! { |item| item + natoms_s }
+       self.remove(sd + md)
+       #  Cache the list of dummy atoms
+       @dummies = self.find_dummy_atoms
+       self
+  end
+  
+  #  Internal subroutine for from_formula
+  def Molecule.from_formula_sub(str, idx)
+    idx0 = idx
+    f = nil
+       c = str[idx]
+       if c == ?(
+         f, idx = from_formula_sub(str, idx + 1)
+         if str[idx] != ?)
+           raise "Missing right parenthesis"
+         end
+         idx += 1
+         if (c = str[idx]) == nil || c < ?0 || c > ?9
+           #  No connection across the right parenthesis
+           return f, idx
+         end
+       else
+         s = str[idx..-1]
+         key = lookup_fragment_from_string(s)
+         if key != nil
+           f = known_fragment(key)
+               idx += key.length
+         end
+         if f == nil
+           ss = str[0..idx - 1] + "<?>" + str[idx..-1]
+           raise "Cannot find atom/fragment that matches \"#{s}\": #{ss}"
+         end
+       end
+       c = str[idx]
+       valence = f.dummies.length
+       if valence == 2 && c != nil && c >= ?0 && c <= ?9
+         #  Concatenate the fragment n times (like (CH2)8)
+         n = c - ?0
+         idx += 1
+         while (c = str[idx]) != nil && c >= ?0 && c <= ?9
+               n = n * 10 + c - ?0
+               idx += 1
+         end
+         if n == 0
+               raise "0 times repeat is not allowed"
+         end
+         if n > 1
+               f1 = f.dup
+               (n - 1).times { f.add_formula(f1, 1) }
+         end
+       end
+       #  Add the fragment(s) from the following substring
+       while true
+         if (c = str[idx]) == nil || c == ?)
+           #  End of string or parenthesized substring
+           return f, idx
+         end
+         if (valence = f.dummies.length) == 0
+               #  This fragment is saturated
+               return f, idx
+         elsif valence == 1 && idx0 > 0
+           #  This fragment has only one dummy bond and is not the first one
+               return f, idx
+         end
+         ff, idx = from_formula_sub(str, idx)
+         if (c = str[idx]) != nil && (c >= ?0 && c <= ?9)
+               #  The same fragment is added multiple times
+           n = c - ?0
+           idx += 1
+           while (c = str[idx]) != nil && c >= ?0 && c <= ?9
+                 n = n * 10 + c - ?0
+                 idx += 1
+           end
+           if n == 0
+                 raise "0 times repeat is not allowed"
+           end
+         else
+           n = 1
+         end
+         n.times { f.add_formula(ff) }
+       end # Repeat until the string ends or the fragment gets saturated
+  end
+  
+  #  Convert a string to a molecule.
+  def Molecule.from_formula(str, resname = nil)
+    f, idx = from_formula_sub(str, 0)
+       f.guess_names
+#      f.guess_types
+#      if f.nresidues != 2 || resname != nil
+         resname = "RES" if resname == nil
+         f.assign_residue(f.all, "#{resname}.1")
+#      end
+       f
+  end
+  
+  #  Create a fragment from str and replace the group with the generated fragment.
+  #  This method is invoked from MainView when a detachable selection is double-clicked
+  #  and user enters the formula in the dialog box.
+  def dock_formula(str, group = selection)
+    if group.length == 1
+         #  Check if the string is an element name
+         Parameter.atoms.each { |par|
+               if par.atomic_number > 0 && par.name == str
+                 ap = atoms[group[0]]
+                 if ap.atomic_number != par.atomic_number
+                       if ap.name.index(ap.element) == 0
+                         ap.name = ap.name.sub(ap.element, par.name)[0..3]
+                       end
+                       ap.atomic_number = par.atomic_number
+                       ap.atom_type = ""
+                       guess_types(group)
+                 end
+                 return
+               end
+         }
+       end
+       mol = Molecule.from_formula(str)
+       dock_fragment(mol, group)
+  end
+       
+  #  Replace the specified group with the given fragment.
+  def dock_fragment(fragment, group = selection)
+    n0 = self.natoms
+       return if fragment.natoms == 0
+    if group.length == 0
+         add(fragment)
+         sel = atom_group(n0...n0 + fragment.natoms)
+       else
+         frag = fragment.dup
+      bonds_old = bonds_on_border(group)
+         bonds_new = frag.dummies
+         if bonds_new == nil
+           bonds_new = frag.find_dummy_atoms()
+         end
+#        puts "group = #{group.inspect}, bonds_old = #{bonds_old.inspect}, bonds_new = #{bonds_new.inspect}"
+#        frag.dump
+         if bonds_old.length == 0 || bonds_new.length == 0
+           #  Translate the added fragment to the center of the selection
+               v1 = self.center_of_mass(group) - frag.center_of_mass
+               frag.translate(v1)
+               remove(group)
+               n0 = self.natoms
+               add(frag)
+               sel = atom_group(n0...n0 + frag.natoms)
+         else
+           old1 = bonds_old[0][1]  #  The first "root" atom in the molecule
+               old2 = bonds_old[0][0]  #  The first "dummy" atom in the molecule
+               new1 = frag.root_atom(bonds_new[0])  #  The first "root" atom in the fragment
+               new2 = bonds_new[0]                  #  The first "dummy" atom in the fragment
+               oldv = atoms[old2].r - atoms[old1].r
+               newv = frag.atoms[new2].r - frag.atoms[new1].r
+#              puts "old1 = #{old1}, old2 = #{old2}, new1 = #{new1}, new2 = #{new2}, oldv = #{oldv.inspect}, newv = #{newv.inspect}"
+               
+               #  Find the most substituted atoms (which will be arranged in trans conformation)
+               if (atoms[old1].connects.length >= 2)
+                 olddi1 = atoms[old1].connects.sort_by { |n| (n == old2 ? 0 : atoms[n].connects.length) } [0]
+               else
+                 olddi1 = nil
+               end
+               if (frag.atoms[new1].connects.length >= 2)
+                 newdi1 = frag.atoms[new1].connects.sort_by { |n| (n == new2 ? 0 : frag.atoms[n].connects.length) } [0]
+               else
+                 newdi1 = nil
+               end
+       #       dump([old1, old2, olddi1])
+       #       frag.dump([new1, new2, newdi1])
+               sel = dock(frag, old1, new1, oldv, newv, 1.5, 3.1415927, olddi1, newdi1)  #  Add (without removing atoms)
+               (1..bonds_old.length - 1).each { |i|
+                 break if i >= bonds_new.length
+                 #  Create bonds between other "root" atoms
+                 create_bond(bonds_old[i][1], frag.root_atom(bonds_new[i]) + n0)
+               }
+               rem1 = frag.atom_group(bonds_new)
+               rem = group + rem1.offset(n0)            #  The atoms to be removed
+               sel = atom_group(n0...n0 + frag.natoms) - rem1.offset(n0)  #  The atoms to be selected
+               sel = sel.deconvolute(atom_group - rem)  #  Renumber by skipping the atoms in rem
+               remove(rem)
+         end
+       end
+       guess_types(sel)
+       set_undoable_selection(sel)
+  end
+               
+
+  #  Define molecular fragment from dump string
+  def self.fragment_from_dump(str)
+    f = Molecule.new.from_dump(str)
+       f.dummies = f.find_dummy_atoms
+       return f
+  end
+
+  #  Define atom fragments
+  elements = %w(C H Li Be B C N O F Na Mg Al Si P S Cl)
+  valences = [4,1,1,2,3,4,3,2,1,1,2,3,4,3,2,1]
+  degrees = {"N"=>100, "O"=>110, "P"=>95, "S"=>108}
+  elements.each_index { |i|
+    name = elements[i]
+       angle = degrees[name]
+       register_fragment(name, atom_fragment(name, valences[i], nil, angle))
+  }
+  #  Special fragment to represent "open valence"
+  register_fragment("_", atom_fragment("Du", 1, nil, nil))
+  
+  #  Define common molecular fragments
+  formula_name = %w(CO CO2 O2C CH2 CH3 Et Pr iPr Bu tBu)
+  formula_desc = ["CO", "C(O)O_", "OC(O)_", "CH2", "CH3", "CH2CH3", "CH2CH2CH3", "CH(CH3)2", "CH2CH2CH2CH3", "C(CH3)3"]
+  formula_name.each_index { |i|
+    f, idx = from_formula_sub(formula_desc[i], 0)
+       #  Convert the "open valence" atoms to the dummy atoms
+       f.atoms.select { |ap|
+               ap.element == "Du" ? ap : nil
+       }.each_with_index { |ap, j|
+               ap.name = sprintf("_%d", j)
+       }
+       f.dummies = f.find_dummy_atoms
+       register_fragment(formula_name[i], f)
+  }
+  register_fragment("Me", known_fragment("CH3"))
+  register_fragment("Ph", fragment_from_dump(%q(
+   0 BEN.1   C1   ca   C   -0.653   0.585  -1.068 -0.128 [1,2,10]
+   1 BEN.1   _1   ""   Du  -1.158   1.039  -1.898  0.128 [0]
+   2 BEN.1   C2   ca   C    0.729   0.607  -1.003 -0.128 [0,3,4]
+   3 BEN.1   H2   ha   H    1.295   1.076  -1.783  0.128 [2]
+   4 BEN.1   C3   ca   C    1.382   0.021   0.069 -0.128 [2,5,6]
+   5 BEN.1   H3   ha   H    2.452   0.038   0.119  0.128 [4]
+   6 BEN.1   C4   ca   C    0.651  -0.586   1.076 -0.128 [4,7,8]
+   7 BEN.1   H4   ha   H    1.156  -1.039   1.906  0.128 [6]
+   8 BEN.1   C5   ca   C   -0.732  -0.607   1.012 -0.128 [6,9,10]
+   9 BEN.1   H5   ha   H   -1.298  -1.077   1.792  0.128 [8]
+  10 BEN.1   C6   ca   C   -1.384  -0.021  -0.060 -0.128 [0,8,11]
+  11 BEN.1   H6   ha   H   -2.455  -0.038  -0.110  0.128 [10])))
+  register_fragment("C6H6", fragment_from_dump(%q(
+   0 BEN.1   C1   ca   C   -0.653   0.585  -1.068 -0.128 [1,2,10]
+   1 BEN.1   H1   ha   H   -1.158   1.039  -1.898  0.128 [0]
+   2 BEN.1   C2   ca   C    0.729   0.607  -1.003 -0.128 [0,3,4]
+   3 BEN.1   H2   ha   H    1.295   1.076  -1.783  0.128 [2]
+   4 BEN.1   C3   ca   C    1.382   0.021   0.069 -0.128 [2,5,6]
+   5 BEN.1   H3   ha   H    2.452   0.038   0.119  0.128 [4]
+   6 BEN.1   C4   ca   C    0.651  -0.586   1.076 -0.128 [4,7,8]
+   7 BEN.1   H4   ha   H    1.156  -1.039   1.906  0.128 [6]
+   8 BEN.1   C5   ca   C   -0.732  -0.607   1.012 -0.128 [6,9,10]
+   9 BEN.1   H5   ha   H   -1.298  -1.077   1.792  0.128 [8]
+  10 BEN.1   C6   ca   C   -1.384  -0.021  -0.060 -0.128 [0,8,11]
+  11 BEN.1   H6   ha   H   -2.455  -0.038  -0.110  0.128 [10])))
+  register_fragment("C6H5", known_fragment("Ph"))
+  register_fragment("C6H4", fragment_from_dump(%q(
+   0 BEN.1   C1   ca   C   -0.653   0.585  -1.068 -0.128 [1,2,10]
+   1 BEN.1   _1   ""   Du  -1.158   1.039  -1.898  0.128 [0]
+   2 BEN.1   C2   ca   C    0.729   0.607  -1.003 -0.128 [0,3,4]
+   3 BEN.1   H2   ha   H    1.295   1.076  -1.783  0.128 [2]
+   4 BEN.1   C3   ca   C    1.382   0.021   0.069 -0.128 [2,5,6]
+   5 BEN.1   H3   ha   H    2.452   0.038   0.119  0.128 [4]
+   6 BEN.1   C4   ca   C    0.651  -0.586   1.076 -0.128 [4,7,8]
+   7 BEN.1   _2   ""   Du   1.156  -1.039   1.906  0.128 [6]
+   8 BEN.1   C5   ca   C   -0.732  -0.607   1.012 -0.128 [6,9,10]
+   9 BEN.1   H5   ha   H   -1.298  -1.077   1.792  0.128 [8]
+  10 BEN.1   C6   ca   C   -1.384  -0.021  -0.060 -0.128 [0,8,11]
+  11 BEN.1   H6   ha   H   -2.455  -0.038  -0.110  0.128 [10])))
+  register_fragment("C6H3", fragment_from_dump(%q(
+   0 BEN.1   C1   ca   C   -0.653   0.585  -1.068 -0.128 [1,2,10]
+   1 BEN.1   _1   ""   Du  -1.158   1.039  -1.898  0.128 [0]
+   2 BEN.1   C2   ca   C    0.729   0.607  -1.003 -0.128 [0,3,4]
+   3 BEN.1   H2   ha   H    1.295   1.076  -1.783  0.128 [2]
+   4 BEN.1   C3   ca   C    1.382   0.021   0.069 -0.128 [2,5,6]
+   5 BEN.1   _2   ""   Du   2.452   0.038   0.119  0.128 [4]
+   6 BEN.1   C4   ca   C    0.651  -0.586   1.076 -0.128 [4,7,8]
+   7 BEN.1   H4   ha   H    1.156  -1.039   1.906  0.128 [6]
+   8 BEN.1   C5   ca   C   -0.732  -0.607   1.012 -0.128 [6,9,10]
+   9 BEN.1   _3   ""   Du  -1.298  -1.077   1.792  0.128 [8]
+  10 BEN.1   C6   ca   C   -1.384  -0.021  -0.060 -0.128 [0,8,11]
+  11 BEN.1   H6   ha   H   -2.455  -0.038  -0.110  0.128 [10])))
+
+  #  Returns an arbitrary unit vector that is orthogonal to v
+  def orthogonal_vector(v)
+    vx = v.cross(Vector3D[1, 0, 0])
+       if vx.length < 1e-3
+         vx = v.cross(Vector3D[0, 1, 0])
+       end
+       vx.normalize
+  end
+  
+  #  Add missing hydrogen (or other atom) according to the given geometry type.
+  #  atype can be one of the following:
+  #  "td": tetrahedral, "tr": trigonal, "py": pyramidal (like amine nitrogen), "li": linear
+  def add_hydrogen(idx, atype, bond = 1.07, anum = 1)
+    nc = atoms[idx].connects.length
+       p = atoms[idx].cr
+       if nc == 0
+         vx = Vector3D[1, 0, 0]
+       else
+         v = atoms[idx].connects.map { |i| (atoms[i].cr - p).normalize }
+         vx = Vector3D[0, 0, 0]
+         v.each { |v0| vx += v0 }
+         if nc == 3
+           if vx.length > 1e-3
+                 vx = vx.normalize
+               else
+                 #  The three atoms are in trigonal positions
+                 vx = v[0].cross(v[1]).normalize
+                 if vx.length < 1e-3
+                   vx = orthogonal_vector(v[0])
+                 end
+               end
+         elsif nc == 2
+           if vx.length < 1e-3
+                 vx = orthognal_vector(v[0])
+               else
+                 vx = vx.normalize
+               end
+           vy = v[0] - v[1]
+               if vy.length < 1e-3
+                 vy = orthogonal_vector(vx)
+               else
+                 vy = vy.normalize
+               end
+               vz = vx.cross(vy).normalize
+         elsif nc == 1
+           i = atoms[idx].connects[0]
+           #  Find most substituted atom connected to atoms[i]
+               vy = nil
+               if (atoms[i].connects.length >= 2)
+                 j = atoms[i].connects.sort_by { |n| (n == i ? 0 : atoms[n].connects.length) }
+                 vy = nil
+                 j.each { |j0|
+                   vy = vx.cross(atoms[j0].cr - atoms[i].cr)
+                   if vy.length > 1e-3
+                         #  The atoms j[0]-i-idx are not linear
+                     vz = vx.cross(vy).normalize
+                         vy = vy.normalize
+                         break
+                   end
+                 }
+               end
+           if !vy
+                 vy = orthogonal_vector(vx)
+               else
+                 vy = vy.normalize
+               end
+               vz = vx.cross(vy).normalize
+         else
+           vx = Vector3D[1, 0, 0]
+               vy = Vector3D[0, 1, 0]
+               vz = Vector3D[0, 0, 1]
+         end
+       end
+       vn = []
+       type = nil
+    if atype == "td"
+         raise "The atom #{idx} already has #{nc} bonds" if nc >= 4
+         cs = -0.333333  #  cos(109.47)
+         sn =  0.942809  #  sin(109.47) = sqrt(2)*2/3
+         cs2 = -0.577359 #  cos(125.27)
+         sn2 =  0.816490 #  sin(125.27)
+         if nc == 3
+               vn << -vx
+         elsif nc == 2
+           vn << vx * cs2 + vz * sn2
+               vn << vx * cs2 - vz * sn2
+         else
+           vn << vx if nc == 0
+           vn << vx * cs + vy * sn
+               vn << vx * cs - vy * sn * 0.5 + vz * sn * 0.86603
+               vn << vx * cs - vy * sn * 0.5 - vz * sn * 0.86603
+         end
+         type = "hc" if anum == 1
+       elsif atype == "tr"
+         raise "The atom #{idx} already has #{nc} bonds" if nc >= 3
+         if nc == 2
+           vn << -vx
+         else
+           vn << vx if nc == 0
+               vn << vx * 0.5 + vy * 0.86603
+               vn << vx * 0.5 - vy * 0.86603
+         end
+         type = "ha" if anum == 1
+       elsif atype == "py"
+         raise "The atom #{idx} already has #{nc} bonds" if nc >= 3
+         cs = -0.292376  #  cos(107)
+         sn =  0.956303  #  sin(107)
+         cs2 = -0.491527 #  cos(119.4)
+         sn2 =  0.870862 #  sin(119.4)
+         if nc == 2
+           vn << vx * cs2 + vz * sn2
+         else
+           vn << vx if nc == 0
+               vn << vx * cs + vy * sn
+               vn << vx * cs + vy * (cs * (1 - cs) / sn) + vz * ((1 - cs) / sn * Math.sqrt(1 + 2 * cs))
+         end
+         type = "h2" if anum == 1
+       elsif atype == "be"
+         raise "The atom #{idx} already has #{nc} bonds" if nc >= 2
+         cs = -0.258819  #  cos(105)
+         sn =  0.965926  #  sin(105)
+         vn << vx if nc == 0
+         vn << vx * cs + vy * sn
+         type = "ho" if anum == 1
+       elsif atype == "li"
+         raise "The atom #{idx} already has #{nc} bonds" if nc >= 2
+         vn << vx if nc == 0
+         vn << -vx
+         type = "hz" if anum == 1
+       else
+         raise "Unknown atom type #{atype}"
+       end
+       aname = Parameter.atom(anum).name
+       name = atoms[idx].name
+       name = name.gsub(/\A[A-Za-z]*/, aname)[0..2]
+       vn.each_with_index { |v, i|
+         ap = create_atom(sprintf("%s%d", name, i + 1), idx + i + 1)
+         ap.atomic_number = anum
+         ap.atom_type = (type || ap.element)
+         assign_residue(atom_group(ap.index), atoms[idx].res_seq)
+         ap.r = atoms[idx].r + v * bond
+         create_bond(idx, ap.index)
+       }
+       #  Update selection
+#      if selection.count > 0
+#        sel = selection.convolute(IntGroup[0..idx, (idx + vn.count + 1)..(natoms - 1)])
+#        self.selection = sel
+#      end
+  end
+  
+  def add_hydrogen_on_group(group, atype, bond = 1.07, anum = 1)
+    group.reverse_each { |i|
+         add_hydrogen(i, atype, bond, anum)
+       }
+    self
+  end
+
+end
diff --git a/Scripts/gaff.par b/Scripts/gaff.par
new file mode 100644 (file)
index 0000000..f02d018
--- /dev/null
@@ -0,0 +1,5018 @@
+! AMBER General Force Field for organic mol., add. info. at the end (June, 2003)
+! 
+! Original file was taken from the AMBER home page: http://ambermd.org/ (as of Nov 13, 2009).
+! Converted for Molby by amberparm2molby.pl by Toshi Nagata.
+! Thanks to the AMBER development team, the force field parameters are in the public domain. Hereby I acknowledge their great scientific contribution, with references to the literature as below.
+! 1. D.A. Case, T.E. Cheatham, III, T. Darden, H. Gohlke, R. Luo, K.M. Merz, Jr., A. Onufriev, C. Simmerling, B. Wang and R. Woods. The Amber biomolecular simulation programs. J. Computat. Chem. 26, 1668-1688 (2005).
+! 2. J.W. Ponder and D.A. Case. Force fields for protein simulations. Adv. Prot. Chem. 66, 27-85 (2003). Similar information for nucleic acids is given by T.E. Cheatham, III and M.A. Young. Molecular dynamics simulation of nucleic acids: Successes, limitations and promise. Biopolymers 56, 232-256 (2001).
+! 3. D.A. Case, T.A. Darden, T.E. Cheatham, III, C.L. Simmerling, J. Wang, R.E. Duke, R. Luo, M. Crowley, Ross C. Walker,W. Zhang, K.M. Merz, B.Wang, S. Hayik, A. Roitberg, G. Seabra, I. Kolossváry, K.F.Wong, F. Paesani, J. Vanicek, X.Wu, S.R. Brozell, T. Steinbrecher, H. Gohlke, L. Yang, C. Tan, J. Mongan, V. Hornak, G. Cui, D.H. Mathews, M.G. Seetin, C. Sagui, V. Babin, and P.A. Kollman (2008), AMBER 10, University of California, San Francisco.
+!
+vdw h1  0.0157  1.3870  0.0157  1.3870 0   1.008 ! H bonded to aliphatic carbon with 1 electrwd. group
+vdw h2  0.0157  1.2870  0.0157  1.2870 0   1.008 ! H bonded to aliphatic carbon with 2 electrwd. group
+vdw h3  0.0157  1.1870  0.0157  1.1870 0   1.008 ! H bonded to aliphatic carbon with 3 electrwd. group
+vdw h4  0.0150  1.4090  0.0150  1.4090 0   1.008 ! H bonded to non-sp3 carbon with 1 electrwd. group
+vdw h5  0.0150  1.3590  0.0150  1.3590 0   1.008 ! H bonded to non-sp3 carbon with 2 electrwd. group
+vdw ha  0.0150  1.4590  0.0150  1.4590 0   1.008 ! H bonded to aromatic carbon
+vdw hc  0.0157  1.4870  0.0157  1.4870 0   1.008 ! H bonded to aliphatic carbon without electrwd. group
+vdw hn  0.0157  0.6000  0.0157  0.6000 0   1.008 ! H bonded to nitrogen atoms
+vdw ho  0.0000  0.0000  0.0000  0.0000 0   1.008 ! Hydroxyl group
+vdw hp  0.0157  0.6000  0.0157  0.6000 0   1.008 ! H bonded to phosphate
+vdw hs  0.0157  0.6000  0.0157  0.6000 0   1.008 ! Hydrogen bonded to sulphur
+vdw hw  0.0000  0.0000  0.0000  0.0000 0   1.008 ! Hydrogen in water
+vdw hx  0.0157  1.1000  0.0157  1.1000 0   1.008 ! H bonded to C next to positively charged group
+vdw o   0.2100  1.6612  0.2100  1.6612 0  16.000 ! Oxygen with one connected atom
+vdw oh  0.2104  1.7210  0.2104  1.7210 0  16.000 ! Oxygen in hydroxyl group
+vdw os  0.1700  1.6837  0.1700  1.6837 0  16.000 ! Ether and ester oxygen
+vdw ow  0.1520  1.7683  0.1520  1.7683 0  16.000 ! Oxygen in water
+vdw c   0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp2 C carbonyl group
+vdw c1  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp C
+vdw c2  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp2 C
+vdw c3  0.1094  1.9080  0.1094  1.9080 0  12.010 ! Sp3 C
+vdw ca  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp2 C in pure aromatic systems
+vdw cc  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp2 carbons in non-pure aromatic systems
+vdw cd  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp2 carbons in non-pure aromatic systems, identical to cc
+vdw ce  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Inner Sp2 carbons in conjugated systems
+vdw cf  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Inner Sp2 carbons in conjugated systems, identical to ce
+vdw cg  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Inner Sp carbons in conjugated systems
+vdw ch  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Inner Sp carbons in conjugated systems, identical to cg
+vdw cp  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Head Sp2 C that connect two rings in biphenyl sys.
+vdw cq  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Head Sp2 C that connect two rings in biphenyl sys. identical to
+vdw cu  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp2 carbons in triangle systems
+vdw cv  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp2 carbons in square systems
+vdw cx  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp3 carbons in triangle systems
+vdw cy  0.0860  1.9080  0.0860  1.9080 0  12.010 ! Sp3 carbons in square systems
+vdw n   0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp2 nitrogen in amide groups
+vdw  n  0.1700  1.8240  0.1700  1.8240 0   0.000
+vdw n1  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp N
+vdw n2  0.1700  1.8240  0.1700  1.8240 0  14.010 ! aliphatic Sp2 N with two connected atoms
+vdw n3  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp3 N with three connected atoms
+vdw n4  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp3 N with four connected atoms
+vdw na  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp2 N with three connected atoms
+vdw nb  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp2 N in pure aromatic systems
+vdw nc  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp2 N in non-pure aromatic systems
+vdw nd  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Sp2 N in non-pure aromatic systems, identical to nc
+vdw ne  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Inner Sp2 N in conjugated systems
+vdw nf  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Inner Sp2 N in conjugated systems, identical to ne
+vdw nh  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Amine N connected one or more aromatic rings
+vdw no  0.1700  1.8240  0.1700  1.8240 0  14.010 ! Nitro N
+vdw s   0.2500  2.0000  0.2500  2.0000 0  32.060 ! S with one connected atom
+vdw s2  0.2500  2.0000  0.2500  2.0000 0  32.060 ! S with two connected atom, involved at least one double bond
+vdw s4  0.2500  2.0000  0.2500  2.0000 0  32.060 ! S with three connected atoms
+vdw s6  0.2500  2.0000  0.2500  2.0000 0  32.060 ! S with four connected atoms
+vdw sx  0.2500  2.0000  0.2500  2.0000 0  32.060 ! Special s4 in conjugated systems
+vdw sy  0.2500  2.0000  0.2500  2.0000 0  32.060 ! Special s6 in conjugated systems
+vdw sh  0.2500  2.0000  0.2500  2.0000 0  32.060 ! Sp3 S connected with hydrogen
+vdw ss  0.2500  2.0000  0.2500  2.0000 0  32.060 ! Sp3 S in thio-ester and thio-ether
+vdw p2  0.2000  2.1000  0.2000  2.1000 0  30.970 ! Phosphate with two connected atoms
+vdw p3  0.2000  2.1000  0.2000  2.1000 0  30.970 ! Phosphate with three connected atoms, such as PH3
+vdw p4  0.2000  2.1000  0.2000  2.1000 0  30.970 ! Phosphate with three connected atoms, such as O=P(CH3)2
+vdw p5  0.2000  2.1000  0.2000  2.1000 0  30.970 ! Phosphate with four connected atoms, such as O=P(OH)3
+vdw pb  0.2000  2.1000  0.2000  2.1000 0  30.970 ! Sp2 P in pure aromatic systems
+vdw px  0.2000  2.1000  0.2000  2.1000 0  30.970 ! Special p4 in conjugated systems
+vdw py  0.2000  2.1000  0.2000  2.1000 0  30.970 ! Special p5 in conjugated systems
+vdw f   0.0610  1.7500  0.0610  1.7500 0  19.000 ! Fluorine
+vdw cl  0.2650  1.9480  0.2650  1.9480 0  35.450 ! Chlorine
+vdw br  0.3200  2.2200  0.3200  2.2200 0  79.900 ! Bromine
+vdw i   0.4000  2.3500  0.4000  2.3500 0 126.900 ! Iodine
+
+bond ow   hw     553.0  0.96 ! TIP3P water
+bond br   br     123.2  2.54 ! SOURCE1     4    0.0000    0.0000
+bond br   c      240.3  1.95 ! SOURCE2     2    0.0285    0.0285
+bond br   c1     352.7  1.79 ! SOURCE2     4    0.0022    0.0024
+bond br   c2     278.7  1.88 ! SOURCE1    31    0.0000    0.0000
+bond br   c3     229.5  1.97 ! SOURCE1   100    0.0000    0.0000
+bond br   ca     269.6  1.90 ! SOURCE1   127    0.0028    0.0058
+bond br   cx     261.4  1.91 ! SOURCE1     8    0.0000    0.0000
+bond br   i      142.4  2.67 ! SOURCE1     2    0.0245    0.0245
+bond br   n      320.2  1.87 ! SOURCE3     4    0.0037    0.0046
+bond br   n1     330.4  1.86 ! SOURCE0     1
+bond br   n2     219.0  2.04 ! SOURCE3     5    0.0956    0.1082
+bond br   n3     265.9  1.95 ! SOURCE3     2    0.0000    0.0000
+bond br   n4     282.4  1.93 ! SOURCE3     3    0.0012    0.0013
+bond br   na     237.3  2.00 ! SOURCE3     7    0.1938    0.2156
+bond br   nh     270.9  1.94 ! SOURCE3     1    0.0000    0.0000
+bond br   no     191.0  2.10 ! SOURCE3     1    0.0000    0.0000
+bond br   o      278.9  1.80 ! SOURCE0     1
+bond br   oh     237.2  1.87 ! SOURCE3     1    0.0000    0.0000
+bond br   os     225.6  1.89 ! SOURCE3     2    0.0000    0.0000
+bond br   p2     174.3  2.21 ! SOURCE3     9    0.0470    0.0510
+bond br   p3     167.0  2.23 ! SOURCE3     3    0.0096    0.0101
+bond br   p4     188.8  2.17 ! SOURCE0     1
+bond br   p5     179.3  2.20 ! SOURCE3     3    0.0094    0.0099
+bond br   s      170.6  2.22 ! SOURCE0     1
+bond br   s4     134.3  2.34 ! SOURCE3     1    0.0000    0.0000
+bond br   s6     172.7  2.21 ! SOURCE3     3    0.0417    0.0443
+bond br   sh     174.4  2.21 ! SOURCE3     1    0.0000    0.0000
+bond br   ss     176.6  2.20 ! SOURCE3     3    0.0033    0.0035
+bond c    c      290.1  1.55 ! SOURCE1    31    0.0080    0.0100
+bond c    c1     379.8  1.46 ! SOURCE0     1
+bond c    c2     449.9  1.41 ! SOURCE3     2    0.0370    0.0370
+bond c    c3     328.3  1.51 ! SOURCE1  2949    0.0049    0.0060
+bond c    ca     349.7  1.49 ! SOURCE1   480    0.0031    0.0055
+bond c    cc     377.4  1.46 ! SOURCE3   132    0.0165    0.0210
+bond c    cd     377.4  1.46 ! SOURCE3   132    0.0165    0.0210
+bond c    ce     363.8  1.47 ! SOURCE1   601    0.0091    0.0105
+bond c    cf     363.8  1.47 ! SOURCE1   601    0.0091    0.0105
+bond c    cg     389.3  1.45 ! SOURCE3     2    0.0000    0.0000
+bond c    ch     389.3  1.45 ! SOURCE3     2    same as    c -cg
+bond c    cl     293.5  1.77 ! SOURCE3     6    0.0235    0.0250
+bond c    cu     441.4  1.41 ! SOURCE2     1    0.0000    0.0000
+bond c    cx     350.8  1.49 ! SOURCE1   105    0.0000    0.0000
+bond c    cy     308.5  1.53 ! SOURCE1    18    0.0000    0.0000
+bond c    f      387.9  1.32 ! SOURCE2     6    0.0140    0.0147
+bond c    ha     325.1  1.10 ! SOURCE3    53    0.0078    0.0102
+bond c    i      198.9  2.21 ! SOURCE3     4    0.0332    0.0365
+bond c    n      478.2  1.34 ! SOURCE1  1235    0.0162    0.0215
+bond c    n2     374.6  1.42 ! SOURCE0     1
+bond c    n4     255.5  1.55 ! SOURCE3     4    0.0388    0.0388
+bond c    nc     438.8  1.37 ! SOURCE3     2    0.0108    0.0108
+bond c    nd     438.8  1.37 ! SOURCE3     2    same as    c -nc
+bond c    ne     388.0  1.41 ! SOURCE3     3    0.0088    0.0094
+bond c    nf     388.0  1.41 ! SOURCE3     3    same as    c -ne
+bond c    no     260.1  1.54 ! SOURCE0     1
+bond c    o      648.0  1.21 ! SOURCE1  3682    0.0134    0.0165
+bond c    oh     466.4  1.31 ! SOURCE1   271    0.0026    0.0041
+bond c    os     411.3  1.34 ! SOURCE1  1044    0.0114    0.0171
+bond c    p2     210.3  1.90 ! SOURCE0     1
+bond c    p3     219.0  1.88 ! SOURCE3     6    0.0094    0.0129
+bond c    p4     220.6  1.88 ! SOURCE0     1
+bond c    p5     225.9  1.87 ! SOURCE0     1
+bond c    pe     204.9  1.91 ! SOURCE3     3    0.0023    0.0025
+bond c    pf     204.9  1.91 ! SOURCE3     3    same as    c -pe
+bond c    px     208.3  1.90 ! SOURCE3     1    0.0000    0.0000
+bond c    py     227.6  1.87 ! SOURCE3     6    0.0141    0.0199
+bond c    s      328.9  1.68 ! SOURCE1   401    0.0077    0.0128
+bond c    s4     200.4  1.87 ! SOURCE0     1
+bond c    s6     200.4  1.87 ! SOURCE0     1
+bond c    sh     249.6  1.78 ! SOURCE3     6    0.0157    0.0171
+bond c    ss     261.9  1.76 ! SOURCE1    20    0.0000    0.0000
+bond c    sx     193.3  1.89 ! SOURCE3     5    0.0070    0.0088
+bond c    sy     202.8  1.86 ! SOURCE3     5    0.0063    0.0085
+bond c1   c1     986.2  1.18 ! SOURCE1   265    0.0022    0.0031
+bond c1   c2     625.0  1.31 ! SOURCE1    18    0.0000    0.0000
+bond c1   c3     368.3  1.47 ! SOURCE1   215    0.0013    0.0017
+bond c1   ca     404.1  1.44 ! SOURCE0     1
+bond c1   cg     845.8  1.22 ! SOURCE3    22    0.0074    0.0101
+bond c1   ch     845.8  1.22 ! SOURCE3    22    same as    c1-cg
+bond c1   cl     419.7  1.63 ! SOURCE2     6    0.0046    0.0050
+bond c1   cx     399.1  1.44 ! SOURCE1    38    0.0000    0.0000
+bond c1   f      469.4  1.27 ! SOURCE2     2    0.0085    0.0085
+bond c1   ha     375.9  1.07 ! SOURCE3    63    0.0022    0.0035
+bond c1   hc     385.6  1.06 ! SOURCE0     1
+bond c1   i      318.8  1.99 ! SOURCE2     4    0.0025    0.0032
+bond c1   n      503.0  1.33 ! SOURCE0     1
+bond c1   n1    1014.5  1.14 ! SOURCE1   170    0.0038    0.0055
+bond c1   n2     769.8  1.21 ! SOURCE3     5    0.0089    0.0115
+bond c1   n3     409.8  1.39 ! SOURCE2     1    0.0000    0.0000
+bond c1   n4     378.2  1.42 ! SOURCE3     3    0.0030    0.0032
+bond c1   na     452.0  1.36 ! SOURCE3     8    0.0031    0.0034
+bond c1   ne     741.8  1.22 ! SOURCE3     1    0.0000    0.0000
+bond c1   nf     741.8  1.22 ! SOURCE3     1    same as    c1-ne
+bond c1   ng     465.7  1.35 ! SOURCE3     3    0.0025    0.0027
+bond c1   nh     465.7  1.35 ! SOURCE3     3    0.0025    0.0027
+bond c1   no     393.0  1.41 ! SOURCE3     3    0.0004    0.0005
+bond c1   o      777.0  1.17 ! SOURCE2     9    0.0044    0.0052
+bond c1   oh     435.6  1.33 ! SOURCE3     1    0.0000    0.0000
+bond c1   os     437.1  1.32 ! SOURCE3     3    0.0139    0.0148
+bond c1   p2     289.3  1.77 ! SOURCE0     1
+bond c1   p3     275.1  1.79 ! SOURCE0     1
+bond c1   p4     275.1  1.79 ! SOURCE0     1
+bond c1   p5     302.2  1.75 ! SOURCE3     2    0.0000    0.0000
+bond c1   s      371.8  1.63 ! SOURCE1    14    0.0000    0.0000
+bond c1   s2     410.0  1.59 ! SOURCE3     1    0.0000    0.0000
+bond c1   s4     272.9  1.75 ! SOURCE3     2    0.0000    0.0000
+bond c1   s6     290.4  1.72 ! SOURCE3     2    0.0000    0.0000
+bond c1   sh     324.5  1.68 ! SOURCE0     1
+bond c1   ss     325.4  1.68 ! SOURCE1    10    0.0000    0.0000
+bond c2   c2     589.7  1.32 ! SOURCE1   974    0.0048    0.0096
+bond c2   c3     328.3  1.51 ! SOURCE1  2536    0.0016    0.0021
+bond c2   ca     357.2  1.48 ! SOURCE0     1
+bond c2   cc     522.6  1.36 ! SOURCE1   771    0.0152    0.0185
+bond c2   cd     522.6  1.36 ! SOURCE1   771    0.0152    0.0185
+bond c2   ce     560.5  1.34 ! SOURCE3    62    0.0085    0.0128
+bond c2   cf     560.5  1.34 ! SOURCE3    62    same as    c2-ce
+bond c2   cl     328.8  1.72 ! SOURCE1   163    0.0093    0.0098
+bond c2   cu     573.9  1.33 ! SOURCE2     1    0.0000    0.0000
+bond c2   cx     333.3  1.50 ! SOURCE2     1    0.0000    0.0000
+bond c2   cy     309.4  1.53 ! SOURCE2     1    0.0000    0.0000
+bond c2   f      368.7  1.34 ! SOURCE1    34    0.0000    0.0000
+bond c2   h4     348.6  1.08 ! SOURCE3    40    0.0037    0.0058
+bond c2   h5     357.5  1.08 ! SOURCE3     3    0.0061    0.0067
+bond c2   ha     344.3  1.09 ! SOURCE3   797    0.0030    0.0046
+bond c2   hc     344.3  1.09 ! SOURCE3   789    0.0030    0.0046
+bond c2   hx     350.1  1.08 ! SOURCE3     3    0.0008    0.0008
+bond c2   i      223.2  2.15 ! SOURCE3     2    0.0000    0.0000
+bond c2   n      390.5  1.41 ! SOURCE3     9    0.0109    0.0124
+bond c2   n1     546.0  1.31 ! SOURCE3     4    0.0161    0.0161
+bond c2   n2     581.1  1.29 ! SOURCE1   103    0.0095    0.0100
+bond c2   n3     486.3  1.34 ! SOURCE0     1
+bond c2   n4     309.1  1.48 ! SOURCE3     5    0.0047    0.0064
+bond c2   na     411.1  1.39 ! SOURCE3    31    0.0271    0.0289
+bond c2   nc     533.0  1.31 ! SOURCE1    99    0.0065    0.0095
+bond c2   nd     533.0  1.31 ! SOURCE1    99    same as    c2-nc
+bond c2   ne     597.7  1.28 ! SOURCE3    37    0.0090    0.0110
+bond c2   nf     597.7  1.28 ! SOURCE3    37    same as    c2-ne
+bond c2   nh     462.6  1.35 ! SOURCE3    38    0.0385    0.0413
+bond c2   no     327.6  1.46 ! SOURCE3     4    0.0013    0.0013
+bond c2   o      546.2  1.26 ! SOURCE3     4    0.0144    0.0144
+bond c2   oh     425.4  1.33 ! SOURCE1    53    0.0000    0.0000
+bond c2   os     392.6  1.36 ! SOURCE1   315    0.0088    0.0097
+bond c2   p2     375.9  1.67 ! SOURCE3    62    0.0078    0.0147
+bond c2   p3     246.6  1.83 ! SOURCE3     5    0.0041    0.0042
+bond c2   p4     254.0  1.82 ! SOURCE0     1
+bond c2   p5     255.2  1.82 ! SOURCE0     1
+bond c2   pe     355.3  1.69 ! SOURCE3    52    0.0318    0.0542
+bond c2   pf     355.3  1.69 ! SOURCE3    52    same as    c2-pe
+bond c2   s      281.5  1.73 ! SOURCE3     4    0.0034    0.0034
+bond c2   s2     393.1  1.61 ! SOURCE2     1    0.0000    0.0000
+bond c2   s4     263.2  1.76 ! SOURCE0     1
+bond c2   s6     263.2  1.76 ! SOURCE0     1
+bond c2   sh     255.3  1.77 ! SOURCE3     3    0.0068    0.0072
+bond c2   ss     280.0  1.74 ! SOURCE1   209    0.0135    0.0155
+bond c3   c3     303.1  1.54 ! SOURCE1 14664    0.0043    0.0048
+bond c3   ca     323.5  1.51 ! SOURCE1  1813    0.0000    0.0000
+bond c3   cc     337.3  1.50 ! SOURCE3    50    0.0059    0.0096
+bond c3   cd     337.3  1.50 ! SOURCE3    50    0.0059    0.0096
+bond c3   ce     331.3  1.50 ! SOURCE3     9    0.0019    0.0024
+bond c3   cf     331.3  1.50 ! SOURCE3     9    same as    c3-ce
+bond c3   cl     279.0  1.79 ! SOURCE1   267    0.0151    0.0194
+bond c3   cu     359.4  1.48 ! SOURCE1     7    0.0000    0.0000
+bond c3   cv     347.6  1.49 ! SOURCE1    11    0.0000    0.0000
+bond c3   cx     322.5  1.51 ! SOURCE1   712    0.0044    0.0045
+bond c3   cy     308.5  1.53 ! SOURCE1   376    0.0000    0.0000
+bond c3   f      363.8  1.34 ! SOURCE1   617    0.0241    0.0281
+bond c3   h1     335.9  1.09 ! SOURCE3  2175    0.0045    0.0082
+bond c3   h2     326.4  1.10 ! SOURCE3    66    0.0110    0.0280
+bond c3   h3     335.9  1.09 ! SOURCE2     1    0.0000    0.0000
+bond c3   hc     337.3  1.09 ! SOURCE3  2815    0.0040    0.0059
+bond c3   hx     338.7  1.09 ! SOURCE3   146    0.0023    0.0066
+bond c3   i      219.1  2.16 ! SOURCE1    15    0.0000    0.0000
+bond c3   n      330.6  1.46 ! SOURCE1   187    0.0065    0.0079
+bond c3   n1     325.1  1.47 ! SOURCE3
+bond c3   n2     313.8  1.48 ! SOURCE1   129    0.0136    0.0138
+bond c3   n3     320.6  1.47 ! SOURCE1  1678    0.0013    0.0017
+bond c3   n4     293.6  1.50 ! SOURCE1  1370    0.0000    0.0000
+bond c3   na     334.7  1.46 ! SOURCE3    23    0.0096    0.0119
+bond c3   nc     334.7  1.46 ! SOURCE3     9    0.0091    0.0109
+bond c3   nd     334.7  1.46 ! SOURCE3     9    same as    c3-nc
+bond c3   nh     332.7  1.46 ! SOURCE3    27    0.0067    0.0085
+bond c3   no     265.4  1.53 ! SOURCE1    83    0.0163    0.0212
+bond c3   o      422.6  1.33 ! SOURCE3     4    0.0100    0.0100
+bond c3   oh     314.1  1.43 ! SOURCE1   914    0.0110    0.0129
+bond c3   os     301.5  1.44 ! SOURCE1  3123    0.0112    0.0126
+bond c3   p2     234.3  1.85 ! SOURCE3     9    0.0100    0.0125
+bond c3   p3     240.6  1.84 ! SOURCE3   109    0.0091    0.0107
+bond c3   p4     247.2  1.83 ! SOURCE3    29    0.0091    0.0138
+bond c3   p5     259.7  1.81 ! SOURCE1    84    0.0000    0.0000
+bond c3   px     252.7  1.82 ! SOURCE3    28    0.0079    0.0098
+bond c3   py     259.7  1.81 ! SOURCE3    13    0.0144    0.0163
+bond c3   s      212.9  1.84 ! SOURCE3     4    0.0185    0.0185
+bond c3   s4     233.8  1.81 ! SOURCE1   139    0.0022    0.0023
+bond c3   s6     254.0  1.77 ! SOURCE1   118    0.0080    0.0103
+bond c3   sh     225.3  1.82 ! SOURCE3    12    0.0042    0.0051
+bond c3   ss     225.8  1.82 ! SOURCE1   358    0.0049    0.0075
+bond c3   sx     232.6  1.81 ! SOURCE3    30    0.0050    0.0067
+bond c3   sy     248.9  1.78 ! SOURCE3    31    0.0034    0.0039
+bond ca   ca     478.4  1.39 ! SOURCE1  6228    0.0096    0.0147
+bond ca   cc     411.7  1.43 ! SOURCE1    80    0.0000    0.0000
+bond ca   cd     411.7  1.43 ! SOURCE1    80    0.0000    0.0000
+bond ca   ce     366.0  1.47 ! SOURCE1    71    0.0029    0.0030
+bond ca   cf     366.0  1.47 ! SOURCE1    71    0.0029    0.0030
+bond ca   cg     406.6  1.44 ! SOURCE1    71    0.0045    0.0045
+bond ca   ch     406.6  1.44 ! SOURCE1    71    0.0045    0.0045
+bond ca   cl     322.8  1.73 ! SOURCE1   704    0.0095    0.0095
+bond ca   cp     466.1  1.40 ! SOURCE3    14    0.0098    0.0110
+bond ca   cx     350.8  1.49 ! SOURCE1    98    0.0064    0.0118
+bond ca   f      363.8  1.34 ! SOURCE1   205    0.0069    0.0089
+bond ca   h4     342.9  1.09 ! SOURCE3    57    0.0016    0.0026
+bond ca   h5     347.2  1.08 ! SOURCE3    15    0.0038    0.0048
+bond ca   ha     344.3  1.09 ! SOURCE3  1496    0.0024    0.0045
+bond ca   i      252.4  2.10 ! SOURCE1    51    0.0000    0.0000
+bond ca   n      372.3  1.42 ! SOURCE3     9    0.0092    0.0098
+bond ca   n1     398.1  1.40 ! SOURCE3
+bond ca   n2     320.6  1.47 ! SOURCE2     1    0.0000    0.0000
+bond ca   n4     325.6  1.47 ! SOURCE1    23    0.0000    0.0000
+bond ca   na     470.3  1.35 ! SOURCE1   150    0.0091    0.0103
+bond ca   nb     483.1  1.34 ! SOURCE3   104    0.0052    0.0076
+bond ca   nc     492.9  1.34 ! SOURCE1  1826    0.0012    0.0020
+bond ca   nd     492.9  1.34 ! SOURCE1  1826    0.0012    0.0020
+bond ca   ne     361.8  1.43 ! SOURCE1    52    0.0000    0.0000
+bond ca   nf     361.8  1.43 ! SOURCE1    52    0.0000    0.0000
+bond ca   nh     449.0  1.36 ! SOURCE1   137    0.0084    0.0085
+bond ca   no     322.6  1.47 ! SOURCE1   556    0.0000    0.0000
+bond ca   o      519.7  1.27 ! SOURCE3     2    0.0000    0.0000
+bond ca   oh     386.1  1.36 ! SOURCE1   551    0.0000    0.0000
+bond ca   os     372.4  1.37 ! SOURCE1  1092    0.0048    0.0071
+bond ca   p2     243.0  1.84 ! SOURCE0     1
+bond ca   p3     252.7  1.82 ! SOURCE1   145    0.0171    0.0187
+bond ca   p4     264.3  1.81 ! SOURCE0     1
+bond ca   p5     271.6  1.79 ! SOURCE1   571    0.0020    0.0028
+bond ca   pe     249.6  1.83 ! SOURCE3    10    0.0034    0.0042
+bond ca   pf     249.6  1.83 ! SOURCE3    10    0.0034    0.0042
+bond ca   px     252.1  1.82 ! SOURCE3     5    0.0153    0.0168
+bond ca   py     268.3  1.80 ! SOURCE3     3    0.0051    0.0054
+bond ca   s      277.9  1.74 ! SOURCE3     2    0.0000    0.0000
+bond ca   s4     245.2  1.79 ! SOURCE1    51    0.0038    0.0048
+bond ca   s6     263.9  1.76 ! SOURCE1   229    0.0030    0.0036
+bond ca   sh     245.8  1.79 ! SOURCE3     2    0.0055    0.0055
+bond ca   ss     256.6  1.77 ! SOURCE1   297    0.0034    0.0041
+bond ca   sx     233.8  1.81 ! SOURCE3     3    0.0015    0.0016
+bond ca   sy     247.7  1.78 ! SOURCE3    13    0.0068    0.0094
+bond cc   cc     418.3  1.43 ! SOURCE1   740    0.0058    0.0069
+bond cc   cd     504.0  1.37 ! SOURCE3   523    0.0156    0.0217
+bond cc   cf     519.2  1.36 ! SOURCE3     1    0.0000    0.0000
+bond cc   cg     420.9  1.43 ! SOURCE1   560    0.0000    0.0000
+bond cc   ch     420.9  1.43 ! SOURCE1   560    0.0000    0.0000
+bond cc   cl     348.3  1.70 ! SOURCE3     1    0.0000    0.0000
+bond cc   h4     350.1  1.08 ! SOURCE3   599    0.0028    0.0037
+bond cc   h5     356.0  1.08 ! SOURCE3    40    0.0040    0.0051
+bond cc   ha     347.2  1.08 ! SOURCE3   740    0.0029    0.0039
+bond cc   n      426.0  1.38 ! SOURCE3    56    0.0075    0.0109
+bond cc   n2     561.3  1.30 ! SOURCE3     4    0.0096    0.0096
+bond cc   na     438.8  1.37 ! SOURCE3   440    0.0107    0.0144
+bond cc   nc     431.6  1.38 ! SOURCE1    88    0.0000    0.0000
+bond cc   nd     494.6  1.33 ! SOURCE3   203    0.0171    0.0239
+bond cc   nh     449.0  1.36 ! SOURCE3     6    0.0038    0.0040
+bond cc   oh     405.9  1.35 ! SOURCE3     3    0.0077    0.0077
+bond cc   os     376.1  1.37 ! SOURCE3    86    0.0139    0.0192
+bond cc   pd     318.2  1.73 ! SOURCE3    84    same as    cd-pc
+bond cc   ss     279.3  1.74 ! SOURCE3    52    0.0162    0.0194
+!bond cd   cd     418.3  1.43 ! SOURCE1   740    0.0058    0.0069
+bond cd   cd     504.0  1.37 ! SOURCE3   523    same as    cc-cd
+bond cd   ce     519.2  1.36 ! SOURCE3     1    same as    cc-cf
+bond cd   cg     420.9  1.43 ! SOURCE1   560    0.0000    0.0000
+bond cd   ch     420.9  1.43 ! SOURCE1   560    0.0000    0.0000
+bond cd   cl     348.3  1.70 ! SOURCE3     1    same as    cc-cl
+bond cd   h4     350.1  1.08 ! SOURCE3   599    0.0028    0.0037
+bond cd   h5     356.0  1.08 ! SOURCE3    40    0.0040    0.0051
+bond cd   ha     347.2  1.08 ! SOURCE3   740    0.0029    0.0039
+bond cd   n      426.0  1.38 ! SOURCE3    56    0.0075    0.0109
+bond cd   n2     561.3  1.30 ! SOURCE3     4    0.0096    0.0096
+bond cd   na     438.8  1.37 ! SOURCE3   440    0.0107    0.0144
+bond cd   nc     494.6  1.33 ! SOURCE3   203    0.0171    0.0239
+bond cd   nd     431.6  1.38 ! SOURCE1    88    0.0000    0.0000
+bond cd   nh     449.0  1.36 ! SOURCE3     6    0.0038    0.0040
+bond cd   oh     405.9  1.35 ! SOURCE3     3    0.0077    0.0077
+bond cd   os     376.1  1.37 ! SOURCE3    86    0.0139    0.0192
+bond cd   pc     318.2  1.73 ! SOURCE3    84    0.0088    0.0161
+bond cd   ss     279.3  1.74 ! SOURCE3    52    0.0162    0.0194
+bond ce   ce     390.5  1.45 ! SOURCE1    66    0.0060    0.0060
+bond ce   cf     562.4  1.34 ! SOURCE1   543    0.0035    0.0045
+bond ce   cg     415.6  1.43 ! SOURCE1    22    0.0000    0.0000
+bond ce   ch     415.6  1.43 ! SOURCE1    22    0.0000    0.0000
+bond ce   ha     341.5  1.09 ! SOURCE3    55    0.0030    0.0056
+bond ce   n1     522.2  1.32 ! SOURCE3     3    0.0121    0.0121
+bond ce   n2     599.8  1.28 ! SOURCE1    75    0.0000    0.0000
+bond ce   ne     381.8  1.41 ! SOURCE3     7    0.0093    0.0103
+bond ce   nh     484.7  1.34 ! SOURCE3     1    0.0000    0.0000
+bond ce   p2     259.1  1.81 ! SOURCE0     1
+bond ce   pe     256.5  1.82 ! SOURCE3     8    0.0090    0.0108
+bond ce   px     254.6  1.82 ! SOURCE3     6    0.0034    0.0046
+bond ce   py     272.3  1.79 ! SOURCE3     5    0.0034    0.0045
+bond ce   s      324.5  1.68 ! SOURCE0     1
+bond ce   sx     239.7  1.80 ! SOURCE3     5    0.0080    0.0082
+bond ce   sy     248.9  1.78 ! SOURCE3     5    0.0105    0.0114
+!bond cf   cf     390.5  1.45 ! SOURCE1    66    0.0060    0.0060
+bond cf   cf     562.4  1.34 ! SOURCE1   543    same as    ce-cf
+bond cf   cg     415.6  1.43 ! SOURCE1    22    0.0000    0.0000
+bond cf   ch     415.6  1.43 ! SOURCE1    22    0.0000    0.0000
+bond cf   ha     341.5  1.09 ! SOURCE3    55    0.0030    0.0056
+bond cf   n1     522.2  1.32 ! SOURCE3     3    0.0121    0.0121
+bond cf   n2     599.8  1.28 ! SOURCE1    75    same as    ce-n2
+bond cf   nf     381.8  1.41 ! SOURCE3     7    same as    ce-ne
+bond cf   nh     484.7  1.34 ! SOURCE3     1    same as    ce-nh
+bond cf   p2     259.1  1.81 ! SOURCE0     1    same as    ce-p2
+bond cf   pf     256.5  1.82 ! SOURCE3     8    same as    ce-pe
+bond cf   px     254.6  1.82 ! SOURCE3     6    same as    ce-px
+bond cf   py     272.3  1.79 ! SOURCE3     5    same as    ce-py
+bond cf   s      324.5  1.68 ! SOURCE0     1    same as    ce- s
+bond cf   sx     239.7  1.80 ! SOURCE3     5    same as    ce-sx
+bond cf   sy     248.9  1.78 ! SOURCE3     5    same as    ce-sy
+bond cg   cg     494.2  1.38 ! SOURCE1    42    0.0000    0.0000
+bond cg   ch     949.5  1.19 ! SOURCE1    80    0.0015    0.0015
+bond cg   n1     994.7  1.14 ! SOURCE1   316    0.0011    0.0018
+bond cg   ne     450.5  1.36 ! SOURCE3     4    0.0189    0.0204
+bond cg   pe     429.8  1.62 ! SOURCE3    11    0.1928    0.2008
+!bond ch   ch     494.2  1.38 ! SOURCE1    42    0.0000    0.0000
+bond ch   ch     949.5  1.19 ! SOURCE1    80    same as    cg-ch
+bond ch   n1     994.7  1.14 ! SOURCE1   316    0.0011    0.0018
+bond ch   nf     450.5  1.36 ! SOURCE3     4    same as    cg-ne
+bond ch   pf     429.8  1.62 ! SOURCE3    11    same as    cg-pe
+bond cl   cl     143.3  2.27 ! SOURCE1     2    0.0395    0.0395
+bond cl   cx     301.8  1.75 ! SOURCE1    64    0.0000    0.0000
+bond cl   cy     292.0  1.77 ! SOURCE2     2    0.0070    0.0070
+bond cl   f      298.6  1.65 ! SOURCE2     2    0.0500    0.0500
+bond cl   i      163.5  2.55 ! SOURCE1     6    0.0605    0.0893
+bond cl   n      331.8  1.73 ! SOURCE3     3    0.0107    0.0113
+bond cl   n1     431.6  1.63 ! SOURCE0     1
+bond cl   n2     263.4  1.82 ! SOURCE3     6    0.0784    0.1020
+bond cl   n3     298.6  1.77 ! SOURCE3     4    0.0070    0.0095
+bond cl   n4     311.1  1.75 ! SOURCE3     4    0.0085    0.0098
+bond cl   na     253.2  1.83 ! SOURCE3     7    0.1858    0.2083
+bond cl   nh     303.2  1.76 ! SOURCE3     1    0.0000    0.0000
+bond cl   no     250.1  1.84 ! SOURCE2     1    0.0000    0.0000
+bond cl   o      557.6  1.48 ! SOURCE3     4    0.0000    0.0000
+bond cl   oh     309.7  1.69 ! SOURCE2     1    0.0000    0.0000
+bond cl   os     278.8  1.73 ! SOURCE3     4    0.0000    0.0000
+bond cl   p2     217.5  2.07 ! SOURCE3     6    0.0086    0.0108
+bond cl   p3     249.4  2.01 ! SOURCE1   111    0.0000    0.0000
+bond cl   p4     249.4  2.01 ! SOURCE1   111    0.0000    0.0000
+bond cl   p5     249.4  2.01 ! SOURCE1   111    0.0000    0.0000
+bond cl   pb     255.6  2.00 ! SOURCE1    46    0.0000    0.0000
+bond cl   s      208.7  2.07 ! SOURCE1     6    0.0000    0.0000
+bond cl   s2     172.7  2.16 ! SOURCE2     1    0.0000    0.0000
+bond cl   s4     208.7  2.07 ! SOURCE1     6    0.0000    0.0000
+bond cl   s6     208.7  2.07 ! SOURCE1     6    0.0000    0.0000
+bond cl   sh     208.7  2.07 ! SOURCE1     6    0.0000    0.0000
+bond cl   ss     208.7  2.07 ! SOURCE1     6    0.0000    0.0000
+bond cl   sx     208.7  2.07 ! SOURCE1     6    0.0000    0.0000
+bond cl   sy     208.7  2.07 ! SOURCE1     6    0.0000    0.0000
+bond cp   cp     346.5  1.49 ! SOURCE1   242    0.0007    0.0010
+bond cp   nb     470.3  1.35 ! SOURCE3     2    0.0000    0.0000
+bond cq   cq     346.5  1.49 ! SOURCE1   242    0.0007    0.0010
+bond cu   cu     653.7  1.29 ! SOURCE1    10    0.0000    0.0000
+bond cu   cx     327.3  1.51 ! SOURCE1    20    0.0000    0.0000
+bond cu   ha     353.0  1.08 ! SOURCE2     3    0.0104    0.0111
+bond cv   cv     568.1  1.33 ! SOURCE1    25    0.0000    0.0000
+bond cv   cy     323.5  1.51 ! SOURCE1    50    0.0000    0.0000
+bond cv   ha     344.3  1.09 ! SOURCE3     2    0.0000    0.0000
+bond cx   cv     328.3  1.51 ! SOURCE1  2536      Same        as
+bond cx   cx     337.3  1.50 ! SOURCE1  1204    0.0159    0.0183
+bond cx   cy     321.5  1.52 ! SOURCE3     2    0.0000    0.0000
+bond cx   f      347.2  1.36 ! SOURCE2     3    0.0047    0.0050
+bond cx   h1     344.3  1.09 ! SOURCE3    10    0.0013    0.0017
+bond cx   h2     350.1  1.08 ! SOURCE3     2    0.0000    0.0000
+bond cx   hc     345.8  1.09 ! SOURCE3    44    0.0009    0.0011
+bond cx   n      315.7  1.48 ! SOURCE3     1    0.0000    0.0000
+bond cx   n2     309.1  1.48 ! SOURCE3     2    0.0000    0.0000
+bond cx   n3     318.7  1.47 ! SOURCE1   134    0.0000    0.0000
+bond cx   oh     387.4  1.36 ! SOURCE3     3    0.0017    0.0018
+bond cx   os     320.1  1.42 ! SOURCE3     7    0.0205    0.0222
+bond cx   p3     227.6  1.87 ! SOURCE2     1    0.0000    0.0000
+bond cx   s4     225.3  1.82 ! SOURCE2     1    0.0000    0.0000
+bond cx   s6     283.7  1.73 ! SOURCE2     1    0.0000    0.0000
+bond cx   ss     229.2  1.81 ! SOURCE2     1    0.0000    0.0000
+bond cy   cy     286.8  1.55 ! SOURCE1   742    0.0013    0.0041
+bond cy   h1     330.4  1.10 ! SOURCE3    17    0.0042    0.0058
+bond cy   hc     334.5  1.09 ! SOURCE3    63    0.0012    0.0014
+bond cy   n3     307.2  1.48 ! SOURCE1    21    0.0000    0.0000
+bond cy   oh     325.2  1.42 ! SOURCE3     2    0.0000    0.0000
+bond cy   os     280.7  1.46 ! SOURCE3     4    0.0101    0.0101
+bond f    n      391.7  1.40 ! SOURCE3     3    0.0106    0.0112
+bond f    n1     375.7  1.41 ! SOURCE0     1
+bond f    n2     337.5  1.44 ! SOURCE3     5    0.0292    0.0377
+bond f    n3     380.6  1.41 ! SOURCE1     9    0.0000    0.0000
+bond f    n4     526.8  1.31 ! SOURCE3     2    0.0000    0.0000
+bond f    na     374.5  1.41 ! SOURCE3     7    0.0537    0.0611
+bond f    nh     357.1  1.43 ! SOURCE3     3    0.0080    0.0085
+bond f    no     314.4  1.47 ! SOURCE2     1    0.0000    0.0000
+bond f    o      442.2  1.33 ! SOURCE0     1
+bond f    oh     305.4  1.44 ! SOURCE3     1    0.0000    0.0000
+bond f    os     326.2  1.42 ! SOURCE3     2    0.0000    0.0000
+bond f    p2     287.3  1.54 ! SOURCE3     7    0.1437    0.2054
+bond f    p3     254.5  1.58 ! SOURCE2     8    0.0093    0.0103
+bond f    p4     246.0  1.59 ! SOURCE0     1
+bond f    p5     253.8  1.58 ! SOURCE1    72    0.0000    0.0000
+bond f    s      233.3  1.66 ! SOURCE0     1
+bond f    s2     244.4  1.64 ! SOURCE2     1    0.0000    0.0000
+bond f    s4     282.4  1.59 ! SOURCE2     4    0.0065    0.0065
+bond f    s6     312.1  1.56 ! SOURCE2     5    0.0163    0.0220
+bond f    sh     240.4  1.65 ! SOURCE3     1    0.0000    0.0000
+bond f    ss     250.5  1.63 ! SOURCE3     3    0.0147    0.0156
+bond hn   n      410.2  1.01 ! SOURCE3   149    0.0089    0.0098
+bond hn   n1     455.1  0.99 ! SOURCE2     1    0.0000    0.0000
+bond hn   n2     375.5  1.03 ! SOURCE3   108    0.0070    0.0096
+bond hn   n3     394.1  1.02 ! SOURCE3   157    0.0063    0.0086
+bond hn   n4     369.0  1.03 ! SOURCE3   264    0.0054    0.0082
+bond hn   na     406.6  1.01 ! SOURCE3    46    0.0076    0.0107
+bond hn   nh     401.2  1.01 ! SOURCE3   209    0.0068    0.0091
+bond hn   no     385.6  1.02 ! SOURCE3     1    0.0000    0.0000
+bond ho   o      357.9  0.98 ! SOURCE3     1    0.0000    0.0000
+bond ho   oh     369.6  0.97 ! SOURCE3   367    0.0069    0.0105
+bond hp   p2     385.1  1.34 ! SOURCE3    87    0.1430    0.1706
+bond hp   p3     303.1  1.41 ! SOURCE3   101    0.0170    0.0617
+bond hp   p4     368.7  1.35 ! SOURCE3    17    0.1277    0.1577
+bond hp   p5     305.0  1.41 ! SOURCE3     7    0.0049    0.0062
+bond hs   s      286.4  1.35 ! SOURCE3     1    0.0000    0.0000
+bond hs   s4     266.4  1.38 ! SOURCE3     5    0.0004    0.0004
+bond hs   s6     280.8  1.36 ! SOURCE3     5    0.0012    0.0015
+bond hs   sh     302.2  1.34 ! SOURCE3    98    0.0150    0.0486
+bond i    i      109.2  2.92 ! SOURCE1     1    0.0000    0.0000
+bond i    n      278.3  2.10 ! SOURCE3     5    0.0153    0.0156
+bond i    n1     302.1  2.06 ! SOURCE0     1
+bond i    n2     182.6  2.30 ! SOURCE3     6    0.1010    0.1186
+bond i    n3     231.8  2.19 ! SOURCE3     3    0.0412    0.0437
+bond i    n4     246.6  2.15 ! SOURCE3     3    0.0158    0.0168
+bond i    na     260.5  2.13 ! SOURCE3     8    0.0838    0.1276
+bond i    nh     249.2  2.15 ! SOURCE3     1    0.0000    0.0000
+bond i    no     211.0  2.23 ! SOURCE3     1    0.0000    0.0000
+bond i    o      323.8  1.98 ! SOURCE0     1
+bond i    oh     247.9  2.10 ! SOURCE3     2    0.0000    0.0000
+bond i    os     233.6  2.13 ! SOURCE3     3    0.0138    0.0146
+bond i    p2     108.2  2.64 ! SOURCE3     6    0.0266    0.0297
+bond i    p3     123.6  2.57 ! SOURCE3     3    0.0015    0.0016
+bond i    p4     183.0  2.35 ! SOURCE3     4    0.2600    0.2600
+bond i    p5     117.3  2.60 ! SOURCE3     3    0.0135    0.0143
+bond i    s      175.1  2.43 ! SOURCE0     1
+bond i    s4      82.8  2.87 ! SOURCE0     1
+bond i    s6      82.8  2.87 ! SOURCE3     1    0.0000    0.0000
+bond i    sh     138.5  2.56 ! SOURCE0     1
+bond i    ss     135.9  2.57 ! SOURCE3     3    0.0061    0.0065
+bond n    n      469.7  1.39 ! SOURCE3     5    0.0035    0.0038
+bond n    n1     553.9  1.34 ! SOURCE0     1
+bond n    n2     499.7  1.37 ! SOURCE3     9    0.0164    0.0200
+bond n    n3     443.3  1.41 ! SOURCE3     5    0.0077    0.0087
+bond n    n4     410.8  1.43 ! SOURCE3     5    0.0096    0.0098
+bond n    na     486.8  1.38 ! SOURCE3    11    0.0052    0.0071
+bond n    nc     532.1  1.35 ! SOURCE3     4    0.0055    0.0058
+bond n    nd     532.1  1.35 ! SOURCE3     4    same as    n -nc
+bond n    nh     437.7  1.41 ! SOURCE3     4    0.0035    0.0037
+bond n    no     381.2  1.46 ! SOURCE3     4    0.0278    0.0327
+bond n    o      646.6  1.26 ! SOURCE3     9    0.0336    0.0381
+bond n    oh     395.4  1.41 ! SOURCE3     6    0.0078    0.0106
+bond n    os     372.3  1.43 ! SOURCE3     7    0.0364    0.0528
+bond n    p2     310.3  1.73 ! SOURCE3     8    0.0143    0.0217
+bond n    p3     282.2  1.77 ! SOURCE3     9    0.0102    0.0118
+bond n    p4     309.5  1.73 ! SOURCE3     1    0.0000    0.0000
+bond n    p5     327.0  1.71 ! SOURCE3     4    0.0084    0.0109
+bond n    pc     304.8  1.74 ! SOURCE3     3    0.0009    0.0010
+bond n    pd     304.8  1.74 ! SOURCE3     3    same as    n -pc
+bond n    s      247.5  1.77 ! SOURCE3     3    0.0011    0.0011
+bond n    s4     238.2  1.78 ! SOURCE3     4    0.0154    0.0214
+bond n    s6     270.8  1.73 ! SOURCE3     4    0.0177    0.0224
+bond n    sh     273.6  1.73 ! SOURCE3     4    0.0108    0.0128
+bond n    ss     281.6  1.72 ! SOURCE3     7    0.0120    0.0133
+!bond n1   n1    1221.7  1.12 ! SOURCE1    19    0.0000    0.0000
+bond n1   n1    1365.7  1.10 ! SOURCE3
+bond n1   n2     857.4  1.22 ! SOURCE1    19    0.0000    0.0000
+bond n1   n3     535.7  1.35 ! SOURCE0     1
+bond n1   n4     518.2  1.36 ! SOURCE0     1
+bond n1   na     535.7  1.35 ! SOURCE0     1
+bond n1   nc     857.4  1.22 ! SOURCE1    38    0.0000    0.0000
+bond n1   nd     857.4  1.22 ! SOURCE1    38    0.0000    0.0000
+bond n1   ne     751.9  1.25 ! SOURCE2     1    0.0000    0.0000
+bond n1   nf     751.9  1.25 ! SOURCE2     1    same as    n1-ne
+bond n1   nh     553.9  1.34 ! SOURCE0     1
+bond n1   no     454.8  1.40 ! SOURCE0     1
+bond n1   o      617.5  1.28 ! SOURCE3     5    0.0340    0.0438
+bond n1   oh     569.8  1.30 ! SOURCE0     1
+bond n1   os     550.5  1.31 ! SOURCE0     1
+bond n1   p2     358.8  1.68 ! SOURCE3     2    0.0282    0.0282
+bond n1   p3     376.7  1.66 ! SOURCE0     1
+bond n1   p4     353.0  1.68 ! SOURCE3
+bond n1   p5     482.7  1.57 ! SOURCE1   132    0.0000    0.0000
+bond n1   s      328.7  1.66 ! SOURCE3     6    0.0693    0.0789
+bond n1   s2     604.3  1.45 ! SOURCE2     2    0.0010    0.0010
+bond n1   s4     336.8  1.65 ! SOURCE0     1
+bond n1   s6     670.3  1.42 ! SOURCE2     2    0.0000    0.0000
+bond n1   sh     376.1  1.61 ! SOURCE0     1
+bond n1   ss     376.1  1.61 ! SOURCE0     1
+bond n2   n2     702.7  1.27 ! SOURCE3    27    0.0228    0.0347
+bond n2   n3     574.8  1.33 ! SOURCE2     1    0.0000    0.0000
+bond n2   n4     200.8  1.68 ! SOURCE3     7    0.2778    0.3138
+bond n2   na     450.4  1.40 ! SOURCE3    14    0.0399    0.0575
+bond n2   nc     743.9  1.25 ! SOURCE1    13    same as    n2-nd
+bond n2   nd     743.9  1.25 ! SOURCE1    13    0.0000    0.0000
+bond n2   ne     685.5  1.28 ! SOURCE3    30    0.0224    0.0302
+bond n2   nf     685.5  1.28 ! SOURCE3    30    same as    n2-ne
+bond n2   nh     525.1  1.36 ! SOURCE3    22    0.0218    0.0300
+bond n2   no     231.9  1.63 ! SOURCE3     4    0.1591    0.1933
+bond n2   o      789.9  1.21 ! SOURCE3    20    0.0284    0.0344
+bond n2   oh     416.2  1.39 ! SOURCE1    67    0.0000    0.0000
+bond n2   os     400.5  1.41 ! SOURCE3    10    0.0120    0.0147
+bond n2   p2     438.3  1.60 ! SOURCE3    35    0.0392    0.0737
+bond n2   p3     286.5  1.76 ! SOURCE3     7    0.0320    0.0374
+bond n2   p4     317.7  1.72 ! SOURCE0     1
+bond n2   p5     445.8  1.60 ! SOURCE1     7    0.0000    0.0000
+bond n2   pe     527.9  1.54 ! SOURCE3    20    0.1112    0.1392
+bond n2   pf     527.9  1.54 ! SOURCE3    20    same as    n2-pe
+bond n2   s      458.1  1.54 ! SOURCE1    37    0.0000    0.0000
+bond n2   s2     499.0  1.51 ! SOURCE2     1    0.0000    0.0000
+bond n2   s4     376.1  1.61 ! SOURCE0     1
+bond n2   s6     468.9  1.53 ! SOURCE3     3    0.0144    0.0144
+bond n2   sh     266.6  1.74 ! SOURCE3     5    0.0405    0.0511
+bond n2   ss     331.4  1.66 ! SOURCE1    36    0.0000    0.0000
+bond n3   n3     383.6  1.45 ! SOURCE1    44    0.0000    0.0000
+bond n3   n4     434.9  1.41 ! SOURCE1    13    0.0000    0.0000
+bond n3   na     426.7  1.42 ! SOURCE1    68    0.0000    0.0000
+bond n3   nh     426.7  1.42 ! SOURCE1    68    0.0000    0.0000
+bond n3   no     394.5  1.45 ! SOURCE3     3    0.0196    0.0208
+bond n3   o      564.0  1.30 ! SOURCE3     4    0.1197    0.1217
+bond n3   oh     413.5  1.40 ! SOURCE1    28    0.0000    0.0000
+bond n3   os     359.6  1.44 ! SOURCE1    34    0.0301    0.0315
+bond n3   p2     366.6  1.67 ! SOURCE0     1
+bond n3   p3     312.8  1.73 ! SOURCE1    40    0.0000    0.0000
+bond n3   p4     341.1  1.70 ! SOURCE1    88    0.0000    0.0000
+bond n3   p5     373.6  1.66 ! SOURCE1   501    0.0033    0.0086
+bond n3   s      232.3  1.79 ! SOURCE3     3    0.0168    0.0178
+bond n3   s4     251.3  1.76 ! SOURCE3     6    0.0563    0.0766
+bond n3   s6     353.8  1.63 ! SOURCE1    99    0.0090    0.0136
+bond n3   sh     265.9  1.74 ! SOURCE3     3    0.0145    0.0154
+bond n3   ss     277.9  1.72 ! SOURCE3     5    0.0172    0.0207
+bond n4   n4     349.9  1.48 ! SOURCE3     4    0.0089    0.0089
+bond n4   na     407.0  1.44 ! SOURCE3     9    0.0242    0.0390
+bond n4   nh     369.7  1.47 ! SOURCE3     5    0.0097    0.0108
+bond n4   no     354.2  1.48 ! SOURCE0     1
+bond n4   o      463.6  1.36 ! SOURCE3     3    0.0039    0.0041
+bond n4   oh     408.2  1.40 ! SOURCE3     3    0.0108    0.0115
+bond n4   os     381.8  1.42 ! SOURCE3     5    0.0225    0.0249
+bond n4   p2     185.9  1.94 ! SOURCE3    10    0.0514    0.0643
+bond n4   p3     215.1  1.88 ! SOURCE3     5    0.0143    0.0146
+bond n4   p4     187.6  1.94 ! SOURCE3     1    0.0000    0.0000
+bond n4   p5     242.9  1.83 ! SOURCE3     5    0.0082    0.0087
+bond n4   py     204.2  1.90 ! SOURCE3     4    0.0000    0.0000
+bond n4   s      210.3  1.83 ! SOURCE3     3    0.0004    0.0004
+bond n4   s4     151.0  1.97 ! SOURCE3     3    0.0187    0.0198
+bond n4   s6     172.7  1.91 ! SOURCE3     5    0.0414    0.0432
+bond n4   sh     221.5  1.81 ! SOURCE3     3    0.0025    0.0027
+bond n4   ss     221.0  1.81 ! SOURCE3     5    0.0061    0.0064
+bond na   na     453.3  1.40 ! SOURCE1    40    0.0000    0.0000
+bond na   nc     535.7  1.35 ! SOURCE3   152    0.0117    0.0180
+bond na   nd     535.7  1.35 ! SOURCE3   152    0.0117    0.0180
+bond na   nh     453.3  1.40 ! SOURCE1    40    0.0000    0.0000
+bond na   no     401.9  1.44 ! SOURCE3     9    0.0241    0.0289
+bond na   o      644.3  1.27 ! SOURCE1    25    0.0345    0.0347
+bond na   oh     412.2  1.40 ! SOURCE3     9    0.0161    0.0217
+bond na   os     355.2  1.44 ! SOURCE3    45    0.0349    0.0423
+bond na   p2     297.8  1.75 ! SOURCE3    11    0.0164    0.0192
+bond na   p3     288.0  1.76 ! SOURCE3     8    0.0101    0.0113
+bond na   p4     492.4  1.56 ! SOURCE3     5    0.2116    0.2161
+bond na   p5     325.3  1.71 ! SOURCE3    11    0.0197    0.0238
+bond na   pc     311.1  1.73 ! SOURCE3    81    0.0124    0.0207
+bond na   pd     311.1  1.73 ! SOURCE3    81    same as    na-pc
+bond na   py     327.8  1.71 ! SOURCE3     2    0.0000    0.0000
+bond na   s      248.7  1.77 ! SOURCE3     8    0.0083    0.0095
+bond na   s4     231.7  1.79 ! SOURCE3    10    0.0316    0.0421
+bond na   s6     274.3  1.73 ! SOURCE3    10    0.0169    0.0201
+bond na   sh     278.6  1.72 ! SOURCE3     9    0.0101    0.0113
+bond na   ss     270.1  1.73 ! SOURCE3    38    0.0280    0.0412
+bond na   sy     274.3  1.73 ! SOURCE3     1
+bond nb   nb     550.2  1.34 ! SOURCE1    15    0.0307    0.0314
+bond nb   pb     461.1  1.59 ! SOURCE1   162    0.0076    0.0091
+bond nc   nc     486.8  1.38 ! SOURCE3     9    0.0145    0.0164
+bond nc   nd     602.9  1.31 ! SOURCE3     9    0.0205    0.0221
+bond nc   os     414.9  1.40 ! SOURCE1    46    0.0163    0.0188
+bond nc   ss     433.5  1.56 ! SOURCE1    74    0.0000    0.0000
+bond nc   sy     439.8  1.56 ! SOURCE3     2
+!bond nd   nd     486.8  1.38 ! SOURCE3     9    0.0145    0.0164
+bond nd   nd     602.9  1.31 ! SOURCE3     9    same as    nc-nd
+bond nd   os     414.9  1.40 ! SOURCE1    46    0.0163    0.0188
+bond nd   ss     433.5  1.56 ! SOURCE1    74    0.0000    0.0000
+bond nd   sy     439.8  1.56 ! SOURCE3     2
+bond ne   ne     355.3  1.48 ! SOURCE3    19    0.0960    0.1705
+bond ne   nf     738.6  1.26 ! SOURCE3     1    0.0000    0.0000
+bond ne   o      736.4  1.23 ! SOURCE3    40    0.0207    0.0255
+bond ne   p2     493.9  1.56 ! SOURCE3    14    0.0950    0.1325
+bond ne   pe     327.8  1.71 ! SOURCE3    28    0.0848    0.1076
+bond ne   px     336.6  1.70 ! SOURCE3    11    0.0578    0.0883
+bond ne   py     320.2  1.72 ! SOURCE3    15    0.0497    0.0702
+bond ne   s      463.5  1.54 ! SOURCE3    22    0.1396    0.1708
+bond ne   sx     207.3  1.84 ! SOURCE3     7    0.0760    0.1060
+bond ne   sy     257.1  1.75 ! SOURCE3     7    0.0640    0.0814
+!bond nf   nf     355.3  1.48 ! SOURCE3    19    same as    ne-ne
+bond nf   nf     738.6  1.26 ! SOURCE3     1    same as    ne-nf
+bond nf   o      736.4  1.23 ! SOURCE3    40    same as    ne- o
+bond nf   p2     493.9  1.56 ! SOURCE3    14    same as    ne-p2
+bond nf   pf     327.8  1.71 ! SOURCE3    28    same as    ne-pe
+bond nf   px     336.6  1.70 ! SOURCE3    11    same as    ne-px
+bond nf   py     320.2  1.72 ! SOURCE3    15    same as    ne-py
+bond nf   s      463.5  1.54 ! SOURCE3    22    same as    ne- s
+bond nf   sx     207.3  1.84 ! SOURCE3     7    same as    ne-sx
+bond nf   sy     257.1  1.75 ! SOURCE3     7    same as    ne-sy
+bond nh   nh     453.3  1.40 ! SOURCE1    40    0.0000    0.0000
+bond nh   no     381.2  1.46 ! SOURCE3     4    0.0364    0.0370
+bond nh   o      596.2  1.29 ! SOURCE3     3    0.0424    0.0450
+bond nh   oh     359.6  1.44 ! SOURCE3     1    0.0000    0.0000
+bond nh   os     358.5  1.44 ! SOURCE3     3    0.0029    0.0031
+bond nh   p2     357.8  1.68 ! SOURCE3    17    0.0422    0.0872
+bond nh   p3     312.8  1.73 ! SOURCE3     3    0.0015    0.0016
+bond nh   p4     333.1  1.71 ! SOURCE3     3    0.0008    0.0008
+bond nh   p5     365.6  1.67 ! SOURCE3     3    0.0007    0.0007
+bond nh   s      237.0  1.78 ! SOURCE3     3    0.0072    0.0076
+bond nh   s4     259.1  1.75 ! SOURCE3     3    0.0192    0.0203
+bond nh   s6     300.0  1.69 ! SOURCE3     3    0.0036    0.0038
+bond nh   sh     288.3  1.71 ! SOURCE3     1    0.0000    0.0000
+bond nh   ss     288.3  1.71 ! SOURCE1    52    0.0015    0.0015
+bond nh   sy     324.2  1.66 ! SOURCE3     1    0.0000    0.0000
+bond no   no     138.3  1.82 ! SOURCE3     1    0.0000    0.0000
+bond no   o      761.2  1.22 ! SOURCE1  1838    0.0023    0.0049
+bond no   oh     400.5  1.41 ! SOURCE2     1    0.0000    0.0000
+bond no   os     320.8  1.48 ! SOURCE3     4    0.0526    0.0532
+bond no   p2     306.3  1.74 ! SOURCE3    10    0.2024    0.2231
+bond no   p3     234.7  1.84 ! SOURCE3     3    0.0005    0.0005
+bond no   p4     220.4  1.87 ! SOURCE3     3    0.0005    0.0006
+bond no   p5     240.5  1.83 ! SOURCE3     4    0.0019    0.0020
+bond no   s      263.8  1.74 ! SOURCE3     2    0.0000    0.0000
+bond no   s4     143.0  2.00 ! SOURCE3     3    0.0295    0.0313
+bond no   s6     149.6  1.98 ! SOURCE3     3    0.0490    0.0520
+bond no   sh     225.4  1.80 ! SOURCE3     1    0.0000    0.0000
+bond no   ss     212.4  1.83 ! SOURCE3     3    0.0230    0.0244
+bond o    o      384.3  1.43 ! SOURCE3     2    0.0500    0.0500
+bond o    oh     294.6  1.52 ! SOURCE3     2    0.0000    0.0000
+bond o    os     306.3  1.50 ! SOURCE3     3    0.0110    0.0117
+bond o    p2     449.7  1.51 ! SOURCE3    17    0.0167    0.0306
+bond o    p3     440.4  1.52 ! SOURCE3    35    0.0214    0.0297
+bond o    p4     456.4  1.50 ! SOURCE3    42    0.0343    0.0749
+bond o    p5     487.7  1.48 ! SOURCE1   263    0.0180    0.0205
+bond o    pe     432.6  1.52 ! SOURCE3    20    0.0138    0.0171
+bond o    pf     432.6  1.52 ! SOURCE3    20    same as    o -pe
+bond o    px     459.2  1.50 ! SOURCE3    37    0.0125    0.0160
+bond o    py     477.5  1.49 ! SOURCE3    63    0.0063    0.0091
+bond o    s      194.8  1.80 ! SOURCE3     2    0.0000    0.0000
+bond o    s2     333.6  1.60 ! SOURCE3     3    0.0666    0.0707
+bond o    s4     448.7  1.50 ! SOURCE1    90    0.0000    0.0000
+bond o    s6     541.1  1.44 ! SOURCE1  1038    0.0072    0.0128
+bond o    sh     328.0  1.60 ! SOURCE3     2    0.0000    0.0000
+bond o    ss     398.5  1.54 ! SOURCE3     3    0.0434    0.0501
+bond o    sx     434.2  1.51 ! SOURCE3    40    0.0083    0.0130
+bond o    sy     493.0  1.47 ! SOURCE3    92    0.0077    0.0114
+bond oh   oh     340.5  1.47 ! SOURCE3     1    0.0000    0.0000
+bond oh   os     342.6  1.47 ! SOURCE3     3    0.0065    0.0072
+bond oh   p2     316.8  1.63 ! SOURCE3     8    0.0600    0.0916
+bond oh   p3     278.8  1.68 ! SOURCE3     3    0.0140    0.0148
+bond oh   p4     307.4  1.64 ! SOURCE3     4    0.0092    0.0092
+bond oh   p5     321.2  1.62 ! SOURCE3    92    0.0293    0.0451
+bond oh   py     332.1  1.61 ! SOURCE3    79    0.0111    0.0138
+bond oh   s      190.0  1.81 ! SOURCE3     2    0.0000    0.0000
+bond oh   s4     274.3  1.67 ! SOURCE3     3    0.0173    0.0184
+bond oh   s6     344.1  1.59 ! SOURCE3    13    0.0072    0.0091
+bond oh   sh     258.6  1.69 ! SOURCE3     2    0.0003    0.0003
+bond oh   ss     265.6  1.68 ! SOURCE3     4    0.0131    0.0131
+bond oh   sy     302.6  1.63 ! SOURCE3     4    0.0153    0.0154
+bond os   os     343.6  1.47 ! SOURCE1    20    0.0047    0.0067
+bond os   p2     371.9  1.57 ! SOURCE1    16    0.0000    0.0000
+bond os   p3     272.2  1.69 ! SOURCE3     6    0.0141    0.0201
+bond os   p4     311.6  1.64 ! SOURCE3     4    0.0056    0.0057
+bond os   p5     342.5  1.60 ! SOURCE1   248    0.0332    0.0400
+bond os   py     328.5  1.62 ! SOURCE3    17    0.0082    0.0139
+bond os   s      195.8  1.80 ! SOURCE3     3    0.0049    0.0052
+bond os   s4     253.9  1.70 ! SOURCE3     8    0.0194    0.0223
+bond os   s6     355.0  1.58 ! SOURCE1    75    0.0019    0.0030
+bond os   sh     273.6  1.67 ! SOURCE3     3    0.0100    0.0106
+bond os   ss     250.5  1.70 ! SOURCE3     9    0.0241    0.0277
+bond os   sy     253.9  1.70 ! SOURCE3     1    0.0000    0.0000
+bond p2   p2     490.3  1.79 ! SOURCE3    25    0.3198    0.3488
+bond p2   p3     211.9  2.15 ! SOURCE3     9    0.1116    0.1777
+bond p2   p4     200.4  2.18 ! SOURCE0     1
+bond p2   p5     199.9  2.18 ! SOURCE0     1
+bond p2   pe     401.6  1.87 ! SOURCE3    16    0.3079    0.3571
+bond p2   pf     401.6  1.87 ! SOURCE3    16    same as    p2-pe
+bond p2   s      361.6  1.77 ! SOURCE3    26    0.2523    0.3014
+bond p2   s4     139.4  2.19 ! SOURCE0     1
+bond p2   s6     142.3  2.18 ! SOURCE0     1
+bond p2   sh     224.0  1.97 ! SOURCE3    10    0.2263    0.2829
+bond p2   ss     226.6  1.97 ! SOURCE3    10    0.2189    0.2739
+bond p3   p3     186.5  2.21 ! SOURCE1    41    0.0000    0.0000
+bond p3   p4     185.7  2.22 ! SOURCE3     3    0.0010    0.0011
+bond p3   p5     186.9  2.21 ! SOURCE3     9    0.0257    0.0265
+bond p3   s      179.7  2.07 ! SOURCE0     1
+bond p3   s4     173.2  2.09 ! SOURCE3     8    0.1935    0.2235
+bond p3   s6     176.9  2.08 ! SOURCE3    11    0.1085    0.1420
+bond p3   sh     157.3  2.13 ! SOURCE3     3    0.0073    0.0078
+bond p3   ss     161.0  2.12 ! SOURCE3     3    0.0056    0.0059
+bond p4   p4     273.1  2.03 ! SOURCE1     1    0.0000    0.0000
+bond p4   p5     178.0  2.24 ! SOURCE0     1
+bond p4   s      152.7  2.15 ! SOURCE3     5    0.0571    0.0601
+bond p4   s4     123.2  2.25 ! SOURCE0     1
+bond p4   s6     118.9  2.27 ! SOURCE0     1
+bond p4   sh     163.1  2.12 ! SOURCE3     4    0.0006    0.0008
+bond p4   ss     167.0  2.10 ! SOURCE3     4    0.0042    0.0044
+bond p5   p5     261.4  2.05 ! SOURCE1     1    0.0000    0.0000
+bond p5   s      250.8  1.92 ! SOURCE1    89    0.0097    0.0140
+bond p5   s4     191.9  2.04 ! SOURCE0     1
+bond p5   s6     191.9  2.04 ! SOURCE0     1
+bond p5   sh     175.0  2.08 ! SOURCE3     3    0.0033    0.0035
+bond p5   ss     164.8  2.11 ! SOURCE3     6    0.0379    0.0531
+bond pe   pe     240.7  2.09 ! SOURCE3     7    0.1280    0.1369
+bond pe   pf     260.8  2.06 ! SOURCE3     1    0.0000    0.0000
+bond pe   px     291.4  2.00 ! SOURCE3    12    0.2364    0.2609
+bond pe   py     278.6  2.02 ! SOURCE3    12    0.2453    0.2617
+bond pe   s      374.7  1.76 ! SOURCE3    31    0.2925    0.3197
+bond pe   sx     145.9  2.17 ! SOURCE3     9    0.1092    0.1743
+bond pe   sy     133.0  2.21 ! SOURCE3     6    0.0106    0.0127
+!bond pf   pf     240.7  2.09 ! SOURCE3     7    same as    pe-pe
+bond pf   pf     260.8  2.06 ! SOURCE3     1    same as    pe-pf
+bond pf   px     291.4  2.00 ! SOURCE3    12    same as    pe-px
+bond pf   py     278.6  2.02 ! SOURCE3    12    same as    pe-py
+bond pf   s      374.7  1.76 ! SOURCE3    31    same as    pe- s
+bond pf   sx     145.9  2.17 ! SOURCE3     9    same as    pe-sx
+bond pf   sy     133.0  2.21 ! SOURCE3     6    same as    pe-sy
+bond px   py     192.3  2.20 ! SOURCE3     5    0.0221    0.0238
+bond px   sx     125.4  2.24 ! SOURCE3     3    0.0112    0.0119
+bond px   sy     123.7  2.25 ! SOURCE3     3    0.0256    0.0272
+bond py   py     197.5  2.19 ! SOURCE3     8    0.0110    0.0132
+bond py   sx     121.2  2.26 ! SOURCE3     7    0.0595    0.0603
+bond py   sy     141.7  2.18 ! SOURCE3     5    0.0044    0.0047
+bond s    s      169.0  2.03 ! SOURCE3     1    0.0000    0.0000
+bond s    s2     229.2  1.90 ! SOURCE1     5    0.0000    0.0000
+bond s    s4     152.8  2.08 ! SOURCE3     4    0.0345    0.0345
+bond s    s6     166.0  2.04 ! SOURCE3     3    0.0293    0.0311
+bond s    sh     142.0  2.11 ! SOURCE3     2    0.0000    0.0000
+bond s    ss     148.5  2.09 ! SOURCE3     1    0.0000    0.0000
+bond s4   s4     151.5  2.08 ! SOURCE0     1
+bond s4   s6     151.5  2.08 ! SOURCE0     1
+bond s4   sh     125.7  2.17 ! SOURCE3     3    0.0214    0.0227
+bond s4   ss     126.2  2.17 ! SOURCE3     5    0.0241    0.0247
+bond s6   s6     151.5  2.08 ! SOURCE0     1
+bond s6   sh     142.6  2.11 ! SOURCE3     3    0.0136    0.0144
+bond s6   ss     139.6  2.12 ! SOURCE3     5    0.0198    0.0209
+bond sh   sh     158.9  2.06 ! SOURCE2     1    0.0000    0.0000
+bond sh   ss     155.8  2.07 ! SOURCE3     3    0.0027    0.0029
+bond ss   ss     161.7  2.05 ! SOURCE1   225    0.0015    0.0015
+bond sx   sx      80.9  2.39 ! SOURCE3     3    0.0174    0.0185
+bond sx   sy     105.3  2.25 ! SOURCE3     5    0.0703    0.0737
+bond sy   sy     106.4  2.25 ! SOURCE3     3    0.0272    0.0289
+
+angle hw   ow   hw     100.0 104.52 ! TIP3P water
+angle hw   hw   ow       0.0 127.74 ! (found in crystallographic water with 3 bonds)
+angle br   c    br      66.9 113.10 ! SOURCE0    1
+angle br   c    c3      63.3 110.74 ! SOURCE3    1   0.0000   0.0000
+angle br   c    o       63.2 121.46 ! SOURCE3    5   1.5866   1.6264
+angle c    c    c       62.3 111.68 ! SOURCE3    2   6.1226   6.1226
+angle c    c    c3      61.7 116.86 ! SOURCE3    5   0.1552   0.1653
+angle c    c    cc      64.0 111.67 ! SOURCE3    4   5.1992   5.5146
+angle c    c    cd      64.0 111.67 ! SOURCE3    4   5.1992   5.5146
+angle c    c    ha      44.8 115.43 ! SOURCE2    3   0.5556   0.6549
+angle c    c    hc      44.6 117.08 ! SOURCE0    1
+angle c    c    n       69.9 104.81 ! SOURCE3    2   0.0000   0.0000
+angle c    c    o       66.8 122.34 ! SOURCE3   24   2.7100   3.2125
+angle c    c    oh      68.2 113.23 ! SOURCE3    5   0.4643   0.5615
+angle c1   c    c1      65.1 115.32 ! SOURCE0    1
+angle c1   c    o       69.9 122.34 ! SOURCE0    1
+angle c2   c    c2      67.2 116.78 ! SOURCE0    1
+angle c2   c    ha      48.6 115.95 ! SOURCE0    1
+angle c2   c    o       72.8 119.12 ! SOURCE3    2   0.0000   0.0000
+angle c2   c    s       64.7 119.16 ! SOURCE3    2   0.0000   0.0000
+angle c3   c    c3      62.8 116.05 ! SOURCE3   11   0.9292   1.0986
+angle c3   c    ca      62.3 119.53 ! SOURCE3    3   0.4442   0.4712
+angle c3   c    cc      67.2 104.51 ! SOURCE3    1   0.0000   0.0000
+angle c3   c    cd      67.2 104.51 ! SOURCE3    1  same as c3-c -cc
+angle c3   c    ce      63.2 117.28 ! SOURCE3    2   0.7766   0.7766
+angle c3   c    cf      63.2 117.28 ! SOURCE3    2  same as c3-c -ce
+angle c3   c    cg      64.3 115.00 ! SOURCE2    1   0.0000   0.0000
+angle c3   c    ch      64.3 115.00 ! SOURCE2    1  same as c3-c -cg
+angle c3   c    cl      58.2 111.99 ! SOURCE3    2   0.0125   0.0125
+angle c3   c    f       66.9 110.70 ! SOURCE2    1   0.0000   0.0000
+angle c3   c    ha      46.0 115.22 ! SOURCE3   15   0.2737   0.3181
+angle c3   c    i       56.9 112.94 ! SOURCE3    1   0.0000   0.0000
+angle c3   c    n       67.9 115.15 ! SOURCE3  153   1.9677   2.7443
+angle c3   c    n2      66.6 114.53 ! SOURCE0    1
+angle c3   c    n4      64.6 112.26 ! SOURCE3    2   0.0000   0.0000
+angle c3   c    ne      67.2 113.38 ! SOURCE3    1   0.0000   0.0000
+angle c3   c    nf      67.2 113.38 ! SOURCE3    1  same as c3-c -ne
+angle c3   c    nh      68.2 113.58 ! SOURCE0    1
+angle c3   c    o       68.0 123.11 ! SOURCE3  267   2.2483   3.0977
+angle c3   c    oh      69.8 112.20 ! SOURCE3   14   1.4815   1.8324
+angle c3   c    os      69.3 111.96 ! SOURCE3   15   1.8520   2.3072
+angle c3   c    p3      74.5 116.42 ! SOURCE3    3   0.1217   0.1291
+angle c3   c    p5      74.2 118.90 ! SOURCE0    1
+angle c3   c    pe      74.2 114.85 ! SOURCE3    1   0.0000   0.0000
+angle c3   c    pf      74.2 114.85 ! SOURCE3    1  same as c3-c -pe
+angle c3   c    px      74.2 115.60 ! SOURCE3    1   0.0000   0.0000
+angle c3   c    py      74.5 118.16 ! SOURCE3    3   1.0121   1.0735
+angle c3   c    s       62.0 123.73 ! SOURCE3    9   1.2078   1.4528
+angle c3   c    s4      59.6 114.79 ! SOURCE0    1
+angle c3   c    s6      59.7 114.72 ! SOURCE0    1
+angle c3   c    sh      62.0 114.21 ! SOURCE3    3   2.0662   2.3916
+angle c3   c    ss      62.4 114.32 ! SOURCE3    5   2.0411   2.7478
+angle c3   c    sx      59.5 113.97 ! SOURCE3    3   0.0575   0.0610
+angle c3   c    sy      59.9 114.28 ! SOURCE3    3   0.6921   0.7341
+angle ca   c    ca      62.8 119.53 ! SOURCE3    1   0.0000   0.0000
+angle ca   c    cc      64.8 114.09 ! SOURCE3    2   0.0000   0.0000
+angle ca   c    cd      64.8 114.09 ! SOURCE3    2   0.0000   0.0000
+angle ca   c    ha      46.8 114.12 ! SOURCE3    1   0.0000   0.0000
+angle ca   c    n       69.4 112.03 ! SOURCE3    3   5.1757   5.5118
+angle ca   c    o       68.7 123.44 ! SOURCE3   18   2.1221   2.5574
+angle ca   c    oh      69.5 115.46 ! SOURCE3    3   1.8325   2.0619
+angle ca   c    os      68.8 115.54 ! SOURCE3    5   2.1366   2.6708
+angle ca   c    s       62.2 124.28 ! SOURCE3    2   2.3863   2.3863
+angle ca   c    sh      61.1 118.63 ! SOURCE3    1   0.0000   0.0000
+angle cc   c    cc      65.3 114.45 ! SOURCE3    7   5.3959   6.2434
+angle cd   c    cd      65.3 114.45 ! SOURCE3    7   5.3959   6.2434
+angle cc   c    cd      65.7 112.79 ! SOURCE3    1   0.0000   0.0000
+angle cc   c    n       70.2 111.86 ! SOURCE3   36   1.6194   2.3407
+angle cd   c    n       70.2 111.86 ! SOURCE3   36   1.6194   2.3407
+angle cc   c    o       68.9 125.71 ! SOURCE3   66   1.9961   2.4784
+angle cd   c    o       68.9 125.71 ! SOURCE3   66   1.9961   2.4784
+angle cc   c    os      70.5 112.30 ! SOURCE3    6   2.3802   2.7842
+angle cd   c    os      70.5 112.30 ! SOURCE3    6   2.3802   2.7842
+angle cd   c    oh      71.5 111.59 ! SOURCE3    1   0.0000   0.0000
+angle cc   c    oh      71.5 111.59 ! SOURCE3    1  same as cd-c -oh
+angle ce   c    ce      64.1 116.78 ! SOURCE3    1   0.0000   0.0000
+angle cf   c    cf      64.1 116.78 ! SOURCE3    1  same as ce-c -ce
+angle ce   c    ha      46.9 115.22 ! SOURCE3    7   1.6044   2.4188
+angle cf   c    ha      46.9 115.22 ! SOURCE3    7  same as ce-c -ha
+angle ce   c    n       68.9 115.01 ! SOURCE3    2   0.5478   0.5478
+angle cf   c    n       68.9 115.01 ! SOURCE3    2  same as ce-c -n
+angle ce   c    o       69.3 122.92 ! SOURCE3   17   2.3941   3.5085
+angle cf   c    o       69.3 122.92 ! SOURCE3   17   2.3941   3.5085
+angle ce   c    oh      70.8 112.41 ! SOURCE3    3   0.3261   0.3261
+angle cf   c    oh      70.8 112.41 ! SOURCE3    3   0.3261   0.3261
+angle ce   c    s       64.1 117.80 ! SOURCE3    1   0.0000   0.0000
+angle cf   c    s       64.1 117.80 ! SOURCE3    1  same as ce-c -s
+angle cg   c    cg      65.4 115.38 ! SOURCE3    1   0.0000   0.0000
+angle ch   c    ch      65.4 115.38 ! SOURCE3    1  same as cg-c -cg
+angle cg   c    ha      47.8 113.90 ! SOURCE2    1   0.0000   0.0000
+angle ch   c    ha      47.8 113.90 ! SOURCE2    1  same as cg-c -ha
+angle cg   c    o       70.2 122.31 ! SOURCE3    2   0.0000   0.0000
+angle ch   c    o       70.2 122.31 ! SOURCE3    2  same as cg-c -o
+angle cl   c    cl      54.8 111.30 ! SOURCE2    1   0.0000   0.0000
+angle cl   c    f       59.0 112.00 ! SOURCE2    1   0.0000   0.0000
+angle cl   c    ha      40.4 109.90 ! SOURCE2    1   0.0000   0.0000
+angle cl   c    o       59.8 121.51 ! SOURCE3    6   1.5810   1.6987
+angle cl   c    s       56.7 127.60 ! SOURCE2    1   0.0000   0.0000
+angle cx   c    cx      85.4 64.60 ! SOURCE2    1   0.0000   0.0000
+angle cy   c    cy      70.1 90.55 ! SOURCE2    2   2.4500   2.4500
+angle cy   c    o       64.1 135.74 ! SOURCE3    4   2.0890   2.0890
+angle cy   c    os      74.8 94.37 ! SOURCE3    2   0.0000   0.0000
+angle f    c    f       72.2 107.35 ! SOURCE2    2   0.2500   0.2500
+angle f    c    h       51.5 109.90 ! SOURCE2    1   0.0000   0.0000
+angle f    c    o       73.2 123.44 ! SOURCE0    1
+angle f    c    s       63.4 124.00 ! SOURCE2    1   0.0000   0.0000
+angle h    c    o       53.1 127.30 ! SOURCE2    1   0.0000   0.0000
+angle ha   c    ha      37.9 115.61 ! SOURCE3    4   0.0329   0.0458
+angle ha   c    i       36.7 110.58 ! SOURCE3    1   0.0000   0.0000
+angle ha   c    n       52.4 112.37 ! SOURCE3    4   0.5562   0.6424
+angle ha   c    o       54.3 121.94 ! SOURCE3   51   1.7337   2.3235
+angle ha   c    oh      54.0 111.82 ! SOURCE3    4   1.9074   1.9375
+angle ha   c    os      53.2 110.34 ! SOURCE3    8   1.6046   1.9344
+angle ha   c    s       44.3 119.56 ! SOURCE3    3   0.7139   0.7586
+angle hc   c    hc      39.3 115.68 ! SOURCE0    1
+angle hc   c    n       51.1 120.00 ! SOURCE0    1
+angle hc   c    nh      52.5 112.70 ! SOURCE0    1
+angle hc   c    o       55.4 120.00 ! SOURCE0    1
+angle hc   c    oh      54.1 113.70 ! SOURCE0    1
+angle hc   c    os      52.9 113.58 ! SOURCE0    1
+angle i    c    i       59.8 116.45 ! SOURCE3    1   0.0000   0.0000
+angle i    c    o       55.5 122.02 ! SOURCE3    4   1.0210   1.2961
+angle n    c    n       75.4 111.70 ! SOURCE3   11   2.6976   3.1820
+angle n    c    nc      73.0 116.84 ! SOURCE3    2   0.8166   0.8166
+angle n    c    nd      73.0 116.84 ! SOURCE3    2  same as n -c -nc
+angle n    c    o       75.8 122.03 ! SOURCE3  221   1.7197   2.3565
+angle n    c    os      74.7 115.25 ! SOURCE3    1   0.0000   0.0000
+angle n    c    s       65.7 123.88 ! SOURCE3    5   1.0157   1.2935
+angle n    c    ss      67.7 107.69 ! SOURCE3    1   0.0000   0.0000
+angle n2   c    n2      71.8 110.31 ! SOURCE0    1
+angle n2   c    na      70.7 118.60 ! SOURCE0    1
+angle n2   c    o       73.0 122.50 ! SOURCE0    1
+angle n3   c    n3      72.4 112.98 ! SOURCE0    1
+angle n3   c    o       74.1 122.26 ! SOURCE0    1
+angle n4   c    n4      64.7 114.64 ! SOURCE3    1   0.0000   0.0000
+angle n4   c    o       69.6 118.83 ! SOURCE3    4   3.8516   3.8516
+angle na   c    na      73.2 115.40 ! SOURCE0    1
+angle na   c    o       75.0 122.85 ! SOURCE0    1
+angle nc   c    o       73.9 125.29 ! SOURCE3    2   0.9029   0.9029
+angle nd   c    o       73.9 125.29 ! SOURCE3    2  same as nc-c -o
+angle ne   c    ne      72.4 110.31 ! SOURCE3    1   0.0000   0.0000
+angle nf   c    nf      72.4 110.31 ! SOURCE3    1  same as ne-c -ne
+angle ne   c    o       72.8 124.39 ! SOURCE3    3   0.6099   0.6469
+angle nf   c    o       72.8 124.39 ! SOURCE3    3  same as ne-c -o
+angle nh   c    nh      75.2 110.98 ! SOURCE0    1
+angle nh   c    o       74.8 124.62 ! SOURCE0    1
+angle no   c    no      66.5 109.28 ! SOURCE0    1
+angle no   c    o       68.0 125.36 ! SOURCE0    1
+angle o    c    o       79.1 127.33 ! SOURCE3   14   3.9382   4.4102
+angle o    c    oh      77.4 122.88 ! SOURCE3   33   1.6641   2.1896
+angle o    c    os      76.2 122.43 ! SOURCE3   39   2.7251   3.2180
+angle o    c    p2      75.7 123.10 ! SOURCE0    1
+angle o    c    p3      77.1 120.79 ! SOURCE0    1
+angle o    c    pe      75.2 123.02 ! SOURCE3    3   0.1324   0.1404
+angle o    c    pf      75.2 123.02 ! SOURCE3    3  same as o -c -pe
+angle o    c    px      76.7 119.10 ! SOURCE3    1   0.0000   0.0000
+angle o    c    py      77.8 120.52 ! SOURCE3    6   4.7048   6.3825
+angle o    c    s       68.2 120.44 ! SOURCE3    2   0.0000   0.0000
+angle o    c    s4      61.2 121.15 ! SOURCE0    1
+angle o    c    s6      61.7 119.45 ! SOURCE0    1
+angle o    c    sh      64.2 121.41 ! SOURCE3    4   1.2105   1.4052
+angle o    c    ss      64.6 122.29 ! SOURCE3    7   1.3372   1.9240
+angle o    c    sx      60.7 121.15 ! SOURCE3    5   3.5627   3.6452
+angle o    c    sy      61.9 119.32 ! SOURCE3    5   2.2318   2.4495
+angle oh   c    oh      78.9 110.56 ! SOURCE3    2   0.5498   0.5498
+angle oh   c    s       66.6 123.44 ! SOURCE3    1   0.0000   0.0000
+angle os   c    os      77.8 107.49 ! SOURCE3    1   0.0000   0.0000
+angle p2   c    p2      94.4 113.75 ! SOURCE0    1
+angle p3   c    p3      93.5 118.04 ! SOURCE3    1   0.0000   0.0000
+angle p3   c    py     107.5 90.08 ! SOURCE3    1   0.0000   0.0000
+angle p5   c    p5      92.0 123.76 ! SOURCE0    1
+angle pe   c    pe      93.9 113.77 ! SOURCE3    1   0.0000   0.0000
+angle pf   c    pf      93.9 113.77 ! SOURCE3    1  same as pe-c -pe
+angle py   c    py      92.1 123.80 ! SOURCE3    1   0.0000   0.0000
+angle s    c    s       65.0 120.40 ! SOURCE3    2   1.2766   1.2766
+angle s    c    sh      62.5 121.94 ! SOURCE3    2   2.5846   2.5846
+angle s    c    ss      61.9 125.90 ! SOURCE3    1   0.0000   0.0000
+angle s4   c    s4      61.3 108.81 ! SOURCE0    1
+angle s6   c    s6      59.4 115.75 ! SOURCE0    1
+angle sh   c    sh      62.5 115.33 ! SOURCE3    1   0.0000   0.0000
+angle ss   c    ss      63.8 113.00 ! SOURCE3    1   0.0000   0.0000
+angle sx   c    sx      60.8 108.80 ! SOURCE3    1   0.0000   0.0000
+angle sy   c    sy      59.5 115.78 ! SOURCE3    1   0.0000   0.0000
+angle br   c1   c1      54.9 180.00 ! SOURCE3    1   0.0000   0.0000
+angle c    c1   c1      56.3 180.00 ! SOURCE0    1
+angle c1   c1   c1      64.4 180.00 ! SOURCE3    1   0.0000   0.0000
+angle c1   c1   c2      60.8 180.00 ! SOURCE3    2   0.0000   0.0000
+angle c1   c1   c3      56.4 177.99 ! SOURCE3    4   1.0065   1.1721
+angle c1   c1   ca      56.9 180.00 ! SOURCE0    1
+angle c1   c1   cl      51.4 180.00 ! SOURCE0    1
+angle c1   c1   f       61.0 180.00 ! SOURCE0    1
+angle c1   c1   ha      44.8 178.38 ! SOURCE3   41   1.6952   2.0683
+angle c1   c1   hc      44.7 180.00 ! SOURCE0    1
+angle c1   c1   i       49.8 180.00 ! SOURCE3    1   0.0000   0.0000
+angle c1   c1   n       62.1 177.18 ! SOURCE3    1   0.0000   0.0000
+angle c1   c1   n1      67.2 180.00 ! SOURCE0    1
+angle c1   c1   n2      65.2 180.00 ! SOURCE0    1
+angle c1   c1   n3      59.8 180.00 ! SOURCE0    1
+angle c1   c1   n4      59.1 179.56 ! SOURCE3    3   0.2919   0.3096
+angle c1   c1   na      61.2 176.75 ! SOURCE3    8   2.6227   2.9328
+angle c1   c1   nh      61.1 179.27 ! SOURCE3    3   0.2222   0.2357
+angle c1   c1   no      59.4 180.00 ! SOURCE3    3   0.0000   0.0000
+angle c1   c1   o       66.8 180.00 ! SOURCE3    1   0.0000   0.0000
+angle c1   c1   oh      62.7 176.65 ! SOURCE3    1   0.0000   0.0000
+angle c1   c1   os      62.8 176.42 ! SOURCE3    2   0.0000   0.0000
+angle c1   c1   p2      65.2 180.00 ! SOURCE0    1
+angle c1   c1   p3      66.4 169.63 ! SOURCE3    2   0.0000   0.0000
+angle c1   c1   p4      64.5 180.00 ! SOURCE0    1
+angle c1   c1   p5      66.5 176.17 ! SOURCE3    2   0.0000   0.0000
+angle c1   c1   s       55.7 179.97 ! SOURCE3    1   0.0000   0.0000
+angle c1   c1   s4      54.1 167.47 ! SOURCE3    2   0.0000   0.0000
+angle c1   c1   s6      53.7 174.38 ! SOURCE3    2   0.0000   0.0000
+angle c1   c1   sh      54.1 180.00 ! SOURCE0    1
+angle c1   c1   ss      55.2 173.22 ! SOURCE3    2   0.0000   0.0000
+angle c2   c1   c2      58.2 180.00 ! SOURCE3    1   0.0000   0.0000
+angle c2   c1   o       63.1 179.50 ! SOURCE2    1   0.0000   0.0000
+angle c2   c1   s2      56.7 172.98 ! SOURCE3    1   0.0000   0.0000
+angle c3   c1   n1      58.3 177.57 ! SOURCE2    3   2.3778   2.5773
+angle cg   c1   ha      44.0 177.41 ! SOURCE3   22   2.3752   2.4947
+angle ch   c1   ha      44.0 177.41 ! SOURCE3   22  same as cg-c1-ha
+!angle n1   c1   n1      93.2 102.01 ! SOURCE0    1
+angle n1   c1   n3      63.7 169.70 ! SOURCE2    1   0.0000   0.0000
+angle n1   c1   os      64.7 178.59 ! SOURCE3    1   0.0000   0.0000
+angle n1   c1   p3      67.7 171.20 ! SOURCE2    1   0.0000   0.0000
+angle n1   c1   ss      55.8 178.68 ! SOURCE3    1   0.0000   0.0000
+angle n2   c1   o       69.1 171.79 ! SOURCE3    2   0.3594   0.3594
+angle n2   c1   s       57.8 174.05 ! SOURCE3    2   0.1358   0.1358
+angle ne   c1   o       68.7 172.33 ! SOURCE3    1   0.0000   0.0000
+angle nf   c1   o       68.7 172.33 ! SOURCE3    1  same as ne-c1-o
+angle br   c2   br      68.6 115.06 ! SOURCE3    1   0.0000   0.0000
+angle br   c2   c2      63.1 122.42 ! SOURCE3    3   0.0663   0.0703
+angle br   c2   ha      43.2 113.28 ! SOURCE3    1   0.0000   0.0000
+angle c    c2   c       66.6 118.88 ! SOURCE0    1
+angle c    c2   c2      67.9 120.70 ! SOURCE0    1
+angle c    c2   c3      63.9 119.70 ! SOURCE0    1
+angle c    c2   ha      47.7 121.33 ! SOURCE3    4   0.2462   0.2462
+angle c    c2   hc      48.0 119.70 ! SOURCE0    1
+angle c1   c2   c1      72.3 116.77 ! SOURCE0    1
+angle c1   c2   c2      70.3 121.62 ! SOURCE0    1
+angle c1   c2   c3      65.1 121.60 ! SOURCE2    2   1.0000   1.0000
+angle c1   c2   f       68.0 124.90 ! SOURCE2    1   0.0000   0.0000
+angle c1   c2   ha      50.4 121.37 ! SOURCE3    8   0.0055   0.0055
+angle c2   c2   c2      69.8 121.81 ! SOURCE3   10   0.3648   0.3843
+angle c2   c2   c3      64.3 123.42 ! SOURCE3   41   1.8126   2.6057
+angle c2   c2   ca      66.9 117.00 ! SOURCE0    1
+angle c2   c2   cc      70.2 117.21 ! SOURCE3    2   0.3418   0.3418
+angle c2   c2   cd      70.2 117.21 ! SOURCE3    2  same as c2-c2-cc
+angle c2   c2   cl      58.4 122.79 ! SOURCE3    3   0.2303   0.2442
+angle c2   c2   cx      69.7 105.70 ! SOURCE2    1   0.0000   0.0000
+angle c2   c2   cy      69.7 103.30 ! SOURCE2    1   0.0000   0.0000
+angle c2   c2   f       67.8 124.01 ! SOURCE3    3   1.3603   1.4428
+angle c2   c2   h4      49.3 124.68 ! SOURCE3   39   3.2023   3.8280
+angle c2   c2   ha      50.0 120.94 ! SOURCE3  254   0.8639   1.3150
+angle c2   c2   hc      50.3 119.70 ! SOURCE0    1
+angle c2   c2   hx      49.0 126.45 ! SOURCE3    3   0.0207   0.0219
+angle c2   c2   i       56.3 121.03 ! SOURCE3    2   0.0000   0.0000
+angle c2   c2   n       68.8 123.45 ! SOURCE3    7   3.0021   4.5840
+angle c2   c2   n2      71.3 126.01 ! SOURCE3    1   0.0000   0.0000
+angle c2   c2   n3      70.3 124.55 ! SOURCE0    1
+angle c2   c2   n4      67.2 121.52 ! SOURCE3    5   1.2100   1.2656
+angle c2   c2   na      69.8 121.38 ! SOURCE3   26   5.2386   6.9463
+angle c2   c2   nh      69.8 124.99 ! SOURCE3    7   0.8702   0.9929
+angle c2   c2   no      67.6 121.89 ! SOURCE3    4   1.1592   1.1594
+angle c2   c2   o       71.0 130.89 ! SOURCE3    2   0.0201   0.0201
+angle c2   c2   oh      71.6 122.28 ! SOURCE3    4   0.5600   0.5600
+angle c2   c2   os      71.2 121.43 ! SOURCE3   26   4.0372   5.2559
+angle c2   c2   p2      84.7 115.10 ! SOURCE0    1
+angle c2   c2   p3      75.2 124.83 ! SOURCE3    5   2.0793   2.1222
+angle c2   c2   p4      77.2 119.76 ! SOURCE0    1
+angle c2   c2   p5      75.4 125.97 ! SOURCE0    1
+angle c2   c2   s       61.3 129.37 ! SOURCE3    2   0.0000   0.0000
+angle c2   c2   s4      62.9 119.84 ! SOURCE0    1
+angle c2   c2   s6      62.8 120.01 ! SOURCE0    1
+angle c2   c2   sh      61.0 125.70 ! SOURCE3    3   1.2624   1.3390
+angle c2   c2   ss      63.9 118.62 ! SOURCE3   18   3.5098   4.5127
+angle c3   c2   c3      62.7 116.52 ! SOURCE3   15   2.1251   3.1001
+angle c3   c2   cc      63.2 125.64 ! SOURCE3    1   0.0000   0.0000
+angle c3   c2   cd      63.2 125.64 ! SOURCE3    1  same as c3-c2-cc
+angle c3   c2   ce      65.7 117.40 ! SOURCE2    2   8.2000   8.2000
+angle c3   c2   cf      65.7 117.40 ! SOURCE2    2  same as c3-c2-ce
+angle c3   c2   h4      43.8 127.53 ! SOURCE3    1   0.0000   0.0000
+angle c3   c2   ha      45.7 117.30 ! SOURCE3   33   1.4120   1.7453
+angle c3   c2   hc      45.1 120.00 ! SOURCE0    1
+angle c3   c2   n2      67.1 121.24 ! SOURCE3   10   3.4840   4.3825
+angle c3   c2   na      65.0 122.54 ! SOURCE3    1   0.0000   0.0000
+angle c3   c2   ne      67.0 122.15 ! SOURCE3    4   0.2197   0.2197
+angle c3   c2   nf      67.0 122.15 ! SOURCE3    4  same as c3-c2-ne
+angle c3   c2   nh      66.7 118.59 ! SOURCE3    6   1.7963   2.2622
+angle c3   c2   o       69.1 116.88 ! SOURCE3    2   0.0663   0.0663
+angle c3   c2   oh      67.4 118.81 ! SOURCE3    3   0.1564   0.1659
+angle c3   c2   p2      79.0 122.74 ! SOURCE3    2   0.0000   0.0000
+angle c3   c2   s       62.8 115.44 ! SOURCE3    2   0.0115   0.0115
+angle ca   c2   ca      63.5 117.88 ! SOURCE0    1
+angle ca   c2   hc      45.3 123.30 ! SOURCE0    1
+angle cc   c2   ha      49.1 120.76 ! SOURCE3   11   0.8455   1.4155
+angle cd   c2   ha      49.1 120.76 ! SOURCE3   11   0.8455   1.4155
+angle ce   c2   ha      49.6 121.19 ! SOURCE3  122   0.4300   0.5318
+angle cf   c2   ha      49.6 121.19 ! SOURCE3  122  same as ce-c2-ha
+angle cl   c2   cl      55.3 114.77 ! SOURCE3    1   0.0000   0.0000
+angle cl   c2   ha      40.9 113.20 ! SOURCE3    1   0.0000   0.0000
+angle cx   c2   ha      44.3 125.40 ! SOURCE2    1   0.0000   0.0000
+angle f    c2   f       70.7 109.60 ! SOURCE2    2   0.6000   0.6000
+angle f    c2   ha      51.3 110.00 ! SOURCE2    1   0.0000   0.0000
+angle h4   c2   n       49.2 119.41 ! SOURCE3    1   0.0000   0.0000
+angle h4   c2   n2      53.3 116.67 ! SOURCE3    2   2.4512   2.4512
+angle h4   c2   na      50.4 116.25 ! SOURCE3   15   3.1414   3.7439
+angle h4   c2   os      52.2 113.70 ! SOURCE3   13   1.8718   2.0464
+angle h4   c2   ss      42.8 118.47 ! SOURCE3    9   2.2828   2.5335
+angle h5   c2   n2      52.0 123.09 ! SOURCE3    2   0.0000   0.0000
+angle h5   c2   na      48.3 126.39 ! SOURCE3    4   0.3299   0.3299
+angle ha   c2   ha      38.0 117.65 ! SOURCE3  349   1.0413   1.3426
+angle ha   c2   n       50.5 113.40 ! SOURCE3    4   0.9474   1.2182
+angle ha   c2   n1      51.8 120.76 ! SOURCE3    8   0.6630   0.6632
+angle ha   c2   n2      52.4 120.54 ! SOURCE3   92   0.8283   1.4571
+angle ha   c2   n3      52.4 113.54 ! SOURCE0    1
+angle ha   c2   na      51.2 112.42 ! SOURCE3    8   0.5805   0.6507
+angle ha   c2   ne      52.5 121.18 ! SOURCE3   68   0.5649   0.6824
+angle ha   c2   nf      52.5 121.18 ! SOURCE3   68  same as ha-c2-ne
+angle ha   c2   nh      51.3 116.68 ! SOURCE3   13   2.3477   2.5734
+angle ha   c2   no      49.1 112.14 ! SOURCE3    2   0.0264   0.0264
+angle ha   c2   o       54.2 117.23 ! SOURCE3    2   0.0201   0.0201
+angle ha   c2   oh      52.3 116.18 ! SOURCE3    2   0.0000   0.0000
+angle ha   c2   os      52.4 112.69 ! SOURCE3   13   2.2236   2.5851
+angle ha   c2   p2      55.7 121.48 ! SOURCE3  122   0.3598   0.4329
+angle ha   c2   p3      52.0 114.31 ! SOURCE3    3   0.0000   0.0000
+angle ha   c2   p4      51.6 117.86 ! SOURCE0    1
+angle ha   c2   p5      51.2 120.10 ! SOURCE3    2   0.0000   0.0000
+angle ha   c2   pe      55.0 121.46 ! SOURCE3  104   0.5185   0.7821
+angle ha   c2   pf      55.0 121.46 ! SOURCE3  104  same as ha-c2-pe
+angle ha   c2   s       43.4 115.70 ! SOURCE3    2   0.0000   0.0000
+angle ha   c2   s2      46.2 118.74 ! SOURCE3    2   0.0000   0.0000
+angle ha   c2   s4      42.8 115.30 ! SOURCE3    2   0.0000   0.0000
+angle ha   c2   s6      42.6 116.60 ! SOURCE3    2   0.0000   0.0000
+angle ha   c2   sh      43.2 111.74 ! SOURCE3    1   0.0000   0.0000
+angle ha   c2   ss      43.2 116.72 ! SOURCE3    7   2.0736   2.7543
+angle hc   c2   hc      37.8 118.92 ! SOURCE0    1
+angle hc   c2   n       50.3 114.04 ! SOURCE0    1
+angle hc   c2   n2      52.4 120.40 ! SOURCE0    1
+angle hc   c2   na      49.7 119.10 ! SOURCE0    1
+angle hc   c2   nh      52.0 113.36 ! SOURCE0    1
+angle hc   c2   no      49.1 112.12 ! SOURCE0    1
+angle hc   c2   oh      52.3 116.22 ! SOURCE0    1
+angle hc   c2   os      51.7 116.11 ! SOURCE0    1
+angle hc   c2   p3      51.4 117.19 ! SOURCE0    1
+angle hc   c2   p5      51.3 119.58 ! SOURCE0    1
+angle hc   c2   s4      42.7 116.12 ! SOURCE0    1
+angle hc   c2   s6      42.8 115.45 ! SOURCE0    1
+angle hc   c2   sh      42.4 115.63 ! SOURCE0    1
+angle hc   c2   ss      43.4 115.62 ! SOURCE0    1
+angle hn   c2   n4      11.9 112.74 ! SOURCE0    1
+angle hx   c2   n4      48.4 113.03 ! SOURCE3    3   0.3651   0.3873
+angle i    c2   i       61.0 117.94 ! SOURCE3    1   0.0000   0.0000
+angle n    c2   n       71.5 113.23 ! SOURCE3    1   0.0000   0.0000
+angle n    c2   n2      70.6 125.95 ! SOURCE3    2   5.0202   5.0202
+angle n    c2   na      74.6 105.42 ! SOURCE3    1   0.0000   0.0000
+angle n    c2   ss      67.4 109.31 ! SOURCE3    1   0.0000   0.0000
+angle n2   c2   n2      78.0 113.82 ! SOURCE3    1   0.0000   0.0000
+angle n2   c2   na      71.7 123.62 ! SOURCE3    1   0.0000   0.0000
+angle n2   c2   nh      72.6 124.27 ! SOURCE3   12   1.4852   2.4114
+angle n2   c2   oh      74.4 122.08 ! SOURCE3    1   0.0000   0.0000
+angle n2   c2   os      74.6 118.96 ! SOURCE3    2   1.1624   1.1624
+angle n2   c2   ss      62.9 129.77 ! SOURCE3    1   0.0000   0.0000
+angle n3   c2   n3      73.4 118.47 ! SOURCE0    1
+angle n4   c2   n4      67.7 113.93 ! SOURCE3    1   0.0000   0.0000
+angle na   c2   na      73.7 109.33 ! SOURCE3    3   2.8004   3.0187
+angle ne   c2   nh      73.3 122.72 ! SOURCE3    2   0.0000   0.0000
+angle nf   c2   nh      73.3 122.72 ! SOURCE3    2  same as ne-c2-nh
+angle nh   c2   nh      72.9 117.50 ! SOURCE3   18   4.1761   4.7058
+angle no   c2   no      68.6 113.90 ! SOURCE3    1   0.0000   0.0000
+angle o    c2   o       77.9 121.69 ! SOURCE0    1
+angle o    c2   s       64.0 127.68 ! SOURCE3    2   0.0547   0.0547
+angle oh   c2   oh      76.0 114.33 ! SOURCE3    1   0.0000   0.0000
+angle os   c2   os      74.2 115.80 ! SOURCE3    1   0.0000   0.0000
+angle p2   c2   p2     100.6 129.80 ! SOURCE0    1
+angle p3   c2   p3      97.1 115.54 ! SOURCE3    1   0.0000   0.0000
+angle p5   c2   p5      95.3 121.85 ! SOURCE0    1
+angle s    c2   s       62.5 121.67 ! SOURCE0    1
+angle s4   c2   s4      61.9 120.32 ! SOURCE0    1
+angle s4   c2   s6      62.0 119.95 ! SOURCE0    1
+angle s6   c2   s6      62.0 119.97 ! SOURCE0    1
+angle sh   c2   sh      64.1 110.48 ! SOURCE3    1   0.0000   0.0000
+angle sh   c2   ss      62.7 117.82 ! SOURCE0    1
+angle ss   c2   ss      62.8 120.24 ! SOURCE3    1   0.0000   0.0000
+angle br   c3   br      66.6 111.75 ! SOURCE2    2   0.9500   0.9500
+angle br   c3   c1      62.8 111.80 ! SOURCE2    3   0.2000   0.2160
+angle br   c3   c3      63.0 109.25 ! SOURCE3   10   0.5567   0.5685
+angle br   c3   h1      43.1 103.04 ! SOURCE3    5   0.2592   0.3092
+angle br   c3   hc      42.4 106.50 ! SOURCE0    1
+angle c    c3   c       63.7 112.95 ! SOURCE3    3   2.9833   3.2539
+angle c    c3   c2      64.6 109.73 ! SOURCE3    3   5.3858   5.7300
+angle c    c3   c3      63.8 110.53 ! SOURCE3   62   1.7223   1.9636
+angle c    c3   ca      64.1 111.23 ! SOURCE3    2   0.0000   0.0000
+angle c    c3   h1      47.6 107.66 ! SOURCE3   66   1.2143   1.4015
+angle c    c3   hc      47.2 109.68 ! SOURCE3  614   0.4058   0.6426
+angle c    c3   hx      47.4 108.64 ! SOURCE3    1   0.0000   0.0000
+angle c    c3   n       66.7 111.56 ! SOURCE3   28   0.7771   1.7981
+angle c    c3   n3      65.8 113.91 ! SOURCE3    2   0.0000   0.0000
+angle c    c3   n4      65.8 111.54 ! SOURCE3    1   0.0000   0.0000
+angle c    c3   na      68.6 105.72 ! SOURCE3    2   2.6464   2.6464
+angle c    c3   oh      68.4 109.46 ! SOURCE3    2   1.1805   1.1805
+angle c    c3   os      68.0 109.82 ! SOURCE3   10   1.4728   2.0612
+angle c    c3   ss      61.7 111.58 ! SOURCE3    5   1.4553   1.9506
+angle c1   c3   c1      66.5 109.00 ! SOURCE2    1   0.0000   0.0000
+angle c1   c3   c3      64.2 111.59 ! SOURCE0    1
+angle c1   c3   cl      58.5 110.63 ! SOURCE2    3   1.1556   1.2257
+angle c1   c3   h1      49.1 105.97 ! SOURCE3    2   0.4303   0.4303
+angle c1   c3   hc      48.3 109.75 ! SOURCE3   12   0.7064   0.8436
+angle c1   c3   os      68.1 112.83 ! SOURCE3    2   0.4762   0.4762
+angle c2   c3   c2      64.6 109.71 ! SOURCE0    1
+angle c2   c3   c3      63.7 110.96 ! SOURCE3   12   2.5022   3.2903
+angle c2   c3   f       66.4 111.30 ! SOURCE2    2   0.4000   0.4000
+angle c2   c3   h1      47.0 110.46 ! SOURCE3   17   1.0324   1.1525
+angle c2   c3   hc      47.0 110.49 ! SOURCE3  159   0.5121   0.7479
+angle c2   c3   n3      66.5 111.47 ! SOURCE3    3   2.1065   2.2409
+angle c2   c3   oh      68.3 109.72 ! SOURCE3    2   1.8365   1.8365
+angle c2   c3   os      68.3 108.88 ! SOURCE3    2   1.3604   1.3604
+angle c2   c3   ss      63.6 104.97 ! SOURCE3    2   2.2248   2.2248
+angle c3   c3   c3      63.2 110.63 ! SOURCE3  507   1.7605   2.7845
+angle c3   c3   ca      62.5 114.61 ! SOURCE3    8   2.6906   2.7907
+angle c3   c3   cc      64.7 108.10 ! SOURCE3    5   4.5525   4.8691
+angle c3   c3   cd      64.7 108.10 ! SOURCE3    5  same as c3-c3-cc
+angle c3   c3   cl      57.8 110.33 ! SOURCE3   20   0.9282   1.1495
+angle c3   c3   cx      65.6 104.10 ! SOURCE3    2   0.0000   0.0000
+angle c3   c3   cy      63.6 109.62 ! SOURCE3    5   1.6598   2.0747
+angle c3   c3   f       66.2 109.41 ! SOURCE3   18   0.9661   1.1878
+angle c3   c3   h1      46.4 110.07 ! SOURCE3  457   0.9255   1.1542
+angle c3   c3   h2      46.0 111.59 ! SOURCE3    8   0.7200   1.1217
+angle c3   c3   hc      46.4 110.05 ! SOURCE3 2092   0.5379   0.6991
+angle c3   c3   hx      46.0 111.74 ! SOURCE3   15   1.0925   1.2365
+angle c3   c3   i       58.5 110.96 ! SOURCE3    2   0.0000   0.0000
+angle c3   c3   n       65.9 112.13 ! SOURCE3   31   1.0775   2.0700
+angle c3   c3   n2      66.4 109.16 ! SOURCE3    8   1.3147   1.4079
+angle c3   c3   n3      66.2 110.38 ! SOURCE3   69   2.0699   2.9054
+angle c3   c3   n4      66.0 108.93 ! SOURCE3   11   2.7418   3.0139
+angle c3   c3   na      65.8 112.59 ! SOURCE3    4   2.2797   2.6730
+angle c3   c3   nh      66.6 109.78 ! SOURCE3    2   0.0333   0.0333
+angle c3   c3   oh      67.7 109.43 ! SOURCE3   48   0.9661   1.5023
+angle c3   c3   os      67.8 108.42 ! SOURCE3  122   1.0750   1.6759
+angle c3   c3   p5      77.0 114.24 ! SOURCE3    4   2.0849   2.5420
+angle c3   c3   s6      62.4 111.90 ! SOURCE3    2   1.7244   1.7244
+angle c3   c3   sh      61.5 111.09 ! SOURCE3    4   2.8882   2.8907
+angle c3   c3   ss      61.1 112.69 ! SOURCE3   24   2.0616   2.1842
+angle ca   c3   ca      63.6 112.47 ! SOURCE3    3   0.4951   0.5285
+angle ca   c3   cc      65.2 108.08 ! SOURCE3    8   3.1217   4.1083
+angle ca   c3   cd      65.2 108.08 ! SOURCE3    8  same as ca-c3-cc
+angle ca   c3   h1      46.8 110.95 ! SOURCE3   12   0.8880   1.1170
+angle ca   c3   hc      47.0 110.15 ! SOURCE3   47   1.1349   1.2602
+angle ca   c3   n2      66.0 112.10 ! SOURCE3    1   0.0000   0.0000
+angle ca   c3   nc      68.2 106.51 ! SOURCE3    1   0.0000   0.0000
+angle ca   c3   nd      68.2 106.51 ! SOURCE3    1  same as ca-c3-nc
+angle ca   c3   oh      67.2 112.97 ! SOURCE3    1   0.0000   0.0000
+angle ca   c3   os      67.7 110.51 ! SOURCE3    4   3.2176   3.2176
+angle cc   c3   cc      65.5 107.99 ! SOURCE3   10   5.0855   5.1937
+angle cd   c3   cd      65.5 107.99 ! SOURCE3   10   5.0855   5.1937
+angle cc   c3   cd      67.3 102.35 ! SOURCE3    1   0.0000   0.0000
+angle cc   c3   h1      47.0 111.62 ! SOURCE3   20   0.7861   1.0215
+angle cd   c3   h1      47.0 111.62 ! SOURCE3   20   0.7861   1.0215
+angle cc   c3   hc      47.2 110.86 ! SOURCE3   85   0.8792   1.0276
+angle cd   c3   hc      47.2 110.86 ! SOURCE3   85   0.8792   1.0276
+angle cc   c3   nc      68.4 107.04 ! SOURCE3    2   0.0000   0.0000
+angle cd   c3   nd      68.4 107.04 ! SOURCE3    2  same as cc-c3-nc
+angle cc   c3   os      67.8 111.28 ! SOURCE3    7   2.5177   2.9265
+angle cd   c3   os      67.8 111.28 ! SOURCE3    7  same as cc-c3-os
+angle cd   c3   sh      61.1 114.02 ! SOURCE3    1   0.0000   0.0000
+angle cc   c3   sh      61.1 114.02 ! SOURCE3    1  same as cd-c3-sh
+angle ce   c3   hc      47.0 110.98 ! SOURCE3   27   0.1170   0.1559
+angle cf   c3   hc      47.0 110.98 ! SOURCE3   27  same as ce-c3-hc
+angle cl   c3   cl      54.2 111.03 ! SOURCE2    6   0.9889   1.1324
+angle cl   c3   f       59.0 109.60 ! SOURCE2    3   0.3333   0.3742
+angle cl   c3   h1      40.7 105.93 ! SOURCE3   19   0.9624   1.1883
+angle cl   c3   hc      40.3 107.65 ! SOURCE2    2   2.2500   2.2500
+angle cx   c3   hc      46.7 111.35 ! SOURCE3    4   0.0000   0.0000
+angle cy   c3   hc      46.5 110.17 ! SOURCE3   16   0.5607   0.5693
+angle f    c3   f       71.3 107.16 ! SOURCE2   10   0.9600   1.1324
+angle f    c3   h1      51.6 107.85 ! SOURCE3   14   0.8579   0.9537
+angle f    c3   h2      51.4 108.41 ! SOURCE3    6   0.4791   0.5081
+angle f    c3   hc      51.3 108.92 ! SOURCE2    5   2.4272   3.0534
+angle f    c3   n2      69.2 110.40 ! SOURCE2    3   2.3333   2.6470
+angle h1   c3   h1      39.2 109.55 ! SOURCE3 1888   0.8483   1.1205
+angle h1   c3   n       49.8 109.32 ! SOURCE3   91   0.8246   1.0325
+angle h1   c3   n2      49.3 109.61 ! SOURCE3   63   0.7888   1.0452
+angle h1   c3   n3      49.4 109.92 ! SOURCE3  313   0.8219   1.1810
+angle h1   c3   na      49.9 109.45 ! SOURCE3   53   0.7716   0.9555
+angle h1   c3   nc      50.1 108.57 ! SOURCE3    6   0.0720   0.0764
+angle h1   c3   nd      50.1 108.57 ! SOURCE3    6  same as h1-c3-nc
+angle h1   c3   nh      49.7 109.96 ! SOURCE3   70   0.4865   0.7000
+angle h1   c3   no      48.1 107.56 ! SOURCE3    3   0.0000   0.0000
+angle h1   c3   o       52.0 117.19 ! SOURCE3    6   0.0003   0.0003
+angle h1   c3   oh      51.0 109.88 ! SOURCE3   63   1.1555   1.3172
+angle h1   c3   os      50.8 108.82 ! SOURCE3  541   0.5769   0.8042
+angle h1   c3   s       41.2 112.60 ! SOURCE3    6   0.0026   0.0026
+angle h1   c3   s4      42.9 108.66 ! SOURCE3  201   0.2964   0.3834
+angle h1   c3   s6      43.9 108.11 ! SOURCE3  160   0.3521   0.5518
+angle h1   c3   sh      42.4 109.21 ! SOURCE3   22   1.0660   1.2028
+angle h1   c3   ss      42.4 109.34 ! SOURCE3  356   0.4558   0.6573
+angle h1   c3   sx      42.8 108.69 ! SOURCE3   90   0.2163   0.2749
+angle h1   c3   sy      43.7 108.09 ! SOURCE3   93   0.2266   0.2556
+angle h2   c3   h2      39.0 109.19 ! SOURCE3   29   1.8754   3.1352
+angle h2   c3   i       38.7 104.99 ! SOURCE3    2   0.0000   0.0000
+angle h2   c3   n       50.0 108.31 ! SOURCE3    4   0.0000   0.0000
+angle h2   c3   n2      49.1 110.22 ! SOURCE3    6   0.2011   0.2133
+angle h2   c3   n3      49.3 109.91 ! SOURCE3    4   0.0000   0.0000
+angle h2   c3   na      50.3 107.66 ! SOURCE3    6   1.2854   1.4096
+angle h2   c3   nc      49.8 109.47 ! SOURCE3   10   0.2445   0.3133
+angle h2   c3   nd      49.8 109.47 ! SOURCE3   10  same as h2-c3-nc
+angle h2   c3   nh      49.5 110.80 ! SOURCE3    4   0.0000   0.0000
+angle h2   c3   no      47.8 108.69 ! SOURCE3    4   0.0000   0.0000
+angle h2   c3   o       53.8 108.97 ! SOURCE3    4   0.0000   0.0000
+angle h2   c3   oh      51.3 108.30 ! SOURCE3    6   0.5117   0.5715
+angle h2   c3   os      50.8 108.58 ! SOURCE3   44   0.9621   1.2773
+angle h2   c3   s       42.3 106.75 ! SOURCE3    4   0.0000   0.0000
+angle h2   c3   s4      42.9 108.58 ! SOURCE3    8   0.2408   0.2408
+angle h2   c3   s6      43.8 108.42 ! SOURCE3    4   0.0000   0.0000
+angle h2   c3   sh      42.7 107.87 ! SOURCE3    6   0.4125   0.4376
+angle h2   c3   ss      42.3 109.75 ! SOURCE3   10   0.2525   0.3442
+angle h3   c3   n3      50.1 106.77 ! SOURCE3    1   0.0000   0.0000
+angle h3   c3   nc      49.9 109.37 ! SOURCE3    1   0.0000   0.0000
+angle h3   c3   nd      49.9 109.37 ! SOURCE3    1  same as h3-c3-nc
+angle h3   c3   nh      50.0 108.93 ! SOURCE3    1   0.0000   0.0000
+angle hc   c3   hc      39.4 108.35 ! SOURCE3 2380   0.7199   0.9006
+angle hc   c3   i       38.6 104.99 ! SOURCE0    1
+angle hc   c3   n       49.8 109.50 ! SOURCE0    1
+angle hc   c3   n2      49.3 109.50 ! SOURCE0    1
+angle hc   c3   n3      49.4 109.80 ! SOURCE2    5   1.5200   2.0070
+angle hc   c3   n4      49.0 107.90 ! SOURCE0    1
+angle hc   c3   na      49.9 109.50 ! SOURCE0    1
+angle hc   c3   nh      49.4 111.54 ! SOURCE0    1
+angle hc   c3   no      48.2 107.20 ! SOURCE2    1   0.0000   0.0000
+angle hc   c3   oh      51.1 109.50 ! SOURCE0    1
+angle hc   c3   os      50.9 108.70 ! SOURCE2   13   2.0769   2.3739
+angle hc   c3   p2      52.4 110.18 ! SOURCE3   25   0.3432   0.4057
+angle hc   c3   p3      52.7 110.14 ! SOURCE3  325   0.4003   0.5126
+angle hc   c3   p4      53.2 109.59 ! SOURCE3   87   0.2573   0.3196
+angle hc   c3   p5      53.8 109.64 ! SOURCE3   69   0.5787   0.8112
+angle hc   c3   px      53.5 109.74 ! SOURCE3   84   0.2586   0.3474
+angle hc   c3   py      53.9 109.54 ! SOURCE3   39   0.1670   0.1999
+angle hc   c3   s4      43.1 107.50 ! SOURCE2    1   0.0000   0.0000
+angle hc   c3   s6      43.8 108.20 ! SOURCE0    1
+angle hc   c3   sh      42.7 107.87 ! SOURCE2    3   1.9778   2.0981
+angle hc   c3   ss      42.5 108.76 ! SOURCE2    3   1.4244   1.6891
+angle hx   c3   hx      39.0 110.74 ! SOURCE3  137   0.3838   0.5531
+angle hx   c3   n4      49.0 107.91 ! SOURCE3  148   0.4013   0.5899
+angle i    c3   i       62.0 113.12 ! SOURCE3    1   0.0000   0.0000
+angle n    c3   n       68.8 113.81 ! SOURCE3    1   0.0000   0.0000
+angle n2   c3   n2      69.9 107.70 ! SOURCE3    1   0.0000   0.0000
+angle n2   c3   os      71.1 108.03 ! SOURCE3    1   0.0000   0.0000
+angle n3   c3   n3      69.3 110.48 ! SOURCE3    1   0.0000   0.0000
+angle n3   c3   nc      68.8 113.29 ! SOURCE3    1   0.0000   0.0000
+angle n3   c3   nd      68.8 113.29 ! SOURCE3    1  same as n3-c3-nc
+angle n3   c3   nh      71.1 105.80 ! SOURCE3    1   0.0000   0.0000
+angle n4   c3   n4      67.1 113.32 ! SOURCE3    1   0.0000   0.0000
+angle na   c3   na      69.1 113.49 ! SOURCE3    1   0.0000   0.0000
+angle na   c3   os      71.2 109.19 ! SOURCE3    2   0.6009   0.6009
+angle nc   c3   nc      70.0 110.61 ! SOURCE3    1   0.0000   0.0000
+angle nd   c3   nd      70.0 110.61 ! SOURCE3    1  same as nc-c3-nc
+angle nc   c3   nh      69.3 112.43 ! SOURCE3    1   0.0000   0.0000
+angle nd   c3   nh      69.3 112.43 ! SOURCE3    1  same as nc-c3-nh
+angle nc   c3   os      69.3 115.41 ! SOURCE3    3   0.8415   1.0288
+angle nd   c3   os      69.3 115.41 ! SOURCE3    3  same as nc-c3-os
+angle nh   c3   nh      71.4 105.87 ! SOURCE3    1   0.0000   0.0000
+angle no   c3   no      67.0 108.76 ! SOURCE3    1   0.0000   0.0000
+angle o    c3   o       73.4 122.30 ! SOURCE3    1   0.0000   0.0000
+angle oh   c3   oh      72.1 111.08 ! SOURCE3    1   0.0000   0.0000
+angle oh   c3   os      71.1 113.27 ! SOURCE3    1   0.0000   0.0000
+angle oh   c3   sh      63.3 115.46 ! SOURCE3    1   0.0000   0.0000
+angle os   c3   os      71.7 110.24 ! SOURCE3   17   1.6690   2.1340
+angle os   c3   ss      65.0 109.20 ! SOURCE3    1   0.0000   0.0000
+angle p2   c3   p2      98.2 110.48 ! SOURCE3    1   0.0000   0.0000
+angle p3   c3   p3      98.9 110.16 ! SOURCE3    1   0.0000   0.0000
+angle p5   c3   p5      97.9 116.24 ! SOURCE3    1   0.0000   0.0000
+angle s    c3   s       58.3 123.35 ! SOURCE3    1   0.0000   0.0000
+angle s4   c3   s4      62.4 112.29 ! SOURCE3    2   1.2724   1.2724
+angle s4   c3   s6      62.6 113.52 ! SOURCE0    1
+angle s6   c3   s6      63.7 111.75 ! SOURCE3    1   0.0000   0.0000
+angle sh   c3   sh      60.8 116.26 ! SOURCE3    1   0.0000   0.0000
+angle sh   c3   ss      62.3 110.73 ! SOURCE0    1
+angle ss   c3   ss      62.8 109.02 ! SOURCE3    2   0.2226   0.2226
+angle br   ca   br      67.3 117.60 ! SOURCE0    1
+angle br   ca   ca      63.5 118.13 ! SOURCE3    8   0.5232   0.6041
+angle c    ca   c       62.7 120.00 ! SOURCE0    1
+angle c    ca   c3      62.6 118.06 ! SOURCE0    1
+angle c    ca   ca      64.6 120.14 ! SOURCE3   40   0.3838   0.4788
+angle c    ca   ha      46.5 115.90 ! SOURCE0    1
+angle c    ca   hc      11.2 120.00 ! SOURCE0    1
+angle c    ca   nc      64.4 130.80 ! SOURCE0    1
+angle c    ca   nd      64.4 130.80 ! SOURCE0    1  same as c -ca-nc
+angle c1   ca   c1      64.7 120.00 ! SOURCE0    1
+angle c1   ca   ca      65.9 120.00 ! SOURCE0    1
+angle c2   ca   c2      62.9 120.00 ! SOURCE0    1
+angle c2   ca   ca      64.7 120.60 ! SOURCE0    1
+angle c3   ca   c2      62.2 120.00 ! SOURCE0    1
+angle c3   ca   c3      62.4 116.80 ! SOURCE0    1
+angle c3   ca   ca      63.8 120.63 ! SOURCE3   60   0.5436   0.7175
+angle c3   ca   cp      63.7 120.63 ! SOURCE3   60   0.5436   0.7175
+angle c3   ca   cq      63.7 120.63 ! SOURCE3   60   0.5436   0.7175
+angle ca   ca   ca      67.2 119.97 ! SOURCE3 1969   0.1024   0.3480
+angle ca   ca   cc      66.0 120.10 ! SOURCE3  103   0.2645   0.3451
+angle ca   ca   cd      66.0 120.10 ! SOURCE3  103   0.2645   0.3451
+angle ca   ca   ce      64.9 120.66 ! SOURCE3   14   0.1371   0.1564
+angle ca   ca   cf      64.9 120.66 ! SOURCE3   14  same as ca-ca-ce
+angle ca   ca   cg      65.9 120.05 ! SOURCE3    6   0.2260   0.2397
+angle ca   ca   ch      65.9 120.05 ! SOURCE3    6  same as ca-ca-cg
+angle ca   ca   cl      58.4 119.90 ! SOURCE2    2   0.0000   0.0000
+angle ca   ca   cp      67.2 119.07 ! SOURCE3   14   2.0133   2.3077
+angle ca   ca   cq      67.2 119.07 ! SOURCE3   14   2.0133   2.3077
+angle ca   ca   cx      64.8 119.68 ! SOURCE3    2   0.0000   0.0000
+angle ca   ca   f       67.6 118.74 ! SOURCE3    2   0.0000   0.0000
+angle ca   ca   h4      48.2 121.09 ! SOURCE3   57   1.0923   1.4696
+angle ca   ca   ha      48.5 120.01 ! SOURCE3 2980   0.1509   0.2511
+angle ca   ca   i       58.6 118.47 ! SOURCE3   10   0.4657   0.6181
+angle ca   ca   n       68.0 119.89 ! SOURCE3   18   0.1731   0.2095
+angle ca   ca   n2      66.8 119.57 ! SOURCE0    1
+angle ca   ca   n3      18.5 121.19 ! SOURCE0    1
+angle ca   ca   n4      67.3 118.41 ! SOURCE3    6   0.1594   0.1691
+angle ca   ca   na      70.2 118.34 ! SOURCE3   54   2.1861   3.6168
+angle ca   ca   nb      69.2 122.63 ! SOURCE3   83   0.9345   1.1249
+angle ca   ca   nc      70.1 119.72 ! SOURCE3   22   2.1776   3.3994
+angle ca   ca   nd      70.1 119.72 ! SOURCE3   22   2.1776   3.3994
+angle ca   ca   ne      67.7 119.88 ! SOURCE3   24   0.2867   0.3637
+angle ca   ca   nf      67.7 119.88 ! SOURCE3   24   0.2867   0.3637
+angle ca   ca   nh      69.3 120.13 ! SOURCE3  193   0.4375   0.6341
+angle ca   ca   no      66.9 119.54 ! SOURCE3   10   1.0504   1.3187
+angle ca   ca   o       70.9 123.43 ! SOURCE3    4   0.0000   0.0000
+angle ca   ca   oh      69.8 119.94 ! SOURCE3   14   0.1421   0.1627
+angle ca   ca   os      69.8 119.20 ! SOURCE3   52   0.4006   0.5240
+angle ca   ca   p2      77.9 114.36 ! SOURCE0    1
+angle ca   ca   p3      76.3 120.73 ! SOURCE3    6   0.1200   0.1273
+angle ca   ca   p4      77.1 120.30 ! SOURCE0    1
+angle ca   ca   p5      77.6 119.98 ! SOURCE0    1
+angle ca   ca   pe      76.3 120.45 ! SOURCE3   20   0.2577   0.2719
+angle ca   ca   pf      76.3 120.45 ! SOURCE3   20   0.2577   0.2719
+angle ca   ca   px      76.4 120.53 ! SOURCE3   10   0.4207   0.4509
+angle ca   ca   py      77.4 119.98 ! SOURCE3    6   0.0632   0.0670
+angle ca   ca   s       62.2 122.55 ! SOURCE3    4   0.0000   0.0000
+angle ca   ca   s4      61.7 119.15 ! SOURCE0    1
+angle ca   ca   s6      62.5 119.11 ! SOURCE0    1
+angle ca   ca   sh      61.5 120.13 ! SOURCE3    4   0.0431   0.0431
+angle ca   ca   ss      62.0 119.93 ! SOURCE3   16   0.3295   0.3901
+angle ca   ca   sx      61.2 119.18 ! SOURCE3    6   0.0409   0.0434
+angle ca   ca   sy      61.6 119.89 ! SOURCE3   24   1.1928   1.8813
+angle cd   ca   nb      66.9 126.21 ! SOURCE3    1   0.0000   0.0000
+angle cc   ca   nb      66.9 126.21 ! SOURCE3    1  same as cd-ca-nb
+angle cl   ca   cl      54.2 118.72 ! SOURCE0    1
+angle cp   ca   ha      48.0 121.08 ! SOURCE3   12   1.5789   1.7484
+angle cq   ca   ha      48.0 121.08 ! SOURCE3   12   1.5789   1.7484
+angle cp   ca   sy      63.9 111.18 ! SOURCE3    2   0.0000   0.0000
+angle cq   ca   sy      63.9 111.18 ! SOURCE3    2   0.0000   0.0000
+angle f    ca   f       68.1 117.50 ! SOURCE0    1
+angle h4   ca   n       49.5 116.02 ! SOURCE0    1
+angle h4   ca   na      51.9 114.65 ! SOURCE3    5   1.5115   1.5484
+angle h4   ca   nb      51.8 115.94 ! SOURCE3   52   0.5536   0.7370
+angle h4   ca   nc      51.5 118.36 ! SOURCE0    1
+angle h4   ca   nd      51.5 118.36 ! SOURCE0    1  same as h4-ca-nc
+angle h4   ca   os      52.3 111.15 ! SOURCE0    1
+angle h4   ca   ss      42.4 116.19 ! SOURCE0    1
+angle h5   ca   nb      51.8 116.35 ! SOURCE3   30   0.4960   0.5545
+angle h5   ca   nc      50.7 122.11 ! SOURCE0    1
+angle h5   ca   nd      50.7 122.11 ! SOURCE0    1  same as h5-ca-nc
+angle ha   ca   n2      48.1 116.00 ! SOURCE2    1   0.0000   0.0000
+angle ha   ca   p2      50.1 122.56 ! SOURCE0    1
+angle i    ca   i       62.3 119.28 ! SOURCE0    1
+angle n    ca   nc      69.7 123.86 ! SOURCE0    1
+angle n    ca   nd      69.7 123.86 ! SOURCE0    1  same as n -ca-nc
+angle n    ca   nh      71.3 116.16 ! SOURCE0    1
+angle n2   ca   n2      66.5 120.00 ! SOURCE0    1
+angle n2   ca   na      69.2 119.60 ! SOURCE0    1
+angle n4   ca   n4      67.7 116.82 ! SOURCE0    1
+angle na   ca   na      74.5 113.32 ! SOURCE0    1
+angle na   ca   nb      70.2 128.56 ! SOURCE3    3   0.3052   0.3533
+angle nb   ca   nb      71.2 125.84 ! SOURCE3   17   2.5680   4.1190
+angle nb   ca   nd      71.1 126.50 ! SOURCE3    2   0.0000   0.0000
+angle nb   ca   nc      71.1 126.50 ! SOURCE3    2  same as nb-ca-nd
+angle nb   ca   nh      72.6 118.96 ! SOURCE3    3   0.0580   0.0706
+angle nc   ca   nc      70.7 128.74 ! SOURCE0    1
+angle nd   ca   nd      70.7 128.74 ! SOURCE0    1  same as nc-ca-nc
+angle nc   ca   nh      72.8 118.86 ! SOURCE0    1
+angle nd   ca   nh      72.8 118.86 ! SOURCE0    1  same as nc-ca-nh
+angle nh   ca   nh      71.4 120.98 ! SOURCE0    1
+angle no   ca   no      67.4 117.14 ! SOURCE0    1
+angle o    ca   o       75.5 126.82 ! SOURCE0    1
+angle oh   ca   oh      72.6 120.00 ! SOURCE0    1
+angle os   ca   os      74.0 113.73 ! SOURCE0    1
+angle p2   ca   p2      94.5 121.20 ! SOURCE0    1
+angle p3   ca   p3      95.2 121.46 ! SOURCE0    1
+angle p5   ca   p5      97.3 120.00 ! SOURCE0    1
+angle s    ca   s       61.4 125.14 ! SOURCE0    1
+angle s4   ca   s4      65.0 105.81 ! SOURCE0    1
+angle s6   ca   s6      66.0 105.81 ! SOURCE0    1
+angle sh   ca   sh      61.0 120.24 ! SOURCE0    1
+angle ss   ca   ss      62.9 115.15 ! SOURCE0    1
+angle c    cc   c2      65.4 121.76 ! SOURCE3    1   0.0000   0.0000
+angle c    cd   c2      65.4 121.76 ! SOURCE3    1  same as c -cc-c2
+angle c    cc   c3      64.9 112.75 ! SOURCE3    4   0.2063   0.2063
+angle c    cd   c3      64.9 112.75 ! SOURCE3    4   0.2063   0.2063
+angle c    cc   ca      63.6 122.95 ! SOURCE3    1   0.0000   0.0000
+angle c    cd   ca      63.6 122.95 ! SOURCE3    1  same as c -cc-ca
+angle c    cc   cc      63.7 122.69 ! SOURCE3    2   0.0000   0.0000
+angle c    cd   cd      63.7 122.69 ! SOURCE3    2   0.0000   0.0000
+angle c    cc   cd      65.7 119.92 ! SOURCE3   61   2.5574   3.8679
+angle c    cd   cc      65.7 119.92 ! SOURCE3   61   2.5574   3.8679
+angle c    cc   cl      57.8 121.79 ! SOURCE3    1   0.0000   0.0000
+angle c    cd   cl      57.8 121.79 ! SOURCE3    1  same as c -cc-cl
+angle c    cc   ha      47.0 117.02 ! SOURCE3   56   1.2989   1.9713
+angle c    cd   ha      47.0 117.02 ! SOURCE3   56   1.2989   1.9713
+angle c    cc   n2      68.4 120.89 ! SOURCE3    1   0.0000   0.0000
+angle c    cd   n2      68.4 120.89 ! SOURCE3    1  same as c -cc-n2
+angle c    cc   nd      70.1 112.91 ! SOURCE3    1   0.0000   0.0000
+angle c    cd   nc      70.1 112.91 ! SOURCE3    1  same as c -cc-nd
+angle c    cc   oh      68.5 118.74 ! SOURCE3    3   5.1845   5.1845
+angle c    cd   oh      68.5 118.74 ! SOURCE3    3   5.1845   5.1845
+angle c2   cc   c3      63.3 126.11 ! SOURCE3    2   0.0000   0.0000
+angle c2   cd   c3      63.3 126.11 ! SOURCE3    2   0.0000   0.0000
+angle c2   cc   ca      64.4 128.22 ! SOURCE3    1   0.0000   0.0000
+angle c2   cd   ca      64.4 128.22 ! SOURCE3    1  same as c2-cc-ca
+angle c2   cc   cc      65.3 125.41 ! SOURCE3    3   2.0867   2.0867
+angle c2   cd   cd      65.3 125.41 ! SOURCE3    3   2.0867   2.0867
+angle c2   cc   cd      69.1 117.02 ! SOURCE3    2   0.0703   0.0703
+angle c2   cd   cc      69.1 117.02 ! SOURCE3    2  same as c2-cc-cd
+angle c2   cc   ha      48.7 122.72 ! SOURCE3    2   0.0092   0.0092
+angle c2   cd   ha      48.7 122.72 ! SOURCE3    2  same as c2-cc-ha
+angle c2   cc   n       67.7 126.90 ! SOURCE3    1   0.0000   0.0000
+angle c2   cd   n       67.7 126.90 ! SOURCE3    1  same as c2-cc-n
+angle c2   cc   os      70.5 119.31 ! SOURCE3    2   2.2167   2.2167
+angle c2   cd   os      70.5 119.31 ! SOURCE3    2  same as c2-cc-os
+angle c3   cc   ca      64.1 117.81 ! SOURCE3    8   9.2044   9.2060
+angle c3   cd   ca      64.1 117.81 ! SOURCE3    8  same as c3-cc-ca
+angle c3   cc   cc      64.7 115.97 ! SOURCE3    4   2.7731   3.0507
+angle c3   cd   cd      64.7 115.97 ! SOURCE3    4   2.7731   3.0507
+angle c3   cc   cd      64.8 119.45 ! SOURCE3   35   7.1944   8.2040
+angle c3   cd   cc      64.8 119.45 ! SOURCE3   35   7.1944   8.2040
+angle c3   cc   cf      65.4 117.84 ! SOURCE3    1   0.0000   0.0000
+angle c3   cd   ce      65.4 117.84 ! SOURCE3    1  same as c3-cc-cf
+angle c3   cc   ha      45.1 121.52 ! SOURCE3   32   2.8716   3.2091
+angle c3   cd   ha      45.1 121.52 ! SOURCE3   32   2.8716   3.2091
+angle c3   cc   n2      64.5 131.55 ! SOURCE3    1   0.0000   0.0000
+angle c3   cd   n2      64.5 131.55 ! SOURCE3    1  same as c3-cc-n2
+angle c3   cc   na      65.5 122.80 ! SOURCE3    4   0.4055   0.4308
+angle c3   cd   na      65.5 122.80 ! SOURCE3    4   0.4055   0.4308
+angle c3   cc   nc      65.8 121.44 ! SOURCE3    2   0.1912   0.1912
+angle c3   cd   nd      65.8 121.44 ! SOURCE3    2  same as c3-cc-nc
+angle c3   cc   nd      66.4 121.96 ! SOURCE3    6   4.6612   4.8451
+angle c3   cd   nc      66.4 121.96 ! SOURCE3    6   4.6612   4.8451
+angle ca   cc   cc      67.7 111.04 ! SOURCE3    9   5.2494   7.9455
+angle ca   cd   cd      67.7 111.04 ! SOURCE3    9   5.2494   7.9455
+angle ca   cc   cd      68.2 113.51 ! SOURCE3   26   6.3054   7.4229
+angle ca   cd   cc      68.2 113.51 ! SOURCE3   26   6.3054   7.4229
+angle ca   cc   h4      45.6 128.66 ! SOURCE3   10   2.0866   3.1167
+angle ca   cd   h4      45.6 128.66 ! SOURCE3   10   2.0866   3.1167
+angle ca   cc   ha      46.4 124.04 ! SOURCE3   34   2.5190   3.6691
+angle ca   cd   ha      46.4 124.04 ! SOURCE3   34   2.5190   3.6691
+angle ca   cc   n       72.5 104.87 ! SOURCE3    1   0.0000   0.0000
+angle ca   cd   n       72.5 104.87 ! SOURCE3    1  same as ca-cc-n
+angle ca   cc   nd      70.2 115.05 ! SOURCE3    6   3.7100   4.3938
+angle ca   cd   nc      70.2 115.05 ! SOURCE3    6   3.7100   4.3938
+angle ca   cc   os      69.5 116.24 ! SOURCE3    4   3.6507   5.0903
+angle ca   cd   os      69.5 116.24 ! SOURCE3    4  same as ca-cc-os
+angle ca   cc   ss      64.9 111.09 ! SOURCE3    4   0.0000   0.0000
+angle ca   cd   ss      64.9 111.09 ! SOURCE3    4  same as ca-cc-ss
+angle cc   cc   cc      67.9 110.70 ! SOURCE3   54   2.3111   3.4091
+angle cd   cd   cd      67.9 110.70 ! SOURCE3   54   2.3111   3.4091
+angle cc   cc   cd      68.2 114.19 ! SOURCE3  517   5.4334   6.5960
+angle cc   cd   cd      68.2 114.19 ! SOURCE3  517   5.4334   6.5960
+angle cc   cc   cf      65.6 123.92 ! SOURCE3    1   0.0000   0.0000
+angle cd   cd   ce      65.6 123.92 ! SOURCE3    1  same as cc-cc-cf
+angle cc   cc   h4      45.6 129.47 ! SOURCE3  171   1.7718   2.2734
+angle cd   cd   h4      45.6 129.47 ! SOURCE3  171   1.7718   2.2734
+angle cc   cc   ha      46.6 123.74 ! SOURCE3  656   2.7165   3.3195
+angle cd   cd   ha      46.6 123.74 ! SOURCE3  656   2.7165   3.3195
+angle cc   cc   n       67.9 119.89 ! SOURCE3   36   0.1731   0.2095
+angle cd   cd   n       67.9 119.89 ! SOURCE3   36   0.1731   0.2095
+angle cc   cc   n2      69.3 121.15 ! SOURCE3    6   0.4642   0.4642
+angle cd   cd   n2      69.3 121.15 ! SOURCE3    6   0.4642   0.4642
+angle cc   cc   na      72.2 106.80 ! SOURCE3   33   0.4847   0.6297
+angle cd   cd   na      72.2 106.80 ! SOURCE3   33  same as cc-cc-na
+angle cc   cc   nc      70.0 113.42 ! SOURCE3    4   1.1985   1.3957
+angle cd   cd   nd      70.0 113.42 ! SOURCE3    4  same as cc-cc-nc
+angle cc   cc   nd      71.1 112.56 ! SOURCE3  141   3.3765   4.2871
+angle cd   cd   nc      71.1 112.56 ! SOURCE3  141   3.3765   4.2871
+angle cc   cc   os      68.4 120.43 ! SOURCE3    4   7.5873   8.6974
+angle cd   cd   os      68.4 120.43 ! SOURCE3    4  same as cc-cc-os
+angle cc   cc   ss      63.8 115.02 ! SOURCE3    2   0.0000   0.0000
+angle cd   cd   ss      63.8 115.02 ! SOURCE3    2   0.0000   0.0000
+angle cd   cc   cd      69.3 115.51 ! SOURCE3    2   0.0000   0.0000
+angle cc   cd   cc      69.3 115.51 ! SOURCE3    2   0.0000   0.0000
+angle cd   cc   h4      47.2 129.11 ! SOURCE3  418   2.0265   3.1355
+angle cc   cd   h4      47.2 129.11 ! SOURCE3  418   2.0265   3.1355
+angle cd   cc   ha      48.4 122.89 ! SOURCE3  584   2.3225   2.9334
+angle cc   cd   ha      48.4 122.89 ! SOURCE3  584   2.3225   2.9334
+angle cd   cc   n       70.7 115.52 ! SOURCE3   52   1.0811   1.3322
+angle cc   cd   n       70.7 115.52 ! SOURCE3   52   1.0811   1.3322
+angle cd   cc   na      72.9 109.42 ! SOURCE3  265   1.3410   2.6051
+angle cc   cd   na      72.9 109.42 ! SOURCE3  265   1.3410   2.6051
+angle cd   cc   nc      71.0 114.98 ! SOURCE3   23   4.6219   5.3935
+angle cc   cd   nd      71.0 114.98 ! SOURCE3   23   4.6219   5.3935
+angle cd   cc   nh      68.4 125.04 ! SOURCE3    1   0.0000   0.0000
+angle cc   cd   nh      68.4 125.04 ! SOURCE3    1  same as cd-cc-nh
+angle cd   cc   oh      68.8 126.44 ! SOURCE3    3   0.5018   0.5018
+angle cc   cd   oh      68.8 126.44 ! SOURCE3    3   0.5018   0.5018
+angle cd   cc   os      70.0 120.30 ! SOURCE3   64   3.4271   5.4354
+angle cc   cd   os      70.0 120.30 ! SOURCE3   64   3.4271   5.4354
+angle cd   cc   ss      63.6 118.17 ! SOURCE3   37   3.1217   5.1653
+angle cc   cd   ss      63.6 118.17 ! SOURCE3   37   3.1217   5.1653
+angle cl   cc   nd      59.8 125.30 ! SOURCE3    1   0.0000   0.0000
+angle cl   cd   nc      59.8 125.30 ! SOURCE3    1  same as cl-cc-nd
+angle h4   cc   n       50.4 117.62 ! SOURCE3   53   0.6887   0.9721
+angle h4   cd   n       50.4 117.62 ! SOURCE3   53   0.6887   0.9721
+angle h4   cc   na      50.2 119.66 ! SOURCE3  294   0.9445   2.4702
+angle h4   cd   na      50.2 119.66 ! SOURCE3  294   0.9445   2.4702
+angle h4   cc   nc      50.0 120.03 ! SOURCE3   16   1.9828   2.3863
+angle h4   cd   nd      50.0 120.03 ! SOURCE3   16   1.9828   2.3863
+angle h4   cc   nd      51.4 119.11 ! SOURCE3  135   1.2486   1.6946
+angle h4   cd   nc      51.4 119.11 ! SOURCE3  135   1.2486   1.6946
+angle h4   cc   os      52.3 111.89 ! SOURCE3   61   1.5084   2.3500
+angle h4   cd   os      52.3 111.89 ! SOURCE3   61   1.5084   2.3500
+angle h4   cc   ss      43.0 117.75 ! SOURCE3   40   2.0527   3.1156
+angle h4   cd   ss      43.0 117.75 ! SOURCE3   40   2.0527   3.1156
+angle h5   cc   na      49.8 122.10 ! SOURCE3   16   1.2773   1.4626
+angle h5   cd   na      49.8 122.10 ! SOURCE3   16   1.2773   1.4626
+angle h5   cc   nc      49.3 123.70 ! SOURCE3    6   0.2952   0.3547
+angle h5   cd   nd      49.3 123.70 ! SOURCE3    6   0.2952   0.3547
+angle h5   cc   nd      50.1 125.38 ! SOURCE3   40   1.6952   2.2157
+angle h5   cd   nc      50.1 125.38 ! SOURCE3   40   1.6952   2.2157
+angle h5   cc   os      51.3 116.33 ! SOURCE3   12   2.1788   3.2919
+angle h5   cd   os      51.3 116.33 ! SOURCE3   12   2.1788   3.2919
+angle h5   cc   ss      42.2 122.00 ! SOURCE3    6   0.5719   0.7237
+angle h5   cd   ss      42.2 122.00 ! SOURCE3    6   0.5719   0.7237
+angle ha   cc   na      49.8 121.50 ! SOURCE2    1   0.0000   0.0000
+angle ha   cd   na      49.8 121.50 ! SOURCE2    1  same as ha-cc-na
+angle ha   cc   nc      50.7 116.54 ! SOURCE3    5   1.4298   1.4482
+angle ha   cd   nd      50.7 116.54 ! SOURCE3    5   1.4298   1.4482
+angle ha   cc   nd      51.4 118.88 ! SOURCE3   20   2.6038   2.8923
+angle ha   cd   nc      51.4 118.88 ! SOURCE3   20   2.6038   2.8923
+angle ha   cc   os      52.5 110.86 ! SOURCE3    7   1.2407   1.3846
+angle ha   cd   os      52.5 110.86 ! SOURCE3    7   1.2407   1.3846
+angle ha   cc   ss      42.3 121.64 ! SOURCE2    5   1.0080   1.3276
+angle ha   cd   ss      42.3 121.64 ! SOURCE2    5  same as ha-cc-ss
+angle n    cc   c       68.2 116.06 ! SOURCE3    1   0.0000   0.0000
+angle c    cd   n       68.2 116.06 ! SOURCE3    1  same as n -cc-c
+angle n    cc   nh      72.2 117.01 ! SOURCE3    2   0.0000   0.0000
+angle n    cd   nh      72.2 117.01 ! SOURCE3    2   0.0000   0.0000
+angle na   cc   nc      69.7 125.32 ! SOURCE3    1   0.0000   0.0000
+angle na   cd   nd      69.7 125.32 ! SOURCE3    1  same as na-cc-nc
+angle na   cc   nd      74.8 112.02 ! SOURCE3   17   1.9564   2.2434
+angle na   cd   nc      74.8 112.02 ! SOURCE3   17   1.9564   2.2434
+angle nc   cc   nd      72.5 118.69 ! SOURCE3    9   6.4647   6.6642
+angle nc   cd   nd      72.5 118.69 ! SOURCE3    9   6.4647   6.6642
+angle nc   cc   ss      64.6 119.86 ! SOURCE3    2   0.0000   0.0000
+angle nd   cd   ss      64.6 119.86 ! SOURCE3    2  same as nc-cc-ss
+angle nd   cc   nh      72.4 120.11 ! SOURCE3    5   0.7833   0.9313
+angle nc   cd   nh      72.4 120.11 ! SOURCE3    5   0.7833   0.9313
+angle nd   cc   os      73.0 119.14 ! SOURCE3   16   7.0412   7.4096
+angle nc   cd   os      73.0 119.14 ! SOURCE3   16   7.0412   7.4096
+angle nd   cc   ss      66.5 114.51 ! SOURCE3    8   0.2795   0.3449
+angle nc   cd   ss      66.5 114.51 ! SOURCE3    8   0.2795   0.3449
+angle nh   cc   nh      72.9 115.96 ! SOURCE3    1   0.0000   0.0000
+angle nh   cd   nh      72.9 115.96 ! SOURCE3    1  same as nh-cc-nh
+angle os   cc   ss      62.0 132.01 ! SOURCE3    1   0.0000   0.0000
+angle os   cd   ss      62.0 132.01 ! SOURCE3    1  same as os-cc-ss
+angle ss   cc   ss      62.2 122.30 ! SOURCE3    2   0.0000   0.0000
+angle ss   cd   ss      62.2 122.30 ! SOURCE3    2  same as ss-cc-ss
+angle c    cd   nd      64.3 130.80 ! SOURCE3    1   0.0000   0.0000
+angle c    cc   nc      64.3 130.80 ! SOURCE3    1  same as c -cd-nd
+angle c3   cd   os      67.8 115.89 ! SOURCE3    1   0.0000   0.0000
+angle c3   cc   os      67.8 115.89 ! SOURCE3    1  same as c3-cd-os
+angle cd   cd   nh      68.6 118.98 ! SOURCE3    1   0.0000   0.0000
+angle cc   cc   nh      68.6 118.98 ! SOURCE3    1  same as cd-cd-nh
+angle cd   cd   pc      80.8 115.36 ! SOURCE3   84   3.0583   3.2889
+angle cc   cc   pd      80.8 115.36 ! SOURCE3   84  same as cd-cd-pc
+angle ha   cd   pc      53.6 121.76 ! SOURCE3   84   2.0450   2.2216
+angle ha   cc   pd      53.6 121.76 ! SOURCE3   84  same as ha-cd-pc
+angle n    cd   nc      70.9 123.86 ! SOURCE3    1   0.0000   0.0000
+angle n    cc   nd      70.9 123.86 ! SOURCE3    1  same as n -cd-nc
+angle nd   cd   nh      73.9 111.94 ! SOURCE3    1   0.0000   0.0000
+angle nc   cc   nh      73.9 111.94 ! SOURCE3    1  same as nd-cd-nh
+angle c    ce   c       63.5 118.85 ! SOURCE3    1   0.0000   0.0000
+angle c    cf   c       63.5 118.85 ! SOURCE3    1  same as c -ce-c
+angle c    ce   c2      65.8 120.42 ! SOURCE3   13   1.3613   1.8877
+angle c    cf   c2      65.8 120.42 ! SOURCE3   13  same as c -ce-c2
+angle c    ce   c3      63.1 118.00 ! SOURCE3    4   2.0537   2.1860
+angle c    cf   c3      63.1 118.00 ! SOURCE3    4  same as c -ce-c3
+angle c    ce   cf      64.3 126.41 ! SOURCE3    2   5.7847   5.7847
+angle c    cf   ce      64.3 126.41 ! SOURCE3    2  same as c -ce-cf
+angle c    ce   ha      46.6 117.26 ! SOURCE3   11   2.2096   2.7158
+angle c    cf   ha      46.6 117.26 ! SOURCE3   11   2.2096   2.7158
+angle c    ce   nh      66.7 122.76 ! SOURCE3    1   0.0000   0.0000
+angle c    cf   nh      66.7 122.76 ! SOURCE3    1  same as c -ce-nh
+angle c2   ce   c3      64.3 122.89 ! SOURCE3    7   0.8750   1.0449
+angle c2   cf   c3      64.3 122.89 ! SOURCE3    7  same as c2-ce-c3
+angle c2   ce   ca      65.2 123.08 ! SOURCE3    6   2.1558   2.1589
+angle c2   cf   ca      65.2 123.08 ! SOURCE3    6  same as c2-ce-ca
+angle c2   ce   ce      65.7 123.08 ! SOURCE3   12   0.5820   0.6518
+angle c2   cf   cf      65.7 123.08 ! SOURCE3   12  same as c2-ce-ce
+angle c2   ce   cg      66.5 122.22 ! SOURCE3    3   0.7952   0.8434
+angle c2   cf   ch      66.5 122.22 ! SOURCE3    3  same as c2-ce-cg
+angle c2   ce   ha      49.6 121.10 ! SOURCE3   46   1.8238   2.4054
+angle c2   cf   ha      49.6 121.10 ! SOURCE3   46  same as c2-ce-ha
+angle c2   ce   n2      70.3 128.70 ! SOURCE3    1   0.0000   0.0000
+angle c2   cf   n2      70.3 128.70 ! SOURCE3    1  same as c2-ce-n2
+angle c2   ce   ne      69.7 118.32 ! SOURCE3    7   0.8179   1.0468
+angle c2   cf   nf      69.7 118.32 ! SOURCE3    7  same as c2-ce-ne
+angle c2   ce   p2      77.9 118.24 ! SOURCE0    1
+angle c2   cf   p2      77.9 118.24 ! SOURCE0    1  same as c2-ce-p2
+angle c2   ce   pe      77.6 118.76 ! SOURCE3    8   2.1170   2.3984
+angle c2   cf   pf      77.6 118.76 ! SOURCE3    8  same as c2-ce-pe
+angle c2   ce   px      77.2 119.72 ! SOURCE3    6   0.3888   0.5213
+angle c2   cf   px      77.2 119.72 ! SOURCE3    6  same as c2-ce-px
+angle c2   ce   py      77.4 122.13 ! SOURCE3    5   3.0715   3.1367
+angle c2   cf   py      77.4 122.13 ! SOURCE3    5  same as c2-ce-py
+angle c2   ce   sx      61.6 119.87 ! SOURCE3    5   0.6330   0.8557
+angle c2   cf   sx      61.6 119.87 ! SOURCE3    5  same as c2-ce-sx
+angle c2   ce   sy      62.0 120.35 ! SOURCE3    5   0.4293   0.5401
+angle c2   cf   sy      62.0 120.35 ! SOURCE3    5  same as c2-ce-sy
+angle c3   ce   ca      63.2 117.62 ! SOURCE3    1   0.0000   0.0000
+angle c3   cf   ca      63.2 117.62 ! SOURCE3    1  same as c3-ce-ca
+angle c3   ce   ce      63.3 118.71 ! SOURCE3    4   1.3456   1.5974
+angle c3   cf   cf      63.3 118.71 ! SOURCE3    4  same as c3-ce-ce
+angle c3   ce   n2      68.6 116.94 ! SOURCE3    2   0.0000   0.0000
+angle c3   cf   n2      68.6 116.94 ! SOURCE3    2  same as c3-ce-n2
+angle ca   ce   ca      63.8 117.93 ! SOURCE3    1   0.0000   0.0000
+angle ca   cf   ca      63.8 117.93 ! SOURCE3    1  same as ca-ce-ca
+angle ca   ce   ha      46.7 116.87 ! SOURCE3    4   1.2172   1.4055
+angle ca   cf   ha      46.7 116.87 ! SOURCE3    4  same as ca-ce-ha
+angle ca   ce   n2      68.5 120.72 ! SOURCE3    1   0.0000   0.0000
+angle ca   cf   n2      68.5 120.72 ! SOURCE3    1  same as ca-ce-n2
+angle ce   ce   ce      64.5 118.74 ! SOURCE3    1   0.0000   0.0000
+angle cf   cf   cf      64.5 118.74 ! SOURCE3    1  same as ce-ce-ce
+angle ce   ce   ha      47.5 115.90 ! SOURCE3   12   0.3242   0.4670
+angle cf   cf   ha      47.5 115.90 ! SOURCE3   12   0.3242   0.4670
+angle ce   ce   n2      68.5 123.00 ! SOURCE3    2   0.0000   0.0000
+angle cf   cf   n2      68.5 123.00 ! SOURCE3    2  same as ce-ce-n2
+angle cf   ce   ha      50.6 116.11 ! SOURCE3    4   1.6696   1.6696
+angle ce   cf   ha      50.6 116.11 ! SOURCE3    4   1.6696   1.6696
+angle cg   ce   cg      66.0 116.75 ! SOURCE3    1   0.0000   0.0000
+angle ch   cf   ch      66.0 116.75 ! SOURCE3    1  same as cg-ce-cg
+angle cg   ce   ha      47.8 117.00 ! SOURCE2    1   0.0000   0.0000
+angle ch   cf   ha      47.8 117.00 ! SOURCE2    1  same as cg-ce-ha
+angle cg   ce   n1      69.3 119.50 ! SOURCE2    1   0.0000   0.0000
+angle ch   cf   n1      69.3 119.50 ! SOURCE2    1  same as cg-ce-n1
+angle ha   ce   n2      52.9 119.51 ! SOURCE3    2   0.4623   0.4623
+angle ha   cf   n2      52.9 119.51 ! SOURCE3    2  same as ha-ce-n2
+angle ha   ce   ne      49.2 118.59 ! SOURCE3    5   1.0130   1.1113
+angle ha   cf   nf      49.2 118.59 ! SOURCE3    5  same as ha-ce-ne
+angle ha   ce   nh      52.1 114.99 ! SOURCE3    1   0.0000   0.0000
+angle ha   cf   nh      52.1 114.99 ! SOURCE3    1  same as ha-ce-nh
+angle ha   ce   p2      51.4 120.11 ! SOURCE0    1
+angle ha   cf   p2      51.4 120.11 ! SOURCE0    1  same as ha-ce-p2
+angle ha   ce   pe      51.4 119.33 ! SOURCE3    6   0.7187   0.8966
+angle ha   cf   pf      51.4 119.33 ! SOURCE3    6  same as ha-ce-pe
+angle ha   ce   px      51.7 117.90 ! SOURCE3    6   0.1568   0.1809
+angle ha   cf   px      51.7 117.90 ! SOURCE3    6  same as ha-ce-px
+angle ha   ce   py      52.3 118.72 ! SOURCE3    3   0.2888   0.3064
+angle ha   cf   py      52.3 118.72 ! SOURCE3    3  same as ha-ce-py
+angle ha   ce   sx      41.8 115.45 ! SOURCE3    3   0.6260   0.6640
+angle ha   cf   sx      41.8 115.45 ! SOURCE3    3  same as ha-ce-sx
+angle ha   ce   sy      42.3 114.86 ! SOURCE3    3   0.4447   0.4717
+angle ha   cf   sy      42.3 114.86 ! SOURCE3    3  same as ha-ce-sy
+angle ne   ce   ne      68.1 123.87 ! SOURCE3    1   0.0000   0.0000
+angle nf   cf   nf      68.1 123.87 ! SOURCE3    1  same as ne-ce-ne
+angle pe   ce   pe      92.4 129.79 ! SOURCE3    1   0.0000   0.0000
+angle pf   cf   pf      92.4 129.79 ! SOURCE3    1  same as pe-ce-pe
+angle py   ce   py     102.6 108.06 ! SOURCE3    1   0.0000   0.0000
+angle py   cf   py     102.6 108.06 ! SOURCE3    1  same as py-ce-py
+angle sx   ce   sx      60.6 120.32 ! SOURCE3    1   0.0000   0.0000
+angle sx   cf   sx      60.6 120.32 ! SOURCE3    1  same as sx-ce-sx
+angle sy   ce   sy      61.2 119.97 ! SOURCE3    1   0.0000   0.0000
+angle sy   cf   sy      61.2 119.97 ! SOURCE3    1  same as sy-ce-sy
+angle c    cf   cc      63.9 126.07 ! SOURCE3    1   0.0000   0.0000
+angle c    ce   cd      63.9 126.07 ! SOURCE3    1  same as c -cf-cc
+angle cc   cf   ha      49.3 119.16 ! SOURCE3    1   0.0000   0.0000
+angle cd   ce   ha      49.3 119.16 ! SOURCE3    1  same as cc-cf-ha
+angle ce   cf   cf      63.8 130.92 ! SOURCE3    2   5.4656   5.4656
+angle ce   ce   cf      63.8 130.92 ! SOURCE3    2  same as ce-cf-cf
+angle cf   cf   n1      66.6 127.15 ! SOURCE3    2   2.1936   2.1936
+angle ce   ce   n1      66.6 127.15 ! SOURCE3    2  same as cf-cf-n1
+angle ha   cf   n1      52.5 115.96 ! SOURCE3    2   3.5425   3.5425
+angle ha   ce   n1      52.5 115.96 ! SOURCE3    2  same as ha-cf-n1
+angle c    cg   c1      56.2 179.14 ! SOURCE3    2   0.0000   0.0000
+angle c    ch   c1      56.2 179.14 ! SOURCE3    2  same as c -cg-c1
+angle c1   cg   ca      56.4 180.00 ! SOURCE3    2   0.0000   0.0000
+angle c1   ch   ca      56.4 180.00 ! SOURCE3    2  same as c1-cg-ca
+angle c1   cg   ce      56.9 178.32 ! SOURCE3    1   0.0000   0.0000
+angle c1   ch   cf      56.9 178.32 ! SOURCE3    1  same as c1-cg-ce
+angle c1   cg   cg      58.1 180.00 ! SOURCE3    2   0.0000   0.0000
+angle c1   ch   ch      58.1 180.00 ! SOURCE3    2  same as c1-cg-cg
+angle c1   cg   ne      61.7 170.02 ! SOURCE3    4   0.9102   1.1724
+angle c1   ch   nf      61.7 170.02 ! SOURCE3    4  same as c1-cg-ne
+angle c1   cg   pe      71.8 173.29 ! SOURCE3   11   4.6016   4.9305
+angle c1   ch   pf      71.8 173.29 ! SOURCE3   11  same as c1-cg-pe
+angle ca   cg   n1      58.8 180.00 ! SOURCE3    1   0.0000   0.0000
+angle ca   ch   n1      58.8 180.00 ! SOURCE3    1  same as ca-cg-n1
+angle ce   cg   n1      59.1 179.58 ! SOURCE3    2   0.0000   0.0000
+angle cf   ch   n1      59.1 179.58 ! SOURCE3    2  same as ce-cg-n1
+angle n1   cg   ne      63.4 176.17 ! SOURCE2    3   2.5556   3.0641
+angle n1   ch   nf      63.4 176.17 ! SOURCE2    3  same as n1-cg-ne
+angle f    cl   f        0.0 87.50 ! SOURCE2    1   0.0000   0.0000
+angle ca   cp   ca      67.1 118.75 ! SOURCE3    4   0.0123   0.0123
+angle ca   cp   cp      62.6 127.01 ! SOURCE3   18   7.1022   7.1465
+angle ca   cp   nb      68.7 122.97 ! SOURCE3    2   0.0000   0.0000
+angle cp   cp   cp      72.2 90.00 ! SOURCE3    4   0.0000   0.0000
+angle cp   cp   nb      67.9 116.43 ! SOURCE3    2   0.0000   0.0000
+angle ca   cq   ca      67.1 118.75 ! SOURCE3    4   0.0123   0.0123
+angle ca   cq   cq      62.6 127.01 ! SOURCE3   18   7.1022   7.1465
+angle ca   cq   nb      68.7 122.97 ! SOURCE3    2   0.0000   0.0000
+angle cq   cq   cq      72.2 90.00 ! SOURCE3    4   0.0000   0.0000
+angle cq   cq   nb      67.9 116.43 ! SOURCE3    2   0.0000   0.0000
+angle c    cu   cu      95.0 62.60 ! SOURCE2    1   0.0000   0.0000
+angle cu   cu   cx     101.0 50.80 ! SOURCE2    1   0.0000   0.0000
+angle cu   cu   ha      46.1 147.73 ! SOURCE2    3   1.8889   2.0950
+angle cv   cv   cy      73.3 94.17 ! SOURCE3    2   0.0000   0.0000
+angle cv   cv   ha      47.3 133.70 ! SOURCE3    2   0.0000   0.0000
+angle cx   cv   cx      84.7 63.90 ! SOURCE2    1   0.0000   0.0000
+angle cy   cv   ha      42.9 132.14 ! SOURCE3    2   0.0000   0.0000
+angle c    cx   cx      72.6 88.75 ! SOURCE2    2  31.0500  31.0500
+angle c    cx   hc      46.0 118.50 ! SOURCE2    1   0.0000   0.0000
+angle c2   cx   hc      44.5 124.20 ! SOURCE2    2   0.0000   0.0000
+angle c3   cx   c3      61.5 120.00 ! SOURCE2    1   0.0000   0.0000
+angle c3   cx   cx      61.7 120.67 ! SOURCE3    4   0.0000   0.0000
+angle ca   cx   oh      69.3 112.93 ! SOURCE3    1   0.0000   0.0000
+angle ca   cx   os      66.5 118.12 ! SOURCE3    2   0.0000   0.0000
+angle cl   cx   cl      54.4 114.20 ! SOURCE2    1   0.0000   0.0000
+angle cl   cx   cx      56.9 118.70 ! SOURCE2    1   0.0000   0.0000
+angle cl   cx   h1      40.4 111.43 ! SOURCE3    1   0.0000   0.0000
+angle cl   cx   hc      39.6 115.80 ! SOURCE2    1   0.0000   0.0000
+angle cu   cx   cu      91.5 54.60 ! SOURCE2    1   0.0000   0.0000
+angle cx   cx   cx      85.3 63.75 ! SOURCE2   11   6.2810  11.0052
+angle cx   cx   cy      67.5 100.53 ! SOURCE3    4   0.0000   0.0000
+angle cx   cx   f       64.7 117.25 ! SOURCE3    2   0.0000   0.0000
+angle cx   cx   h1      45.5 119.66 ! SOURCE3   12   0.2763   0.4529
+angle cx   cx   hc      45.8 117.92 ! SOURCE3   92   0.7174   1.1927
+angle cx   cx   n3      65.3 116.30 ! SOURCE2    1   0.0000   0.0000
+angle cx   cx   os      93.3 59.41 ! SOURCE3    4   0.0000   0.0000
+angle cy   cx   hc      44.0 125.43 ! SOURCE3    2   0.0000   0.0000
+angle f    cx   f       70.6 106.90 ! SOURCE2    2   1.4000   1.4000
+angle f    cx   h1      50.3 111.68 ! SOURCE3    1   0.0000   0.0000
+angle f    cx   hc      50.2 112.30 ! SOURCE2    1   0.0000   0.0000
+angle h1   cx   h1      38.4 115.45 ! SOURCE3    4   0.0168   0.0168
+angle h1   cx   os      50.0 115.17 ! SOURCE3    8   0.0226   0.0226
+angle h2   cx   h2      37.9 119.43 ! SOURCE3    1   0.0000   0.0000
+angle h2   cx   n2      47.6 117.18 ! SOURCE3    4   0.0000   0.0000
+angle hc   cx   hc      38.6 114.47 ! SOURCE3   19   0.2816   0.3295
+angle hc   cx   os      50.3 114.10 ! SOURCE2    1   0.0000   0.0000
+angle n    cx   oh      69.2 119.75 ! SOURCE3    2   0.0000   0.0000
+angle n    cx   os      91.6 65.98 ! SOURCE3    1   0.0000   0.0000
+angle n2   cx   n2     102.1 50.16 ! SOURCE3    1   0.0000   0.0000
+angle oh   cx   oh      76.7 107.85 ! SOURCE3    1   0.0000   0.0000
+angle oh   cx   os      71.6 118.12 ! SOURCE3    4   1.3581   1.3581
+angle os   cx   os      93.3 66.95 ! SOURCE3    1   0.0000   0.0000
+angle c    cy   cy      71.4 85.95 ! SOURCE3    4   2.1019   2.1019
+angle c    cy   hc      45.7 113.85 ! SOURCE3    8   0.2067   0.2067
+angle c    cy   os      74.7 88.50 ! SOURCE2    1   0.0000   0.0000
+angle c2   cy   cy      66.1 100.40 ! SOURCE2    1   0.0000   0.0000
+angle c3   cy   cy      62.0 114.15 ! SOURCE3   12   5.4760   6.0450
+angle c3   cy   h1      46.6 109.66 ! SOURCE3    2   0.0000   0.0000
+angle c3   cy   hc      46.2 111.55 ! SOURCE3    5   0.5021   0.6276
+angle c3   cy   n3      65.3 112.92 ! SOURCE3    2   0.0000   0.0000
+angle cl   cy   cy      57.6 112.00 ! SOURCE3    2   0.0000   0.0000
+angle cl   cy   h1      41.0 106.59 ! SOURCE3    1   0.0000   0.0000
+angle cl   cy   hc      39.6 114.00 ! SOURCE2    1   0.0000   0.0000
+angle cv   cy   cy      71.8 85.83 ! SOURCE3    2   0.0000   0.0000
+angle cv   cy   hc      45.8 115.88 ! SOURCE3    4   0.0000   0.0000
+angle cx   cy   cy      66.1 101.23 ! SOURCE3    4   0.0000   0.0000
+angle cx   cy   hc      45.3 118.30 ! SOURCE2    3   5.4000   5.7799
+angle cy   cy   cy      70.2 87.61 ! SOURCE3   32   1.1419   1.5407
+angle cy   cy   h1      44.9 114.84 ! SOURCE3   20   1.8317   2.5651
+angle cy   cy   hc      44.8 115.14 ! SOURCE3  114   0.7232   0.8364
+angle cy   cy   n       64.9 112.13 ! SOURCE3   31   1.0775   2.0700
+angle cy   cy   n3      73.5 87.58 ! SOURCE3    4   0.6135   0.6135
+angle cy   cy   oh      66.0 114.19 ! SOURCE3    4   0.0000   0.0000
+angle cy   cy   os      72.9 91.10 ! SOURCE3    4   0.4160   0.4160
+angle h1   cy   h1      39.0 109.72 ! SOURCE3    6   0.7518   0.8087
+angle h1   cy   n3      48.2 113.36 ! SOURCE3    6   1.3680   1.4509
+angle h1   cy   oh      50.9 111.49 ! SOURCE3    2   0.0000   0.0000
+angle h1   cy   os      49.7 110.94 ! SOURCE3    8   0.6521   0.6522
+angle hc   cy   hc      39.2 109.04 ! SOURCE3   28   0.4802   0.5962
+angle ca   i    cl       0.0 90.21 ! SOURCE3    2   0.0000   0.0000
+angle ca   i    o        0.0 97.08 ! SOURCE3    5   0.1786   0.2233
+angle cl   i    cl       0.0 179.58 ! SOURCE3    1   0.0000   0.0000
+angle br   n    br      66.6 116.20 ! SOURCE3    1   0.0000   0.0000
+angle br   n    c       61.9 120.77 ! SOURCE3    5   2.1068   2.6390
+angle br   n    ca      62.1 118.19 ! SOURCE0    1
+angle br   n    cd      62.3 118.19 ! SOURCE3    1   0.0000   0.0000
+angle br   n    cc      62.3 118.19 ! SOURCE3    1  same as br-n -cd
+angle c    n    c       67.4 119.63 ! SOURCE3   13   6.4901   7.0653
+angle c    n    c1      68.5 117.04 ! SOURCE3    1   0.0000   0.0000
+angle c    n    c2      65.1 122.15 ! SOURCE3    9   4.1277   5.1016
+angle c    n    c3      63.9 121.35 ! SOURCE3   54   1.7456   2.3808
+angle c    n    ca      64.3 123.71 ! SOURCE3   10   3.3441   3.8159
+angle c    n    cc      65.2 124.19 ! SOURCE3   57   1.8239   2.2262
+angle c    n    cd      65.2 124.19 ! SOURCE3   57   1.8239   2.2262
+angle c    n    cl      58.2 115.32 ! SOURCE3    3   0.5092   0.5401
+angle c    n    cx      65.9 112.77 ! SOURCE3    1   0.0000   0.0000
+angle c    n    f       68.3 108.63 ! SOURCE3    3   4.4109   4.6785
+angle c    n    hn      49.2 118.46 ! SOURCE3  157   1.8119   2.4094
+angle c    n    i       56.3 120.38 ! SOURCE3    5   1.6364   2.1600
+angle c    n    n       68.2 118.42 ! SOURCE3   10   2.3102   2.8922
+angle c    n    n2      68.1 120.59 ! SOURCE3    9   1.9051   3.2410
+angle c    n    n3      67.1 120.43 ! SOURCE3    5   0.9122   0.9481
+angle c    n    n4      68.8 112.32 ! SOURCE3    5   1.2355   1.2622
+angle c    n    na      68.2 119.20 ! SOURCE3   11   2.1211   2.3032
+angle c    n    nc      68.6 120.31 ! SOURCE3    4   1.9095   2.2197
+angle c    n    nd      68.6 120.31 ! SOURCE3    4  same as c -n -nc
+angle c    n    nh      68.0 117.06 ! SOURCE3    4   0.4170   0.4454
+angle c    n    no      66.5 118.16 ! SOURCE3    4   4.5037   5.4870
+angle c    n    o       71.6 118.90 ! SOURCE3    9   4.4216   5.4085
+angle c    n    oh      69.5 113.39 ! SOURCE3    6   1.2395   1.3345
+angle c    n    os      69.1 113.14 ! SOURCE3    7   2.9643   3.0839
+angle c    n    p2      76.5 124.56 ! SOURCE3    8   2.7083   3.6907
+angle c    n    p3      75.8 122.54 ! SOURCE3    9   3.4441   4.4802
+angle c    n    p4      76.8 123.44 ! SOURCE3    1   0.0000   0.0000
+angle c    n    p5      78.3 121.27 ! SOURCE3    4   2.2442   2.6505
+angle c    n    pc      77.0 122.23 ! SOURCE3    3   2.7141   2.8787
+angle c    n    pd      77.0 122.23 ! SOURCE3    3  same as c -n -pc
+angle c    n    s       59.0 126.55 ! SOURCE3    3   4.0857   4.3365
+angle c    n    s4      60.1 120.41 ! SOURCE3    4   2.6351   3.1760
+angle c    n    s6      61.7 119.57 ! SOURCE3    4   2.7801   3.2191
+angle c    n    sh      61.9 119.54 ! SOURCE3    4   1.5284   1.7681
+angle c    n    ss      62.0 120.37 ! SOURCE3    7   1.1024   1.4450
+angle c1   n    c1      73.5 102.69 ! SOURCE0    1
+angle c1   n    ca      65.9 118.88 ! SOURCE0    1
+angle c1   n    cd      67.0 118.88 ! SOURCE3    1   0.0000   0.0000
+angle c1   n    cc      67.0 118.88 ! SOURCE3    1  same as c1-n -cd
+angle c2   n    c2      65.2 116.75 ! SOURCE3    1   0.0000   0.0000
+angle c2   n    c3      62.1 123.87 ! SOURCE3    1   0.0000   0.0000
+angle c2   n    ca      64.9 116.54 ! SOURCE0    1
+angle c2   n    cd      65.9 116.54 ! SOURCE3    1   0.0000   0.0000
+angle c2   n    cc      65.9 116.54 ! SOURCE3    1  same as c2-n -cd
+angle c2   n    hn      47.4 117.86 ! SOURCE3    5   3.0657   3.7489
+angle c3   n    c3      64.0 112.62 ! SOURCE3    3   1.9371   2.0546
+angle c3   n    ca      62.4 121.15 ! SOURCE0    1
+angle c3   n    cc      63.3 121.17 ! SOURCE3    4   1.1758   1.2476
+angle c3   n    cd      63.3 121.17 ! SOURCE3    4   1.1758   1.2476
+angle c3   n    hn      46.0 116.78 ! SOURCE3   39   1.6197   2.1985
+angle ca   n    ca      64.5 116.80 ! SOURCE3    1   0.0000   0.0000
+angle ca   n    cd      65.0 118.42 ! SOURCE3    1   0.0000   0.0000
+angle ca   n    cc      65.0 118.42 ! SOURCE3    1  same as ca-n -cd
+angle ca   n    cl      56.9 117.72 ! SOURCE0    1
+angle ca   n    f       64.6 114.92 ! SOURCE0    1
+angle ca   n    hn      47.6 114.59 ! SOURCE3    4   0.3352   0.3353
+angle ca   n    i       56.6 119.30 ! SOURCE0    1
+angle ca   n    n2      66.8 118.25 ! SOURCE3    1   0.0000   0.0000
+angle ca   n    n4      64.1 122.98 ! SOURCE0    1
+angle ca   n    na      66.8 117.57 ! SOURCE0    1
+angle ca   n    nc      68.8 113.03 ! SOURCE0    1
+angle ca   n    nd      68.8 113.03 ! SOURCE0    1  same as ca-n -nc
+angle ca   n    nh      66.4 116.45 ! SOURCE0    1
+angle ca   n    p2      79.6 112.32 ! SOURCE0    1
+angle ca   n    p3      74.2 125.11 ! SOURCE0    1
+angle ca   n    s4      60.0 118.40 ! SOURCE0    1
+angle ca   n    s6      61.6 117.32 ! SOURCE0    1
+angle ca   n    ss      62.2 116.60 ! SOURCE0    1
+angle cc   n    cc      68.8 108.92 ! SOURCE3   11   0.2418   0.3167
+angle cd   n    cd      68.8 108.92 ! SOURCE3   11  same as cc-n -cc
+angle cc   n    hn      47.5 121.52 ! SOURCE3    4   1.9166   1.9166
+angle cd   n    hn      47.5 121.52 ! SOURCE3    4   1.9166   1.9166
+angle cc   n    nc      69.9 113.03 ! SOURCE3    1   0.0000   0.0000
+angle cd   n    nd      69.9 113.03 ! SOURCE3    1  same as cc-n -nc
+angle cd   n    cl      57.3 117.72 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    cl      57.3 117.72 ! SOURCE3    1  same as cd-n -cl
+angle cd   n    f       65.6 114.92 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    f       65.6 114.92 ! SOURCE3    1  same as cd-n -f
+angle cd   n    i       56.6 119.30 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    i       56.6 119.30 ! SOURCE3    1  same as cd-n -i
+angle cd   n    n       66.5 121.37 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    n       66.5 121.37 ! SOURCE3    1  same as cd-n -n
+angle cd   n    n2      70.1 110.87 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    n2      70.1 110.87 ! SOURCE3    1  same as cd-n -n2
+angle cd   n    na      67.9 117.57 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    na      67.9 117.57 ! SOURCE3    1  same as cd-n -na
+angle cd   n    nh      67.1 117.52 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    nh      67.1 117.52 ! SOURCE3    1  same as cd-n -nh
+angle cd   n    no      66.4 115.92 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    no      66.4 115.92 ! SOURCE3    1  same as cd-n -no
+angle cd   n    o       70.1 120.54 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    o       70.1 120.54 ! SOURCE3    1  same as cd-n -o
+angle cd   n    oh      67.3 118.15 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    oh      67.3 118.15 ! SOURCE3    1  same as cd-n -oh
+angle cd   n    os      67.6 115.56 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    os      67.6 115.56 ! SOURCE3    1  same as cd-n -os
+angle cd   n    p2      80.2 112.32 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    p2      80.2 112.32 ! SOURCE3    1  same as cd-n -p2
+angle cd   n    p3      74.7 125.11 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    p3      74.7 125.11 ! SOURCE3    1  same as cd-n -p3
+angle cd   n    p5      77.9 121.00 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    p5      77.9 121.00 ! SOURCE3    1  same as cd-n -p5
+angle cd   n    s       60.8 118.29 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    s       60.8 118.29 ! SOURCE3    1  same as cd-n -s
+angle cd   n    s4      60.3 118.40 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    s4      60.3 118.40 ! SOURCE3    1  same as cd-n -s4
+angle cd   n    s6      62.0 117.32 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    s6      62.0 117.32 ! SOURCE3    1  same as cd-n -s6
+angle cd   n    sh      61.6 119.13 ! SOURCE3    1   0.0000   0.0000
+angle cc   n    sh      61.6 119.13 ! SOURCE3    1  same as cd-n -sh
+angle cd   n    ss      62.6 116.60 ! SOURCE3    2   1.8318   1.8318
+angle cc   n    ss      62.6 116.60 ! SOURCE3    2  same as cd-n -ss
+angle cl   n    cl      54.3 111.69 ! SOURCE3    1   0.0000   0.0000
+angle cx   n    os      95.6 54.04 ! SOURCE3    1   0.0000   0.0000
+angle f    n    f       67.9 102.98 ! SOURCE3    1   0.0000   0.0000
+angle hn   n    hn      39.7 117.85 ! SOURCE3   15   1.8879   2.3694
+angle hn   n    i       36.1 117.24 ! SOURCE3    2   0.4435   0.4435
+angle hn   n    n       50.1 113.12 ! SOURCE3    7   2.2740   3.2954
+angle hn   n    n2      49.6 118.33 ! SOURCE3    5   2.0148   2.2377
+angle hn   n    n3      49.3 114.33 ! SOURCE3    3   2.6378   2.7978
+angle hn   n    n4      48.9 112.68 ! SOURCE3    3   1.8617   1.9746
+angle hn   n    na      50.4 113.55 ! SOURCE3    8   1.7554   1.9324
+angle hn   n    nh      49.6 112.41 ! SOURCE3    1   0.0000   0.0000
+angle hn   n    no      48.7 110.11 ! SOURCE3    1   0.0000   0.0000
+angle hn   n    o       53.8 116.32 ! SOURCE3    2   0.0175   0.0175
+angle hn   n    oh      50.7 109.21 ! SOURCE3    3   0.2949   0.3165
+angle hn   n    os      50.1 108.90 ! SOURCE3    3   0.5220   0.5537
+angle hn   n    p2      52.5 118.05 ! SOURCE3    7   2.5423   3.0564
+angle hn   n    p3      50.9 119.63 ! SOURCE3    2   0.0000   0.0000
+angle hn   n    p4      53.0 115.71 ! SOURCE3    1   0.0000   0.0000
+angle hn   n    p5      53.6 116.26 ! SOURCE3    3   0.6453   0.6845
+angle hn   n    s       41.1 114.92 ! SOURCE3    2   0.0260   0.0260
+angle hn   n    s4      41.2 112.46 ! SOURCE3    1   0.0000   0.0000
+angle hn   n    s6      42.5 112.68 ! SOURCE3    1   0.0000   0.0000
+angle hn   n    sh      42.2 114.91 ! SOURCE3    1   0.0000   0.0000
+angle hn   n    ss      42.4 115.60 ! SOURCE3    3   0.6047   0.6414
+angle i    n    i       60.7 118.20 ! SOURCE3    1   0.0000   0.0000
+angle n    n    n       69.9 114.62 ! SOURCE3    1   0.0000   0.0000
+angle n2   n    n2      70.2 116.89 ! SOURCE3    1   0.0000   0.0000
+angle n3   n    n3      68.0 117.94 ! SOURCE3    1   0.0000   0.0000
+angle n4   n    n4      68.4 112.69 ! SOURCE3    1   0.0000   0.0000
+angle na   n    na      69.6 117.38 ! SOURCE3    1   0.0000   0.0000
+angle nc   n    nc      71.3 116.41 ! SOURCE3    1   0.0000   0.0000
+angle nd   n    nd      71.3 116.41 ! SOURCE3    1  same as nc-n -nc
+angle nc   n    p2      80.7 117.21 ! SOURCE0    1
+angle nd   n    p2      80.7 117.21 ! SOURCE0    1  same as nc-n -p2
+angle nc   n    pc      80.5 117.21 ! SOURCE3    1   0.0000   0.0000
+angle nd   n    pd      80.5 117.21 ! SOURCE3    1  same as nc-n -pc
+angle nh   n    nh      68.6 115.18 ! SOURCE3    1   0.0000   0.0000
+angle no   n    no      68.5 108.66 ! SOURCE3    1   0.0000   0.0000
+angle o    n    o       73.4 128.61 ! SOURCE3    3   1.0018   1.0626
+angle oh   n    oh      72.0 107.26 ! SOURCE3    1   0.0000   0.0000
+angle os   n    os      71.3 106.53 ! SOURCE3    1   0.0000   0.0000
+angle p2   n    p2      98.0 119.62 ! SOURCE0    1
+angle p3   n    p3     100.7 108.73 ! SOURCE3    3   0.2443   0.2591
+angle p4   n    p4     102.8 108.55 ! SOURCE0    1
+angle p5   n    p5     108.5 99.99 ! SOURCE0    1
+angle pc   n    pc      97.6 119.62 ! SOURCE3    1   0.0000   0.0000
+angle pd   n    pd      97.6 119.62 ! SOURCE3    1  same as pc-n -pc
+angle s    n    s       58.5 126.00 ! SOURCE0    1
+angle s4   n    s4      61.0 113.75 ! SOURCE3    1   0.0000   0.0000
+angle s6   n    s6      61.2 119.68 ! SOURCE3    1   0.0000   0.0000
+angle sh   n    sh      61.5 119.03 ! SOURCE3    1   0.0000   0.0000
+angle ss   n    ss      62.1 118.49 ! SOURCE3    1   0.0000   0.0000
+angle c2   n1   o       73.1 116.94 ! SOURCE3    2   0.0060   0.0060
+angle c2   n1   s       64.7 118.00 ! SOURCE3    2   0.0121   0.0121
+angle cf   n1   o       71.1 122.40 ! SOURCE3    1   0.0000   0.0000
+angle ce   n1   o       71.1 122.40 ! SOURCE3    1  same as cf-n1-o
+angle cf   n1   s       64.8 117.34 ! SOURCE3    1   0.0000   0.0000
+angle ce   n1   s       64.8 117.34 ! SOURCE3    1  same as cf-n1-s
+angle o    n1   p2      84.7 116.05 ! SOURCE3    1   0.0000   0.0000
+angle p2   n1   s       80.3 119.93 ! SOURCE3    1   0.0000   0.0000
+!angle n1   n1   s       54.6 180.00 ! SOURCE3    1   0.0000   0.0000
+angle br   n2   br      63.9 106.60 ! SOURCE0    1
+angle br   n2   c2      59.1 112.40 ! SOURCE3    1   0.0000   0.0000
+angle br   n2   n2      61.1 110.42 ! SOURCE3    1   0.0000   0.0000
+angle br   n2   o       60.1 114.47 ! SOURCE3    1   0.0000   0.0000
+angle br   n2   p2      80.1 111.03 ! SOURCE3    1   0.0000   0.0000
+angle br   n2   s       62.4 115.78 ! SOURCE3    1   0.0000   0.0000
+angle c    n2   c       62.7 123.80 ! SOURCE0    1
+angle c    n2   c2      66.2 120.97 ! SOURCE0    1
+angle c    n2   ca      62.4 120.50 ! SOURCE0    1
+angle c1   n2   c1      74.4 121.10 ! SOURCE0    1
+angle c1   n2   c3      58.7 151.88 ! SOURCE3    4  13.3413  15.8282
+angle c1   n2   cl      55.4 118.80 ! SOURCE2    1   0.0000   0.0000
+angle c1   n2   hn      51.5 126.50 ! SOURCE2    3   6.6667   7.6267
+angle c1   n2   n2      76.8 113.40 ! SOURCE0    1
+angle c1   n2   o       79.2 113.59 ! SOURCE0    1
+angle c1   n2   p2      84.8 119.57 ! SOURCE0    1
+angle c1   n2   s       69.8 117.67 ! SOURCE0    1
+angle c2   n2   c2      70.8 118.18 ! SOURCE0    1
+angle c2   n2   c3      66.1 115.30 ! SOURCE3    8   2.6931   4.2940
+angle c2   n2   ca      65.0 119.94 ! SOURCE0    1
+angle c2   n2   cl      56.7 112.64 ! SOURCE3    1   0.0000   0.0000
+angle c2   n2   f       68.3 108.14 ! SOURCE3    1   0.0000   0.0000
+angle c2   n2   hn      52.6 110.33 ! SOURCE3    9   0.5299   0.6498
+angle c2   n2   i       51.9 114.74 ! SOURCE3    2   0.0139   0.0139
+angle c2   n2   n       70.4 117.24 ! SOURCE3    5   3.9916   5.0254
+angle c2   n2   n2      77.9 103.59 ! SOURCE3    2   0.0000   0.0000
+angle c2   n2   n3      71.3 118.14 ! SOURCE0    1
+angle c2   n2   n4      62.4 112.22 ! SOURCE3    3   0.0383   0.0406
+angle c2   n2   na      69.3 117.58 ! SOURCE3    8   1.3943   1.6671
+angle c2   n2   nh      70.7 117.61 ! SOURCE3    6   2.3364   3.2642
+angle c2   n2   no      64.2 111.54 ! SOURCE3    1   0.0000   0.0000
+angle c2   n2   o       75.5 116.94 ! SOURCE0    1
+angle c2   n2   oh      72.4 109.82 ! SOURCE3    4   0.5608   0.7131
+angle c2   n2   os      72.0 110.01 ! SOURCE3    5   3.1049   4.2241
+angle c2   n2   p2      85.0 116.00 ! SOURCE0    1
+angle c2   n2   p3      77.5 119.30 ! SOURCE3    3   2.6860   2.8489
+angle c2   n2   p4      79.3 118.77 ! SOURCE0    1
+angle c2   n2   s       68.7 118.00 ! SOURCE0    1
+angle c2   n2   s4      68.1 112.29 ! SOURCE0    1
+angle c2   n2   s6      69.4 116.24 ! SOURCE0    1
+angle c2   n2   sh      63.1 115.48 ! SOURCE3    1   0.0000   0.0000
+angle c2   n2   ss      65.0 118.04 ! SOURCE3    4   1.9749   2.2804
+angle c3   n2   c3      63.8 110.70 ! SOURCE0    1
+angle c3   n2   ca      60.6 123.20 ! SOURCE0    1
+angle c3   n2   hn      45.2 118.40 ! SOURCE0    1
+angle c3   n2   n1      68.9 115.38 ! SOURCE3    1   0.0000   0.0000
+angle c3   n2   n2      69.3 111.18 ! SOURCE3    7   0.3839   0.4349
+angle c3   n2   nh      68.1 109.99 ! SOURCE3    1   0.0000   0.0000
+angle c3   n2   o       70.3 112.40 ! SOURCE2    1   0.0000   0.0000
+angle c3   n2   p2      82.1 114.21 ! SOURCE3    2   2.2772   2.2772
+angle c3   n2   s       65.7 116.72 ! SOURCE3    1   0.0000   0.0000
+angle c3   n2   s6      66.7 113.84 ! SOURCE3    1   0.0000   0.0000
+angle ca   n2   ca      63.6 112.20 ! SOURCE0    1
+angle ca   n2   hn      45.1 120.00 ! SOURCE0    1
+angle ca   n2   n2      68.8 113.53 ! SOURCE0    1
+angle ca   n2   o       69.4 116.00 ! SOURCE2    1   0.0000   0.0000
+angle ca   n2   p2      80.9 118.11 ! SOURCE0    1
+angle ca   n2   s       64.9 120.11 ! SOURCE0    1
+angle cc   n2   hn      52.5 109.65 ! SOURCE3    3   0.0968   0.0968
+angle cd   n2   hn      52.5 109.65 ! SOURCE3    3   0.0968   0.0968
+angle cd   n2   cl      55.9 115.79 ! SOURCE3    1   0.0000   0.0000
+angle cc   n2   cl      55.9 115.79 ! SOURCE3    1  same as cd-n2-cl
+angle ce   n2   hn      53.9 106.50 ! SOURCE3    1   0.0000   0.0000
+angle cf   n2   hn      53.9 106.50 ! SOURCE3    1  same as ce-n2-hn
+angle ce   n2   nh      70.5 119.08 ! SOURCE3    2   0.0000   0.0000
+angle cf   n2   nh      70.5 119.08 ! SOURCE3    2  same as ce-n2-nh
+angle ce   n2   o       77.4 112.16 ! SOURCE0    1
+angle cf   n2   o       77.4 112.16 ! SOURCE0    1  same as ce-n2-o
+angle ce   n2   oh      72.6 109.87 ! SOURCE3    1   0.0000   0.0000
+angle cf   n2   oh      72.6 109.87 ! SOURCE3    1  same as ce-n2-oh
+angle ce   n2   s       69.3 116.28 ! SOURCE0    1
+angle cf   n2   s       69.3 116.28 ! SOURCE0    1  same as ce-n2-s
+angle cl   n2   n1      59.3 108.70 ! SOURCE2    1   0.0000   0.0000
+angle cl   n2   n2      58.7 110.47 ! SOURCE3    1   0.0000   0.0000
+angle cl   n2   o       58.2 114.03 ! SOURCE0    1
+angle cl   n2   p2      74.0 112.98 ! SOURCE3    1   0.0000   0.0000
+angle cl   n2   s       58.5 115.77 ! SOURCE3    1   0.0000   0.0000
+angle cx   n2   n2      90.5 64.92 ! SOURCE3    2   0.0000   0.0000
+angle f    n2   n2      68.3 114.60 ! SOURCE2    1   0.0000   0.0000
+angle f    n2   o       71.2 110.10 ! SOURCE2    1   0.0000   0.0000
+angle f    n2   p2      84.3 107.10 ! SOURCE3    1   0.0000   0.0000
+angle f    n2   s       67.1 110.73 ! SOURCE3    1   0.0000   0.0000
+angle hn   n2   hn      38.6 120.00 ! SOURCE0    1
+angle hn   n2   n1      55.3 114.10 ! SOURCE2    1   0.0000   0.0000
+angle hn   n2   n2      55.8 105.01 ! SOURCE3   19   1.1377   1.5183
+angle hn   n2   ne      54.7 108.56 ! SOURCE3   29   4.7084   5.5708
+angle hn   n2   nf      54.7 108.56 ! SOURCE3   29  same as hn-n2-ne
+angle hn   n2   o       57.6 107.37 ! SOURCE3    1   0.0000   0.0000
+angle hn   n2   p2      58.6 112.09 ! SOURCE3   18   3.1633   4.0663
+angle hn   n2   p4      54.5 111.33 ! SOURCE3    1   0.0000   0.0000
+angle hn   n2   p5      56.3 122.34 ! SOURCE3    1   0.0000   0.0000
+angle hn   n2   pe      61.3 111.41 ! SOURCE3   20   3.8728   4.9895
+angle hn   n2   pf      61.3 111.41 ! SOURCE3   20  same as hn-n2-pe
+angle hn   n2   s       49.1 108.17 ! SOURCE3    1   0.0000   0.0000
+angle hn   n2   s2      48.3 115.80 ! SOURCE2    1   0.0000   0.0000
+angle hn   n2   s4      46.3 111.21 ! SOURCE3    1   0.0000   0.0000
+angle hn   n2   s6      48.4 112.59 ! SOURCE3    2   0.0000   0.0000
+angle i    n2   n2      53.7 111.79 ! SOURCE3    1   0.0000   0.0000
+angle i    n2   o       52.3 116.82 ! SOURCE3    1   0.0000   0.0000
+angle i    n2   p2      72.3 113.26 ! SOURCE3    1   0.0000   0.0000
+angle i    n2   s       56.3 116.85 ! SOURCE3    1   0.0000   0.0000
+angle n    n2   n2      74.7 110.42 ! SOURCE3    1   0.0000   0.0000
+angle n    n2   o       76.2 111.38 ! SOURCE3    1   0.0000   0.0000
+angle n    n2   p2      85.2 117.30 ! SOURCE3    1   0.0000   0.0000
+angle n    n2   s       69.6 115.74 ! SOURCE3    1   0.0000   0.0000
+angle n2   n2   n1      79.0 180.00 ! dac: for azides
+angle n2   n2   n2      78.2 109.49 ! SOURCE3    2   0.0000   0.0000
+angle n2   n2   n3      76.6 108.88 ! SOURCE0    1
+angle n2   n2   n4      65.8 106.45 ! SOURCE3    1   0.0000   0.0000
+angle n2   n2   na      73.1 112.23 ! SOURCE0    1
+angle n2   n2   nh      74.8 111.70 ! SOURCE3    5   0.2774   0.3475
+angle n2   n2   no      67.7 105.97 ! SOURCE3    1   0.0000   0.0000
+angle n2   n2   o       80.2 110.43 ! SOURCE0    1
+angle n2   n2   oh      74.0 111.51 ! SOURCE3    1   0.0000   0.0000
+angle n2   n2   os      74.7 108.38 ! SOURCE3    1   0.0000   0.0000
+angle n2   n2   p2      90.1 109.15 ! SOURCE0    1
+angle n2   n2   p3      81.7 113.05 ! SOURCE3    1   0.0000   0.0000
+angle n2   n2   p4      81.4 118.77 ! SOURCE0    1
+angle n2   n2   p5      89.8 110.46 ! SOURCE0    1
+angle n2   n2   s       71.2 115.91 ! SOURCE0    1
+angle n2   n2   s4      71.6 107.30 ! SOURCE0    1
+angle n2   n2   s6      73.0 111.25 ! SOURCE0    1
+angle n2   n2   sh      66.0 111.10 ! SOURCE3    1   0.0000   0.0000
+angle n2   n2   ss      68.5 112.14 ! SOURCE3    1   0.0000   0.0000
+angle n3   n2   n3      73.0 115.07 ! SOURCE0    1
+angle n3   n2   o       76.9 114.00 ! SOURCE2    1   0.0000   0.0000
+angle n3   n2   p2      86.7 115.34 ! SOURCE0    1
+angle n3   n2   s       70.0 117.13 ! SOURCE0    1
+angle n4   n2   n4      60.0 106.70 ! SOURCE0    1
+angle n4   n2   o       64.9 112.20 ! SOURCE3    1   0.0000   0.0000
+angle n4   n2   p2      79.5 113.07 ! SOURCE3    1   0.0000   0.0000
+angle n4   n2   s       62.4 118.50 ! SOURCE3    1   0.0000   0.0000
+angle na   n2   na      71.7 107.00 ! SOURCE0    1
+angle na   n2   o       74.5 113.09 ! SOURCE3    1   0.0000   0.0000
+angle na   n2   p2      83.9 119.16 ! SOURCE3    1   0.0000   0.0000
+angle na   n2   s       68.3 118.26 ! SOURCE3    1   0.0000   0.0000
+angle ne   n2   nh      74.7 111.38 ! SOURCE3    1   0.0000   0.0000
+angle nf   n2   nh      74.7 111.38 ! SOURCE3    1  same as ne-n2-nh
+angle ne   n2   o       80.0 110.31 ! SOURCE0    1
+angle nf   n2   o       80.0 110.31 ! SOURCE0    1  same as ne-n2-o
+angle ne   n2   s       71.0 116.22 ! SOURCE0    1
+angle nf   n2   s       71.0 116.22 ! SOURCE0    1  same as ne-n2-s
+angle nh   n2   nh      69.7 121.20 ! SOURCE0    1
+angle nh   n2   o       76.2 112.96 ! SOURCE3    3   0.2935   0.3515
+angle nh   n2   p2      84.9 118.83 ! SOURCE3    2   0.1024   0.1024
+angle nh   n2   s       69.6 116.90 ! SOURCE3    2   0.2276   0.2276
+angle no   n2   no      62.8 103.70 ! SOURCE0    1
+angle no   n2   o       70.4 100.76 ! SOURCE3    1   0.0000   0.0000
+angle no   n2   p2      81.3 111.95 ! SOURCE3    1   0.0000   0.0000
+angle o    n2   o       81.0 115.37 ! SOURCE0    1
+angle o    n2   oh      75.6 112.15 ! SOURCE2    2   1.4500   1.4500
+angle o    n2   os      75.7 110.35 ! SOURCE3    2   0.0042   0.0042
+angle o    n2   p2      88.7 116.08 ! SOURCE0    1
+angle o    n2   p3      82.4 113.43 ! SOURCE3    1   0.0000   0.0000
+angle o    n2   p4      85.2 110.61 ! SOURCE0    1
+angle o    n2   p5      91.7 109.11 ! SOURCE0    1
+angle o    n2   pe      85.2 134.56 ! SOURCE0    1
+angle o    n2   pf      85.2 134.56 ! SOURCE0    1  same as o -n2-pe
+angle o    n2   s       72.1 117.18 ! SOURCE0    1
+angle o    n2   s4      72.1 108.91 ! SOURCE0    1
+angle o    n2   s6      74.3 111.34 ! SOURCE0    1
+angle o    n2   sh      65.6 114.98 ! SOURCE3    1   0.0000   0.0000
+angle o    n2   ss      68.4 115.34 ! SOURCE3    1   0.0000   0.0000
+angle oh   n2   oh      74.8 101.70 ! SOURCE0    1
+angle oh   n2   p2      86.1 115.11 ! SOURCE3    1   0.0000   0.0000
+angle oh   n2   s       69.5 116.08 ! SOURCE3    1   0.0000   0.0000
+angle os   n2   os      71.3 110.29 ! SOURCE0    1
+angle os   n2   p2      87.7 110.20 ! SOURCE3    1   0.0000   0.0000
+angle os   n2   s       70.5 112.23 ! SOURCE3    1   0.0000   0.0000
+angle p2   n2   p2     107.1 116.80 ! SOURCE0    1
+angle p2   n2   p3      98.4 124.48 ! SOURCE3    1   0.0000   0.0000
+angle p2   n2   p4      98.3 128.37 ! SOURCE0    1
+angle p2   n2   p5     104.4 123.47 ! SOURCE0    1
+angle p2   n2   s       85.9 117.84 ! SOURCE0    1
+angle p2   n2   s4      86.3 112.10 ! SOURCE0    1
+angle p2   n2   s6      86.9 115.70 ! SOURCE0    1
+angle p2   n2   sh      80.4 118.45 ! SOURCE3    1   0.0000   0.0000
+angle p2   n2   ss      82.0 120.43 ! SOURCE3    1   0.0000   0.0000
+angle p3   n2   p3      96.0 120.40 ! SOURCE0    1
+angle p3   n2   s       80.1 120.86 ! SOURCE3    1   0.0000   0.0000
+angle p4   n2   s       77.8 131.84 ! SOURCE0    1
+angle p5   n2   p5     105.8 120.60 ! SOURCE0    1
+angle p5   n2   s       85.3 119.89 ! SOURCE0    1
+angle pe   n2   s       88.6 115.73 ! SOURCE0    1
+angle pf   n2   s       88.6 115.73 ! SOURCE0    1  same as pe-n2-s
+angle s    n2   s       68.5 120.88 ! SOURCE0    1
+angle s    n2   s4      69.2 113.00 ! SOURCE0    1
+angle s    n2   s6      69.0 119.61 ! SOURCE0    1
+angle s    n2   sh      63.6 122.05 ! SOURCE3    1   0.0000   0.0000
+angle s    n2   ss      66.6 118.19 ! SOURCE3    1   0.0000   0.0000
+angle s4   n2   s4      66.0 119.18 ! SOURCE0    1
+angle s4   n2   s6      67.5 119.18 ! SOURCE0    1
+angle s6   n2   s6      69.3 119.18 ! SOURCE0    1
+angle sh   n2   sh      60.0 123.93 ! SOURCE0    1
+angle sh   n2   ss      61.3 123.93 ! SOURCE0    1
+angle ss   n2   ss      62.9 123.93 ! SOURCE0    1
+angle br   n3   br      66.5 107.15 ! SOURCE3    1   0.0000   0.0000
+angle br   n3   c3      62.7 106.93 ! SOURCE3    2   0.0000   0.0000
+angle c    n3   c       63.1 127.17 ! SOURCE0    1
+angle c1   n3   c1      64.1 123.34 ! SOURCE0    1
+angle c1   n3   f       68.2 104.70 ! SOURCE2    1   0.0000   0.0000
+angle c1   n3   hn      47.7 118.31 ! SOURCE0    1
+angle c1   n3   o       70.0 116.63 ! SOURCE0    1
+angle c2   n3   c2      66.2 124.68 ! SOURCE0    1
+angle c2   n3   hn      49.1 119.38 ! SOURCE0    1
+angle c3   n3   c3      64.0 110.90 ! SOURCE3   40   1.6285   2.3048
+angle c3   n3   ca      16.9 116.39 ! SOURCE0    1
+angle c3   n3   cl      58.1 107.23 ! SOURCE3    3   0.3463   0.3673
+angle c3   n3   cy      61.2 120.16 ! SOURCE3    2   0.0000   0.0000
+angle c3   n3   f       66.8 103.13 ! SOURCE3    2   0.0000   0.0000
+angle c3   n3   hn      47.1 109.92 ! SOURCE3  120   1.5757   2.2590
+angle c3   n3   i       57.0 108.48 ! SOURCE3    2   0.0000   0.0000
+angle c3   n3   n       67.2 109.97 ! SOURCE3    2   0.0000   0.0000
+angle c3   n3   n2      66.2 118.75 ! SOURCE2    2   2.6500   2.6500
+angle c3   n3   n3      66.8 108.15 ! SOURCE3   15   1.0563   1.3999
+angle c3   n3   n4      67.2 109.65 ! SOURCE3    3   0.1080   0.1146
+angle c3   n3   nh      67.4 108.44 ! SOURCE3    1   0.0000   0.0000
+angle c3   n3   no      66.0 111.27 ! SOURCE3    1   0.0000   0.0000
+angle c3   n3   o       68.7 113.31 ! SOURCE3    5   8.7272   8.9081
+angle c3   n3   oh      68.9 106.62 ! SOURCE3    4   0.1844   0.2167
+angle c3   n3   os      68.2 105.73 ! SOURCE3    4   0.3563   0.3760
+angle c3   n3   p3      75.8 121.93 ! SOURCE3    3   5.2806   5.6009
+angle c3   n3   p5      78.7 119.35 ! SOURCE3    4   0.9279   0.9604
+angle c3   n3   s       61.5 110.02 ! SOURCE3    1   0.0000   0.0000
+angle c3   n3   s4      61.4 112.91 ! SOURCE3    3   0.8469   0.8983
+angle c3   n3   s6      63.9 115.81 ! SOURCE3    3   0.5687   0.6032
+angle c3   n3   sh      62.1 112.70 ! SOURCE3    1   0.0000   0.0000
+angle c3   n3   ss      61.6 116.01 ! SOURCE3    3   1.1261   1.1944
+angle cl   n3   cl      53.8 108.28 ! SOURCE3    1   0.0000   0.0000
+angle cl   n3   hn      39.9 104.39 ! SOURCE3    2   0.0000   0.0000
+angle cl   n3   n3      59.6 107.65 ! SOURCE3    1   0.0000   0.0000
+angle cy   n3   cy      70.5 89.57 ! SOURCE3    2   0.2809   0.2809
+angle cy   n3   hn      45.7 114.80 ! SOURCE3    2   0.0000   0.0000
+angle f    n3   f       67.7 102.22 ! SOURCE2    4   0.6250   0.7562
+angle f    n3   hn      50.8 99.80 ! SOURCE2    1   0.0000   0.0000
+angle hn   n3   hn      41.3 107.13 ! SOURCE3   44   1.2683   1.9687
+angle hn   n3   i       35.4 109.98 ! SOURCE3    2   0.0000   0.0000
+angle hn   n3   n       51.0 106.57 ! SOURCE3    6   1.0152   1.0767
+angle hn   n3   n2      51.4 115.94 ! SOURCE0    1
+angle hn   n3   n3      50.2 103.98 ! SOURCE3   18   1.2193   1.8593
+angle hn   n3   n4      50.9 106.40 ! SOURCE3    5   0.5735   0.5863
+angle hn   n3   na      50.3 107.89 ! SOURCE0    1
+angle hn   n3   nh      50.4 107.39 ! SOURCE3   11   1.2116   1.6294
+angle hn   n3   no      50.3 104.78 ! SOURCE3    3   1.0489   1.1126
+angle hn   n3   o       53.1 113.32 ! SOURCE3    3   4.1430   4.3945
+angle hn   n3   oh      53.1 101.11 ! SOURCE3    4   0.9921   0.9921
+angle hn   n3   os      51.7 100.92 ! SOURCE3    6   0.6490   0.7295
+angle hn   n3   p2      54.2 120.26 ! SOURCE0    1
+angle hn   n3   p3      52.9 116.89 ! SOURCE3    9   3.4043   3.8816
+angle hn   n3   p4      55.0 113.05 ! SOURCE3    2   0.0000   0.0000
+angle hn   n3   p5      56.0 113.68 ! SOURCE3    6   1.9887   2.1781
+angle hn   n3   s       41.5 109.47 ! SOURCE0    1
+angle hn   n3   s4      42.5 108.93 ! SOURCE3    7   1.5575   1.7819
+angle hn   n3   s6      45.4 112.52 ! SOURCE3    9   3.3273   3.7049
+angle hn   n3   sh      43.1 108.67 ! SOURCE3    3   2.3593   2.5025
+angle hn   n3   ss      43.4 109.85 ! SOURCE3    5   1.8572   2.3215
+angle i    n3   i       60.0 111.27 ! SOURCE3    1   0.0000   0.0000
+angle n    n3   n       70.3 110.55 ! SOURCE3    1   0.0000   0.0000
+angle n2   n3   n2      71.8 118.73 ! SOURCE0    1
+angle n2   n3   o       74.1 114.91 ! SOURCE0    1
+angle n3   n3   n3      69.6 105.71 ! SOURCE3    3   0.3357   0.3561
+angle n4   n3   n4      69.0 113.48 ! SOURCE3    1   0.0000   0.0000
+angle n4   n3   nh      70.9 107.14 ! SOURCE0    1
+angle na   n3   na      69.2 112.00 ! SOURCE0    1
+angle nh   n3   nh      70.8 107.15 ! SOURCE3    1   0.0000   0.0000
+angle no   n3   no      67.0 115.26 ! SOURCE3    1   0.0000   0.0000
+angle o    n3   o       71.9 126.14 ! SOURCE0    1
+angle o    n3   p2      84.3 117.02 ! SOURCE0    1
+angle o    n3   p4      83.4 116.65 ! SOURCE0    1
+angle o    n3   s       62.0 119.81 ! SOURCE0    1
+angle o    n3   s4      64.6 114.09 ! SOURCE0    1
+angle o    n3   s6      68.8 113.80 ! SOURCE0    1
+angle oh   n3   oh      72.8 107.18 ! SOURCE3    1   0.0000   0.0000
+angle os   n3   os      70.8 106.52 ! SOURCE3    1   0.0000   0.0000
+angle p2   n3   p2      97.5 130.13 ! SOURCE0    1
+angle p3   n3   p3      98.6 118.74 ! SOURCE3    3   3.1825   3.3755
+angle p4   n3   p4     101.5 116.35 ! SOURCE0    1
+angle p5   n3   p5     102.2 119.42 ! SOURCE3    1   0.0000   0.0000
+angle s    n3   s       56.5 131.36 ! SOURCE3    1   0.0000   0.0000
+angle s4   n3   s4      60.1 120.02 ! SOURCE3    1   0.0000   0.0000
+angle s4   n3   s6      62.0 120.95 ! SOURCE0    1
+angle s6   n3   s6      63.3 126.13 ! SOURCE3    1   0.0000   0.0000
+angle sh   n3   sh      61.2 118.63 ! SOURCE3    1   0.0000   0.0000
+angle sh   n3   ss      61.3 119.67 ! SOURCE0    1
+angle ss   n3   ss      61.6 119.57 ! SOURCE3    1   0.0000   0.0000
+angle br   n4   br      65.1 114.82 ! SOURCE3    1   0.0000   0.0000
+angle br   n4   hn      41.4 108.44 ! SOURCE3    7   0.5572   0.5630
+angle c    n4   c       61.5 108.61 ! SOURCE3    1   0.0000   0.0000
+angle c    n4   hn      44.7 110.86 ! SOURCE3   10   0.9869   1.0073
+angle c1   n4   c1      65.5 113.87 ! SOURCE3    1   0.0000   0.0000
+angle c1   n4   hn      48.6 110.19 ! SOURCE3    7   1.0735   1.0847
+angle c2   n4   c2      63.0 112.58 ! SOURCE3    1   0.0000   0.0000
+angle c2   n4   h4      11.3 112.46 ! SOURCE0    1
+angle c2   n4   hn      46.4 111.36 ! SOURCE3   13   1.1254   1.2672
+angle c3   n4   c3      62.8 110.64 ! SOURCE3   13   1.0808   1.3060
+angle c3   n4   ca      63.0 112.41 ! SOURCE3    2   0.0000   0.0000
+angle c3   n4   cl      57.9 108.04 ! SOURCE3    3   0.0000   0.0000
+angle c3   n4   hn      46.2 110.11 ! SOURCE3  100   1.0473   1.3136
+angle c3   n4   n3      66.7 108.72 ! SOURCE3    2   0.0000   0.0000
+angle c3   n4   n4      63.7 114.07 ! SOURCE3    4   0.0000   0.0000
+angle c3   n4   nh      64.8 111.73 ! SOURCE3    1   0.0000   0.0000
+angle c3   n4   no      65.2 109.08 ! SOURCE3    1   0.0000   0.0000
+angle c3   n4   o       67.3 111.66 ! SOURCE3    1   0.0000   0.0000
+angle c3   n4   oh      65.9 113.73 ! SOURCE3    1   0.0000   0.0000
+angle c3   n4   os      67.4 107.42 ! SOURCE3    3   3.3865   3.5920
+angle c3   n4   p2      71.9 112.52 ! SOURCE3    1   0.0000   0.0000
+angle c3   n4   p3      74.4 110.73 ! SOURCE3    3   1.9878   2.1084
+angle c3   n4   p5      75.1 113.22 ! SOURCE3    3   0.3791   0.4021
+angle c3   n4   s       59.2 113.55 ! SOURCE3    1   0.0000   0.0000
+angle c3   n4   s4      57.2 108.23 ! SOURCE3    3   0.3955   0.4195
+angle c3   n4   s6      57.7 111.56 ! SOURCE3    3   1.7773   1.8851
+angle c3   n4   sh      59.1 115.81 ! SOURCE3    1   0.0000   0.0000
+angle c3   n4   ss      59.7 113.68 ! SOURCE3    3   1.0752   1.1405
+angle ca   n4   ca      63.2 114.48 ! SOURCE3    1   0.0000   0.0000
+angle ca   n4   hn      47.5 108.52 ! SOURCE3    5   0.9354   1.1693
+angle cl   n4   cl      52.7 114.91 ! SOURCE3    1   0.0000   0.0000
+angle cl   n4   hn      39.5 108.87 ! SOURCE3    7   0.7640   0.7719
+angle f    n4   f       70.5 109.05 ! SOURCE3    1   0.0000   0.0000
+angle f    n4   hn      51.7 108.39 ! SOURCE3    4   0.0000   0.0000
+angle hn   n4   hn      40.5 108.11 ! SOURCE3  208   1.1062   1.4126
+angle hn   n4   i       36.4 108.72 ! SOURCE3    7   1.2587   1.2717
+angle hn   n4   n       49.6 109.08 ! SOURCE3   13   1.3969   1.6047
+angle hn   n4   n2      42.3 109.68 ! SOURCE3   19   0.4842   0.6266
+angle hn   n4   n3      49.9 110.40 ! SOURCE3   11   0.6778   0.7307
+angle hn   n4   n4      48.1 108.66 ! SOURCE3   18   1.2886   1.2967
+angle hn   n4   na      49.4 109.38 ! SOURCE3   25   0.9072   1.0758
+angle hn   n4   nh      48.4 109.92 ! SOURCE3   12   0.5359   0.7304
+angle hn   n4   no      49.2 104.38 ! SOURCE3    2   0.0000   0.0000
+angle hn   n4   o       52.1 109.26 ! SOURCE3    6   1.9991   2.1203
+angle hn   n4   oh      51.1 108.09 ! SOURCE3    6   1.5771   1.6728
+angle hn   n4   os      50.2 109.39 ! SOURCE3   10   1.1420   1.4403
+angle hn   n4   p2      47.7 110.50 ! SOURCE3   25   1.0144   1.0664
+angle hn   n4   p3      49.7 109.89 ! SOURCE3   10   2.2397   2.3870
+angle hn   n4   p4      47.7 111.33 ! SOURCE3    3   0.0000   0.0000
+angle hn   n4   p5      51.3 110.00 ! SOURCE3   10   0.8162   1.0282
+angle hn   n4   py      47.4 117.89 ! SOURCE3    8   0.0000   0.0000
+angle hn   n4   s       41.1 106.89 ! SOURCE3    6   1.0159   1.0775
+angle hn   n4   s4      37.1 110.10 ! SOURCE3    6   0.7986   0.8471
+angle hn   n4   s6      38.6 108.94 ! SOURCE3   10   0.5598   0.5715
+angle hn   n4   sh      41.3 108.56 ! SOURCE3    6   0.8047   0.8535
+angle hn   n4   ss      41.1 109.17 ! SOURCE3   10   0.6521   0.8455
+angle i    n4   i       59.0 118.49 ! SOURCE3    1   0.0000   0.0000
+angle n    n4   n       66.7 118.62 ! SOURCE3    1   0.0000   0.0000
+angle n2   n4   n2      59.4 108.64 ! SOURCE3    1   0.0000   0.0000
+angle n3   n4   n3      69.8 111.07 ! SOURCE3    1   0.0000   0.0000
+angle n4   n4   n4      65.2 115.49 ! SOURCE3    1   0.0000   0.0000
+angle na   n4   na      66.3 119.60 ! SOURCE3    1   0.0000   0.0000
+angle nh   n4   nh      67.8 109.38 ! SOURCE3    1   0.0000   0.0000
+angle o    n4   o       70.3 120.97 ! SOURCE3    1   0.0000   0.0000
+angle oh   n4   oh      72.2 108.19 ! SOURCE3    1   0.0000   0.0000
+angle os   n4   os      72.5 104.40 ! SOURCE3    1   0.0000   0.0000
+angle p2   n4   p2      89.6 113.91 ! SOURCE3    2   0.0000   0.0000
+angle p3   n4   p3      89.7 121.38 ! SOURCE3    1   0.0000   0.0000
+angle p5   n4   p5      98.1 107.02 ! SOURCE3    1   0.0000   0.0000
+angle py   n4   py     116.9 69.79 ! SOURCE3    2   0.0000   0.0000
+angle s    n4   s       56.7 124.55 ! SOURCE3    1   0.0000   0.0000
+angle s4   n4   s4      54.8 115.43 ! SOURCE0    1
+angle s6   n4   s6      57.9 109.51 ! SOURCE3    1   0.0000   0.0000
+angle sh   n4   sh      60.4 112.59 ! SOURCE3    1   0.0000   0.0000
+angle ss   n4   ss      61.3 109.20 ! SOURCE3    1   0.0000   0.0000
+angle br   na   br      60.6 123.00 ! SOURCE0    1
+angle br   na   c2      63.6 100.48 ! SOURCE3    2   1.0536   1.0536
+angle br   na   ca      57.1 124.81 ! SOURCE0    1
+angle br   na   cc      57.2 124.62 ! SOURCE3    3   0.5042   0.5348
+angle br   na   cd      57.2 124.62 ! SOURCE3    3  same as br-na-cc
+angle br   na   nc      59.9 119.42 ! SOURCE3    4   1.3872   1.6703
+angle br   na   nd      59.9 119.42 ! SOURCE3    4  same as br-na-nc
+angle br   na   os      63.9 104.99 ! SOURCE3    1   0.0000   0.0000
+angle br   na   p2      75.9 121.01 ! SOURCE0    1
+angle br   na   pc      76.4 120.26 ! SOURCE3    3   2.0229   2.1456
+angle br   na   pd      76.4 120.26 ! SOURCE3    3  same as br-na-pc
+angle br   na   ss      62.5 112.28 ! SOURCE3    1   0.0000   0.0000
+angle c    na   c       64.7 126.40 ! SOURCE0    1
+angle c    na   c2      64.3 125.09 ! SOURCE0    1
+angle c    na   c3      64.7 117.60 ! SOURCE0    1
+angle c    na   ca      65.3 125.20 ! SOURCE0    1
+angle c    na   hn      48.8 118.00 ! SOURCE0    1
+angle c    na   n       66.3 124.66 ! SOURCE0    1
+angle c1   na   c1      67.2 117.20 ! SOURCE0    1
+angle c1   na   c2      64.3 125.20 ! SOURCE0    1
+angle c1   na   ca      66.5 120.57 ! SOURCE0    1
+angle c1   na   cc      65.8 121.35 ! SOURCE3    6   0.6144   0.6517
+angle c1   na   cd      65.8 121.35 ! SOURCE3    6   0.6144   0.6517
+angle c1   na   nc      68.3 120.24 ! SOURCE3    4   1.3854   1.6849
+angle c1   na   nd      68.3 120.24 ! SOURCE3    4  same as c1-na-nc
+angle c1   na   os      70.2 106.96 ! SOURCE3    2   0.0000   0.0000
+angle c1   na   p2      76.5 122.25 ! SOURCE0    1
+angle c1   na   pc      77.3 121.48 ! SOURCE3    3   2.0441   2.1681
+angle c1   na   pd      77.3 121.48 ! SOURCE3    3  same as c1-na-pc
+angle c1   na   ss      61.9 118.30 ! SOURCE3    1   0.0000   0.0000
+angle c2   na   c2      67.8 110.37 ! SOURCE3    6   0.4285   0.5121
+angle c2   na   c3      64.2 117.20 ! SOURCE3    2   0.0000   0.0000
+angle c2   na   ca      66.2 119.26 ! SOURCE3    2   5.6375   5.6375
+angle c2   na   cc      64.0 125.75 ! SOURCE3   10   1.1039   1.5856
+angle c2   na   cd      64.0 125.75 ! SOURCE3   10   1.1039   1.5856
+angle c2   na   cl      58.8 101.01 ! SOURCE3    2   1.5799   1.5799
+angle c2   na   f       68.6 103.11 ! SOURCE3    1   0.0000   0.0000
+angle c2   na   hn      47.6 119.28 ! SOURCE3   14   6.5120   6.6027
+angle c2   na   i       59.0 106.74 ! SOURCE3    1   0.0000   0.0000
+angle c2   na   n       65.6 124.70 ! SOURCE0    1
+angle c2   na   n2      65.0 125.00 ! SOURCE0    1
+angle c2   na   n3      64.6 124.80 ! SOURCE0    1
+angle c2   na   n4      65.2 121.32 ! SOURCE0    1
+angle c2   na   na      65.1 124.60 ! SOURCE0    1
+angle c2   na   nc      67.6 119.95 ! SOURCE3    4   1.8044   1.8457
+angle c2   na   nd      67.6 119.95 ! SOURCE3    4  same as c2-na-nc
+angle c2   na   nh      65.0 124.98 ! SOURCE0    1
+angle c2   na   no      64.3 124.20 ! SOURCE0    1
+angle c2   na   o       68.2 125.90 ! SOURCE0    1
+angle c2   na   oh      65.8 123.90 ! SOURCE0    1
+angle c2   na   os      68.5 110.33 ! SOURCE3    4   2.7760   3.2172
+angle c2   na   p2      76.2 122.14 ! SOURCE0    1
+angle c2   na   p3      74.6 126.10 ! SOURCE0    1
+angle c2   na   p4      81.6 125.00 ! SOURCE0    1
+angle c2   na   p5      76.4 125.10 ! SOURCE0    1
+angle c2   na   pc      77.0 121.56 ! SOURCE3    3   1.5323   1.6252
+angle c2   na   pd      77.0 121.56 ! SOURCE3    3  same as c2-na-pc
+angle c2   na   s       58.9 125.80 ! SOURCE0    1
+angle c2   na   s4      58.4 124.90 ! SOURCE0    1
+angle c2   na   s6      60.2 124.40 ! SOURCE0    1
+angle c2   na   sh      60.2 125.10 ! SOURCE0    1
+angle c2   na   ss      62.3 115.53 ! SOURCE3    5   1.1554   1.4036
+angle c3   na   c3      60.7 125.59 ! SOURCE3    1   0.0000   0.0000
+angle c3   na   ca      63.1 124.36 ! SOURCE3    5   4.0243   4.2557
+angle c3   na   cc      62.6 125.09 ! SOURCE3   18   1.1830   1.2138
+angle c3   na   cd      62.6 125.09 ! SOURCE3   18   1.1830   1.2138
+angle c3   na   n2      68.7 106.67 ! SOURCE3    2   0.0000   0.0000
+angle c3   na   nc      65.7 120.46 ! SOURCE3    8   1.7737   2.1625
+angle c3   na   nd      65.7 120.46 ! SOURCE3    8   1.7737   2.1625
+angle c3   na   os      68.9 104.39 ! SOURCE3    3   1.1329   1.2017
+angle c3   na   p2      75.0 123.12 ! SOURCE0    1
+angle c3   na   pc      75.9 122.11 ! SOURCE3    3   2.6874   2.8504
+angle c3   na   pd      75.9 122.11 ! SOURCE3    3  same as c3-na-pc
+angle c3   na   sh      63.4 110.28 ! SOURCE0    1
+angle c3   na   ss      62.9 110.87 ! SOURCE3    3   0.7788   0.8260
+angle ca   na   ca      67.1 119.80 ! SOURCE2    1   0.0000   0.0000
+angle ca   na   cc      68.5 113.15 ! SOURCE3   18   9.7983   9.8644
+angle ca   na   cd      68.5 113.15 ! SOURCE3   18   9.7983   9.8644
+angle ca   na   cl      53.2 124.79 ! SOURCE0    1
+angle ca   na   f       65.5 116.40 ! SOURCE0    1
+angle ca   na   hn      48.2 122.77 ! SOURCE3   13   5.8799   7.1074
+angle ca   na   i       55.2 121.62 ! SOURCE0    1
+angle ca   na   n       67.3 122.00 ! SOURCE0    1
+angle ca   na   n2      66.9 121.23 ! SOURCE0    1
+angle ca   na   n4      66.4 120.19 ! SOURCE0    1
+angle ca   na   na      66.3 123.76 ! SOURCE0    1
+angle ca   na   nc      69.3 117.85 ! SOURCE3    6   3.3298   3.6536
+angle ca   na   nd      69.3 117.85 ! SOURCE3    6  same as ca-na-nc
+angle ca   na   nh      67.7 118.59 ! SOURCE0    1
+angle ca   na   o       70.9 120.68 ! SOURCE3    4   0.1702   0.1702
+angle ca   na   oh      66.7 124.08 ! SOURCE0    1
+angle ca   na   os      69.7 109.46 ! SOURCE3    1   0.0000   0.0000
+angle ca   na   p2      75.5 125.85 ! SOURCE0    1
+angle ca   na   p3      75.5 124.38 ! SOURCE0    1
+angle ca   na   p4      82.4 124.97 ! SOURCE0    1
+angle ca   na   p5      77.5 123.30 ! SOURCE0    1
+angle ca   na   pc      77.3 122.13 ! SOURCE3    3   2.1112   2.2393
+angle ca   na   pd      77.3 122.13 ! SOURCE3    3  same as ca-na-pc
+angle ca   na   py      72.6 140.88 ! SOURCE3    2   0.0000   0.0000
+angle ca   na   s       59.3 125.64 ! SOURCE0    1
+angle ca   na   s4      60.6 117.23 ! SOURCE0    1
+angle ca   na   s6      61.6 120.69 ! SOURCE0    1
+angle ca   na   sh      60.5 125.44 ! SOURCE0    1
+angle ca   na   ss      62.4 116.60 ! SOURCE0    1
+angle cc   na   cc      68.9 109.90 ! SOURCE3  109   1.2152   1.5547
+angle cd   na   cd      68.9 109.90 ! SOURCE3  109   1.2152   1.5547
+angle cc   na   cd      63.9 128.01 ! SOURCE3    1   0.0000   0.0000
+angle cc   na   cl      53.1 124.61 ! SOURCE3    3   0.4910   0.5208
+angle cd   na   cl      53.1 124.61 ! SOURCE3    3  same as cc-na-cl
+angle cc   na   f       64.6 118.03 ! SOURCE3    4   0.2905   0.3081
+angle cd   na   f       64.6 118.03 ! SOURCE3    4   0.2905   0.3081
+angle cc   na   hn      47.2 124.66 ! SOURCE3   38   4.3279   5.9689
+angle cd   na   hn      47.2 124.66 ! SOURCE3   38   4.3279   5.9689
+angle cc   na   i       54.3 125.70 ! SOURCE3    6   0.6256   0.7821
+angle cd   na   i       54.3 125.70 ! SOURCE3    6   0.6256   0.7821
+angle cc   na   n       66.5 123.19 ! SOURCE3   13   0.1564   0.3010
+angle cd   na   n       66.5 123.19 ! SOURCE3   13   0.1564   0.3010
+angle cc   na   n2      66.0 122.96 ! SOURCE3   15   0.5269   0.9350
+angle cd   na   n2      66.0 122.96 ! SOURCE3   15   0.5269   0.9350
+angle cc   na   n4      65.9 120.31 ! SOURCE3   10   3.0227   3.4394
+angle cd   na   n4      65.9 120.31 ! SOURCE3   10   3.0227   3.4394
+angle cc   na   na      65.9 123.43 ! SOURCE3   23   0.1445   0.2088
+angle cd   na   na      65.9 123.43 ! SOURCE3   23   0.1445   0.2088
+angle cc   na   nc      70.2 113.02 ! SOURCE3   38   1.6684   2.2867
+angle cd   na   nd      70.2 113.02 ! SOURCE3   38   1.6684   2.2867
+angle cc   na   nh      66.2 122.25 ! SOURCE3   19   0.1526   0.2010
+angle cd   na   nh      66.2 122.25 ! SOURCE3   19   0.1526   0.2010
+angle cc   na   no      65.4 121.78 ! SOURCE3    9   0.2814   0.3521
+angle cd   na   no      65.4 121.78 ! SOURCE3    9   0.2814   0.3521
+angle cc   na   o       69.0 125.21 ! SOURCE3   10   0.0087   0.0124
+angle cd   na   o       69.0 125.21 ! SOURCE3   10   0.0087   0.0124
+angle cc   na   oh      66.7 122.38 ! SOURCE3   10   0.1099   0.1570
+angle cd   na   oh      66.7 122.38 ! SOURCE3   10   0.1099   0.1570
+angle cc   na   os      67.3 115.74 ! SOURCE3   41   4.3528   5.4093
+angle cd   na   os      67.3 115.74 ! SOURCE3   41   4.3528   5.4093
+angle cc   na   p2      75.3 125.86 ! SOURCE3   14   1.2242   2.2993
+angle cd   na   p2      75.3 125.86 ! SOURCE3   14   1.2242   2.2993
+angle cc   na   p3      75.0 125.25 ! SOURCE3    8   0.1571   0.1998
+angle cd   na   p3      75.0 125.25 ! SOURCE3    8   0.1571   0.1998
+angle cc   na   p4      81.1 127.73 ! SOURCE3    7   3.1941   3.6077
+angle cd   na   p4      81.1 127.73 ! SOURCE3    7  same as cc-na-p4
+angle cc   na   p5      76.8 124.70 ! SOURCE3   13   0.8852   1.4225
+angle cd   na   p5      76.8 124.70 ! SOURCE3   13   0.8852   1.4225
+angle cc   na   s       59.1 125.66 ! SOURCE3    8   0.1504   0.1880
+angle cd   na   s       59.1 125.66 ! SOURCE3    8   0.1504   0.1880
+angle cc   na   s4      59.4 121.03 ! SOURCE3   10   0.4358   0.5589
+angle cd   na   s4      59.4 121.03 ! SOURCE3   10   0.4358   0.5589
+angle cc   na   s6      61.0 122.19 ! SOURCE3   10   0.7706   0.9634
+angle cd   na   s6      61.0 122.19 ! SOURCE3   10   0.7706   0.9634
+angle cc   na   sh      60.7 123.96 ! SOURCE3   10   0.2394   0.3424
+angle cd   na   sh      60.7 123.96 ! SOURCE3   10   0.2394   0.3424
+angle cc   na   ss      61.6 119.18 ! SOURCE3   36   4.3591   5.0538
+angle cd   na   ss      61.6 119.18 ! SOURCE3   36   4.3591   5.0538
+angle cl   na   cl      48.7 122.80 ! SOURCE0    1
+angle cl   na   nc      55.7 119.36 ! SOURCE3    4   1.4294   1.7128
+angle cl   na   nd      55.7 119.36 ! SOURCE3    4  same as cl-na-nc
+angle cl   na   os      58.6 106.58 ! SOURCE3    1   0.0000   0.0000
+angle cl   na   p2      68.7 121.29 ! SOURCE0    1
+angle cl   na   pc      69.2 120.51 ! SOURCE3    3   2.0728   2.1985
+angle cl   na   pd      69.2 120.51 ! SOURCE3    3  same as cl-na-pc
+angle cl   na   ss      56.7 111.91 ! SOURCE3    1   0.0000   0.0000
+angle f    na   f       62.2 120.20 ! SOURCE0    1
+angle f    na   nc      66.6 118.05 ! SOURCE3    4   1.5336   1.7931
+angle f    na   nd      66.6 118.05 ! SOURCE3    4  same as f -na-nc
+angle f    na   os      69.2 103.86 ! SOURCE3    1   0.0000   0.0000
+angle f    na   p2      75.5 119.95 ! SOURCE0    1
+angle f    na   pc      76.4 119.10 ! SOURCE3    3   2.2596   2.3967
+angle f    na   pd      76.4 119.10 ! SOURCE3    3  same as f -na-pc
+angle f    na   ss      63.3 108.01 ! SOURCE3    1   0.0000   0.0000
+angle hn   na   hn      39.8 116.80 ! SOURCE0    1
+angle hn   na   nc      50.0 119.61 ! SOURCE3   16   1.4674   1.8079
+angle hn   na   nd      50.0 119.61 ! SOURCE3   16   1.4674   1.8079
+angle hn   na   os      51.4 101.41 ! SOURCE3    7   2.1205   3.0814
+angle hn   na   p2      51.0 122.52 ! SOURCE0    1
+angle hn   na   pc      51.8 121.48 ! SOURCE3    3   2.7676   2.9355
+angle hn   na   pd      51.8 121.48 ! SOURCE3    3  same as hn-na-pc
+angle hn   na   ss      42.2 113.95 ! SOURCE3    1   0.0000   0.0000
+angle i    na   i       58.3 124.20 ! SOURCE0    1
+angle i    na   nc      56.9 120.03 ! SOURCE3    4   1.6724   2.0032
+angle i    na   nd      56.9 120.03 ! SOURCE3    4  same as i -na-nc
+angle i    na   os      59.9 109.91 ! SOURCE3    1   0.0000   0.0000
+angle i    na   p2      73.4 122.28 ! SOURCE0    1
+angle i    na   pc      73.8 121.40 ! SOURCE3    3   2.3347   2.4763
+angle i    na   pd      73.8 121.40 ! SOURCE3    3  same as i -na-pc
+angle i    na   ss      59.0 118.40 ! SOURCE3    1   0.0000   0.0000
+angle n    na   n       67.8 123.80 ! SOURCE0    1
+angle n    na   nc      69.6 119.85 ! SOURCE3    4   1.3529   1.6156
+angle n    na   nd      69.6 119.85 ! SOURCE3    4  same as n -na-nc
+angle n    na   os      72.3 104.71 ! SOURCE3    1   0.0000   0.0000
+angle n    na   p2      78.5 121.35 ! SOURCE0    1
+angle n    na   pc      79.3 120.64 ! SOURCE3    3   1.9015   2.0168
+angle n    na   pd      79.3 120.64 ! SOURCE3    3  same as n -na-pc
+angle n    na   ss      63.8 116.10 ! SOURCE3    1   0.0000   0.0000
+angle n2   na   n2      68.6 116.71 ! SOURCE0    1
+angle n2   na   nc      68.9 119.96 ! SOURCE3    4   3.7508   4.5041
+angle n2   na   nd      68.9 119.96 ! SOURCE3    4  same as n2-na-nc
+angle n2   na   os      69.6 111.53 ! SOURCE3    1   0.0000   0.0000
+angle n2   na   p2      77.1 124.88 ! SOURCE0    1
+angle n2   na   pc      78.2 123.18 ! SOURCE3    3   4.5205   4.7947
+angle n2   na   pd      78.2 123.18 ! SOURCE3    3  same as n2-na-pc
+angle n2   na   ss      61.4 124.64 ! SOURCE3    1   0.0000   0.0000
+angle n3   na   n3      65.8 124.00 ! SOURCE0    1
+angle n4   na   n4      68.6 111.70 ! SOURCE0    1
+angle n4   na   nc      69.1 116.44 ! SOURCE3    4   3.4363   3.6604
+angle n4   na   nd      69.1 116.44 ! SOURCE3    4  same as n4-na-nc
+angle n4   na   os      71.6 102.97 ! SOURCE3    1   0.0000   0.0000
+angle n4   na   p2      77.0 123.56 ! SOURCE0    1
+angle n4   na   pc      78.1 121.98 ! SOURCE3    3   4.2317   4.4884
+angle n4   na   pd      78.1 121.98 ! SOURCE3    3  same as n4-na-pc
+angle na   na   na      66.8 123.60 ! SOURCE0    1
+angle na   na   nc      69.1 119.64 ! SOURCE3    4   1.4391   1.6920
+angle na   na   nd      69.1 119.64 ! SOURCE3    4  same as na-na-nc
+angle na   na   os      70.2 109.47 ! SOURCE3    1   0.0000   0.0000
+angle na   na   p2      78.1 121.72 ! SOURCE0    1
+angle na   na   pc      78.9 120.91 ! SOURCE3    3   2.1716   2.3033
+angle na   na   pd      78.9 120.91 ! SOURCE3    3  same as na-na-pc
+angle na   na   ss      63.5 116.50 ! SOURCE3    1   0.0000   0.0000
+angle nc   na   nc      71.2 117.08 ! SOURCE3   31   1.3370   1.8121
+angle nd   na   nd      71.2 117.08 ! SOURCE3   31   1.3370   1.8121
+angle nc   na   nh      68.8 120.55 ! SOURCE3    8   0.9372   1.1436
+angle nd   na   nh      68.8 120.55 ! SOURCE3    8  same as nc-na-nh
+angle nc   na   no      68.2 119.15 ! SOURCE3    4   1.3563   1.6049
+angle nd   na   no      68.2 119.15 ! SOURCE3    4  same as nc-na-no
+angle nc   na   o       72.0 122.79 ! SOURCE3    6   0.9658   1.3154
+angle nd   na   o       72.0 122.79 ! SOURCE3    6  same as nc-na-o
+angle nc   na   oh      69.7 119.22 ! SOURCE3    4   1.4515   1.7201
+angle nd   na   oh      69.7 119.22 ! SOURCE3    4  same as nc-na-oh
+angle nc   na   os      68.3 119.65 ! SOURCE3    4   1.2975   1.5019
+angle nd   na   os      68.3 119.65 ! SOURCE3    4  same as nc-na-os
+angle nc   na   p2      79.2 119.99 ! SOURCE3    4   3.1175   3.6009
+angle nd   na   p2      79.2 119.99 ! SOURCE3    4  same as nc-na-p2
+angle nc   na   p3      78.7 120.07 ! SOURCE3    4   3.2166   3.7188
+angle nd   na   p3      78.7 120.07 ! SOURCE3    4  same as nc-na-p3
+angle nc   na   p4      86.2 119.77 ! SOURCE3    3   0.3532   0.3747
+angle nd   na   p4      86.2 119.77 ! SOURCE3    3  same as nc-na-p4
+angle nc   na   p5      80.8 118.95 ! SOURCE3    4   2.8603   3.1194
+angle nd   na   p5      80.8 118.95 ! SOURCE3    4  same as nc-na-p5
+angle nc   na   pc      80.3 118.66 ! SOURCE3   27   1.2284   1.5082
+angle nd   na   pd      80.3 118.66 ! SOURCE3   27  same as nc-na-pc
+angle nc   na   s       61.6 122.26 ! SOURCE3    4   0.7754   0.9173
+angle nd   na   s       61.6 122.26 ! SOURCE3    4  same as nc-na-s
+angle nc   na   s4      61.5 119.20 ! SOURCE3    4   1.9615   2.3841
+angle nd   na   s4      61.5 119.20 ! SOURCE3    4  same as nc-na-s4
+angle nc   na   s6      63.4 119.24 ! SOURCE3    4   1.8561   2.2262
+angle nd   na   s6      63.4 119.24 ! SOURCE3    4  same as nc-na-s6
+angle nc   na   sh      63.3 120.50 ! SOURCE3    4   1.2479   1.5016
+angle nd   na   sh      63.3 120.50 ! SOURCE3    4  same as nc-na-sh
+angle nc   na   ss      62.9 120.50 ! SOURCE3    4   1.2895   1.5615
+angle nd   na   ss      62.9 120.50 ! SOURCE3    4  same as nc-na-ss
+angle nh   na   nh      66.8 123.60 ! SOURCE0    1
+angle nh   na   os      69.6 111.37 ! SOURCE3    1   0.0000   0.0000
+angle nh   na   p2      78.3 120.86 ! SOURCE0    1
+angle nh   na   pc      79.1 120.38 ! SOURCE3    6   1.2708   1.3513
+angle nh   na   pd      79.1 120.38 ! SOURCE3    6  same as nh-na-pc
+angle nh   na   ss      64.7 112.35 ! SOURCE3    2   5.2951   5.2951
+angle no   na   no      65.2 122.80 ! SOURCE0    1
+angle no   na   os      70.3 106.55 ! SOURCE3    1   0.0000   0.0000
+angle no   na   pc      78.6 120.11 ! SOURCE3    3   1.9631   2.0821
+angle no   na   pd      78.6 120.11 ! SOURCE3    3  same as no-na-pc
+angle no   na   ss      63.5 114.95 ! SOURCE3    1   0.0000   0.0000
+angle o    na   o       74.0 126.20 ! SOURCE0    1
+angle o    na   os      70.8 118.39 ! SOURCE3    1   0.0000   0.0000
+angle o    na   p2      79.5 122.80 ! SOURCE0    1
+angle o    na   pc      80.4 122.34 ! SOURCE3    3   1.2169   1.2908
+angle o    na   pd      80.4 122.34 ! SOURCE3    3  same as o -na-pc
+angle oh   na   oh      68.1 122.20 ! SOURCE0    1
+angle oh   na   p2      78.9 120.76 ! SOURCE0    1
+angle oh   na   pc      79.7 119.99 ! SOURCE3    3   2.0491   2.1734
+angle oh   na   pd      79.7 119.99 ! SOURCE3    3  same as oh-na-pc
+angle oh   na   ss      64.9 113.04 ! SOURCE3    1   0.0000   0.0000
+angle os   na   os      71.3 104.45 ! SOURCE3    2   0.0983   0.0983
+angle os   na   p2      79.2 117.86 ! SOURCE3    1   0.0000   0.0000
+angle os   na   p3      83.6 104.70 ! SOURCE3    1   0.0000   0.0000
+angle os   na   p5      82.6 111.41 ! SOURCE3    1   0.0000   0.0000
+angle os   na   pc      79.1 119.91 ! SOURCE3    3   1.7915   1.9002
+angle os   na   pd      79.1 119.91 ! SOURCE3    3  same as os-na-pc
+angle os   na   s4      64.8 105.88 ! SOURCE3    2   0.0000   0.0000
+angle os   na   s6      64.8 112.00 ! SOURCE3    2   0.0000   0.0000
+angle os   na   ss      65.3 109.64 ! SOURCE3    3   3.9028   4.1395
+angle p2   na   p2      96.6 120.91 ! SOURCE0    1
+angle p2   na   p3      94.7 124.80 ! SOURCE0    1
+angle p2   na   p5      96.3 123.99 ! SOURCE0    1
+angle p2   na   pc      97.2 120.72 ! SOURCE3    3   0.2270   0.2407
+angle p2   na   pd      97.2 120.72 ! SOURCE3    3  same as p2-na-pc
+angle p2   na   s       75.7 121.85 ! SOURCE0    1
+angle p2   na   s4      74.9 122.47 ! SOURCE0    1
+angle p2   na   s6      76.3 122.50 ! SOURCE0    1
+angle p2   na   sh      76.7 121.75 ! SOURCE0    1
+angle p2   na   ss      76.4 121.88 ! SOURCE0    1
+angle p3   na   p3      93.7 126.60 ! SOURCE0    1
+angle p3   na   pc      95.8 123.32 ! SOURCE3    3   3.9391   4.1781
+angle p3   na   pd      95.8 123.32 ! SOURCE3    3  same as p3-na-pc
+angle p5   na   p5      97.1 124.60 ! SOURCE0    1
+angle p5   na   pc      97.3 122.69 ! SOURCE3    3   3.4637   3.6738
+angle p5   na   pd      97.3 122.69 ! SOURCE3    3  same as p5-na-pc
+angle p5   na   ss      78.2 118.52 ! SOURCE3    1   0.0000   0.0000
+angle pc   na   pc      97.6 120.78 ! SOURCE3   27   1.2682   1.6457
+angle pd   na   pd      97.6 120.78 ! SOURCE3   27  same as pc-na-pc
+angle pc   na   s       76.2 121.47 ! SOURCE3    3   1.0058   1.0668
+angle pd   na   s       76.2 121.47 ! SOURCE3    3  same as pc-na-s
+angle pc   na   s4      75.5 121.51 ! SOURCE3    3   2.5684   2.7242
+angle pd   na   s4      75.5 121.51 ! SOURCE3    3  same as pc-na-s4
+angle pc   na   s6      77.0 121.55 ! SOURCE3    3   2.5517   2.7065
+angle pd   na   s6      77.0 121.55 ! SOURCE3    3  same as pc-na-s6
+angle pc   na   sh      77.3 121.08 ! SOURCE3    3   1.7859   1.8942
+angle pd   na   sh      77.3 121.08 ! SOURCE3    3  same as pc-na-sh
+angle pc   na   ss      77.0 121.20 ! SOURCE3    3   1.8191   1.9295
+angle pd   na   ss      77.0 121.20 ! SOURCE3    3  same as pc-na-ss
+angle py   na   py     122.7 78.25 ! SOURCE3    1   0.0000   0.0000
+angle s    na   s       58.5 126.00 ! SOURCE0    1
+angle s    na   ss      62.5 112.49 ! SOURCE3    1   0.0000   0.0000
+angle s4   na   s4      58.1 124.20 ! SOURCE0    1
+angle s4   na   s6      62.0 112.86 ! SOURCE0    1
+angle s4   na   ss      62.2 111.92 ! SOURCE3    1   0.0000   0.0000
+angle s6   na   s6      60.5 123.20 ! SOURCE0    1
+angle s6   na   ss      61.4 119.10 ! SOURCE3    1   0.0000   0.0000
+angle sh   na   sh      60.4 124.60 ! SOURCE0    1
+angle sh   na   ss      61.6 118.79 ! SOURCE3    1   0.0000   0.0000
+angle ss   na   ss      62.9 113.24 ! SOURCE3    2   6.6084   6.6084
+angle sy   na   sy      60.5 123.20 ! SOURCE0    1
+angle ca   nb   ca      68.6 115.86 ! SOURCE3   46   0.9817   1.1645
+angle ca   nb   cp      68.0 117.04 ! SOURCE3    2   0.0000   0.0000
+angle ca   nb   cq      68.0 117.04 ! SOURCE3    2   0.0000   0.0000
+angle ca   nb   nb      69.4 118.89 ! SOURCE3   10   0.4394   0.6031
+angle nb   nb   nb      70.4 121.04 ! SOURCE3    1   0.0000   0.0000
+angle c    nc   ca      66.6 120.66 ! SOURCE0    1
+angle c    nd   ca      66.6 120.66 ! SOURCE0    1  same as c -nc-ca
+angle c    nc   cd      67.0 119.25 ! SOURCE3    2   1.4095   1.4095
+angle c    nd   cc      67.0 119.25 ! SOURCE3    2  same as c -nc-cd
+angle c3   nc   cd      67.6 109.51 ! SOURCE3    9   5.0602   5.4142
+angle c3   nd   cc      67.6 109.51 ! SOURCE3    9  same as c3-nc-cd
+angle ca   nc   ca      70.7 109.95 ! SOURCE0    1
+angle ca   nd   ca      70.7 109.95 ! SOURCE0    1  same as ca-nc-ca
+angle ca   nc   cd      71.1 108.82 ! SOURCE3   11   4.2211   5.0835
+angle ca   nd   cc      71.1 108.82 ! SOURCE3   11   4.2211   5.0835
+angle ca   nc   n       73.8 104.69 ! SOURCE0    1
+angle ca   nd   n       73.8 104.69 ! SOURCE0    1  same as ca-nc-n
+angle ca   nc   na      74.1 104.16 ! SOURCE0    1
+angle ca   nd   na      74.1 104.16 ! SOURCE0    1  same as ca-nc-na
+angle ca   nc   os      73.1 104.34 ! SOURCE3    1   0.0000   0.0000
+angle ca   nd   os      73.1 104.34 ! SOURCE3    1  same as ca-nc-os
+angle ca   nc   ss      67.8 116.29 ! SOURCE0    1
+angle ca   nd   ss      67.8 116.29 ! SOURCE0    1  same as ca-nc-ss
+angle cc   nc   cc      68.6 110.19 ! SOURCE3    1   0.0000   0.0000
+angle cd   nd   cd      68.6 110.19 ! SOURCE3    1  same as cc-nc-cc
+angle cc   nc   cd      70.5 107.47 ! SOURCE3   26   4.9214   5.4053
+angle cc   nd   cd      70.5 107.47 ! SOURCE3   26   4.9214   5.4053
+angle cc   nc   na      73.4 102.97 ! SOURCE3    1   0.0000   0.0000
+angle cd   nd   na      73.4 102.97 ! SOURCE3    1  same as cc-nc-na
+angle cc   nc   nd      72.5 107.94 ! SOURCE3    6   1.2473   1.4052
+angle cd   nd   nc      72.5 107.94 ! SOURCE3    6   1.2473   1.4052
+angle cd   nc   n       74.0 104.15 ! SOURCE3    4   1.8118   2.1512
+angle cc   nd   n       74.0 104.15 ! SOURCE3    4  same as cd-nc-n
+angle cd   nc   na      74.2 103.73 ! SOURCE3  122   1.9430   2.3292
+angle cc   nd   na      74.2 103.73 ! SOURCE3  122   1.9430   2.3292
+angle cd   nc   nc      71.4 109.51 ! SOURCE3   15   3.8384   5.1636
+angle cc   nd   nd      71.4 109.51 ! SOURCE3   15   3.8384   5.1636
+angle cd   nc   os      72.2 106.99 ! SOURCE3   10   4.4186   4.6698
+angle cc   nd   os      72.2 106.99 ! SOURCE3   10   4.4186   4.6698
+angle cd   nc   ss      70.3 108.50 ! SOURCE3    4   0.6893   0.7311
+angle cc   nd   ss      70.3 108.50 ! SOURCE3    4   0.6893   0.7311
+angle na   nc   nd      76.0 105.47 ! SOURCE3    6   0.4926   0.6349
+angle na   nd   nc      76.0 105.47 ! SOURCE3    6   0.4926   0.6349
+angle nd   nc   os      74.4 107.22 ! SOURCE3    3   0.4201   0.4707
+angle nc   nd   os      74.4 107.22 ! SOURCE3    3  same as nd-nc-os
+angle nc   nd   nd      73.2 111.15 ! SOURCE3    3   0.4213   0.4528
+angle nc   nc   nd      73.2 111.15 ! SOURCE3    3  same as nc-nd-nd
+angle c    ne   c2      67.4 118.53 ! SOURCE3    3   3.0224   3.2058
+angle c    nf   c2      67.4 118.53 ! SOURCE3    3  same as c -ne-c2
+angle c1   ne   ca      62.9 137.74 ! SOURCE3    1   0.0000   0.0000
+angle c1   nf   ca      62.9 137.74 ! SOURCE3    1  same as c1-ne-ca
+angle c1   ne   cg      64.4 140.00 ! SOURCE2    1   0.0000   0.0000
+angle c1   nf   ch      64.4 140.00 ! SOURCE2    1  same as c1-ne-cg
+angle c2   ne   ca      66.7 118.67 ! SOURCE3    2   0.7082   0.7082
+angle c2   nf   ca      66.7 118.67 ! SOURCE3    2  same as c2-ne-ca
+angle c2   ne   ce      67.3 118.17 ! SOURCE3    3   1.1667   1.2374
+angle c2   nf   cf      67.3 118.17 ! SOURCE3    3  same as c2-ne-ce
+angle c2   ne   n2      74.6 113.31 ! SOURCE0    1
+angle c2   nf   n2      74.6 113.31 ! SOURCE0    1  same as c2-ne-n2
+angle c2   ne   ne      69.2 110.86 ! SOURCE3    7   3.2451   4.5874
+angle c2   nf   nf      69.2 110.86 ! SOURCE3    7  same as c2-ne-ne
+angle c2   ne   p2      80.8 134.03 ! SOURCE0    1
+angle c2   nf   p2      80.8 134.03 ! SOURCE0    1  same as c2-ne-p2
+angle c2   ne   pe      79.2 120.52 ! SOURCE3    8   6.9715   8.1381
+angle c2   nf   pf      79.2 120.52 ! SOURCE3    8  same as c2-ne-pe
+angle c2   ne   px      80.6 117.75 ! SOURCE3    5   0.8192   0.8581
+angle c2   nf   px      80.6 117.75 ! SOURCE3    5  same as c2-ne-px
+angle c2   ne   py      80.1 117.04 ! SOURCE3    3   1.3574   1.4398
+angle c2   nf   py      80.1 117.04 ! SOURCE3    3  same as c2-ne-py
+angle c2   ne   sx      61.0 111.98 ! SOURCE3    3   0.3856   0.4090
+angle c2   nf   sx      61.0 111.98 ! SOURCE3    3  same as c2-ne-sx
+angle c2   ne   sy      62.9 114.81 ! SOURCE3    3   1.2186   1.2925
+angle c2   nf   sy      62.9 114.81 ! SOURCE3    3  same as c2-ne-sy
+angle ca   ne   n2      70.0 113.11 ! SOURCE3    2   0.3839   0.3839
+angle ca   nf   n2      70.0 113.11 ! SOURCE3    2  same as ca-ne-n2
+angle ca   ne   nf      69.9 114.72 ! SOURCE3    2   0.0000   0.0000
+angle ca   nf   ne      69.9 114.72 ! SOURCE3    2   0.0000   0.0000
+angle ca   ne   o       71.1 113.96 ! SOURCE3    3   0.9256   1.1253
+angle ca   nf   o       71.1 113.96 ! SOURCE3    3  same as ca-ne-o
+angle ca   ne   p2      83.1 118.09 ! SOURCE3    1   0.0000   0.0000
+angle ca   nf   p2      83.1 118.09 ! SOURCE3    1  same as ca-ne-p2
+angle ca   ne   s       65.8 120.11 ! SOURCE3    1   0.0000   0.0000
+angle ca   nf   s       65.8 120.11 ! SOURCE3    1  same as ca-ne-s
+angle ce   ne   n2      71.2 111.19 ! SOURCE3    1   0.0000   0.0000
+angle cf   nf   n2      71.2 111.19 ! SOURCE3    1  same as ce-ne-n2
+angle ce   ne   o       72.3 112.16 ! SOURCE3    1   0.0000   0.0000
+angle cf   nf   o       72.3 112.16 ! SOURCE3    1  same as ce-ne-o
+angle ce   ne   p2      83.8 117.02 ! SOURCE3    1   0.0000   0.0000
+angle cf   nf   p2      83.8 117.02 ! SOURCE3    1  same as ce-ne-p2
+angle ce   ne   s       67.1 116.28 ! SOURCE3    1   0.0000   0.0000
+angle cf   nf   s       67.1 116.28 ! SOURCE3    1  same as ce-ne-s
+angle cg   ne   n1      70.6 120.20 ! SOURCE2    1   0.0000   0.0000
+angle ch   nf   n1      70.6 120.20 ! SOURCE2    1  same as cg-ne-n1
+angle cg   ne   n2      72.0 113.39 ! SOURCE3    1   0.0000   0.0000
+angle ch   nf   n2      72.0 113.39 ! SOURCE3    1  same as cg-ne-n2
+angle cg   ne   o       73.2 114.70 ! SOURCE2    1   0.0000   0.0000
+angle ch   nf   o       73.2 114.70 ! SOURCE2    1  same as cg-ne-o
+angle cg   ne   p2      84.0 119.57 ! SOURCE3    1   0.0000   0.0000
+angle ch   nf   p2      84.0 119.57 ! SOURCE3    1  same as cg-ne-p2
+angle cg   ne   s       67.7 117.70 ! SOURCE3    1   0.0000   0.0000
+angle ch   nf   s       67.7 117.70 ! SOURCE3    1  same as cg-ne-s
+angle n2   ne   n2      78.6 107.22 ! SOURCE0    1
+angle n2   nf   n2      78.6 107.22 ! SOURCE0    1  same as n2-ne-n2
+angle n2   ne   ne      70.9 110.72 ! SOURCE3    9   5.6539   6.1488
+angle n2   nf   nf      70.9 110.72 ! SOURCE3    9  same as n2-ne-ne
+angle n2   ne   o       78.1 114.10 ! SOURCE3    1   0.0000   0.0000
+angle n2   nf   o       78.1 114.10 ! SOURCE3    1  same as n2-ne-o
+angle n2   ne   p2      91.6 109.66 ! SOURCE0    1
+angle n2   nf   p2      91.6 109.66 ! SOURCE0    1  same as n2-ne-p2
+angle n2   ne   pe      84.2 112.15 ! SOURCE3    7   5.8874   6.5273
+angle n2   nf   pf      84.2 112.15 ! SOURCE3    7  same as n2-ne-pe
+angle n2   ne   px      83.2 115.97 ! SOURCE3    3   1.8719   1.9854
+angle n2   nf   px      83.2 115.97 ! SOURCE3    3  same as n2-ne-px
+angle n2   ne   py      82.9 114.60 ! SOURCE3    3   2.7587   2.9261
+angle n2   nf   py      82.9 114.60 ! SOURCE3    3  same as n2-ne-py
+angle n2   ne   s       71.3 115.90 ! SOURCE3    1   0.0000   0.0000
+angle n2   nf   s       71.3 115.90 ! SOURCE3    1  same as n2-ne-s
+angle n2   ne   sx      63.8 107.29 ! SOURCE3    1   0.0000   0.0000
+angle n2   nf   sx      63.8 107.29 ! SOURCE3    1  same as n2-ne-sx
+angle n2   ne   sy      65.5 111.21 ! SOURCE3    1   0.0000   0.0000
+angle n2   nf   sy      65.5 111.21 ! SOURCE3    1  same as n2-ne-sy
+angle ne   ne   o       72.3 110.45 ! SOURCE3   10   1.5341   1.8535
+angle nf   nf   o       72.3 110.45 ! SOURCE3   10  same as ne-ne-o
+angle ne   ne   p2      85.3 114.39 ! SOURCE3    6   3.7554   4.0528
+angle nf   nf   p2      85.3 114.39 ! SOURCE3    6  same as ne-ne-p2
+angle ne   ne   s       67.6 115.95 ! SOURCE3    6   3.1034   3.4604
+angle nf   nf   s       67.6 115.95 ! SOURCE3    6  same as ne-ne-s
+angle o    ne   o       76.9 124.09 ! SOURCE3    2   8.7534   8.7534
+angle o    nf   o       76.9 124.09 ! SOURCE3    2  same as o -ne-o
+angle o    ne   pe      78.3 132.32 ! SOURCE3   11  22.7306  23.9559
+angle o    nf   pf      78.3 132.32 ! SOURCE3   11  same as o -ne-pe
+angle o    ne   px      86.1 110.62 ! SOURCE3    1   0.0000   0.0000
+angle o    nf   px      86.1 110.62 ! SOURCE3    1  same as o -ne-px
+angle o    ne   py      85.2 110.79 ! SOURCE3    4   1.6818   1.6818
+angle o    nf   py      85.2 110.79 ! SOURCE3    4  same as o -ne-py
+angle o    ne   s       72.0 117.19 ! SOURCE3    2   0.0225   0.0225
+angle o    nf   s       72.0 117.19 ! SOURCE3    2  same as o -ne-s
+angle o    ne   sx      63.8 108.92 ! SOURCE3    1   0.0000   0.0000
+angle o    nf   sx      63.8 108.92 ! SOURCE3    1  same as o -ne-sx
+angle o    ne   sy      66.1 111.34 ! SOURCE3    1   0.0000   0.0000
+angle o    nf   sy      66.1 111.34 ! SOURCE3    1  same as o -ne-sy
+angle p2   ne   pe     104.6 116.81 ! SOURCE3    1   0.0000   0.0000
+angle p2   nf   pf     104.6 116.81 ! SOURCE3    1  same as p2-ne-pe
+angle p2   ne   px     100.1 128.35 ! SOURCE3    1   0.0000   0.0000
+angle p2   nf   px     100.1 128.35 ! SOURCE3    1  same as p2-ne-px
+angle p2   ne   py     101.4 123.47 ! SOURCE3    1   0.0000   0.0000
+angle p2   nf   py     101.4 123.47 ! SOURCE3    1  same as p2-ne-py
+angle p2   ne   sx      80.5 112.12 ! SOURCE3    1   0.0000   0.0000
+angle p2   nf   sx      80.5 112.12 ! SOURCE3    1  same as p2-ne-sx
+angle p2   ne   sy      81.8 115.73 ! SOURCE3    1   0.0000   0.0000
+angle p2   nf   sy      81.8 115.73 ! SOURCE3    1  same as p2-ne-sy
+angle pe   ne   s       83.5 115.73 ! SOURCE3    1   0.0000   0.0000
+angle pf   nf   s       83.5 115.73 ! SOURCE3    1  same as pe-ne-s
+angle px   ne   s       78.5 131.84 ! SOURCE3    1   0.0000   0.0000
+angle px   nf   s       78.5 131.84 ! SOURCE3    1  same as px-ne-s
+angle py   ne   s       83.1 116.18 ! SOURCE3    4   3.7135   3.7135
+angle py   nf   s       83.1 116.18 ! SOURCE3    4  same as py-ne-s
+angle s    ne   s       68.6 120.87 ! SOURCE3    1   0.0000   0.0000
+angle s    nf   s       68.6 120.87 ! SOURCE3    1  same as s -ne-s
+angle s    ne   sx      63.7 112.96 ! SOURCE3    1   0.0000   0.0000
+angle s    nf   sx      63.7 112.96 ! SOURCE3    1  same as s -ne-sx
+angle s    ne   sy      63.9 119.63 ! SOURCE3    1   0.0000   0.0000
+angle s    nf   sy      63.9 119.63 ! SOURCE3    1  same as s -ne-sy
+angle br   nh   br      67.1 106.27 ! SOURCE0    1
+angle br   nh   ca      62.0 111.88 ! SOURCE3    1   0.0000   0.0000
+angle br   nh   hn      42.1 101.56 ! SOURCE3    1   0.0000   0.0000
+angle c    nh   c       67.0 119.57 ! SOURCE0    1
+angle c    nh   ca      65.6 123.57 ! SOURCE0    1
+angle c    nh   hn      49.5 115.73 ! SOURCE0    1
+angle c    nh   n4      68.0 111.35 ! SOURCE0    1
+angle c1   nh   c1      67.7 116.98 ! SOURCE3    1   0.0000   0.0000
+angle c1   nh   ca      65.9 122.36 ! SOURCE3    3   1.1328   1.2016
+angle c1   nh   hn      49.0 118.07 ! SOURCE3    1   0.0000   0.0000
+angle c2   nh   c2      65.5 124.69 ! SOURCE3    1   0.0000   0.0000
+angle c2   nh   c3      63.2 123.71 ! SOURCE3    8   2.9758   3.5348
+angle c2   nh   ca      65.5 123.66 ! SOURCE3    3   0.9054   0.9603
+angle c2   nh   hn      49.0 117.94 ! SOURCE3   63   3.2395   3.5550
+angle c3   nh   c3      63.5 114.54 ! SOURCE3    4   4.3647   5.3186
+angle c3   nh   ca      64.6 117.77 ! SOURCE3    8   1.6597   1.7521
+angle c3   nh   cc      63.7 121.18 ! SOURCE3    1   0.0000   0.0000
+angle c3   nh   cd      63.7 121.18 ! SOURCE3    1  same as c3-nh-cc
+angle c3   nh   hn      46.5 114.95 ! SOURCE3   19   1.8868   2.4787
+angle c3   nh   n2      67.9 112.35 ! SOURCE3    9   3.0433   4.0058
+angle c3   nh   p2      77.1 123.35 ! SOURCE3    1   0.0000   0.0000
+angle ca   nh   ca      64.3 127.46 ! SOURCE3    2   0.0002   0.0002
+angle ca   nh   cl      57.7 113.15 ! SOURCE3    1   0.0000   0.0000
+angle ca   nh   f       67.9 106.09 ! SOURCE3    3   1.0050   1.0660
+angle ca   nh   hn      49.6 113.79 ! SOURCE3   94   2.6056   3.1958
+angle ca   nh   i       55.5 117.83 ! SOURCE3    1   0.0000   0.0000
+angle ca   nh   n       68.8 112.95 ! SOURCE3    4   0.9096   1.0790
+angle ca   nh   n2      68.1 120.05 ! SOURCE3    8   2.5677   2.6934
+angle ca   nh   n3      68.2 114.21 ! SOURCE3    6   1.6106   2.2412
+angle ca   nh   n4      68.6 108.94 ! SOURCE3    5   0.6400   0.6562
+angle ca   nh   na      68.6 114.54 ! SOURCE3    8   0.6124   0.7807
+angle ca   nh   nh      68.5 114.87 ! SOURCE3    6   2.0206   2.1432
+angle ca   nh   no      67.3 113.92 ! SOURCE3    4   2.5876   2.9561
+angle ca   nh   o       69.6 121.92 ! SOURCE3    2   3.9630   3.9630
+angle ca   nh   oh      68.5 112.80 ! SOURCE3    1   0.0000   0.0000
+angle ca   nh   os      69.3 110.17 ! SOURCE3    3   0.6080   0.6448
+angle ca   nh   p2      78.0 125.27 ! SOURCE3    8   3.8796   5.1798
+angle ca   nh   p3      76.1 125.70 ! SOURCE3    3   5.4491   5.7796
+angle ca   nh   p4      77.4 124.01 ! SOURCE3    3   2.4334   2.5810
+angle ca   nh   p5      78.2 125.61 ! SOURCE3    3   0.4984   0.5287
+angle ca   nh   s       59.4 122.54 ! SOURCE3    3   2.5457   2.7001
+angle ca   nh   s4      62.1 115.62 ! SOURCE3    3   0.3238   0.3434
+angle ca   nh   s6      63.7 115.88 ! SOURCE3    3   0.6984   0.7407
+angle ca   nh   sh      61.8 121.41 ! SOURCE3    1   0.0000   0.0000
+angle ca   nh   ss      61.8 121.50 ! SOURCE3    3   2.4754   2.6255
+angle ca   nh   sy      64.0 117.74 ! SOURCE3    1   0.0000   0.0000
+angle cc   nh   hn      48.9 117.16 ! SOURCE3   11   2.3900   2.6137
+angle cd   nh   hn      48.9 117.16 ! SOURCE3   11   2.3900   2.6137
+angle ce   nh   hn      50.1 114.73 ! SOURCE3    1   0.0000   0.0000
+angle cf   nh   hn      50.1 114.73 ! SOURCE3    1  same as ce-nh-hn
+angle ce   nh   o       68.2 129.43 ! SOURCE3    1   0.0000   0.0000
+angle cf   nh   o       68.2 129.43 ! SOURCE3    1  same as ce-nh-o
+angle cl   nh   cl      54.4 106.60 ! SOURCE0    1
+angle cl   nh   hn      40.1 104.14 ! SOURCE3    1   0.0000   0.0000
+angle f    nh   f       66.9 101.70 ! SOURCE3    1   0.0000   0.0000
+angle f    nh   hn      49.8 101.23 ! SOURCE3    1   0.0000   0.0000
+angle hn   nh   hn      40.1 114.43 ! SOURCE3   56   3.1626   3.8013
+angle hn   nh   i       36.5 107.57 ! SOURCE3    1   0.0000   0.0000
+angle hn   nh   n       51.1 105.95 ! SOURCE3    4   0.4635   0.5421
+angle hn   nh   n2      50.7 115.14 ! SOURCE3   21   2.5438   3.0032
+angle hn   nh   n3      50.1 109.12 ! SOURCE3    5   2.1577   2.3680
+angle hn   nh   n4      49.7 104.40 ! SOURCE3    3   0.4767   0.5056
+angle hn   nh   na      50.9 107.91 ! SOURCE3   26   1.2007   1.5528
+angle hn   nh   nh      50.9 108.08 ! SOURCE3    4   3.3278   3.3278
+angle hn   nh   no      49.9 104.71 ! SOURCE3    2   1.4480   1.4480
+angle hn   nh   o       53.0 116.45 ! SOURCE3    2   0.6063   0.6063
+angle hn   nh   oh      51.1 103.15 ! SOURCE3    1   0.0000   0.0000
+angle hn   nh   os      50.7 104.76 ! SOURCE3    3   0.4604   0.4883
+angle hn   nh   p2      54.4 118.18 ! SOURCE3   21   3.2394   3.6927
+angle hn   nh   p3      53.1 116.19 ! SOURCE3    3   2.8792   3.0539
+angle hn   nh   p4      54.7 112.60 ! SOURCE3    3   0.7766   0.8237
+angle hn   nh   p5      55.3 115.26 ! SOURCE3    3   0.8644   0.9168
+angle hn   nh   s       40.8 114.37 ! SOURCE3    1   0.0000   0.0000
+angle hn   nh   s4      43.1 107.48 ! SOURCE3    3   1.3162   1.3960
+angle hn   nh   s6      44.8 106.95 ! SOURCE3    3   0.5772   0.6122
+angle hn   nh   sh      43.3 112.25 ! SOURCE3    1   0.0000   0.0000
+angle hn   nh   ss      43.0 113.89 ! SOURCE3    3   1.3228   1.4030
+angle hn   nh   sy      45.2 109.10 ! SOURCE3    1   0.0000   0.0000
+angle i    nh   i       59.8 115.82 ! SOURCE0    1
+angle n    nh   o       71.8 115.63 ! SOURCE0    1
+angle n2   nh   n2      70.8 117.50 ! SOURCE3    2   1.1907   1.1907
+angle n2   nh   n3      69.6 115.54 ! SOURCE3    1   0.0000   0.0000
+angle n2   nh   o       70.4 126.06 ! SOURCE3    1   0.0000   0.0000
+angle n3   nh   n3      69.5 110.98 ! SOURCE3    1   0.0000   0.0000
+angle n4   nh   n4      68.2 108.36 ! SOURCE3    1   0.0000   0.0000
+angle na   nh   na      70.1 112.01 ! SOURCE3    1   0.0000   0.0000
+angle nh   nh   nh      70.1 112.23 ! SOURCE3    1   0.0000   0.0000
+angle no   nh   no      68.6 108.55 ! SOURCE3    1   0.0000   0.0000
+angle o    nh   o       72.2 128.06 ! SOURCE0    1
+angle oh   nh   oh      70.9 106.27 ! SOURCE0    1
+angle os   nh   os      71.2 105.27 ! SOURCE0    1
+angle p2   nh   p2      98.1 127.33 ! SOURCE3    2   2.7857   2.7857
+angle p3   nh   p3      96.0 125.08 ! SOURCE0    1
+angle p5   nh   p5     104.7 112.76 ! SOURCE0    1
+angle s    nh   s       59.7 118.73 ! SOURCE3    1   0.0000   0.0000
+angle s4   nh   s4      62.6 112.39 ! SOURCE0    1
+angle s6   nh   s6      62.5 120.27 ! SOURCE0    1
+angle sh   nh   sh      62.3 119.00 ! SOURCE0    1
+angle ss   nh   ss      62.2 119.25 ! SOURCE0    1
+angle br   no   o       58.5 113.19 ! SOURCE3    2   0.0000   0.0000
+angle c    no   o       67.1 115.26 ! SOURCE0    1
+angle c1   no   o       71.3 116.63 ! SOURCE3    6   0.0000   0.0000
+angle c2   no   o       69.3 116.87 ! SOURCE3    8   0.4199   0.4200
+angle c3   no   o       67.0 116.56 ! SOURCE3    6   0.3733   0.3959
+angle ca   no   o       68.7 118.10 ! SOURCE3   10   0.8958   1.1524
+angle cl   no   o       57.3 115.08 ! SOURCE3    2   0.0000   0.0000
+angle hn   no   o       55.3 115.49 ! SOURCE3    2   0.0000   0.0000
+angle i    no   o       54.6 116.31 ! SOURCE3    2   0.0000   0.0000
+angle n    no   o       71.7 115.41 ! SOURCE3    8   0.3612   0.3748
+angle n2   no   o       65.8 115.10 ! SOURCE2    2   2.4000   2.4000
+angle n3   no   o       72.0 115.56 ! SOURCE3    6   0.6060   0.6427
+angle n4   no   o       72.9 109.00 ! SOURCE3    2   0.0000   0.0000
+angle na   no   o       72.2 115.49 ! SOURCE3   18   0.4677   0.5640
+angle nh   no   o       71.6 115.71 ! SOURCE3    8   0.4808   0.4811
+angle no   no   o       59.9 112.38 ! SOURCE3    4   0.0000   0.0000
+angle o    no   o       76.4 127.55 ! SOURCE3   89   2.7577   3.8475
+angle o    no   oh      74.1 114.70 ! SOURCE3    2   0.0000   0.0000
+angle o    no   os      71.8 114.01 ! SOURCE3    8   0.9760   0.9778
+angle o    no   p2      82.1 117.38 ! SOURCE3   20   0.6173   0.8083
+angle o    no   p3      77.7 116.78 ! SOURCE3    6   0.4648   0.4929
+angle o    no   p4      76.7 116.64 ! SOURCE3    6   0.0084   0.0089
+angle o    no   p5      78.2 116.69 ! SOURCE3    8   0.4507   0.4507
+angle o    no   s       64.0 119.81 ! SOURCE3    4   0.0042   0.0042
+angle o    no   s4      57.1 114.49 ! SOURCE3    6   0.5349   0.5674
+angle o    no   s6      57.8 114.39 ! SOURCE3    6   0.7836   0.8311
+angle o    no   sh      62.9 116.10 ! SOURCE3    2   0.0000   0.0000
+angle o    no   ss      62.3 115.58 ! SOURCE3    6   0.5525   0.5860
+angle br   oh   ho      42.2 101.60 ! SOURCE3    1   0.0000   0.0000
+angle c    oh   ho      51.2 107.37 ! SOURCE3   34   1.4300   1.6830
+angle c1   oh   ho      50.2 108.76 ! SOURCE3    1   0.0000   0.0000
+angle c2   oh   ho      49.9 108.98 ! SOURCE3    6   1.6080   2.2379
+angle c3   oh   ho      47.1 108.16 ! SOURCE3   42   1.1979   1.3034
+angle ca   oh   ho      48.9 109.47 ! SOURCE3    7   0.9655   1.0405
+angle cc   oh   ho      49.7 107.72 ! SOURCE3    3   1.0115   1.0115
+angle cd   oh   ho      49.7 107.72 ! SOURCE3    3   1.0115   1.0115
+angle cl   oh   ho      40.5 102.40 ! SOURCE2    1   0.0000   0.0000
+angle cx   oh   ho      49.6 106.17 ! SOURCE3    3   0.0608   0.0644
+angle cy   oh   ho      47.7 106.88 ! SOURCE3    2   0.0000   0.0000
+angle f    oh   ho      48.5 96.80 ! SOURCE2    1   0.0000   0.0000
+angle ho   oh   ho      41.9 104.80 ! SOURCE2    1   0.0000   0.0000
+angle ho   oh   i       35.7 107.98 ! SOURCE3    2   0.0000   0.0000
+angle ho   oh   n       50.5 101.03 ! SOURCE3    6   1.1335   1.4086
+angle ho   oh   n2      50.6 102.74 ! SOURCE3    9   1.5684   2.1286
+angle ho   oh   n3      50.6 102.33 ! SOURCE3    5   0.9483   1.2591
+angle ho   oh   n4      49.4 106.63 ! SOURCE3    3   0.2612   0.2770
+angle ho   oh   na      50.2 103.71 ! SOURCE3    9   1.0282   1.2590
+angle ho   oh   nh      49.3 101.78 ! SOURCE3    1   0.0000   0.0000
+angle ho   oh   no      50.3 102.17 ! SOURCE3    1   0.0000   0.0000
+angle ho   oh   o       47.3 100.87 ! SOURCE0    1
+angle ho   oh   oh      49.3 98.72 ! SOURCE3    2   0.0000   0.0000
+angle ho   oh   os      49.4 98.77 ! SOURCE3    3   0.6106   0.6830
+angle ho   oh   p2      55.9 109.45 ! SOURCE3    8   2.2353   3.3491
+angle ho   oh   p3      53.8 110.64 ! SOURCE3    3   0.4894   0.5191
+angle ho   oh   p4      55.3 110.19 ! SOURCE3    4   0.2049   0.2372
+angle ho   oh   p5      55.9 110.14 ! SOURCE3   92   2.0406   3.8033
+angle ho   oh   py      56.2 110.73 ! SOURCE3   79   1.5384   1.7835
+angle ho   oh   s       40.9 100.15 ! SOURCE3    2   0.0000   0.0000
+angle ho   oh   s4      43.6 106.31 ! SOURCE3    3   1.2438   1.3192
+angle ho   oh   s6      45.5 109.20 ! SOURCE3   13   0.1484   0.1856
+angle ho   oh   sh      43.0 106.24 ! SOURCE3    2   0.0661   0.0661
+angle ho   oh   ss      43.1 107.06 ! SOURCE3    4   0.9702   0.9967
+angle ho   oh   sy      44.7 106.11 ! SOURCE3    4   0.9258   1.1482
+angle br   os   br      65.1 110.63 ! SOURCE3    1   0.0000   0.0000
+angle c    os   c       64.0 122.65 ! SOURCE3    5   3.3752   3.5418
+angle c    os   c2      68.2 106.90 ! SOURCE2    1   0.0000   0.0000
+angle c    os   c3      63.6 115.14 ! SOURCE3   17   1.5416   1.8967
+angle c    os   ca      64.6 117.61 ! SOURCE3    2   2.1561   2.1561
+angle c    os   cc      64.2 119.62 ! SOURCE3    5   5.2421   6.0675
+angle c    os   cd      64.2 119.62 ! SOURCE3    5   5.2421   6.0675
+angle c    os   cy      70.9 91.10 ! SOURCE3    2   0.0155   0.0155
+angle c    os   oh      66.2 110.50 ! SOURCE3    1   0.0000   0.0000
+angle c    os   sy      61.9 113.49 ! SOURCE3    1   0.0000   0.0000
+angle c1   os   c1      67.0 115.02 ! SOURCE3    1   0.0000   0.0000
+angle c1   os   c3      64.5 113.39 ! SOURCE3    1   0.0000   0.0000
+angle c2   os   c2      66.0 113.14 ! SOURCE3    6   1.9475   2.1932
+angle c2   os   c3      64.2 112.09 ! SOURCE3    7   3.0925   4.1809
+angle c2   os   ca      65.4 113.59 ! SOURCE3    1   0.0000   0.0000
+angle c2   os   n2      64.9 118.13 ! SOURCE3    1   0.0000   0.0000
+angle c2   os   na      68.2 103.85 ! SOURCE3    4   0.6297   0.6297
+angle c2   os   os      68.3 102.77 ! SOURCE3    1   0.0000   0.0000
+angle c2   os   ss      63.1 108.13 ! SOURCE3    1   0.0000   0.0000
+angle c3   os   c3      62.1 113.41 ! SOURCE3   50   2.5452   3.0289
+angle c3   os   ca      62.4 117.60 ! SOURCE3    8   2.5372   2.9174
+angle c3   os   cc      63.1 115.18 ! SOURCE3    8   3.4925   3.8476
+angle c3   os   cd      63.1 115.18 ! SOURCE3    8   3.4925   3.8476
+angle c3   os   cl      56.2 110.50 ! SOURCE2    1   0.0000   0.0000
+angle c3   os   i       54.9 113.70 ! SOURCE3    1   0.0000   0.0000
+angle c3   os   n       65.3 108.57 ! SOURCE3    4   0.5159   0.6121
+angle c3   os   n2      65.9 108.12 ! SOURCE3    7   0.2643   0.3048
+angle c3   os   n3      65.3 107.69 ! SOURCE3    3   0.3554   0.3770
+angle c3   os   n4      64.9 110.50 ! SOURCE3    3   0.5116   0.5426
+angle c3   os   na      64.5 109.91 ! SOURCE3    9   1.1472   1.8268
+angle c3   os   nc      64.8 112.73 ! SOURCE3    2   1.0358   1.0358
+angle c3   os   nd      64.8 112.73 ! SOURCE3    2  same as c3-os-nc
+angle c3   os   nh      65.4 107.26 ! SOURCE3    1   0.0000   0.0000
+angle c3   os   no      63.3 111.47 ! SOURCE3    2   1.6956   1.6956
+angle c3   os   o       65.6 103.00 ! SOURCE3    1   0.0000   0.0000
+angle c3   os   oh      65.7 105.42 ! SOURCE3    2   0.8087   0.8087
+angle c3   os   os      65.9 105.01 ! SOURCE3    7   0.5292   0.6328
+angle c3   os   p2      80.2 115.47 ! SOURCE3    8   1.7016   2.6374
+angle c3   os   p3      76.5 115.97 ! SOURCE3    3   0.3392   0.3597
+angle c3   os   p4      77.6 117.48 ! SOURCE3    4   0.3849   0.3850
+angle c3   os   p5      78.5 118.00 ! SOURCE3   31   1.0807   1.2882
+angle c3   os   py      78.1 117.80 ! SOURCE3   16   0.9119   0.9654
+angle c3   os   s       59.3 109.55 ! SOURCE3    1   0.0000   0.0000
+angle c3   os   s4      61.3 111.50 ! SOURCE3    6   1.2743   1.4240
+angle c3   os   s6      63.3 115.38 ! SOURCE3    2   2.2498   2.2498
+angle c3   os   sh      61.7 112.82 ! SOURCE3    1   0.0000   0.0000
+angle c3   os   ss      60.7 113.19 ! SOURCE3    3   0.2315   0.2455
+angle ca   os   ca      63.6 118.96 ! SOURCE3    3   1.2676   1.4055
+angle ca   os   cc      66.2 109.77 ! SOURCE3    6   3.8556   5.0326
+angle ca   os   cd      66.2 109.77 ! SOURCE3    6   3.8556   5.0326
+angle ca   os   n3      65.4 112.19 ! SOURCE3    1   0.0000   0.0000
+angle ca   os   na      66.5 108.24 ! SOURCE3    1   0.0000   0.0000
+angle ca   os   nc      66.1 113.68 ! SOURCE0    2
+angle ca   os   nd      66.1 113.68 ! SOURCE0    2
+angle cc   os   cc      66.5 109.17 ! SOURCE3   12   6.7381   7.2529
+angle cd   os   cd      66.5 109.17 ! SOURCE3   12   6.7381   7.2529
+angle cc   os   cd      62.7 122.99 ! SOURCE3    3   0.1438   0.1525
+angle cc   os   na      65.5 111.66 ! SOURCE3   28   3.6412   4.1343
+angle cd   os   na      65.5 111.66 ! SOURCE3   28   3.6412   4.1343
+angle cc   os   nc      68.1 107.23 ! SOURCE3    6   2.1966   2.7507
+angle cd   os   nd      68.1 107.23 ! SOURCE3    6   2.1966   2.7507
+angle cc   os   os      66.3 108.47 ! SOURCE3    2   0.0000   0.0000
+angle cd   os   os      66.3 108.47 ! SOURCE3    2  same as cc-os-os
+angle cc   os   ss      59.9 119.59 ! SOURCE3    1   0.0000   0.0000
+angle cd   os   ss      59.9 119.59 ! SOURCE3    1  same as cc-os-ss
+angle cl   os   cl      52.3 110.76 ! SOURCE3    2   0.0000   0.0000
+angle cx   os   cx      85.7 61.19 ! SOURCE3    2   0.0000   0.0000
+angle cx   os   n       88.4 59.99 ! SOURCE3    1   0.0000   0.0000
+angle cx   os   os      90.4 56.52 ! SOURCE3    2   0.0000   0.0000
+angle cy   os   cy      67.4 93.40 ! SOURCE2    2   1.4000   1.4000
+angle f    os   f       63.9 103.30 ! SOURCE2    1   0.0000   0.0000
+angle f    os   os      63.9 109.50 ! SOURCE2    1   0.0000   0.0000
+angle i    os   i       58.1 115.67 ! SOURCE3    1   0.0000   0.0000
+angle n    os   n       67.2 108.31 ! SOURCE3    1   0.0000   0.0000
+angle n2   os   n2      68.8 106.83 ! SOURCE3    1   0.0000   0.0000
+angle n3   os   n3      67.8 104.88 ! SOURCE3    1   0.0000   0.0000
+angle n4   os   n4      65.7 114.68 ! SOURCE3    1   0.0000   0.0000
+angle na   os   na      66.1 109.59 ! SOURCE3    1   0.0000   0.0000
+angle na   os   ss      64.7 104.34 ! SOURCE3    1   0.0000   0.0000
+angle nc   os   nc      68.2 110.40 ! SOURCE2    1   0.0000   0.0000
+angle nd   os   nd      68.2 110.40 ! SOURCE2    1  same as nc-os-nc
+angle nc   os   ss      63.4 110.97 ! SOURCE3    1   0.0000   0.0000
+angle nd   os   ss      63.4 110.97 ! SOURCE3    1  same as nc-os-ss
+angle nh   os   nh      66.6 108.29 ! SOURCE3    1   0.0000   0.0000
+angle no   os   no      64.0 111.86 ! SOURCE3    1   0.0000   0.0000
+angle o    os   o       62.8 114.68 ! SOURCE3    1   0.0000   0.0000
+angle p2   os   p2     103.6 120.02 ! SOURCE3    1   0.0000   0.0000
+angle p2   os   p5     108.3 107.86 ! SOURCE3    1   0.0000   0.0000
+angle p3   os   p3      96.2 121.22 ! SOURCE3    1   0.0000   0.0000
+angle p3   os   py     105.1 105.58 ! SOURCE3    1   0.0000   0.0000
+angle p5   os   p5      99.2 126.25 ! SOURCE3    1   0.0000   0.0000
+angle s    os   s       57.0 118.08 ! SOURCE3    1   0.0000   0.0000
+angle s4   os   s4      62.1 111.63 ! SOURCE3    1   0.0000   0.0000
+angle s6   os   s6      64.8 119.07 ! SOURCE3    2   0.4318   0.4318
+angle sh   os   sh      61.2 118.95 ! SOURCE3    1   0.0000   0.0000
+angle ss   os   ss      60.8 115.64 ! SOURCE3    1   0.0000   0.0000
+angle br   p2   br      40.7 108.60 ! SOURCE0    1
+angle br   p2   c2      39.4 102.32 ! SOURCE3    2   0.0146   0.0146
+angle br   p2   n2      40.4 103.33 ! SOURCE3    1   0.0000   0.0000
+angle br   p2   o       39.4 110.87 ! SOURCE3    1   0.0000   0.0000
+angle br   p2   p2      50.2 115.46 ! SOURCE3    4   6.8089   7.8622
+angle br   p2   s       40.6 110.52 ! SOURCE3    1   0.0000   0.0000
+angle c    p2   c       38.3 90.10 ! SOURCE0    1
+angle c    p2   c2      38.9 97.30 ! SOURCE0    1
+angle c1   p2   c1      39.2 99.04 ! SOURCE0    1
+angle c1   p2   c2      39.8 101.29 ! SOURCE0    1
+angle c1   p2   n2      41.4 101.79 ! SOURCE0    1
+angle c1   p2   o       41.3 107.62 ! SOURCE0    1
+angle c1   p2   p2      53.3 99.54 ! SOURCE0    1
+angle c1   p2   s       41.0 105.90 ! SOURCE0    1
+angle c2   p2   c2      40.4 104.50 ! SOURCE0    1
+angle c2   p2   c3      38.6 101.90 ! SOURCE3    4   0.0911   0.1132
+angle c2   p2   ca      38.8 101.95 ! SOURCE0    1
+angle c2   p2   cl      35.6 102.72 ! SOURCE3    2   0.0000   0.0000
+angle c2   p2   f       41.6 103.47 ! SOURCE3    2   0.0136   0.0136
+angle c2   p2   hp      30.1 97.19 ! SOURCE3    3   0.0204   0.0216
+angle c2   p2   i       33.9 101.94 ! SOURCE3    2   0.0368   0.0368
+angle c2   p2   n       40.9 103.28 ! SOURCE3    4   2.8675   3.3113
+angle c2   p2   n2      43.2 99.88 ! SOURCE0    1
+angle c2   p2   n3      42.0 101.80 ! SOURCE0    1
+angle c2   p2   n4      39.1 98.26 ! SOURCE3    6   0.1327   0.1522
+angle c2   p2   na      40.5 103.99 ! SOURCE3    8   1.4375   1.6834
+angle c2   p2   nh      41.2 105.17 ! SOURCE3    8   0.6846   0.8263
+angle c2   p2   no      41.9 97.97 ! SOURCE3    3   0.3936   0.4175
+angle c2   p2   o       41.5 115.16 ! SOURCE0    1
+angle c2   p2   oh      42.5 102.89 ! SOURCE3    3   0.7723   0.8191
+angle c2   p2   os      43.3 102.12 ! SOURCE3    4   0.7246   0.8783
+angle c2   p2   p2      54.7 99.56 ! SOURCE0    1
+angle c2   p2   p3      48.1 99.27 ! SOURCE3    4   1.1367   1.1590
+angle c2   p2   p4      48.2 96.94 ! SOURCE0    1
+angle c2   p2   p5      48.0 97.61 ! SOURCE0    1
+angle c2   p2   s       42.2 105.53 ! SOURCE0    1
+angle c2   p2   s4      38.3 95.15 ! SOURCE0    1
+angle c2   p2   s6      38.3 95.51 ! SOURCE0    1
+angle c2   p2   sh      40.2 101.49 ! SOURCE3    3   0.0053   0.0057
+angle c2   p2   ss      40.2 101.81 ! SOURCE3    4   0.5882   0.5883
+angle c3   p2   c3      37.4 99.30 ! SOURCE0    1
+angle c3   p2   n2      40.3 100.82 ! SOURCE3    1   0.0000   0.0000
+angle c3   p2   o       40.1 106.72 ! SOURCE3    1   0.0000   0.0000
+angle c3   p2   os      40.7 101.34 ! SOURCE3    1   0.0000   0.0000
+angle c3   p2   p2      51.8 100.48 ! SOURCE3    1   0.0000   0.0000
+angle c3   p2   s       40.0 105.68 ! SOURCE3    1   0.0000   0.0000
+angle ca   p2   ca      37.6 99.70 ! SOURCE0    1
+angle ca   p2   n       41.7 89.97 ! SOURCE0    1
+angle ca   p2   n2      40.5 100.82 ! SOURCE0    1
+angle ca   p2   na      41.7 89.21 ! SOURCE0    1
+angle ca   p2   o       40.3 106.88 ! SOURCE0    1
+angle ca   p2   s       39.8 107.93 ! SOURCE0    1
+angle ce   p2   o       40.6 107.44 ! SOURCE0    1
+angle cf   p2   o       40.6 107.44 ! SOURCE0    1  same as ce-p2-o
+angle ce   p2   s       40.6 105.54 ! SOURCE0    1
+angle cf   p2   s       40.6 105.54 ! SOURCE0    1  same as ce-p2-s
+angle cl   p2   cl      32.0 108.70 ! SOURCE0    1
+angle cl   p2   n2      36.7 103.38 ! SOURCE3    1   0.0000   0.0000
+angle cl   p2   o       36.0 110.57 ! SOURCE3    1   0.0000   0.0000
+angle cl   p2   p2      47.8 103.11 ! SOURCE3    1   0.0000   0.0000
+angle cl   p2   s       36.6 110.11 ! SOURCE3    1   0.0000   0.0000
+angle f    p2   f       42.2 107.10 ! SOURCE0    1
+angle f    p2   n2      43.6 103.57 ! SOURCE3    1   0.0000   0.0000
+angle f    p2   o       43.8 110.61 ! SOURCE3    1   0.0000   0.0000
+angle f    p2   p2      54.5 103.48 ! SOURCE3    1   0.0000   0.0000
+angle f    p2   s       41.2 114.71 ! SOURCE3    2   5.2794   5.2794
+angle hp   p2   hp      22.8 98.76 ! SOURCE0    1
+angle hp   p2   n1      31.1 95.18 ! SOURCE3    2   1.5708   1.5708
+angle hp   p2   n2      32.1 95.54 ! SOURCE3   19   3.3767   4.7352
+angle hp   p2   ne      31.9 100.10 ! SOURCE3   14   5.3381   6.1290
+angle hp   p2   nf      31.9 100.10 ! SOURCE3   14  same as hp-p2-ne
+angle hp   p2   o       32.0 105.58 ! SOURCE3    1   0.0000   0.0000
+angle hp   p2   p2      38.2 101.88 ! SOURCE3   27  12.2440  12.9535
+angle hp   p2   p4      32.7 94.51 ! SOURCE3    1   0.0000   0.0000
+angle hp   p2   p5      33.7 89.07 ! SOURCE3    1   0.0000   0.0000
+angle hp   p2   pe      37.6 97.25 ! SOURCE3   16   7.5144   8.8916
+angle hp   p2   pf      37.6 97.25 ! SOURCE3   16  same as hp-p2-pe
+angle hp   p2   s       30.3 102.52 ! SOURCE3    1   0.0000   0.0000
+angle hp   p2   s4      26.3 89.99 ! SOURCE3    1   0.0000   0.0000
+angle hp   p2   s6      26.7 88.13 ! SOURCE3    1   0.0000   0.0000
+angle i    p2   i       35.8 104.16 ! SOURCE0    1
+angle i    p2   n2      34.6 101.77 ! SOURCE3    1   0.0000   0.0000
+angle i    p2   o       33.3 109.51 ! SOURCE3    1   0.0000   0.0000
+angle i    p2   p2      46.2 102.63 ! SOURCE3    1   0.0000   0.0000
+angle i    p2   s       35.2 110.60 ! SOURCE3    1   0.0000   0.0000
+angle n    p2   n2      43.6 98.85 ! SOURCE3    1   0.0000   0.0000
+angle n    p2   o       43.5 105.08 ! SOURCE3    1   0.0000   0.0000
+angle n    p2   p2      54.5 102.12 ! SOURCE3    1   0.0000   0.0000
+angle n    p2   s       41.2 112.34 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   n2      45.6 98.00 ! SOURCE0    1
+angle n2   p2   n3      44.1 100.42 ! SOURCE0    1
+angle n2   p2   n4      41.5 93.42 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   na      42.6 102.03 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   nh      43.7 101.87 ! SOURCE3    2   0.8491   0.8491
+angle n2   p2   no      43.6 98.12 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   o       43.5 115.34 ! SOURCE0    1
+angle n2   p2   oh      43.0 109.72 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   os      45.4 102.29 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   p3      49.6 99.51 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   p4      48.5 101.73 ! SOURCE0    1
+angle n2   p2   p5      50.5 93.68 ! SOURCE0    1
+angle n2   p2   s       42.5 112.94 ! SOURCE0    1
+angle n2   p2   s4      38.9 97.83 ! SOURCE0    1
+angle n2   p2   s6      39.0 98.14 ! SOURCE0    1
+angle n2   p2   sh      41.8 100.82 ! SOURCE3    1   0.0000   0.0000
+angle n2   p2   ss      41.6 101.76 ! SOURCE3    1   0.0000   0.0000
+angle n3   p2   n3      42.1 106.30 ! SOURCE0    1
+angle n3   p2   o       44.2 106.83 ! SOURCE0    1
+angle n3   p2   p2      55.8 100.58 ! SOURCE0    1
+angle n3   p2   s       43.2 105.75 ! SOURCE0    1
+angle n4   p2   n4      39.6 88.80 ! SOURCE0    1
+angle n4   p2   o       40.7 101.36 ! SOURCE3    1   0.0000   0.0000
+angle n4   p2   p2      52.7 96.53 ! SOURCE3    1   0.0000   0.0000
+angle n4   p2   s       40.1 104.98 ! SOURCE3    1   0.0000   0.0000
+angle na   p2   na      40.2 106.10 ! SOURCE0    1
+angle na   p2   o       42.7 107.46 ! SOURCE3    1   0.0000   0.0000
+angle na   p2   s       41.8 108.15 ! SOURCE3    1   0.0000   0.0000
+angle ne   p2   o       45.7 107.71 ! SOURCE0    1
+angle nf   p2   o       45.7 107.71 ! SOURCE0    1  same as ne-p2-o
+angle ne   p2   s       44.3 105.50 ! SOURCE0    1
+angle nf   p2   s       44.3 105.50 ! SOURCE0    1  same as ne-p2-s
+angle nh   p2   nh      42.3 104.00 ! SOURCE0    1
+angle nh   p2   o       43.7 108.11 ! SOURCE3    2   0.6773   0.6773
+angle nh   p2   p2      53.8 107.73 ! SOURCE3    3   2.7661   3.1678
+angle nh   p2   s       42.3 109.62 ! SOURCE3    2   1.7725   1.7725
+angle no   p2   no      42.1 98.20 ! SOURCE0    1
+angle no   p2   o       43.4 104.87 ! SOURCE3    1   0.0000   0.0000
+angle no   p2   p2      52.7 108.57 ! SOURCE3    3   7.7424   8.2121
+angle no   p2   s       41.8 109.06 ! SOURCE3    2   5.4074   5.4074
+angle o    p2   o       44.4 119.96 ! SOURCE0    1
+angle o    p2   oh      44.3 110.46 ! SOURCE3    1   0.0000   0.0000
+angle o    p2   os      45.6 108.81 ! SOURCE3    1   0.0000   0.0000
+angle o    p2   p2      54.6 114.23 ! SOURCE0    1
+angle o    p2   p3      48.5 106.69 ! SOURCE3    1   0.0000   0.0000
+angle o    p2   p4      48.4 104.37 ! SOURCE0    1
+angle o    p2   p5      48.4 104.49 ! SOURCE0    1
+angle o    p2   pe      46.7 145.96 ! SOURCE0    1
+angle o    p2   pf      46.7 145.96 ! SOURCE0    1  same as o -p2-pe
+angle o    p2   s       42.8 117.42 ! SOURCE0    1
+angle o    p2   s4      37.7 106.59 ! SOURCE0    1
+angle o    p2   s6      38.1 105.04 ! SOURCE0    1
+angle o    p2   sh      40.8 109.60 ! SOURCE3    1   0.0000   0.0000
+angle o    p2   ss      40.9 109.60 ! SOURCE3    1   0.0000   0.0000
+angle oh   p2   oh      45.0 100.10 ! SOURCE0    1
+angle oh   p2   p2      54.7 107.82 ! SOURCE3    2   2.6708   2.6708
+angle oh   p2   s       43.1 109.75 ! SOURCE3    1   0.0000   0.0000
+angle os   p2   os      47.0 98.30 ! SOURCE0    1
+angle os   p2   p2      57.1 101.46 ! SOURCE3    1   0.0000   0.0000
+angle os   p2   s       43.9 108.47 ! SOURCE3    3   1.6089   1.7065
+angle p2   p2   n2      57.6 97.40 ! SOURCE0    1
+angle p2   p2   p3      64.1 101.73 ! SOURCE3    1   0.0000   0.0000
+angle p2   p2   p4      63.4 101.98 ! SOURCE0    1
+angle p2   p2   p5      64.2 99.33 ! SOURCE0    1
+angle p2   p2   s       54.5 111.28 ! SOURCE0    1
+angle p2   p2   s4      51.5 95.73 ! SOURCE0    1
+angle p2   p2   s6      51.6 95.95 ! SOURCE0    1
+angle p2   p2   sh      50.8 113.94 ! SOURCE3    3   8.0148   8.5009
+angle p3   p2   p3      59.9 101.00 ! SOURCE0    1
+angle p3   p2   s       48.1 113.28 ! SOURCE3    2   6.7035   6.7035
+angle p4   p2   s       49.7 103.89 ! SOURCE0    1
+angle p5   p2   p5      62.8 89.40 ! SOURCE0    1
+angle p5   p2   s       50.4 101.21 ! SOURCE0    1
+angle pe   p2   s       54.5 106.35 ! SOURCE0    1
+angle pf   p2   s       54.5 106.35 ! SOURCE0    1  same as pe-p2-s
+angle s    p2   s       44.2 106.60 ! SOURCE0    1
+angle s    p2   s4      38.9 105.29 ! SOURCE0    1
+angle s    p2   s6      38.7 106.93 ! SOURCE0    1
+angle s    p2   sh      40.8 110.73 ! SOURCE3    2   0.0232   0.0232
+angle s    p2   ss      40.3 114.14 ! SOURCE3    4   5.9162   5.9223
+angle s4   p2   s4      40.0 85.30 ! SOURCE0    1
+angle s6   p2   s6      37.4 98.20 ! SOURCE0    1
+angle sh   p2   sh      41.3 98.50 ! SOURCE0    1
+angle ss   p2   ss      41.6 97.90 ! SOURCE0    1
+angle br   p3   br      41.3 103.54 ! SOURCE3    1   0.0000   0.0000
+angle br   p3   hp      26.9 96.36 ! SOURCE3    4   0.6701   0.6701
+angle c    p3   c       36.5 100.90 ! SOURCE3    1   0.0000   0.0000
+angle c    p3   c3      37.6 97.06 ! SOURCE3    3   1.0833   1.1490
+angle c    p3   hp      27.1 96.55 ! SOURCE3    6   0.4924   0.5223
+angle c    p3   os      43.9 81.32 ! SOURCE3    1   0.0000   0.0000
+angle c1   p3   c1      38.5 100.50 ! SOURCE3    1   0.0000   0.0000
+angle c1   p3   f       40.7 96.90 ! SOURCE2    1   0.0000   0.0000
+angle c1   p3   hp      28.1 97.67 ! SOURCE3    2   0.0000   0.0000
+angle c2   p3   c2      37.3 101.77 ! SOURCE3    3   0.0000   0.0000
+angle c2   p3   hp      27.6 97.85 ! SOURCE3    4   0.0000   0.0000
+angle c3   p3   c3      37.5 99.66 ! SOURCE3   40   0.8383   0.9854
+angle c3   p3   ca      37.3 101.94 ! SOURCE3    2   0.0000   0.0000
+angle c3   p3   cl      35.7 99.89 ! SOURCE3    1   0.0000   0.0000
+angle c3   p3   f       39.7 97.80 ! SOURCE2    1   0.0000   0.0000
+angle c3   p3   hp      27.5 97.66 ! SOURCE3    9   0.2559   0.4096
+angle c3   p3   n       38.8 101.77 ! SOURCE3   12   2.2839   2.4449
+angle c3   p3   n2      39.9 96.55 ! SOURCE3    2   0.0000   0.0000
+angle c3   p3   n3      39.3 101.18 ! SOURCE3   10   1.7890   2.2338
+angle c3   p3   n4      38.6 96.94 ! SOURCE3    6   0.4540   0.4815
+angle c3   p3   na      39.2 100.17 ! SOURCE3    4   0.0554   0.0554
+angle c3   p3   nh      38.6 104.50 ! SOURCE3    2   0.0000   0.0000
+angle c3   p3   no      39.0 96.98 ! SOURCE3    2   0.0000   0.0000
+angle c3   p3   o       39.3 111.67 ! SOURCE3   28   3.9148   5.3387
+angle c3   p3   oh      40.6 98.21 ! SOURCE3    2   0.0000   0.0000
+angle c3   p3   os      40.2 99.53 ! SOURCE3    3   1.6667   1.7678
+angle c3   p3   p3      45.8 100.31 ! SOURCE3   18   2.0337   2.1836
+angle c3   p3   p5      45.6 100.90 ! SOURCE3   10   2.6003   2.7070
+angle c3   p3   s4      37.9 98.88 ! SOURCE3    8   5.3873   6.2235
+angle c3   p3   s6      37.6 101.18 ! SOURCE3   12   5.5239   6.4536
+angle c3   p3   sh      37.4 98.71 ! SOURCE3    2   0.0000   0.0000
+angle c3   p3   ss      37.4 99.37 ! SOURCE3    2   0.0000   0.0000
+angle ca   p3   ca      37.9 99.86 ! SOURCE3    1   0.0000   0.0000
+angle ca   p3   hp      27.7 97.50 ! SOURCE3    2   0.0000   0.0000
+angle cl   p3   cl      33.9 102.82 ! SOURCE3    1   0.0000   0.0000
+angle cl   p3   f       37.0 99.20 ! SOURCE2    1   0.0000   0.0000
+angle cl   p3   hp      25.7 96.30 ! SOURCE3    3   0.5848   0.6203
+angle cx   p3   hp      27.5 95.20 ! SOURCE2    1   0.0000   0.0000
+angle f    p3   f       43.1 97.40 ! SOURCE2    8   1.3750   1.6636
+angle f    p3   hp      30.6 96.41 ! SOURCE3    2   0.0000   0.0000
+angle f    p3   n3      41.9 100.60 ! SOURCE2    1   0.0000   0.0000
+angle f    p3   os      42.4 102.20 ! SOURCE2    1   0.0000   0.0000
+angle f    p3   p3      47.1 97.20 ! SOURCE2    1   0.0000   0.0000
+angle hp   p3   hp      22.0 95.52 ! SOURCE3   44   1.2944   2.4200
+angle hp   p3   i       23.4 96.19 ! SOURCE3    4   0.6454   0.6454
+angle hp   p3   n       29.5 95.15 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   n2      29.1 98.28 ! SOURCE3   10   1.6390   1.8860
+angle hp   p3   n3      30.1 94.46 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   n4      28.3 93.21 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   na      29.2 97.27 ! SOURCE3   12   0.7938   0.9318
+angle hp   p3   nh      30.2 94.10 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   no      28.8 93.06 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   o       32.0 101.02 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   oh      30.7 95.95 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   os      30.4 97.35 ! SOURCE3    6   2.6706   2.8326
+angle hp   p3   p2      32.4 99.11 ! SOURCE3   16   2.7761   4.3022
+angle hp   p3   p3      32.1 95.52 ! SOURCE3    4   0.0844   0.0844
+angle hp   p3   p4      32.0 95.95 ! SOURCE3    6   0.0461   0.0489
+angle hp   p3   p5      32.1 95.54 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   s4      26.9 95.49 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   s6      27.4 92.95 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   sh      26.5 94.21 ! SOURCE3    2   0.0000   0.0000
+angle hp   p3   ss      26.6 94.61 ! SOURCE3    2   0.0000   0.0000
+angle i    p3   i       36.6 105.25 ! SOURCE3    1   0.0000   0.0000
+angle n    p3   n       40.0 104.58 ! SOURCE0    1
+angle n    p3   o       42.8 104.99 ! SOURCE3    4   0.0000   0.0000
+angle n2   p3   n2      40.4 103.46 ! SOURCE3    1   0.0000   0.0000
+angle n3   p3   n3      39.3 113.80 ! SOURCE3    1   0.0000   0.0000
+angle n3   p3   o       43.0 107.10 ! SOURCE3    4   0.0000   0.0000
+angle n3   p3   oh      43.1 98.36 ! SOURCE0    1
+angle n4   p3   n4      38.5 100.53 ! SOURCE3    1   0.0000   0.0000
+angle na   p3   na      39.9 106.22 ! SOURCE0    1
+angle nh   p3   nh      40.1 109.11 ! SOURCE3    1   0.0000   0.0000
+angle no   p3   no      39.6 98.33 ! SOURCE3    1   0.0000   0.0000
+angle o    p3   o       43.8 122.18 ! SOURCE3    2   7.8556   7.8556
+angle o    p3   p3      45.1 116.74 ! SOURCE3   14   0.5266   0.7525
+angle o    p3   p5      47.0 107.62 ! SOURCE3    4   0.0000   0.0000
+angle o    p3   s4      38.6 110.70 ! SOURCE3    4   0.7259   0.7259
+angle o    p3   s6      39.5 106.66 ! SOURCE3    6   3.2072   3.4017
+angle oh   p3   oh      42.8 104.48 ! SOURCE3    1   0.0000   0.0000
+angle os   p3   os      42.1 106.65 ! SOURCE3    1   0.0000   0.0000
+angle p2   p3   p2      59.1 103.58 ! SOURCE3    1   0.0000   0.0000
+angle p3   p3   p3      57.0 105.31 ! SOURCE3    4   3.1059   3.5864
+angle p4   p3   p4      58.7 99.09 ! SOURCE0    1
+angle p5   p3   p5      58.8 99.10 ! SOURCE3    1   0.0000   0.0000
+angle s    p3   s       34.1 131.32 ! SOURCE0    1
+angle s4   p3   s4      39.1 98.26 ! SOURCE3    1   0.0000   0.0000
+angle s6   p3   s6      39.4 97.78 ! SOURCE3    1   0.0000   0.0000
+angle sh   p3   sh      36.6 107.58 ! SOURCE3    1   0.0000   0.0000
+angle ss   p3   ss      36.5 109.24 ! SOURCE3    1   0.0000   0.0000
+angle br   p4   br      41.1 110.41 ! SOURCE0    1
+angle br   p4   o       37.8 124.80 ! SOURCE0    1
+angle c2   p4   c2      37.1 104.21 ! SOURCE0    1
+angle c2   p4   hp      27.7 99.50 ! SOURCE3    2   0.0000   0.0000
+angle c2   p4   o       39.4 113.59 ! SOURCE0    1
+angle c3   p4   c3      37.2 102.55 ! SOURCE3    4   0.0192   0.0192
+angle c3   p4   n       39.0 103.26 ! SOURCE3    1   0.0000   0.0000
+angle c3   p4   n2      39.1 103.17 ! SOURCE0    1
+angle c3   p4   n3      39.5 102.37 ! SOURCE3    1   0.0000   0.0000
+angle c3   p4   n4      37.5 99.57 ! SOURCE3    1   0.0000   0.0000
+angle c3   p4   na      37.9 117.67 ! SOURCE3    5  18.6523  19.0404
+angle c3   p4   nh      39.3 102.79 ! SOURCE3    1   0.0000   0.0000
+angle c3   p4   no      38.2 99.80 ! SOURCE3    3   0.2028   0.2151
+angle c3   p4   o       38.8 116.44 ! SOURCE3   25   1.6284   2.6494
+angle c3   p4   oh      41.0 98.56 ! SOURCE3    2   0.4558   0.4558
+angle c3   p4   os      41.2 98.01 ! SOURCE3    2   0.0931   0.0931
+angle c3   p4   p2      44.4 109.27 ! SOURCE0    1
+angle c3   p4   p3      45.1 103.53 ! SOURCE3    1   0.0000   0.0000
+angle c3   p4   p4      48.1 102.12 ! SOURCE0    1
+angle c3   p4   p5      44.6 104.15 ! SOURCE0    1
+angle c3   p4   sh      37.4 100.17 ! SOURCE3    2   0.0815   0.0815
+angle c3   p4   ss      37.4 101.19 ! SOURCE0    1
+angle ca   p4   ca      36.8 107.77 ! SOURCE0    1
+angle ca   p4   o       40.0 111.64 ! SOURCE0    1
+angle cl   p4   cl      33.8 103.51 ! SOURCE3    1   0.0000   0.0000
+angle cl   p4   o       36.0 116.53 ! SOURCE3    2   0.0000   0.0000
+angle hp   p4   hp      22.6 99.21 ! SOURCE3    4   5.1252   6.4572
+angle hp   p4   o       31.4 109.35 ! SOURCE3    6   9.0789  10.8284
+angle hp   p4   p3      31.4 98.96 ! SOURCE3    4   0.0000   0.0000
+angle hp   p4   s       24.3 110.24 ! SOURCE3    4   4.1081   4.1081
+angle i    p4   i       38.5 113.22 ! SOURCE3    2   6.7916   6.7916
+angle i    p4   o       37.7 110.22 ! SOURCE3    4   9.7726   9.7726
+angle n    p4   o       41.0 117.99 ! SOURCE3    1   0.0000   0.0000
+angle n2   p4   n2      41.5 102.54 ! SOURCE0    1
+angle n2   p4   o       40.8 120.28 ! SOURCE0    1
+angle n3   p4   o       42.5 113.27 ! SOURCE3    1   0.0000   0.0000
+angle n4   p4   o       39.6 107.61 ! SOURCE3    1   0.0000   0.0000
+angle na   p4   o       45.2 110.60 ! SOURCE3    5   1.1626   1.3133
+angle nh   p4   nh      43.5 95.30 ! SOURCE3    1   0.0000   0.0000
+angle nh   p4   o       41.9 115.86 ! SOURCE3    3   3.0841   3.2712
+angle no   p4   o       39.4 114.69 ! SOURCE3    3   0.1008   0.1070
+angle o    p4   o       45.1 117.22 ! SOURCE3    6   2.7792   2.7792
+angle o    p4   oh      42.9 117.39 ! SOURCE3    4   0.9528   1.0083
+angle o    p4   os      43.1 116.67 ! SOURCE3    4   0.6527   0.6923
+angle o    p4   p2      44.9 121.35 ! SOURCE0    1
+angle o    p4   p3      45.6 114.00 ! SOURCE3    3   0.6282   0.6663
+angle o    p4   p4      48.8 116.43 ! SOURCE0    1
+angle o    p4   p5      46.0 109.76 ! SOURCE0    1
+angle o    p4   s       37.3 112.78 ! SOURCE3    2   0.0000   0.0000
+angle o    p4   s4      35.8 112.19 ! SOURCE0    1
+angle o    p4   s6      35.2 113.89 ! SOURCE0    1
+angle o    p4   sh      37.0 118.09 ! SOURCE0    1
+angle o    p4   ss      37.5 116.14 ! SOURCE3    4   0.9499   1.0636
+angle oh   p4   oh      45.7 95.71 ! SOURCE3    1   0.0000   0.0000
+angle os   p4   os      44.7 100.34 ! SOURCE3    1   0.0000   0.0000
+angle p2   p4   p2      56.5 110.71 ! SOURCE0    1
+angle p3   p4   p3      54.5 114.98 ! SOURCE3    1   0.0000   0.0000
+angle p4   p4   p4      61.4 107.38 ! SOURCE0    1
+angle p5   p4   p5      55.8 107.78 ! SOURCE0    1
+angle s    p4   s       36.5 106.30 ! SOURCE3    2  25.0119  25.0119
+angle s4   p4   s4      36.6 96.24 ! SOURCE0    1
+angle s6   p4   s6      35.2 102.36 ! SOURCE0    1
+angle sh   p4   sh      38.5 98.81 ! SOURCE3    1   0.0000   0.0000
+angle ss   p4   ss      37.6 104.41 ! SOURCE3    1   0.0000   0.0000
+angle br   p5   br      42.0 103.38 ! SOURCE3    1   0.0000   0.0000
+angle br   p5   o       39.0 114.65 ! SOURCE3    3   1.0286   1.0910
+angle br   p5   oh      40.9 102.92 ! SOURCE3    4   0.5468   0.5468
+angle c    p5   c       36.2 104.16 ! SOURCE0    1
+angle c    p5   o       38.7 113.88 ! SOURCE0    1
+angle c    p5   oh      39.9 102.12 ! SOURCE0    1
+angle c1   p5   c1      38.8 102.89 ! SOURCE3    1   0.0000   0.0000
+angle c1   p5   o       40.3 115.77 ! SOURCE3    2   0.0000   0.0000
+angle c1   p5   oh      41.4 102.79 ! SOURCE3    2   0.0000   0.0000
+angle c2   p5   c2      36.8 106.56 ! SOURCE0    1
+angle c2   p5   o       39.4 114.81 ! SOURCE0    1
+angle c2   p5   oh      40.7 101.69 ! SOURCE0    1
+angle c2   p5   os      40.6 103.34 ! SOURCE0    1
+angle c3   p5   c3      37.0 106.23 ! SOURCE3   14   2.4917   2.6389
+angle c3   p5   hp      26.9 104.49 ! SOURCE3    1   0.0000   0.0000
+angle c3   p5   n3      40.0 102.76 ! SOURCE3    1   0.0000   0.0000
+angle c3   p5   o       39.9 112.50 ! SOURCE3   23   4.1385   4.4203
+angle c3   p5   oh      40.8 101.56 ! SOURCE3   17   1.4803   2.1803
+angle c3   p5   os      40.6 103.83 ! SOURCE3    4   2.0564   2.3806
+angle c3   p5   p4      44.3 106.27 ! SOURCE0    1
+angle c3   p5   s       37.5 113.40 ! SOURCE3    4   1.7378   2.0067
+angle c3   p5   ss      37.0 103.76 ! SOURCE3    1   0.0000   0.0000
+angle ca   p5   ca      37.1 107.53 ! SOURCE0    1
+angle ca   p5   o       39.9 113.98 ! SOURCE0    1
+angle ca   p5   oh      41.0 101.77 ! SOURCE0    1
+angle ca   p5   os      40.9 103.75 ! SOURCE0    1
+angle cl   p5   cl      33.8 103.70 ! SOURCE2    1   0.0000   0.0000
+angle cl   p5   o       36.3 115.32 ! SOURCE3    2   0.0000   0.0000
+angle cl   p5   oh      37.9 102.44 ! SOURCE3    2   0.0000   0.0000
+angle f    p5   f       42.5 99.96 ! SOURCE2    4   0.7600   0.9197
+angle f    p5   o       42.3 116.75 ! SOURCE3    2   0.0000   0.0000
+angle f    p5   oh      43.3 101.98 ! SOURCE3    2   0.0000   0.0000
+angle f    p5   s       38.1 117.40 ! SOURCE2    1   0.0000   0.0000
+angle ho   p5   os       7.9 102.31 ! SOURCE0    1
+angle hp   p5   hp      21.4 101.09 ! SOURCE3    4   1.1290   1.3036
+angle hp   p5   o       30.2 116.58 ! SOURCE3    7   1.0523   1.3282
+angle hp   p5   oh      30.6 101.45 ! SOURCE3    5   0.7264   0.9084
+angle hp   p5   s       26.0 119.20 ! SOURCE2    1   0.0000   0.0000
+angle i    p5   i       35.9 107.17 ! SOURCE3    1   0.0000   0.0000
+angle i    p5   o       33.0 115.93 ! SOURCE3    3   0.0392   0.0415
+angle i    p5   oh      35.4 102.26 ! SOURCE3    4   1.9577   1.9577
+angle n    p5   n       41.7 103.09 ! SOURCE3    1   0.0000   0.0000
+angle n    p5   o       42.1 115.26 ! SOURCE3    4   1.4228   1.6532
+angle n    p5   oh      43.1 102.44 ! SOURCE3    4   0.0999   0.0999
+angle n    p5   os      43.8 100.48 ! SOURCE3    2   0.0000   0.0000
+angle n2   p5   n2      44.0 106.34 ! SOURCE0    1
+angle n2   p5   o       44.3 113.53 ! SOURCE0    1
+angle n2   p5   oh      44.7 102.40 ! SOURCE0    1
+angle n3   p5   n3      43.8 99.11 ! SOURCE3    1   0.0000   0.0000
+angle n3   p5   o       43.2 113.54 ! SOURCE3    7   5.0248   5.6747
+angle n3   p5   oh      43.4 104.18 ! SOURCE3    6   0.4123   0.4373
+angle n3   p5   os      44.9 98.88 ! SOURCE3    2   0.0000   0.0000
+angle n4   p5   n4      39.2 102.20 ! SOURCE3    1   0.0000   0.0000
+angle n4   p5   o       41.1 109.78 ! SOURCE3    5   2.6856   2.7519
+angle n4   p5   oh      42.2 98.48 ! SOURCE3    6   0.3869   0.4104
+angle n4   p5   os      43.3 94.55 ! SOURCE3    2   0.0000   0.0000
+angle na   p5   na      40.6 108.57 ! SOURCE3    1   0.0000   0.0000
+angle na   p5   o       42.4 113.43 ! SOURCE3   11   0.7963   0.8968
+angle na   p5   oh      43.1 102.07 ! SOURCE3   16   1.2552   1.4144
+angle na   p5   os      43.2 103.06 ! SOURCE3    4   0.7463   0.7463
+angle nh   p5   nh      43.5 99.51 ! SOURCE3    1   0.0000   0.0000
+angle nh   p5   o       42.1 118.91 ! SOURCE3    3   1.2480   1.3237
+angle nh   p5   oh      43.4 103.81 ! SOURCE3    2   0.0000   0.0000
+angle nh   p5   os      44.4 100.51 ! SOURCE3    2   0.0000   0.0000
+angle no   p5   no      40.4 95.68 ! SOURCE3    1   0.0000   0.0000
+angle no   p5   o       40.5 112.75 ! SOURCE3    4   3.3675   3.3684
+angle no   p5   oh      41.6 101.35 ! SOURCE3    2   0.0000   0.0000
+angle no   p5   os      41.7 101.70 ! SOURCE3    4   0.0565   0.0565
+angle o    p5   o       46.0 115.80 ! SOURCE3   17   4.6921   5.7902
+angle o    p5   oh      43.8 115.03 ! SOURCE3   86   3.5330   4.9414
+angle o    p5   os      44.0 116.09 ! SOURCE3   35   2.3321   3.2062
+angle o    p5   p2      46.2 114.60 ! SOURCE0    1
+angle o    p5   p3      45.4 115.48 ! SOURCE3    9   1.7451   2.1084
+angle o    p5   p4      45.1 114.66 ! SOURCE0    1
+angle o    p5   p5      49.1 113.44 ! SOURCE0    1
+angle o    p5   s       40.5 116.94 ! SOURCE3    3   2.7818   2.9506
+angle o    p5   s4      39.6 110.23 ! SOURCE0    1
+angle o    p5   s6      39.3 111.75 ! SOURCE0    1
+angle o    p5   sh      38.1 114.56 ! SOURCE3    3   1.6636   1.7645
+angle o    p5   ss      38.0 112.46 ! SOURCE3    6   2.2289   2.7392
+angle oh   p5   oh      44.6 102.45 ! SOURCE3   39   1.8849   2.4223
+angle oh   p5   os      44.9 102.37 ! SOURCE3    8   1.0873   1.5063
+angle oh   p5   p2      48.2 103.53 ! SOURCE0    1
+angle oh   p5   p3      47.5 103.83 ! SOURCE3   13   0.4174   0.4303
+angle oh   p5   p4      47.6 101.79 ! SOURCE0    1
+angle oh   p5   p5      51.4 100.45 ! SOURCE0    1
+angle oh   p5   s       42.2 102.88 ! SOURCE3    3   1.5127   1.6044
+angle oh   p5   s4      40.3 103.24 ! SOURCE0    1
+angle oh   p5   s6      40.7 101.48 ! SOURCE0    1
+angle oh   p5   sh      40.0 101.41 ! SOURCE3    2   0.0000   0.0000
+angle oh   p5   ss      39.0 104.33 ! SOURCE3    6   1.8962   2.0112
+angle os   p5   os      45.2 102.46 ! SOURCE3   16   2.7156   3.4477
+angle os   p5   p3      47.7 103.67 ! SOURCE3    2   0.0000   0.0000
+angle os   p5   p5      50.6 104.48 ! SOURCE0    1
+angle os   p5   s4      40.6 102.52 ! SOURCE0    1
+angle os   p5   s6      40.7 101.89 ! SOURCE0    1
+angle os   p5   sh      39.5 104.59 ! SOURCE3    2   0.0000   0.0000
+angle os   p5   ss      39.9 100.24 ! SOURCE3    3   5.9143   6.2730
+angle p2   p5   p2      57.4 107.14 ! SOURCE0    1
+angle p3   p5   p3      57.0 105.23 ! SOURCE3    3   4.8106   5.1024
+angle p4   p5   p4      57.4 101.62 ! SOURCE0    1
+angle p5   p5   p5      59.4 112.72 ! SOURCE0    1
+angle s    p5   s       39.4 114.13 ! SOURCE3    1   0.0000   0.0000
+angle s6   p5   s6      38.6 105.18 ! SOURCE0    1
+angle sh   p5   sh      38.0 104.56 ! SOURCE3    1   0.0000   0.0000
+angle sh   p5   ss      37.3 107.13 ! SOURCE0    1
+angle ss   p5   ss      36.6 109.61 ! SOURCE3    1   0.0000   0.0000
+angle cd   pc   n       42.8 90.80 ! SOURCE3    3   2.2084   2.3423
+angle cc   pd   n       42.8 90.80 ! SOURCE3    3  same as cd-pc-n
+angle cd   pc   na      43.0 90.18 ! SOURCE3   81   2.4347   2.7619
+angle cc   pd   na      43.0 90.18 ! SOURCE3   81  same as cd-pc-na
+angle c    pe   c2      38.6 97.30 ! SOURCE3    3   0.0316   0.0335
+angle c    pf   c2      38.6 97.30 ! SOURCE3    3  same as c -pe-c2
+angle c2   pe   ca      38.8 101.44 ! SOURCE3    3   0.6764   0.7177
+angle c2   pf   ca      38.8 101.44 ! SOURCE3    3  same as c2-pe-ca
+angle c2   pe   ce      38.7 103.01 ! SOURCE3    4   1.4470   1.4470
+angle c2   pf   cf      38.7 103.01 ! SOURCE3    4  same as c2-pe-ce
+angle c2   pe   cg      40.8 104.03 ! SOURCE3    3   3.6524   3.8740
+angle c2   pf   ch      40.8 104.03 ! SOURCE3    3  same as c2-pe-cg
+angle c2   pe   n2      44.9 94.14 ! SOURCE0    1
+angle c2   pf   n2      44.9 94.14 ! SOURCE0    1  same as c2-pe-n2
+angle c2   pe   ne      41.8 98.70 ! SOURCE3   12   3.8876   5.3383
+angle c2   pf   nf      41.8 98.70 ! SOURCE3   12  same as c2-pe-ne
+angle c2   pe   o       41.1 115.16 ! SOURCE3    2   0.0149   0.0149
+angle c2   pf   o       41.1 115.16 ! SOURCE3    2  same as c2-pe-o
+angle c2   pe   p2      50.9 107.82 ! SOURCE0    1
+angle c2   pf   p2      50.9 107.82 ! SOURCE0    1  same as c2-pe-p2
+angle c2   pe   pe      48.2 102.99 ! SOURCE3    9   7.0590   8.2860
+angle c2   pf   pf      48.2 102.99 ! SOURCE3    9  same as c2-pe-pe
+angle c2   pe   px      51.1 97.37 ! SOURCE3    4   0.5674   0.6655
+angle c2   pf   px      51.1 97.37 ! SOURCE3    4  same as c2-pe-px
+angle c2   pe   py      50.9 96.71 ! SOURCE3    4   0.9336   1.2755
+angle c2   pf   py      50.9 96.71 ! SOURCE3    4  same as c2-pe-py
+angle c2   pe   s       41.1 111.16 ! SOURCE3    2   0.0000   0.0000
+angle c2   pf   s       41.1 111.16 ! SOURCE3    2  same as c2-pe-s
+angle c2   pe   sx      38.5 95.11 ! SOURCE3    4   0.2597   0.2676
+angle c2   pf   sx      38.5 95.11 ! SOURCE3    4  same as c2-pe-sx
+angle c2   pe   sy      37.8 95.56 ! SOURCE3    2   0.0462   0.0462
+angle c2   pf   sy      37.8 95.56 ! SOURCE3    2  same as c2-pe-sy
+angle ca   pe   n2      41.0 102.03 ! SOURCE3    1   0.0000   0.0000
+angle ca   pf   n2      41.0 102.03 ! SOURCE3    1  same as ca-pe-n2
+angle ca   pe   o       40.4 106.88 ! SOURCE3    2   0.0018   0.0018
+angle ca   pf   o       40.4 106.88 ! SOURCE3    2  same as ca-pe-o
+angle ca   pe   p2      51.0 100.79 ! SOURCE3    1   0.0000   0.0000
+angle ca   pf   p2      51.0 100.79 ! SOURCE3    1  same as ca-pe-p2
+angle ca   pe   pf      48.4 99.70 ! SOURCE3    2   0.0000   0.0000
+angle ca   pf   pe      48.4 99.70 ! SOURCE3    2   0.0000   0.0000
+angle ca   pe   s       40.1 107.93 ! SOURCE3    1   0.0000   0.0000
+angle ca   pf   s       40.1 107.93 ! SOURCE3    1  same as ca-pe-s
+angle ce   pe   n2      41.4 100.55 ! SOURCE3    1   0.0000   0.0000
+angle cf   pf   n2      41.4 100.55 ! SOURCE3    1  same as ce-pe-n2
+angle ce   pe   o       40.5 107.44 ! SOURCE3    1   0.0000   0.0000
+angle cf   pf   o       40.5 107.44 ! SOURCE3    1  same as ce-pe-o
+angle ce   pe   p2      51.4 99.56 ! SOURCE3    1   0.0000   0.0000
+angle cf   pf   p2      51.4 99.56 ! SOURCE3    1  same as ce-pe-p2
+angle ce   pe   s       40.7 105.54 ! SOURCE3    1   0.0000   0.0000
+angle cf   pf   s       40.7 105.54 ! SOURCE3    1  same as ce-pe-s
+angle cg   pe   n2      44.3 101.79 ! SOURCE3    1   0.0000   0.0000
+angle ch   pf   n2      44.3 101.79 ! SOURCE3    1  same as cg-pe-n2
+angle cg   pe   o       43.6 107.62 ! SOURCE3    1   0.0000   0.0000
+angle ch   pf   o       43.6 107.62 ! SOURCE3    1  same as cg-pe-o
+angle cg   pe   p2      52.5 104.68 ! SOURCE3    2   5.1435   5.1435
+angle ch   pf   p2      52.5 104.68 ! SOURCE3    2  same as cg-pe-p2
+angle cg   pe   s       42.3 108.60 ! SOURCE3    4   2.6981   2.6981
+angle ch   pf   s       42.3 108.60 ! SOURCE3    4  same as cg-pe-s
+angle n2   pe   n2      45.3 108.14 ! SOURCE0    1
+angle n2   pf   n2      45.3 108.14 ! SOURCE0    1  same as n2-pe-n2
+angle n2   pe   ne      42.9 106.80 ! SOURCE3    6   3.8963   4.5981
+angle n2   pf   nf      42.9 106.80 ! SOURCE3    6  same as n2-pe-ne
+angle n2   pe   o       44.3 115.39 ! SOURCE3    1   0.0000   0.0000
+angle n2   pf   o       44.3 115.39 ! SOURCE3    1  same as n2-pe-o
+angle n2   pe   p2      52.9 111.60 ! SOURCE0    1
+angle n2   pf   p2      52.9 111.60 ! SOURCE0    1  same as n2-pe-p2
+angle n2   pe   pe      48.7 109.40 ! SOURCE3    1   0.0000   0.0000
+angle n2   pf   pf      48.7 109.40 ! SOURCE3    1  same as n2-pe-pe
+angle n2   pe   px      50.3 110.30 ! SOURCE3    3   5.7085   6.0548
+angle n2   pf   px      50.3 110.30 ! SOURCE3    3  same as n2-pe-px
+angle n2   pe   py      54.1 93.68 ! SOURCE3    1   0.0000   0.0000
+angle n2   pf   py      54.1 93.68 ! SOURCE3    1  same as n2-pe-py
+angle n2   pe   s       42.9 114.84 ! SOURCE3    3   3.4424   3.6512
+angle n2   pf   s       42.9 114.84 ! SOURCE3    3  same as n2-pe-s
+angle n2   pe   sx      39.4 97.83 ! SOURCE3    1   0.0000   0.0000
+angle n2   pf   sx      39.4 97.83 ! SOURCE3    1  same as n2-pe-sx
+angle n2   pe   sy      38.6 98.14 ! SOURCE3    1   0.0000   0.0000
+angle n2   pf   sy      38.6 98.14 ! SOURCE3    1  same as n2-pe-sy
+angle ne   pe   o       42.7 110.24 ! SOURCE3    3   3.3676   3.8478
+angle nf   pf   o       42.7 110.24 ! SOURCE3    3  same as ne-pe-o
+angle ne   pe   p2      52.8 104.48 ! SOURCE3    2   7.1207   7.1207
+angle nf   pf   p2      52.8 104.48 ! SOURCE3    2  same as ne-pe-p2
+angle ne   pe   s       42.2 109.19 ! SOURCE3    5   2.9509   3.6708
+angle nf   pf   s       42.2 109.19 ! SOURCE3    5  same as ne-pe-s
+angle o    pe   o       44.0 119.96 ! SOURCE3    1   0.0000   0.0000
+angle o    pf   o       44.0 119.96 ! SOURCE3    1  same as o -pe-o
+angle o    pe   p2      52.7 114.23 ! SOURCE3    1   0.0000   0.0000
+angle o    pf   p2      52.7 114.23 ! SOURCE3    1  same as o -pe-p2
+angle o    pe   pe      42.5 145.96 ! SOURCE3    1   0.0000   0.0000
+angle o    pf   pf      42.5 145.96 ! SOURCE3    1  same as o -pe-pe
+angle o    pe   px      52.1 104.37 ! SOURCE3    1   0.0000   0.0000
+angle o    pf   px      52.1 104.37 ! SOURCE3    1  same as o -pe-px
+angle o    pe   py      51.6 104.49 ! SOURCE3    1   0.0000   0.0000
+angle o    pf   py      51.6 104.49 ! SOURCE3    1  same as o -pe-py
+angle o    pe   s       42.9 117.42 ! SOURCE3    2   0.0426   0.0426
+angle o    pf   s       42.9 117.42 ! SOURCE3    2  same as o -pe-s
+angle o    pe   sx      38.0 106.59 ! SOURCE3    1   0.0000   0.0000
+angle o    pf   sx      38.0 106.59 ! SOURCE3    1  same as o -pe-sx
+angle o    pe   sy      37.6 105.04 ! SOURCE3    1   0.0000   0.0000
+angle o    pf   sy      37.6 105.04 ! SOURCE3    1  same as o -pe-sy
+angle p2   pe   pe      65.6 98.24 ! SOURCE3    1   0.0000   0.0000
+angle p2   pf   pf      65.6 98.24 ! SOURCE3    1  same as p2-pe-pe
+angle p2   pe   px      64.1 108.28 ! SOURCE3    2   6.2959   6.2959
+angle p2   pf   px      64.1 108.28 ! SOURCE3    2  same as p2-pe-px
+angle p2   pe   py      63.0 110.87 ! SOURCE3    3   7.6976   8.1645
+angle p2   pf   py      63.0 110.87 ! SOURCE3    3  same as p2-pe-py
+angle p2   pe   s       53.4 111.28 ! SOURCE3    1   0.0000   0.0000
+angle p2   pf   s       53.4 111.28 ! SOURCE3    1  same as p2-pe-s
+angle p2   pe   sx      51.3 95.73 ! SOURCE3    1   0.0000   0.0000
+angle p2   pf   sx      51.3 95.73 ! SOURCE3    1  same as p2-pe-sx
+angle p2   pe   sy      50.5 95.95 ! SOURCE3    1   0.0000   0.0000
+angle p2   pf   sy      50.5 95.95 ! SOURCE3    1  same as p2-pe-sy
+angle pe   pe   s       50.4 107.91 ! SOURCE3    2   1.5577   1.5577
+angle pf   pf   s       50.4 107.91 ! SOURCE3    2  same as pe-pe-s
+angle px   pe   s       52.0 107.62 ! SOURCE3    2   3.7266   3.7266
+angle px   pf   s       52.0 107.62 ! SOURCE3    2  same as px-pe-s
+angle py   pe   s       51.4 108.73 ! SOURCE3    3   5.0158   5.3201
+angle py   pf   s       51.4 108.73 ! SOURCE3    3  same as py-pe-s
+angle s    pe   s       34.4 178.44 ! SOURCE3    1   0.0000   0.0000
+angle s    pf   s       34.4 178.44 ! SOURCE3    1  same as s -pe-s
+angle s    pe   sx      38.7 108.32 ! SOURCE3    2   3.0318   3.0318
+angle s    pf   sx      38.7 108.32 ! SOURCE3    2  same as s -pe-sx
+angle s    pe   sy      38.4 106.93 ! SOURCE3    1   0.0000   0.0000
+angle s    pf   sy      38.4 106.93 ! SOURCE3    1  same as s -pe-sy
+angle c    px   c3      36.7 101.72 ! SOURCE3    1   0.0000   0.0000
+angle c    px   o       38.0 114.47 ! SOURCE3    1   0.0000   0.0000
+angle c3   px   ca      37.0 104.79 ! SOURCE3    1   0.0000   0.0000
+angle c3   px   ce      37.0 104.86 ! SOURCE3    4   0.5282   0.6354
+angle c3   px   cf      37.0 104.86 ! SOURCE3    4  same as c3-px-ce
+angle c3   px   ne      39.5 102.46 ! SOURCE3    7   1.2108   1.8685
+angle c3   px   nf      39.5 102.46 ! SOURCE3    7  same as c3-px-ne
+angle c3   px   o       39.4 113.68 ! SOURCE3   28   3.8637   4.8990
+angle c3   px   pe      47.8 105.73 ! SOURCE3   10   3.6317   4.4059
+angle c3   px   pf      47.8 105.73 ! SOURCE3   10  same as c3-px-pe
+angle c3   px   py      45.5 103.11 ! SOURCE3    3   0.8183   0.8680
+angle c3   px   sx      36.1 99.55 ! SOURCE3    1   0.0000   0.0000
+angle c3   px   sy      35.3 103.41 ! SOURCE3    1   0.0000   0.0000
+angle ca   px   ca      37.1 104.15 ! SOURCE3    2   3.6168   3.6168
+angle ca   px   o       40.5 107.50 ! SOURCE3    5   5.5918   5.7355
+angle ce   px   ce      37.1 104.21 ! SOURCE3    1   0.0000   0.0000
+angle cf   px   cf      37.1 104.21 ! SOURCE3    1  same as ce-px-ce
+angle ce   px   o       39.4 113.79 ! SOURCE3    6   0.3605   0.3877
+angle cf   px   o       39.4 113.79 ! SOURCE3    6  same as ce-px-o
+angle ne   px   ne      41.9 103.22 ! SOURCE3    2   0.6807   0.6807
+angle nf   px   nf      41.9 103.22 ! SOURCE3    2  same as ne-px-ne
+angle ne   px   o       42.3 114.13 ! SOURCE3   11   6.5928   8.9737
+angle nf   px   o       42.3 114.13 ! SOURCE3   11  same as ne-px-o
+angle o    px   pe      49.4 116.50 ! SOURCE3   12   5.9082   8.2925
+angle o    px   pf      49.4 116.50 ! SOURCE3   12  same as o -px-pe
+angle o    px   py      45.9 114.20 ! SOURCE3    5   1.2921   1.7165
+angle o    px   sx      35.8 112.81 ! SOURCE3    3   0.8296   0.8799
+angle o    px   sy      35.6 113.54 ! SOURCE3    3   0.4723   0.5010
+angle pe   px   pe      61.4 110.71 ! SOURCE3    1   0.0000   0.0000
+angle pf   px   pf      61.4 110.71 ! SOURCE3    1  same as pe-px-pe
+angle py   px   py      56.7 107.78 ! SOURCE3    1   0.0000   0.0000
+angle sx   px   sx      36.8 96.24 ! SOURCE3    1   0.0000   0.0000
+angle sy   px   sy      35.5 102.36 ! SOURCE3    1   0.0000   0.0000
+angle c    py   c       36.2 104.20 ! SOURCE3    1   0.0000   0.0000
+angle c    py   c3      35.7 110.36 ! SOURCE3    1   0.0000   0.0000
+angle c    py   o       38.5 115.25 ! SOURCE3    6   1.8195   2.6519
+angle c    py   oh      40.0 102.14 ! SOURCE3    6   1.0044   1.0654
+angle c    py   os      41.3 95.74 ! SOURCE3    3   8.5794   9.0999
+angle c3   py   n4      37.3 103.83 ! SOURCE3    4   0.0000   0.0000
+angle c3   py   na      38.8 106.89 ! SOURCE3    2   0.0000   0.0000
+angle c3   py   o       38.9 117.87 ! SOURCE3   13   2.2138   2.3554
+angle c3   py   oh      41.2 100.16 ! SOURCE3    2   0.0000   0.0000
+angle c3   py   os      40.2 105.39 ! SOURCE3    1   0.0000   0.0000
+angle c3   py   px      44.9 106.27 ! SOURCE3    2   0.0000   0.0000
+angle c3   py   py      43.5 113.97 ! SOURCE3   10   1.4783   1.6346
+angle c3   py   sx      34.7 106.36 ! SOURCE3    4   0.0000   0.0000
+angle ca   py   ca      37.0 107.55 ! SOURCE3    1   0.0000   0.0000
+angle ca   py   o       39.8 113.98 ! SOURCE3    3   0.5006   0.5309
+angle ca   py   oh      41.1 101.78 ! SOURCE3    2   0.0000   0.0000
+angle ca   py   os      40.7 103.75 ! SOURCE3    2   0.0000   0.0000
+angle ce   py   ce      37.3 106.54 ! SOURCE3    1   0.0000   0.0000
+angle cf   py   cf      37.3 106.54 ! SOURCE3    1  same as ce-py-ce
+angle ce   py   o       40.2 112.16 ! SOURCE3    5   3.1814   3.2594
+angle cf   py   o       40.2 112.16 ! SOURCE3    5  same as ce-py-o
+angle ce   py   oh      40.6 104.77 ! SOURCE3    6   2.0602   2.1852
+angle cf   py   oh      40.6 104.77 ! SOURCE3    6  same as ce-py-oh
+angle ce   py   os      40.8 103.34 ! SOURCE3    2   0.0000   0.0000
+angle cf   py   os      40.8 103.34 ! SOURCE3    2  same as ce-py-os
+angle n4   py   o       38.8 115.58 ! SOURCE3    4   0.0000   0.0000
+angle n4   py   py      63.2 55.10 ! SOURCE3    4   0.0000   0.0000
+angle na   py   o       40.8 122.40 ! SOURCE3    2   0.0000   0.0000
+angle na   py   py      67.7 50.88 ! SOURCE3    2   0.0000   0.0000
+angle ne   py   ne      40.9 106.29 ! SOURCE3    1   0.0000   0.0000
+angle nf   py   nf      40.9 106.29 ! SOURCE3    1  same as ne-py-ne
+angle ne   py   o       42.3 113.21 ! SOURCE3   15   3.5046   3.8894
+angle nf   py   o       42.3 113.21 ! SOURCE3   15  same as ne-py-o
+angle ne   py   oh      42.6 104.70 ! SOURCE3   26   2.5109   2.7513
+angle nf   py   oh      42.6 104.70 ! SOURCE3   26  same as ne-py-oh
+angle ne   py   os      43.3 101.33 ! SOURCE3    2   0.0000   0.0000
+angle nf   py   os      43.3 101.33 ! SOURCE3    2  same as ne-py-os
+angle o    py   oh      43.7 116.14 ! SOURCE3   79   1.6546   2.1455
+angle o    py   os      43.6 116.79 ! SOURCE3   17   1.1002   1.3534
+angle o    py   pe      49.5 114.56 ! SOURCE3   12   2.8971   3.6114
+angle o    py   pf      49.5 114.56 ! SOURCE3   12  same as o -py-pe
+angle o    py   px      46.5 111.37 ! SOURCE3    5   0.3464   0.3803
+angle o    py   py      45.0 120.43 ! SOURCE3   16   5.2423   6.0629
+angle o    py   sx      34.7 118.56 ! SOURCE3    7   6.2302   6.2976
+angle o    py   sy      37.0 111.71 ! SOURCE3    5   1.0701   1.1937
+angle oh   py   oh      45.1 101.78 ! SOURCE3   35   1.6164   2.2937
+angle oh   py   pe      51.0 104.84 ! SOURCE3   22   1.8517   2.0337
+angle oh   py   pf      51.0 104.84 ! SOURCE3   22  same as oh-py-pe
+angle oh   py   px      47.7 104.30 ! SOURCE3    8   1.0656   1.2772
+angle oh   py   py      48.9 100.45 ! SOURCE3    6   0.0000   0.0000
+angle oh   py   sx      37.4 100.94 ! SOURCE3    4   0.0000   0.0000
+angle oh   py   sy      38.5 101.47 ! SOURCE3    6   0.2348   0.2490
+angle os   py   os      45.1 101.25 ! SOURCE3    8   1.4909   2.0860
+angle os   py   py      47.9 104.48 ! SOURCE3    4   0.0000   0.0000
+angle os   py   sx      36.9 103.86 ! SOURCE3    2   0.0000   0.0000
+angle os   py   sy      38.4 102.12 ! SOURCE3    2   0.0000   0.0000
+angle pe   py   pe      61.8 107.14 ! SOURCE3    1   0.0000   0.0000
+angle pf   py   pf      61.8 107.14 ! SOURCE3    1  same as pe-py-pe
+angle py   py   py      55.8 112.70 ! SOURCE3    1   0.0000   0.0000
+angle py   py   sx      58.7 61.54 ! SOURCE3    4   0.0000   0.0000
+angle sy   py   sy      36.1 105.17 ! SOURCE3    1   0.0000   0.0000
+angle c1   s2   o       65.8 117.25 ! SOURCE3    1   0.0000   0.0000
+angle c2   s2   n2      68.7 110.84 ! SOURCE3    1   0.0000   0.0000
+angle c2   s2   o       66.2 114.70 ! SOURCE2    1   0.0000   0.0000
+angle cl   s2   n1      53.4 117.70 ! SOURCE2    1   0.0000   0.0000
+angle f    s2   n1      66.1 116.90 ! SOURCE2    1   0.0000   0.0000
+angle n2   s2   o       67.9 121.20 ! SOURCE2    2   0.8000   0.8000
+angle o    s2   o       68.0 116.17 ! SOURCE3    1   0.0000   0.0000
+angle o    s2   s       63.8 118.30 ! SOURCE2    1   0.0000   0.0000
+angle s    s2   s       63.5 115.04 ! SOURCE3    1   0.0000   0.0000
+angle br   s4   br      64.6 98.02 ! SOURCE0    1
+angle br   s4   c3      62.2 92.98 ! SOURCE3    1   0.0000   0.0000
+angle br   s4   o       59.2 112.07 ! SOURCE3    1   0.0000   0.0000
+angle c    s4   c       63.3 86.83 ! SOURCE0    1
+angle c    s4   c3      61.5 95.07 ! SOURCE0    1
+angle c    s4   o       64.0 106.17 ! SOURCE0    1
+angle c1   s4   c1      65.4 93.55 ! SOURCE3    1   0.0000   0.0000
+angle c1   s4   o       66.0 110.36 ! SOURCE3    2   0.0000   0.0000
+angle c2   s4   c2      62.0 102.29 ! SOURCE0    1
+angle c2   s4   c3      63.5 94.95 ! SOURCE0    1
+angle c2   s4   o       66.6 107.09 ! SOURCE0    1
+angle c3   s4   c3      62.1 96.82 ! SOURCE3   11   1.3798   1.5580
+angle c3   s4   ca      63.0 95.00 ! SOURCE0    1
+angle c3   s4   f       66.3 91.70 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   hs      46.5 90.60 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   i       52.9 90.53 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   n       64.3 96.07 ! SOURCE3    4   0.8172   1.0354
+angle c3   s4   n2      69.1 90.59 ! SOURCE0    1
+angle c3   s4   n3      65.2 94.49 ! SOURCE3    4   1.5239   1.5570
+angle c3   s4   n4      62.0 92.47 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   na      65.1 93.07 ! SOURCE3   10   1.5730   1.8813
+angle c3   s4   nh      64.5 97.08 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   no      62.5 89.53 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   o       65.6 106.21 ! SOURCE3   60   1.3147   2.0426
+angle c3   s4   oh      67.3 93.88 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   os      68.2 90.06 ! SOURCE3    4   0.3752   0.4484
+angle c3   s4   p2      76.4 94.37 ! SOURCE0    1
+angle c3   s4   p3      78.2 96.54 ! SOURCE3    4   1.1521   1.3634
+angle c3   s4   p4      73.7 97.40 ! SOURCE0    1
+angle c3   s4   p5      78.3 99.18 ! SOURCE0    1
+angle c3   s4   s       61.3 98.72 ! SOURCE3    2   0.0185   0.0185
+angle c3   s4   s4      64.3 89.50 ! SOURCE0    1
+angle c3   s4   s6      61.6 97.48 ! SOURCE0    1
+angle c3   s4   sh      60.7 94.66 ! SOURCE3    1   0.0000   0.0000
+angle c3   s4   ss      60.6 95.31 ! SOURCE3    3   1.3294   1.4101
+angle ca   s4   ca      63.3 95.21 ! SOURCE0    1
+angle ca   s4   o       66.0 106.63 ! SOURCE0    1
+angle cl   s4   cl      53.9 97.68 ! SOURCE3    1   0.0000   0.0000
+angle cl   s4   o       58.1 108.34 ! SOURCE3    2   0.0000   0.0000
+angle cx   s4   cx      86.7 48.80 ! SOURCE2    1   0.0000   0.0000
+angle cx   s4   o       64.1 110.00 ! SOURCE2    1   0.0000   0.0000
+angle f    s4   f       70.0 92.71 ! SOURCE2    3   0.1400   0.1490
+angle f    s4   o       70.1 106.81 ! SOURCE2    2   0.0100   0.0100
+angle f    s4   s       59.8 107.50 ! SOURCE2    1   0.0000   0.0000
+angle hn   s4   hs      10.2 86.99 ! SOURCE0    1
+angle hs   s4   hs      37.8 87.00 ! SOURCE3    2   0.0202   0.0202
+angle hs   s4   o       49.8 110.27 ! SOURCE3    5   0.1523   0.1908
+angle i    s4   i       54.4 97.29 ! SOURCE0    1
+angle i    s4   o       47.3 113.91 ! SOURCE3    1   0.0000   0.0000
+angle n    s4   n       68.0 91.30 ! SOURCE0    1
+angle n    s4   o       68.1 105.70 ! SOURCE3    4   1.5958   1.6857
+angle n2   s4   n2      75.8 90.17 ! SOURCE0    1
+angle n2   s4   o       72.1 107.57 ! SOURCE0    1
+angle n3   s4   n3      68.9 91.19 ! SOURCE3    1   0.0000   0.0000
+angle n3   s4   o       67.6 109.07 ! SOURCE3    6   1.7576   2.3605
+angle n4   s4   n4      60.4 94.61 ! SOURCE3    1   0.0000   0.0000
+angle n4   s4   o       63.2 104.91 ! SOURCE3    3   0.4120   0.4370
+angle na   s4   na      63.6 103.10 ! SOURCE0    1
+angle na   s4   o       66.5 109.75 ! SOURCE3   10   2.2612   2.6919
+angle nh   s4   nh      69.0 92.24 ! SOURCE3    1   0.0000   0.0000
+angle nh   s4   o       68.4 107.54 ! SOURCE3    3   0.0378   0.0401
+angle no   s4   no      63.6 83.40 ! SOURCE3    1   0.0000   0.0000
+angle no   s4   o       62.9 103.58 ! SOURCE3    3   1.4245   1.5109
+angle o    s4   o       74.4 110.61 ! SOURCE3    5   3.5677   3.6413
+angle o    s4   oh      71.3 106.38 ! SOURCE3    3   0.9318   0.9883
+angle o    s4   os      69.7 109.02 ! SOURCE3    8   1.3833   1.5005
+angle o    s4   p2      76.2 106.77 ! SOURCE0    1
+angle o    s4   p3      79.8 106.51 ! SOURCE3    8   2.6099   4.0943
+angle o    s4   p4      75.4 103.36 ! SOURCE0    1
+angle o    s4   p5      85.3 96.95 ! SOURCE0    1
+angle o    s4   s       61.7 112.22 ! SOURCE3    4   2.8682   2.8682
+angle o    s4   s4      63.8 104.55 ! SOURCE0    1
+angle o    s4   s6      64.3 102.84 ! SOURCE0    1
+angle o    s4   sh      60.6 107.51 ! SOURCE3    3   0.7081   0.7511
+angle o    s4   ss      60.1 109.49 ! SOURCE3    5   1.4035   1.8509
+angle oh   s4   oh      70.1 100.34 ! SOURCE0    1
+angle os   s4   os      71.3 93.68 ! SOURCE3    2   2.4166   2.4166
+angle p2   s4   p2      98.2 92.62 ! SOURCE0    1
+angle p3   s4   p3     101.4 95.71 ! SOURCE3    2   1.2239   1.2239
+angle p5   s4   p5     104.7 93.86 ! SOURCE0    1
+angle s    s4   s       59.9 108.08 ! SOURCE3    1   0.0000   0.0000
+angle s4   s4   s4      65.4 90.17 ! SOURCE0    1
+angle s4   s4   s6      65.4 90.17 ! SOURCE0    1
+angle s6   s4   s6      64.2 93.52 ! SOURCE0    1
+angle sh   s4   sh      58.8 102.76 ! SOURCE3    1   0.0000   0.0000
+angle sh   s4   ss      58.9 102.64 ! SOURCE0    1
+angle ss   s4   ss      61.1 95.47 ! SOURCE3    1   0.0000   0.0000
+angle br   s6   br      67.1 101.57 ! SOURCE3    1   0.0000   0.0000
+angle br   s6   c3      63.2 98.99 ! SOURCE3    1   0.0000   0.0000
+angle br   s6   f       63.1 100.60 ! SOURCE2    1   0.0000   0.0000
+angle br   s6   o       63.9 107.58 ! SOURCE3    6   0.2828   0.3000
+angle c    s6   c       59.1 99.82 ! SOURCE0    1
+angle c    s6   c3      60.1 101.24 ! SOURCE0    1
+angle c    s6   o       64.0 107.97 ! SOURCE0    1
+angle c    s6   os      64.4 102.12 ! SOURCE0    1
+angle c1   s6   c1      64.1 99.99 ! SOURCE3    1   0.0000   0.0000
+angle c1   s6   o       68.1 108.23 ! SOURCE3    4   0.0000   0.0000
+angle c2   s6   c2      61.9 102.75 ! SOURCE0    1
+angle c2   s6   c3      61.2 104.05 ! SOURCE0    1
+angle c2   s6   o       67.5 106.58 ! SOURCE0    1
+angle c3   s6   c3      61.4 102.83 ! SOURCE3    7   1.1315   1.2531
+angle c3   s6   ca      61.5 103.17 ! SOURCE0    1
+angle c3   s6   f       65.7 97.11 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   hs      44.9 100.62 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   i       50.8 97.74 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   n       63.5 102.97 ! SOURCE3    4   0.8504   0.8785
+angle c3   s6   n2      65.3 107.47 ! SOURCE3    3   0.1592   0.1689
+angle c3   s6   n3      65.2 102.92 ! SOURCE3    3   0.8719   0.9248
+angle c3   s6   n4      61.3 99.40 ! SOURCE3    3   0.4426   0.4695
+angle c3   s6   na      63.7 102.81 ! SOURCE3   10   2.6278   3.1256
+angle c3   s6   nh      64.8 101.13 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   no      60.1 99.57 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   o       66.6 108.32 ! SOURCE3  112   1.2779   1.8014
+angle c3   s6   oh      68.6 95.95 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   os      68.9 95.65 ! SOURCE3    4   1.0005   1.0632
+angle c3   s6   p2      72.5 106.47 ! SOURCE0    1
+angle c3   s6   p3      76.2 103.40 ! SOURCE3    3   0.8029   0.8516
+angle c3   s6   p4      71.1 104.12 ! SOURCE0    1
+angle c3   s6   p5      77.2 103.46 ! SOURCE0    1
+angle c3   s6   s       60.7 104.50 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   s4      61.8 98.10 ! SOURCE0    1
+angle c3   s6   s6      60.6 101.95 ! SOURCE0    1
+angle c3   s6   sh      60.1 101.84 ! SOURCE3    1   0.0000   0.0000
+angle c3   s6   ss      59.7 102.47 ! SOURCE3    3   1.6453   1.7451
+angle ca   s6   ca      61.8 103.08 ! SOURCE0    1
+angle ca   s6   o       67.3 107.49 ! SOURCE0    1
+angle cl   s6   cl      52.9 101.25 ! SOURCE3    1   0.0000   0.0000
+angle cl   s6   f       57.9 99.00 ! SOURCE2    1   0.0000   0.0000
+angle cl   s6   o       58.5 107.66 ! SOURCE3    4   0.0000   0.0000
+angle cx   s6   cx      86.2 54.70 ! SOURCE2    1   0.0000   0.0000
+angle f    s6   f       70.8 94.70 ! SOURCE2    3   0.9333   0.9899
+angle f    s6   o       72.4 106.48 ! SOURCE3    2   0.0000   0.0000
+angle hs   s6   hs      35.8 99.02 ! SOURCE3    2   0.0595   0.0595
+angle hs   s6   o       51.9 107.60 ! SOURCE3   10   0.0263   0.0343
+angle i    s6   i       53.9 99.25 ! SOURCE0    1
+angle i    s6   o       47.7 109.74 ! SOURCE3    2   0.0000   0.0000
+angle n    s6   n       65.5 104.16 ! SOURCE0    1
+angle n    s6   o       70.2 105.91 ! SOURCE3    8   0.2938   0.2953
+angle n2   s6   n2      76.1 98.61 ! SOURCE0    1
+angle n2   s6   o       72.6 116.41 ! SOURCE3    3   4.7923   5.0830
+angle n2   s6   oh      72.1 106.96 ! SOURCE3    2   0.0000   0.0000
+angle n2   s6   os      73.7 103.25 ! SOURCE0    1
+angle n3   s6   n3      69.5 104.28 ! SOURCE3    2   6.7537   6.7537
+angle n3   s6   o       72.9 106.80 ! SOURCE3   14   1.7070   1.7908
+angle n4   s6   n4      60.0 101.85 ! SOURCE3    1   0.0000   0.0000
+angle n4   s6   o       65.8 102.92 ! SOURCE3   10   1.5005   1.5434
+angle na   s6   na      67.7 98.04 ! SOURCE0    1
+angle na   s6   o       70.5 105.67 ! SOURCE3   20   0.6876   0.8019
+angle nh   s6   nh      70.4 94.56 ! SOURCE3    1   0.0000   0.0000
+angle nh   s6   o       70.3 109.12 ! SOURCE3    6   0.9009   0.9556
+angle no   s6   no      61.2 91.63 ! SOURCE3    1   0.0000   0.0000
+angle no   s6   o       62.7 107.43 ! SOURCE3    6   1.4608   1.5494
+angle o    s6   o       74.6 119.82 ! SOURCE3   84   4.1358   5.2345
+angle o    s6   oh      74.1 108.21 ! SOURCE3   18   0.5949   0.7437
+angle o    s6   os      74.6 107.84 ! SOURCE3   12   0.6328   0.7025
+angle o    s6   p2      76.6 106.61 ! SOURCE0    1
+angle o    s6   p3      80.2 107.07 ! SOURCE3   22   1.0007   1.0550
+angle o    s6   p4      73.9 105.67 ! SOURCE0    1
+angle o    s6   p5      81.7 106.64 ! SOURCE0    1
+angle o    s6   s       63.5 110.29 ! SOURCE3    6   2.1124   2.2405
+angle o    s6   s4      63.0 107.85 ! SOURCE0    1
+angle o    s6   s6      63.6 106.07 ! SOURCE0    1
+angle o    s6   sh      62.5 106.81 ! SOURCE3    6   0.5932   0.6292
+angle o    s6   ss      62.1 107.43 ! SOURCE3   10   1.0654   1.1423
+angle oh   s6   p2      75.1 109.67 ! SOURCE3    2   0.0000   0.0000
+angle oh   s6   oh      73.7 100.34 ! SOURCE3    6   0.0076   0.0076
+angle os   s6   os      74.8 98.70 ! SOURCE3    1   0.0000   0.0000
+angle p3   s6   p3      94.9 110.17 ! SOURCE3    4   5.3595   5.3678
+angle p5   s6   p5      99.2 104.49 ! SOURCE0    1
+angle s    s6   s       60.6 109.34 ! SOURCE3    1   0.0000   0.0000
+angle s4   s6   s4      61.5 101.99 ! SOURCE0    1
+angle s4   s6   s6      65.4 90.17 ! SOURCE0    1
+angle s6   s6   s6      61.1 103.29 ! SOURCE0    1
+angle sh   s6   sh      59.4 106.43 ! SOURCE3    1   0.0000   0.0000
+angle sh   s6   ss      60.4 102.64 ! SOURCE0    1
+angle ss   s6   ss      60.5 101.82 ! SOURCE3    1   0.0000   0.0000
+angle br   sh   hs      43.5 95.64 ! SOURCE3    1   0.0000   0.0000
+angle c    sh   hs      46.0 96.07 ! SOURCE3    6   0.9968   1.1164
+angle c2   sh   hs      46.2 95.94 ! SOURCE3    3   0.3373   0.3578
+angle c3   sh   hs      44.9 96.60 ! SOURCE3   12   0.6315   0.8009
+angle ca   sh   hs      45.7 96.61 ! SOURCE3    2   0.2867   0.2867
+angle f    sh   hs      48.1 96.50 ! SOURCE3    1   0.0000   0.0000
+angle hs   sh   hs      37.4 93.72 ! SOURCE3    3   0.4504   0.4777
+angle hs   sh   i       37.1 96.44 ! SOURCE3    1   0.0000   0.0000
+angle hs   sh   n       48.4 95.59 ! SOURCE3    4   3.3575   3.9065
+angle hs   sh   n2      48.1 95.82 ! SOURCE3    5   2.3697   3.1495
+angle hs   sh   n3      48.1 95.98 ! SOURCE3    3   1.1064   1.1735
+angle hs   sh   n4      47.1 93.13 ! SOURCE3    3   0.1579   0.1675
+angle hs   sh   na      48.1 97.38 ! SOURCE3    9   0.7962   1.0223
+angle hs   sh   nh      47.5 101.11 ! SOURCE3    1   0.0000   0.0000
+angle hs   sh   no      47.4 92.93 ! SOURCE3    1   0.0000   0.0000
+angle hs   sh   o       48.2 109.23 ! SOURCE3    2   0.0068   0.0068
+angle hs   sh   oh      48.7 98.64 ! SOURCE3    2   0.0605   0.0605
+angle hs   sh   os      49.3 98.15 ! SOURCE3    3   0.1566   0.1661
+angle hs   sh   p2      56.5 99.12 ! SOURCE3   10   4.3193   5.4110
+angle hs   sh   p3      53.1 95.81 ! SOURCE3    3   0.4144   0.4396
+angle hs   sh   p4      54.0 94.22 ! SOURCE3    4   0.7391   0.7605
+angle hs   sh   p5      54.8 94.52 ! SOURCE3    3   0.5269   0.5589
+angle hs   sh   s       40.9 102.87 ! SOURCE3    2   0.0000   0.0000
+angle hs   sh   s4      42.0 92.16 ! SOURCE3    3   1.5574   1.6519
+angle hs   sh   s6      42.9 93.83 ! SOURCE3    3   1.1843   1.2561
+angle hs   sh   sh      42.8 99.07 ! SOURCE3    2   0.0000   0.0000
+angle hs   sh   ss      42.6 99.17 ! SOURCE3    3   0.2316   0.2457
+angle br   ss   br      67.0 102.92 ! SOURCE3    1   0.0000   0.0000
+angle br   ss   c3      63.1 99.03 ! SOURCE3    1   0.0000   0.0000
+angle c    ss   c       62.2 101.40 ! SOURCE3    1   0.0000   0.0000
+angle c    ss   c2      65.6 92.43 ! SOURCE3    1   0.0000   0.0000
+angle c    ss   c3      61.5 100.29 ! SOURCE3    5   1.7050   2.2127
+angle c1   ss   c1      66.3 98.30 ! SOURCE2    1   0.0000   0.0000
+angle c1   ss   c3      62.9 99.90 ! SOURCE2    1   0.0000   0.0000
+angle c2   ss   c2      63.7 99.56 ! SOURCE3    1   0.0000   0.0000
+angle c2   ss   c3      63.4 95.73 ! SOURCE3    7   4.4315   4.6586
+angle c2   ss   n2      64.5 106.76 ! SOURCE3    1   0.0000   0.0000
+angle c2   ss   na      65.0 100.51 ! SOURCE3    6   6.4994   6.9702
+angle c2   ss   os      69.8 89.76 ! SOURCE3    1   0.0000   0.0000
+angle c2   ss   ss      64.8 92.26 ! SOURCE3    1   0.0000   0.0000
+angle c3   ss   c3      60.6 99.92 ! SOURCE3   14   1.4406   2.0723
+angle c3   ss   ca      61.3 100.62 ! SOURCE3    2   1.9639   1.9639
+angle c3   ss   cl      56.4 99.40 ! SOURCE2    1   0.0000   0.0000
+angle c3   ss   f       63.4 97.49 ! SOURCE3    1   0.0000   0.0000
+angle c3   ss   ha      11.1 96.50 ! SOURCE2    1   0.0000   0.0000
+angle c3   ss   i       56.0 100.00 ! SOURCE3    1   0.0000   0.0000
+angle c3   ss   n       63.7 100.30 ! SOURCE3    4   0.4972   0.6579
+angle c3   ss   n2      65.9 96.42 ! SOURCE3    5   1.1712   1.3604
+angle c3   ss   n3      64.1 98.83 ! SOURCE3    3   0.2742   0.2909
+angle c3   ss   n4      62.9 97.79 ! SOURCE3    3   0.1887   0.2002
+angle c3   ss   na      63.5 100.14 ! SOURCE3   12   1.3814   1.7415
+angle c3   ss   nh      63.7 100.63 ! SOURCE3    1   0.0000   0.0000
+angle c3   ss   no      62.4 98.62 ! SOURCE3    1   0.0000   0.0000
+angle c3   ss   o       64.7 106.60 ! SOURCE3    2   1.6714   1.6714
+angle c3   ss   oh      65.3 98.28 ! SOURCE3    2   1.4326   1.4326
+angle c3   ss   os      65.0 98.21 ! SOURCE3    4   1.4684   1.7097
+angle c3   ss   p2      80.2 98.41 ! SOURCE3    8   0.8438   0.9454
+angle c3   ss   p3      76.3 98.70 ! SOURCE3    3   0.0336   0.0356
+angle c3   ss   p4      76.9 98.16 ! SOURCE3    4   0.1322   0.1361
+angle c3   ss   p5      76.4 99.03 ! SOURCE3    3   0.5264   0.5584
+angle c3   ss   s       59.9 101.90 ! SOURCE3    1   0.0000   0.0000
+angle c3   ss   s4      60.1 96.37 ! SOURCE3    3   0.0190   0.0202
+angle c3   ss   s6      60.9 96.76 ! SOURCE3    3   1.4784   1.5680
+angle c3   ss   sh      60.4 101.93 ! SOURCE3    1   0.0000   0.0000
+angle c3   ss   ss      59.8 104.90 ! SOURCE3   25   3.6163   3.9326
+angle ca   ss   ca      62.4 99.92 ! SOURCE3    1   0.0000   0.0000
+angle ca   ss   cc      63.9 96.98 ! SOURCE3    2   0.0000   0.0000
+angle ca   ss   cd      63.9 96.98 ! SOURCE3    2   0.0000   0.0000
+angle ca   ss   cl      56.5 101.05 ! SOURCE3    1   0.0000   0.0000
+angle ca   ss   n       65.8 97.16 ! SOURCE0    1
+angle ca   ss   na      64.8 99.32 ! SOURCE0    1
+angle ca   ss   nc      69.2 94.76 ! SOURCE3    1   0.0000   0.0000
+angle ca   ss   nd      69.2 94.76 ! SOURCE3    1  same as ca-ss-nc
+angle cc   ss   cc      67.0 89.91 ! SOURCE3   11   2.0978   2.2164
+angle cd   ss   cd      67.0 89.91 ! SOURCE3   11   2.0978   2.2164
+angle cc   ss   cd      61.2 107.91 ! SOURCE3    1   0.0000   0.0000
+angle cc   ss   nc      69.7 95.66 ! SOURCE3    3   0.0000   0.0000
+angle cd   ss   nd      69.7 95.66 ! SOURCE3    3   0.0000   0.0000
+angle cc   ss   os      66.5 98.81 ! SOURCE3    2   2.1583   2.1583
+angle cd   ss   os      66.5 98.81 ! SOURCE3    2  same as cc-ss-os
+angle cc   ss   ss      61.5 102.46 ! SOURCE3    2   0.0000   0.0000
+angle cd   ss   ss      61.5 102.46 ! SOURCE3    2  same as cc-ss-ss
+angle cd   ss   n       66.4 97.16 ! SOURCE3    1   0.0000   0.0000
+angle cc   ss   n       66.4 97.16 ! SOURCE3    1  same as cd-ss-n
+angle cd   ss   na      65.4 99.33 ! SOURCE3   18   1.9415   2.5847
+angle cc   ss   na      65.4 99.33 ! SOURCE3   18  same as cd-ss-na
+angle cl   ss   cl      52.4 103.37 ! SOURCE3    1   0.0000   0.0000
+angle cx   ss   cx      87.5 48.30 ! SOURCE2    1   0.0000   0.0000
+angle f    ss   f       66.2 98.30 ! SOURCE2    1   0.0000   0.0000
+angle f    ss   ss      59.9 108.30 ! SOURCE2    1   0.0000   0.0000
+angle i    ss   i       58.1 106.29 ! SOURCE3    1   0.0000   0.0000
+angle n    ss   n       66.4 103.10 ! SOURCE3    1   0.0000   0.0000
+angle n2   ss   n2      71.1 96.75 ! SOURCE3    1   0.0000   0.0000
+angle n3   ss   n3      66.5 102.34 ! SOURCE3    1   0.0000   0.0000
+angle n4   ss   n4      63.6 101.19 ! SOURCE3    1   0.0000   0.0000
+angle na   ss   na      65.9 102.81 ! SOURCE3    1   0.0000   0.0000
+angle nc   ss   nc      74.4 99.50 ! SOURCE2    1   0.0000   0.0000
+angle nd   ss   nd      74.4 99.50 ! SOURCE2    1  same as nc-ss-nc
+angle nh   ss   nh      65.3 107.89 ! SOURCE3    1   0.0000   0.0000
+angle no   ss   no      61.4 106.43 ! SOURCE3    1   0.0000   0.0000
+angle o    ss   o       69.8 119.30 ! SOURCE2    1   0.0000   0.0000
+angle o    ss   p5      78.8 106.41 ! SOURCE3    1   0.0000   0.0000
+angle o    ss   s6      62.4 105.39 ! SOURCE0    1
+angle o    ss   ss      62.0 112.70 ! SOURCE2    1   0.0000   0.0000
+angle oh   ss   oh      68.2 104.25 ! SOURCE3    1   0.0000   0.0000
+angle os   ss   os      67.8 102.99 ! SOURCE3    1   0.0000   0.0000
+angle p2   ss   p2     105.5 99.52 ! SOURCE3    1   0.0000   0.0000
+angle p3   ss   p3      96.8 101.67 ! SOURCE0    1
+angle p5   ss   p5     103.5 89.83 ! SOURCE3    1   0.0000   0.0000
+angle s    ss   s       57.7 115.04 ! SOURCE0    1
+angle s4   ss   s4      60.9 96.08 ! SOURCE3    1   0.0000   0.0000
+angle s4   ss   s6      59.9 101.26 ! SOURCE0    1
+angle s6   ss   s6      60.5 101.81 ! SOURCE3    1   0.0000   0.0000
+angle sh   ss   sh      60.3 107.54 ! SOURCE3    1   0.0000   0.0000
+angle sh   ss   ss      60.8 106.53 ! SOURCE0    1
+angle ss   ss   ss      60.4 108.71 ! SOURCE3    1   0.0000   0.0000
+angle c    sx   c       62.8 86.85 ! SOURCE3    1   0.0000   0.0000
+angle c    sx   c3      62.0 92.71 ! SOURCE3    3   0.2918   0.3095
+angle c    sx   o       63.5 106.17 ! SOURCE3    5   0.8807   0.9477
+angle c3   sx   ca      62.6 95.00 ! SOURCE3    1   0.0000   0.0000
+angle c3   sx   ce      62.8 94.95 ! SOURCE3    3   0.0006   0.0007
+angle c3   sx   cf      62.8 94.95 ! SOURCE3    3  same as c3-sx-ce
+angle c3   sx   ne      65.3 90.06 ! SOURCE3    5   1.4630   1.9627
+angle c3   sx   nf      65.3 90.06 ! SOURCE3    5  same as c3-sx-ne
+angle c3   sx   o       64.9 107.88 ! SOURCE3   30   0.7662   0.8721
+angle c3   sx   pe      77.0 94.32 ! SOURCE3    7   0.5270   0.5547
+angle c3   sx   pf      77.0 94.32 ! SOURCE3    7  same as c3-sx-pe
+angle c3   sx   px      74.3 96.46 ! SOURCE3    3   1.2587   1.3351
+angle c3   sx   py      74.1 95.67 ! SOURCE3    1   0.0000   0.0000
+angle c3   sx   sx      57.2 91.47 ! SOURCE3    4   1.9919   1.9919
+angle c3   sx   sy      58.7 95.47 ! SOURCE3    3   2.6797   2.8422
+angle ca   sx   ca      62.6 95.21 ! SOURCE3    1   0.0000   0.0000
+angle ca   sx   o       65.3 106.92 ! SOURCE3    3   0.3800   0.4031
+angle ce   sx   ce      63.0 94.96 ! SOURCE3    1   0.0000   0.0000
+angle cf   sx   cf      63.0 94.96 ! SOURCE3    1  same as ce-sx-ce
+angle ce   sx   o       65.4 107.47 ! SOURCE3    5   0.3064   0.3128
+angle cf   sx   o       65.4 107.47 ! SOURCE3    5  same as ce-sx-o
+angle ne   sx   ne      66.4 90.17 ! SOURCE3    1   0.0000   0.0000
+angle nf   sx   nf      66.4 90.17 ! SOURCE3    1  same as ne-sx-ne
+angle ne   sx   o       65.4 109.20 ! SOURCE3    7   1.3979   1.4542
+angle nf   sx   o       65.4 109.20 ! SOURCE3    7  same as ne-sx-o
+angle o    sx   pe      77.0 106.43 ! SOURCE3    9   2.1093   2.8345
+angle o    sx   pf      77.0 106.43 ! SOURCE3    9  same as o -sx-pe
+angle o    sx   px      75.2 104.77 ! SOURCE3    3   1.8677   1.9810
+angle o    sx   py      73.1 109.13 ! SOURCE3    7   5.2928   5.6840
+angle o    sx   sx      55.7 104.65 ! SOURCE3    6   2.8778   3.0524
+angle o    sx   sy      59.4 103.41 ! SOURCE3    5   0.7671   0.9618
+angle pe   sx   pe      99.2 92.62 ! SOURCE3    1   0.0000   0.0000
+angle pf   sx   pf      99.2 92.62 ! SOURCE3    1  same as pe-sx-pe
+angle py   sx   py     110.1 69.23 ! SOURCE3    3  16.4184  17.4143
+angle sx   sx   sx      58.6 84.90 ! SOURCE3    1   0.0000   0.0000
+angle sy   sx   sy      59.2 93.52 ! SOURCE3    1   0.0000   0.0000
+angle c    sy   c       59.2 99.81 ! SOURCE3    1   0.0000   0.0000
+angle c    sy   c3      60.1 101.25 ! SOURCE3    3   1.1172   1.1850
+angle c    sy   o       64.1 107.23 ! SOURCE3   10   0.8191   0.8425
+angle c3   sy   ca      60.9 103.17 ! SOURCE3    1   0.0000   0.0000
+angle c3   sy   ce      60.8 103.81 ! SOURCE3    3   0.3175   0.3368
+angle c3   sy   cf      60.8 103.81 ! SOURCE3    3  same as c3-sy-ce
+angle c3   sy   ne      63.0 103.12 ! SOURCE3    5   3.6732   4.1882
+angle c3   sy   nf      63.0 103.12 ! SOURCE3    5  same as c3-sy-ne
+angle c3   sy   o       66.0 108.48 ! SOURCE3   62   0.7297   0.8576
+angle c3   sy   pe      71.8 106.03 ! SOURCE3    6   2.3845   2.6117
+angle c3   sy   pf      71.8 106.03 ! SOURCE3    6  same as c3-sy-pe
+angle c3   sy   px      71.7 103.62 ! SOURCE3    3   0.6673   0.7078
+angle c3   sy   py      73.5 103.39 ! SOURCE3    3   0.4302   0.4563
+angle c3   sy   sx      56.3 104.64 ! SOURCE3    3   4.3630   4.6276
+angle c3   sy   sy      57.4 100.78 ! SOURCE3    4   1.1633   1.1633
+angle ca   sy   ca      62.1 99.34 ! SOURCE3    3   4.9868   5.2893
+angle ca   sy   nh      65.2 100.83 ! SOURCE3    1   0.0000   0.0000
+angle ca   sy   o       65.8 108.73 ! SOURCE3   26   1.1018   1.2638
+angle ca   sy   oh      65.7 101.84 ! SOURCE3    4   0.9524   1.1069
+angle ca   sy   os      67.6 92.98 ! SOURCE3    1   0.0000   0.0000
+angle ce   sy   ce      61.1 102.78 ! SOURCE3    1   0.0000   0.0000
+angle cf   sy   cf      61.1 102.78 ! SOURCE3    1  same as ce-sy-ce
+angle ce   sy   o       66.4 107.25 ! SOURCE3   10   0.5364   0.5477
+angle cf   sy   o       66.4 107.25 ! SOURCE3   10  same as ce-sy-o
+angle na   sy   na      67.7 98.04 ! SOURCE0    1
+angle nc   sy   nc      75.2 98.04 ! SOURCE0    2
+angle nd   sy   nd      75.2 98.04 ! SOURCE0    2
+angle ne   sy   ne      66.6 98.62 ! SOURCE3    1   0.0000   0.0000
+angle nf   sy   nf      66.6 98.62 ! SOURCE3    1  same as ne-sy-ne
+angle ne   sy   o       68.9 107.06 ! SOURCE3   14   2.1834   2.2705
+angle nf   sy   o       68.9 107.06 ! SOURCE3   14  same as ne-sy-o
+angle nh   sy   o       70.9 108.46 ! SOURCE3    2   0.0000   0.0000
+angle o    sy   o       72.4 121.88 ! SOURCE3   46   0.7319   0.9495
+angle o    sy   oh      72.7 106.93 ! SOURCE3    8   0.6101   0.7424
+angle o    sy   os      70.6 107.62 ! SOURCE3    2   0.0000   0.0000
+angle o    sy   pe      75.4 106.90 ! SOURCE3   12   1.4233   1.4524
+angle o    sy   pf      75.4 106.90 ! SOURCE3   12  same as o -sy-pe
+angle o    sy   px      74.4 106.17 ! SOURCE3    6   0.6656   0.7059
+angle o    sy   py      76.5 106.67 ! SOURCE3   10   0.6188   0.6478
+angle o    sy   sx      58.6 106.33 ! SOURCE3   10   1.9984   2.0456
+angle o    sy   sy      58.8 106.19 ! SOURCE3   12   0.1653   0.1754
+angle py   sy   py      92.8 104.49 ! SOURCE3    1   0.0000   0.0000
+angle sx   sy   sx      56.7 101.99 ! SOURCE3    1   0.0000   0.0000
+angle sy   sy   sy      56.5 103.29 ! SOURCE3    1   0.0000   0.0000
+angle br   n1   c1      51.1 180.00 ! HF/6-31G*
+angle c1   n1   c1      64.9 179.92 ! HF/6-31G*
+angle c1   n1   c2      60.2 177.73 ! HF/6-31G*
+angle c1   n1   c3      59.9 180.00 ! HF/6-31G*
+angle c1   n1   ca      59.9 179.99 ! HF/6-31G*
+angle c1   n1   cl      50.1 179.95 ! HF/6-31G*
+angle c1   n1   f       55.9 179.96 ! HF/6-31G*
+angle c1   n1   hn      45.6 179.98 ! HF/6-31G*
+angle c1   n1   i       46.2 179.95 ! HF/6-31G*
+angle c1   n1   n1      66.9 180.00 ! HF/6-31G*
+angle c1   n1   n2      65.7 171.56 ! HF/6-31G*
+angle c1   n1   n3      60.7 175.59 ! HF/6-31G*
+angle c1   n1   n4      59.7 179.69 ! HF/6-31G*
+angle c1   n1   na      59.9 180.00 ! HF/6-31G*
+angle c1   n1   nh      60.9 176.35 ! HF/6-31G*
+angle c1   n1   o       62.6 179.95 ! HF/6-31G*
+angle c1   n1   oh      62.9 174.31 ! HF/6-31G*
+angle c1   n1   os      62.2 176.61 ! HF/6-31G*
+angle c1   n1   p2      83.5 172.83 ! HF/6-31G*
+angle c1   n1   p3      68.7 173.51 ! HF/6-31G*
+angle c1   n1   p4      68.7 173.64 ! HF/6-31G*
+angle c1   n1   p5      71.5 177.28 ! HF/6-31G*
+angle c1   n1   s       53.3 179.99 ! HF/6-31G*
+angle c1   n1   s2      60.3 178.11 ! HF/6-31G*
+angle c1   n1   s4      55.2 169.60 ! HF/6-31G*
+angle c1   n1   s6      61.8 175.92 ! HF/6-31G*
+angle c1   n1   sh      55.7 174.25 ! HF/6-31G*
+angle c1   n1   ss      55.4 176.06 ! HF/6-31G*
+angle c1   ng   ng      51.9 179.97 ! HF/6-31G*
+angle c2   c1   n1      63.1 180.00 ! HF/6-31G*
+angle c2   c2   n1      71.7 122.98 ! HF/6-31G*
+angle c2   n1   n1      61.6 180.00 ! HF/6-31G*
+angle c2   n2   n1      75.5 115.09 ! HF/6-31G*
+angle c2   na   n1      66.3 124.81 ! HF/6-31G*
+angle c3   n1   n1      58.8 180.00 ! HF/6-31G*
+angle c3   os   n1      66.3 113.50 ! HF/6-31G*
+angle c3   ss   n1      65.9 98.44 ! HF/6-31G*
+angle ca   ca   n1      63.4 118.50 ! HF/6-31G*
+angle ca   n1   n1      59.0 180.00 ! HF/6-31G*
+angle ca   nh   n1      69.4 117.13 ! HF/6-31G*
+angle cl   n1   n1      51.4 179.94 ! HF/6-31G*
+angle f    n1   n1      57.4 179.93 ! HF/6-31G*
+angle h1   c3   n1      60.3 107.31 ! HF/6-31G*
+angle hn   n1   n1      47.1 179.91 ! HF/6-31G*
+angle hn   n3   n1      52.0 110.17 ! HF/6-31G*
+angle hn   n4   n1      51.8 109.39 ! HF/6-31G*
+angle hn   nh   n1      52.3 110.57 ! HF/6-31G*
+angle ho   oh   n1      52.5 107.81 ! HF/6-31G*
+angle hp   p3   n1      31.3 92.98 ! HF/6-31G*
+angle hp   p4   n1      30.5 99.91 ! HF/6-31G*
+angle hp   p5   n1      31.1 101.32 ! HF/6-31G*
+angle hs   s4   n1      51.2 90.51 ! HF/6-31G*
+angle hs   s6   n1      54.7 97.27 ! HF/6-31G*
+angle hs   sh   n1      51.7 93.51 ! HF/6-31G*
+angle i    n1   n1      47.3 179.94 ! HF/6-31G*
+angle n1   n1   n1      69.0 179.97 ! HF/6-31G*
+angle n1   n1   n2      67.7 171.57 ! HF/6-31G*
+angle n1   n1   n3      62.5 175.09 ! HF/6-31G*
+angle n1   n1   n4      61.3 179.91 ! HF/6-31G*
+angle n1   n1   na      61.6 179.97 ! HF/6-31G*
+angle n1   n1   nh      62.7 176.00 ! HF/6-31G*
+angle n1   n1   o       64.4 179.94 ! HF/6-31G*
+angle n1   n1   oh      64.8 173.77 ! HF/6-31G*
+angle n1   n1   os      64.0 176.12 ! HF/6-31G*
+angle n1   n1   p2      69.4 174.71 ! HF/6-31G*
+angle n1   n1   p3      70.2 174.27 ! HF/6-31G*
+angle n1   n1   s       54.6 179.96 ! HF/6-31G*
+angle n1   n1   sh      57.0 175.07 ! HF/6-31G*
+angle n1   n1   ss      56.9 175.61 ! HF/6-31G*
+angle n1   p4   o       49.7 114.59 ! HF/6-31G*
+angle n1   p5   o       44.7 113.78 ! HF/6-31G*
+angle n1   s2   o       73.1 108.46 ! HF/6-31G*
+angle n1   s4   o       70.2 110.09 ! HF/6-31G*
+angle n1   s6   o       78.8 107.52 ! HF/6-31G*
+angle n1   c1   n1      70.2 180.00 ! HF/6-31G*
+angle n1   c2   n1      73.6 124.15 ! HF/6-31G*
+angle n1   c3   n1      80.0 105.07 ! HF/6-31G*
+angle n1   ca   n1      75.8 117.03 ! HF/6-31G*
+angle n1   n2   n1      80.8 112.00 ! HF/6-31G*
+angle n1   n3   n1      72.4 113.21 ! HF/6-31G*
+angle n1   n4   n1      72.7 110.67 ! HF/6-31G*
+angle n1   no   o       73.8 115.00 ! HF/6-31G*
+angle n1   nh   n1      75.1 106.71 ! HF/6-31G*
+angle n1   os   n1      70.3 117.79 ! HF/6-31G*
+angle n1   ss   n1      73.1 96.96 ! HF/6-31G*
+angle n1   s4   n1      72.4 94.02 ! HF/6-31G*
+angle n1   s6   n1      83.7 95.52 ! HF/6-31G*
+angle n1   p2   n1      46.5 86.22 ! HF/6-31G*
+angle n1   p3   n1      45.9 90.44 ! HF/6-31G*
+angle n1   p4   n1      43.5 100.61 ! HF/6-31G*
+angle n1   p5   n1      45.8 101.55 ! HF/6-31G*
+angle o    c1   o       69.3 180.00 ! Guess
+angle c3   c1   c3      51.7 180.00 ! Guess
+angle ca   c1   ca      52.8 180.00 ! Guess
+angle f    c1   f       58.2 180.00 ! Guess
+angle cl   c1   cl      46.6 180.00 ! Guess
+angle br   c1   br      57.8 180.00 ! Guess
+angle i    c1   i       53.4 180.00 ! Guess
+angle s    c1   s       54.6 180.00 ! Guess
+angle s2   c1   s2      55.8 180.00 ! Guess
+angle s4   c1   s4      51.0 180.00 ! Guess
+angle s6   c1   s6      51.7 180.00 ! Guess
+angle sh   c1   sh      53.0 180.00 ! Guess
+angle ss   c1   ss      53.0 180.00 ! Guess
+angle oh   c1   oh      60.9 180.00 ! Guess
+angle os   c1   os      61.0 180.00 ! Guess
+angle p2   c1   p2      80.6 180.00 ! Guess
+angle p3   c1   p3      79.7 180.00 ! Guess
+angle p4   c1   p4      79.7 180.00 ! Guess
+angle p5   c1   p5      81.4 180.00 ! Guess
+angle n    c1   n       60.0 180.00 ! Guess
+angle n2   c1   n2      66.0 180.00 ! Guess
+angle n3   c1   n3      57.4 180.00 ! Guess
+angle n4   c1   n4      56.3 180.00 ! Guess
+angle nh   c1   nh      59.0 180.00 ! Guess
+angle no   c1   no      56.8 180.00 ! Guess
+angle na   c1   na      58.6 180.00 ! Guess
+angle na   c1   na      58.6 180.00 ! Guess
+angle na   c1   na      58.6 180.00 ! Guess
+
+dihe X    c    c    X     0.30       2  180.00
+dihe X    c    c1   X     0.00       2  180.00
+dihe X    c    cg   X     0.00       2  180.00 ! same as X-c-c1-X
+dihe X    c    ch   X     0.00       2  180.00
+dihe X    c    c2   X     2.17       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    c    cu   X     2.17       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    c    cv   X     2.17       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    c    ce   X     2.17       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    c    cf   X     2.17       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    c    c3   X     0.00       2  180.00 ! JCC, 7, (1986), 230
+dihe X    c    cx   X     0.00       2  180.00 ! JCC, 7, (1986), 230
+dihe X    c    cy   X     0.00       2  180.00 ! JCC, 7, (1986), 230
+dihe X    c    ca   X     3.62       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    c    cc   X     2.88       2  180.00 ! statistic value
+dihe X    c    cd   X     2.88       2  180.00 ! statistic value
+dihe X    c    n    X     2.50       2  180.00 ! AA,NMA (no c-n3, c-n4, c-nh)
+dihe X    c    n2   X     4.15       2  180.00 ! double bond, same as X-c2-n2-X
+dihe X    c    nc   X     4.00       2  180.00 ! same as X-C-NC-X
+dihe X    c    nd   X     4.00       2  180.00 ! same as X-C-NC-X
+dihe X    c    ne   X     0.20       2  180.00 ! single bond
+dihe X    c    nf   X     0.20       2  180.00 ! single bond
+dihe X    c    na   X     0.35       4  180.00
+dihe X    c    no   X     0.45       2  180.00
+dihe X    c    oh   X     2.30       2  180.00 ! Junmei et al, 1999
+dihe X    c    os   X     2.70       2  180.00 ! Junmei et al, 1999
+dihe X    c    pc   X     2.00       2  180.00 ! estimated
+dihe X    c    p2   X     6.65       2  180.00 ! double bond, same as X-c2-p2-X
+dihe X    c    pc   X     2.00       2  180.00 ! estimated
+dihe X    c    pd   X     2.00       2  180.00 ! estimated
+dihe X    c    pe   X     0.00       2  180.00 ! single bond
+dihe X    c    pf   X     0.00       2  180.00 ! single bond
+dihe X    c    p3   X     1.55       2  180.00
+dihe X    c    p4   X     1.35       2  180.00
+dihe X    c    px   X     1.35       2  180.00
+dihe X    c    p5   X     1.00       2    0.00
+dihe X    c    py   X     1.00       2    0.00
+dihe X    c    sh   X     2.25       2  180.00
+dihe X    c    ss   X     3.10       2  180.00
+dihe X    c    s4   X     0.20       2  180.00
+dihe X    c    sx   X     0.20       2  180.00
+dihe X    c    s6   X     0.50       2    0.00
+dihe X    c    sy   X     0.50       2    0.00
+dihe X    c1   c1   X     0.00       2  180.00 ! for both triple and single bonds
+dihe X    c1   cg   X     0.00       2  180.00 ! for both triple and single bonds
+dihe X    c1   ch   X     0.00       2  180.00 ! for both triple and single bonds
+dihe X    cg   cg   X     0.00       2  180.00 ! for both triple and single bonds
+dihe X    ch   ch   X     0.00       2  180.00 ! for both triple and single bonds
+dihe X    cg   ch   X     0.00       2  180.00 ! for both triple and single bonds
+dihe X    c1   c2   X     0.00       2  180.00
+dihe X    c1   c3   X     0.00       2  180.00
+dihe X    c1   ca   X     0.00       2  180.00
+dihe X    c1   cb   X     0.00       2  180.00
+dihe X    c1   cc   X     0.00       2  180.00
+dihe X    c1   cd   X     0.00       2  180.00
+dihe X    c1   ce   X     0.00       2  180.00
+dihe X    c1   cf   X     0.00       2  180.00
+dihe X    c1   cu   X     0.00       2  180.00
+dihe X    c1   cv   X     0.00       2  180.00
+dihe X    c1   cx   X     0.00       2  180.00
+dihe X    c1   cy   X     0.00       2  180.00
+dihe X    c1   n    X     0.00       2  180.00
+dihe X    c1   n2   X     0.00       2  180.00
+dihe X    c1   n3   X     0.00       2  180.00
+dihe X    c1   n4   X     0.00       2  180.00
+dihe X    c1   na   X     0.00       2  180.00
+dihe X    c1   nb   X     0.00       2  180.00
+dihe X    c1   nc   X     0.00       2  180.00
+dihe X    c1   nd   X     0.00       2  180.00
+dihe X    c1   ne   X     0.00       2  180.00
+dihe X    c1   nf   X     0.00       2  180.00
+dihe X    c1   nh   X     0.00       2  180.00
+dihe X    c1   no   X     0.00       2  180.00
+dihe X    c1   oh   X     0.00       2  180.00
+dihe X    c1   os   X     0.00       2  180.00
+dihe X    c1   p2   X     0.00       2  180.00
+dihe X    c1   pb   X     0.00       2  180.00
+dihe X    c1   pc   X     0.00       2  180.00
+dihe X    c1   pd   X     0.00       2  180.00
+dihe X    c1   pe   X     0.00       2  180.00
+dihe X    c1   pf   X     0.00       2  180.00
+dihe X    c1   p3   X     0.00       2  180.00
+dihe X    c1   p4   X     0.00       2  180.00
+dihe X    c1   px   X     0.00       2  180.00
+dihe X    c1   p5   X     0.00       2  180.00
+dihe X    c1   py   X     0.00       2  180.00
+dihe X    c1   s2   X     0.00       2  180.00
+dihe X    c1   sh   X     0.00       2  180.00
+dihe X    c1   ss   X     0.00       2  180.00
+dihe X    c1   s4   X     0.00       2  180.00
+dihe X    c1   sx   X     0.00       2  180.00
+dihe X    c1   s6   X     0.00       2  180.00
+dihe X    c1   sy   X     0.00       2  180.00
+dihe X    c2   c2   X     6.65       2  180.00 ! c2=c2 double bond, intrpol.bsd.on C6H6
+dihe X    c2   ce   X     6.65       2  180.00 ! c2=c2 double bond, intrpol.bsd.on C6H6
+dihe X    c2   cf   X     6.65       2  180.00 ! c2=c2 double bond, intrpol.bsd.on C6H6
+dihe X    ce   cf   X     6.65       2  180.00 ! c2=c2 double bond, intrpol.bsd.on C6H6
+dihe X    ce   ce   X     1.00       2  180.00 ! c2-c2 single bond, parm99
+dihe X    cf   cf   X     1.00       2  180.00 ! c2-c2 single bond, parm99
+dihe X    cc   cd   X     4.00       2  180.00 ! statistic value of parm94
+dihe X    cc   cc   X     4.00       2  180.00 ! statistic value of parm94
+dihe X    cd   cd   X     4.00       2  180.00 ! statistic value of parm94
+dihe X    c2   c3   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    c2   ca   X     2.55       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    c2   n    X     0.65       2  180.00
+dihe X    c2   n2   X     4.15       2  180.00 ! double bond, parm99
+dihe X    c2   ne   X     4.15       2  180.00 ! double bond, parm99
+dihe X    c2   nf   X     4.15       2  180.00 ! double bond, parm99
+dihe X    ce   ne   X     0.80       2  180.00 ! single bond
+dihe X    cf   nf   X     0.80       2  180.00 ! single bond
+dihe X    c2   nc   X     4.75       2  180.00 ! statistic value from parm94
+dihe X    c2   nd   X     4.75       2  180.00 ! statistic value from parm94
+dihe X    cc   nd   X     4.75       2  180.00 ! statistic value from parm94
+dihe X    cd   nc   X     4.75       2  180.00 ! statistiv value from parm94
+dihe X    cc   nc   X     4.75       2  180.00 ! statistic value from parm94
+dihe X    cd   nd   X     4.75       2  180.00 ! statistiv value from parm94
+dihe X    c2   n3   X     0.30       2  180.00 ! intrpol.
+dihe X    c2   n4   X     0.00       3  180.00 ! intrpol.
+dihe X    c2   na   X     0.62       2  180.00
+dihe X    cc   na   X     1.70       2  180.00 ! statistic value from parm94
+dihe X    cd   na   X     1.70       2  180.00 ! statistic value from parm94
+dihe X    c2   nh   X     0.68       2  180.00
+dihe X    c2   no   X     0.75       2  180.00
+dihe X    c2   oh   X     1.05       2  180.00 ! parm99
+dihe X    c2   os   X     1.05       2  180.00 ! parm99
+dihe X    c2   p2   X     6.65       2  180.00 ! double bond
+dihe X    c2   pe   X     6.65       2  180.00 ! double bond
+dihe X    c2   pf   X     6.65       2  180.00 ! double bond
+dihe X    ce   pf   X     6.65       2  180.00 ! double bond
+dihe X    ce   pe   X     0.95       2  180.00 ! single bond
+dihe X    cf   pf   X     0.95       2  180.00 ! single bond
+dihe X    c2   pc   X     4.75       2  180.00 ! estimated
+dihe X    c2   pd   X     4.75       2  180.00 ! estimated
+dihe X    cc   pc   X     4.75       2  180.00 ! estimated
+dihe X    cc   pd   X     4.75       2  180.00 ! estimated
+dihe X    cd   pc   X     4.75       2  180.00 ! estimated
+dihe X    cd   pd   X     4.75       2  180.00 ! estimated
+dihe X    c2   p3   X     0.45       2  180.00
+dihe X    c2   p4   X     6.65       2  180.00 ! c2=p4 double bond !!!
+dihe X    ce   p4   X     6.65       2  180.00 ! c2=p4 double bond !!!
+dihe X    cf   p4   X     6.65       2  180.00 ! c2=p4 double bond !!!
+dihe X    c2   px   X     0.33       2    0.00
+dihe X    ce   px   X     0.33       2    0.00
+dihe X    cf   px   X     0.33       2    0.00
+dihe X    c2   p5   X     6.65       2  180.00 ! c2=p5 double bond !!!
+dihe X    ce   p5   X     6.65       2  180.00 ! c2=p5 double bond !!!
+dihe X    cf   p5   X     6.65       2  180.00 ! c2=p5 double bond !!!
+dihe X    c2   py   X     1.43       2  180.00
+dihe X    ce   py   X     1.43       2  180.00
+dihe X    cf   py   X     1.43       2  180.00
+dihe X    c2   sh   X     0.50       2  180.00
+dihe X    c2   ss   X     1.10       2  180.00
+dihe X    c2   s4   X     6.65       2  180.00 ! c2=s4 double bond  !!!
+dihe X    ce   s4   X     6.65       2  180.00 ! c2=s4 double bond  !!!
+dihe X    cf   s4   X     6.65       2  180.00 ! c2=s4 double bond  !!!
+dihe X    c2   sx   X     0.60       2    0.00
+dihe X    ce   sx   X     0.60       2    0.00
+dihe X    cf   sx   X     0.60       2    0.00
+dihe X    c2   s6   X     6.65       2  180.00 ! c2=s6 double bond  !!!
+dihe X    ce   s6   X     6.65       2  180.00 ! c2=s6 double bond  !!!
+dihe X    cf   s6   X     6.65       2  180.00 ! c2=s6 double bond  !!!
+dihe X    c2   sy   X     1.27       2  180.00
+dihe X    ce   sy   X     1.27       2  180.00
+dihe X    cf   sy   X     1.27       2  180.00
+dihe X    c3   c3   X     0.16       3    0.00 ! JCC,7,(1986),230
+dihe X    cx   cx   X     0.16       3    0.00 ! same as X-c3-c3-X
+dihe X    cy   cy   X     0.16       3    0.00 ! same as X-c3-c3-X
+dihe X    c3   ca   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    c3   n    X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    cx   n    X     0.00       2    0.00 ! same as X-c3-n-X
+dihe X    cy   n    X     0.00       2    0.00 ! same as X-c3-n-X
+dihe X    c3   n2   X     0.00       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   ne   X     0.00       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   nf   X     0.00       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   n3   X     0.30       3    0.00 ! Junmei et al, 1999
+dihe X    c3   n4   X     0.16       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   na   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    c3   nh   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    c3   no   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    c3   oh   X     0.17       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   os   X     0.38       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   p2   X     0.27       2  180.00
+dihe X    c3   pe   X     0.27       2  180.00
+dihe X    c3   pf   X     0.27       2  180.00
+dihe X    c3   p3   X     0.13       3    0.00
+dihe X    c3   p4   X     0.13       3    0.00
+dihe X    c3   px   X     0.13       3    0.00
+dihe X    c3   p5   X     0.02       3    0.00
+dihe X    c3   py   X     0.02       3    0.00
+dihe X    c3   sh   X     0.25       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   ss   X     0.33       3    0.00 ! JCC,7,(1986),230
+dihe X    c3   s4   X     0.20       3    0.00
+dihe X    c3   sx   X     0.20       3    0.00
+dihe X    c3   s6   X     0.14       3    0.00
+dihe X    c3   sy   X     0.14       3    0.00
+dihe X    c3   cc   X     0.00       3    0.00 ! same as X-c3-ca-X
+dihe X    c3   cd   X     0.00       3    0.00 ! same as X-c3-ca-X
+dihe X    ca   ca   X     3.62       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    ca   cp   X     3.62       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    ca   cq   X     3.62       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    cp   cp   X     1.00       2  180.00 ! estimated, intrpol.
+dihe X    cq   cq   X     1.00       2  180.00 ! estimated, intrpol.
+dihe X    ca   n    X     0.45       2  180.00
+dihe X    ca   n2   X     0.00       3  180.00
+dihe X    ca   ne   X     0.00       3  180.00
+dihe X    ca   nf   X     0.00       3  180.00
+dihe X    ca   n4   X     1.75       2    0.00
+dihe X    ca   na   X     0.30       2  180.00
+dihe X    ca   nb   X     4.80       2  180.00 ! same as X-CA-NC-X
+dihe X    ca   nc   X     4.80       2  180.00 ! same as X-CA-NC-X
+dihe X    ca   nd   X     4.80       2  180.00 ! same as X-CA-NC-X
+dihe X    ca   nh   X     1.05       2  180.00
+dihe X    cc   nh   X     1.05       2  180.00 ! same as X-ca-nh-X
+dihe X    cd   nh   X     1.05       2  180.00 ! same as X-ca-nh-X
+dihe X    ca   no   X     0.60       2  180.00
+dihe X    ca   oh   X     0.90       2  180.00 ! Junmei et al, 99
+dihe X    ca   os   X     0.90       2  180.00 ! same as X-ca-oh-X
+dihe X    ca   p2   X     0.60       2  180.00
+dihe X    ca   pe   X     0.60       2  180.00 ! same as X-ca-p2-X
+dihe X    ca   pf   X     0.60       2  180.00 ! same as X-ca-p2-X
+dihe X    ca   pc   X     4.80       2  180.00 ! estimated, intrpol
+dihe X    ca   pd   X     4.80       2  180.00 ! estimated, intrpol
+dihe X    ca   p3   X     0.00       2  180.00
+dihe X    ca   p4   X     0.53       2  180.00
+dihe X    ca   px   X     0.53       2  180.00 ! estimated, same as X-ca-p4-X
+dihe X    ca   p5   X     1.47       2  180.00
+dihe X    ca   py   X     1.47       2  180.00 ! estimated, same as X-ca-p5-X
+dihe X    ca   sh   X     0.00       2  180.00
+dihe X    ca   ss   X     0.40       2  180.00
+dihe X    ca   s4   X     0.30       2    0.00
+dihe X    ca   sx   X     0.30       2    0.00 ! estimated, same as X-ca-s4-X
+dihe X    ca   s6   X     1.30       2  180.00
+dihe X    ca   sy   X     1.30       2  180.00 ! estimated, same as X-ca-s6-X
+dihe X    n    cc   X     1.65       2  180.00 ! statistic value from parm94
+dihe X    n    cd   X     1.65       2  180.00 ! statistic value from parm94
+dihe X    n    n    X     1.15       2    0.00
+dihe X    n    n2   X     0.40       2    0.00
+dihe X    n    ne   X     0.40       2    0.00
+dihe X    n    nf   X     0.40       2    0.00
+dihe X    n    n3   X     1.07       2    0.00
+dihe X    n    n4   X     0.95       2    0.00
+dihe X    n    na   X     0.70       2    0.00
+dihe X    n    nc   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    n    nd   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    n    nh   X     1.10       2    0.00
+dihe X    n    no   X     1.38       2  180.00
+dihe X    n    oh   X     1.50       2    0.00
+dihe X    n    os   X     1.10       2    0.00
+dihe X    n    p2   X     1.00       2  180.00
+dihe X    n    pe   X     1.00       2  180.00
+dihe X    n    pf   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    n    pc   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    n    pd   X     1.00       2  180.00
+dihe X    n    p3   X     2.25       2    0.00
+dihe X    n    p4   X     0.33       2    0.00
+dihe X    n    px   X     0.33       2    0.00
+dihe X    n    p5   X     2.20       2  180.00
+dihe X    n    py   X     2.20       2  180.00
+dihe X    n    sh   X     1.10       2    0.00
+dihe X    n    ss   X     1.50       2    0.00
+dihe X    n    s4   X     1.50       3    0.00
+dihe X    n    sx   X     1.50       3    0.00
+dihe X    n    s6   X     1.10       2  180.00
+dihe X    n    sy   X     1.10       2  180.00
+dihe X    n1   c2   X     0.00       2  180.00
+dihe X    n1   c3   X     0.00       2  180.00
+dihe X    n1   ca   X     0.00       2  180.00
+dihe X    n1   cb   X     0.00       2  180.00
+dihe X    n1   cc   X     0.00       2  180.00
+dihe X    n1   cd   X     0.00       2  180.00
+dihe X    n1   ce   X     0.00       2  180.00
+dihe X    n1   cf   X     0.00       2  180.00
+dihe X    n1   cu   X     0.00       2  180.00
+dihe X    n1   cv   X     0.00       2  180.00
+dihe X    n1   cx   X     0.00       2  180.00
+dihe X    n1   cy   X     0.00       2  180.00
+dihe X    n1   n    X     0.00       2  180.00
+dihe X    n1   n1   X     0.00       2  180.00
+dihe X    n1   n2   X     0.00       2  180.00
+dihe X    n1   n3   X     0.00       2  180.00
+dihe X    n1   n4   X     0.00       2  180.00
+dihe X    n1   na   X     0.00       2  180.00
+dihe X    n1   nb   X     0.00       2  180.00
+dihe X    n1   nc   X     0.00       2  180.00
+dihe X    n1   nd   X     0.00       2  180.00
+dihe X    n1   ne   X     0.00       2  180.00
+dihe X    n1   nf   X     0.00       2  180.00
+dihe X    n1   nh   X     0.00       2  180.00
+dihe X    n1   no   X     0.00       2  180.00
+dihe X    n1   oh   X     0.00       2  180.00
+dihe X    n1   os   X     0.00       2  180.00
+dihe X    n1   p2   X     0.00       2  180.00
+dihe X    n1   pb   X     0.00       2  180.00
+dihe X    n1   pc   X     0.00       2  180.00
+dihe X    n1   pd   X     0.00       2  180.00
+dihe X    n1   pe   X     0.00       2  180.00
+dihe X    n1   pf   X     0.00       2  180.00
+dihe X    n1   p3   X     0.00       2  180.00
+dihe X    n1   p4   X     0.00       2  180.00
+dihe X    n1   px   X     0.00       2  180.00
+dihe X    n1   p5   X     0.00       2  180.00
+dihe X    n1   py   X     0.00       2  180.00
+dihe X    n1   s2   X     0.00       2  180.00
+dihe X    n1   sh   X     0.00       2  180.00
+dihe X    n1   ss   X     0.00       2  180.00
+dihe X    n1   s4   X     0.00       2  180.00
+dihe X    n1   sx   X     0.00       2  180.00
+dihe X    n1   s6   X     0.00       2  180.00
+dihe X    n1   sy   X     0.00       2  180.00
+dihe X    n2   n2   X     2.80       1    0.00 ! double bond
+dihe X    n2   ne   X     2.80       1    0.00 ! double bond
+dihe X    n2   nf   X     2.80       1    0.00 ! double bond
+dihe X    ne   nf   X     2.80       1    0.00 ! double bond
+dihe X    ne   ne   X     1.20       2  180.00 ! single bond, intrpol
+dihe X    nf   nf   X     1.20       2  180.00 ! single bond, intrpol
+dihe X    nc   nc   X     4.00       2  180.00 ! estimated, intrpol
+dihe X    nd   nd   X     4.00       2  180.00 ! estimated, intrpol
+dihe X    nc   nd   X     4.00       2  180.00 ! estimated, intrpol
+dihe X    n2   nc   X     2.80       1    0.00
+dihe X    n2   nd   X     2.80       1    0.00
+dihe X    n2   n3   X     6.10       2  180.00
+dihe X    ne   n3   X     6.10       2  180.00
+dihe X    nf   n3   X     6.10       2  180.00
+dihe X    n2   n4   X     8.00       2  180.00
+dihe X    ne   n4   X     8.00       2  180.00
+dihe X    nf   n4   X     8.00       2  180.00
+dihe X    n2   na   X     1.70       2  180.00
+dihe X    ne   na   X     1.70       2  180.00
+dihe X    nf   na   X     1.70       2  180.00
+dihe X    na   nc   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    na   nd   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    n2   nh   X     2.80       2  180.00
+dihe X    ne   nh   X     2.80       2  180.00
+dihe X    nf   nh   X     2.80       2  180.00
+dihe X    n2   no   X     0.75       2  180.00
+dihe X    ne   no   X     0.75       2  180.00
+dihe X    nf   no   X     0.75       2  180.00
+dihe X    n2   oh   X     3.20       2  180.00
+dihe X    ne   oh   X     3.20       2  180.00
+dihe X    nf   oh   X     3.20       2  180.00
+dihe X    n2   os   X     3.00       2  180.00
+dihe X    ne   os   X     3.00       2  180.00
+dihe X    nf   os   X     3.00       2  180.00
+dihe X    nc   os   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    nc   ss   X     4.80       2  180.00 ! estimated, intrpol.
+dihe X    n2   p2   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    n2   pe   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    n2   pf   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    ne   pf   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    n2   pc   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    n2   pd   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    nc   p2   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    nd   p2   X     5.40       2  180.00 ! estimated, intrpol.
+dihe X    nc   pc   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    nd   pd   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    nd   pc   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    nc   pd   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    ne   pe   X     0.60       1    0.00 ! single bond
+dihe X    nf   pf   X     0.60       1    0.00 ! single bond
+dihe X    n2   p3   X     2.10       2  180.00
+dihe X    n2   p4   X     6.65       2  180.00 ! estimated  !!!
+dihe X    ne   p4   X     6.65       2  180.00 ! estimated  !!!
+dihe X    nf   p4   X     6.65       2  180.00 ! estimated  !!!
+dihe X    n2   p5   X     6.67       2  180.00 ! estimated  !!!
+dihe X    ne   p5   X     1.00       3  180.00
+dihe X    nf   p5   X     1.00       3  180.00
+dihe X    ne   px   X     1.00       3  180.00
+dihe X    nf   px   X     1.00       3  180.00
+dihe X    n2   sh   X     2.10       2  180.00
+dihe X    ne   sh   X     2.10       2  180.00
+dihe X    nf   sh   X     2.10       2  180.00
+dihe X    n2   ss   X     1.30       1  180.00
+dihe X    ne   ss   X     1.30       1  180.00
+dihe X    nf   ss   X     1.30       1  180.00
+dihe X    n2   s4   X     6.65       2  180.00 ! estimated !!!
+dihe X    ne   sx   X     1.50       3  180.00
+dihe X    nf   sx   X     1.50       3  180.00
+dihe X    n2   s6   X     6.67       2  180.00 ! estimated  !!!
+dihe X    ne   sy   X     6.80       1  180.00
+dihe X    nf   sy   X     6.80       1  180.00
+dihe X    n3   n3   X     2.25       2    0.00
+dihe X    n3   n4   X     0.25       3    0.00
+dihe X    n3   na   X     1.60       2    0.00
+dihe X    n3   nh   X     1.90       2    0.00
+dihe X    n3   no   X     4.00       2  180.00
+dihe X    n3   oh   X     2.20       2    0.00
+dihe X    n3   os   X     1.80       2    0.00
+dihe X    n3   p2   X     3.20       2  180.00
+dihe X    n3   pe   X     3.20       2  180.00
+dihe X    n3   pf   X     3.20       2  180.00
+dihe X    n3   p3   X     2.35       2    0.00
+dihe X    n3   p4   X     2.10       2  180.00
+dihe X    n3   px   X     2.10       2  180.00
+dihe X    n3   p5   X     3.00       2  180.00
+dihe X    n3   py   X     3.00       2  180.00
+dihe X    n3   sh   X     3.10       2    0.00
+dihe X    n3   ss   X     2.60       2    0.00
+dihe X    n3   s4   X     3.75       2    0.00
+dihe X    n3   sx   X     3.75       2    0.00
+dihe X    n3   s6   X     3.13       2    0.00
+dihe X    n3   sy   X     3.13       2    0.00
+dihe X    n4   n4   X     0.19       3    0.00
+dihe X    n4   na   X     0.23       3    0.00
+dihe X    n4   nh   X     0.18       3    0.00
+dihe X    n4   no   X     0.08       3  180.00
+dihe X    n4   oh   X     0.33       3    0.00
+dihe X    n4   os   X     0.57       3    0.00
+dihe X    n4   p2   X     0.17       3  180.00
+dihe X    n4   pe   X     0.17       3  180.00
+dihe X    n4   pf   X     0.17       3  180.00
+dihe X    n4   p3   X     0.15       3    0.00
+dihe X    n4   p4   X     0.05       3    0.00
+dihe X    n4   px   X     0.05       3    0.00
+dihe X    n4   p5   X     0.09       3    0.00
+dihe X    n4   py   X     0.09       3    0.00
+dihe X    n4   sh   X     0.67       3    0.00
+dihe X    n4   ss   X     0.33       3    0.00
+dihe X    n4   s4   X     0.28       3    0.00
+dihe X    n4   sx   X     0.28       3    0.00
+dihe X    n4   s6   X     0.13       3    0.00
+dihe X    n4   sy   X     0.13       3    0.00
+dihe X    na   na   X     0.90       2    0.00
+dihe X    na   nh   X     1.20       2    0.00
+dihe X    na   no   X     6.00       2  180.00
+dihe X    na   oh   X     1.00       2    0.00
+dihe X    na   os   X     0.65       2    0.00
+dihe X    na   p2   X     1.00       2  180.00
+dihe X    na   pe   X     1.00       2  180.00
+dihe X    na   pf   X     1.00       2  180.00
+dihe X    na   p3   X     1.45       2    0.00
+dihe X    na   p4   X     1.10       3    0.00
+dihe X    na   px   X     1.10       3    0.00
+dihe X    na   p5   X     0.83       2  180.00
+dihe X    na   py   X     0.83       2  180.00
+dihe X    na   sh   X     1.80       2    0.00
+dihe X    na   ss   X     7.80       2    0.00
+dihe X    na   s4   X     1.05       2    0.00
+dihe X    na   sx   X     1.05       2    0.00
+dihe X    na   s6   X     3.67       2  180.00
+dihe X    na   sy   X     3.67       2  180.00
+dihe X    nh   nh   X     1.80       3  180.00
+dihe X    nh   no   X     2.55       2  180.00
+dihe X    nh   oh   X     1.50       2    0.00
+dihe X    nh   os   X     1.50       1    0.00
+dihe X    nh   p2   X     1.40       2  180.00
+dihe X    nh   pe   X     1.40       2  180.00
+dihe X    nh   pf   X     1.40       2  180.00
+dihe X    nh   p3   X     2.35       2    0.00
+dihe X    nh   p4   X     1.18       3    0.00
+dihe X    nh   px   X     1.18       3    0.00
+dihe X    nh   p5   X     0.80       2    0.00
+dihe X    nh   py   X     0.80       2    0.00
+dihe X    nh   sh   X     1.60       2    0.00
+dihe X    nh   ss   X     2.10       2    0.00
+dihe X    nh   s4   X     0.10       3  180.00
+dihe X    nh   sx   X     0.10       3  180.00
+dihe X    nh   s6   X     0.10       2  180.00
+dihe X    nh   sy   X     0.10       2  180.00
+dihe X    no   no   X     1.80       2  180.00
+dihe X    no   oh   X     3.90       2  180.00
+dihe X    no   os   X     3.00       2  180.00
+dihe X    no   p2   X     0.30       2  180.00
+dihe X    no   pe   X     0.30       2  180.00
+dihe X    no   pf   X     0.30       2  180.00
+dihe X    no   p3   X     1.90       2  180.00
+dihe X    no   p4   X     0.57       2  180.00
+dihe X    no   px   X     0.57       2  180.00
+dihe X    no   p5   X     0.40       3    0.00
+dihe X    no   py   X     0.40       3    0.00
+dihe X    no   sh   X     2.30       2  180.00
+dihe X    no   ss   X     2.70       2  180.00
+dihe X    no   s4   X     2.60       2  180.00
+dihe X    no   sx   X     2.60       2  180.00
+dihe X    no   s6   X     0.33       2    0.00
+dihe X    no   sy   X     0.33       2    0.00
+dihe X    oh   oh   X     1.60       2    0.00
+dihe X    oh   os   X     1.60       2    0.00
+dihe X    oh   p2   X     1.50       2  180.00
+dihe X    oh   pe   X     1.50       2  180.00
+dihe X    oh   pf   X     1.50       2  180.00
+dihe X    oh   p3   X     0.40       3  180.00
+dihe X    oh   p4   X     0.70       1    0.00
+dihe X    oh   px   X     0.70       1    0.00
+dihe X    oh   p5   X     0.53       3    0.00
+dihe X    oh   py   X     0.53       3    0.00
+dihe X    oh   sh   X     2.40       2    0.00
+dihe X    oh   ss   X     2.40       2    0.00
+dihe X    oh   s4   X     5.00       1    0.00
+dihe X    oh   sx   X     5.00       1    0.00
+dihe X    oh   s6   X     9.50       1  180.00
+dihe X    oh   sy   X     9.50       1  180.00
+dihe X    os   os   X     1.00       1    0.00
+dihe X    os   ss   X     2.20       2    0.00
+dihe X    os   sh   X     1.80       2    0.00
+dihe X    os   s4   X     1.65       3    0.00
+dihe X    os   sx   X     1.65       3    0.00
+dihe X    os   s6   X     1.20       2  180.00
+dihe X    os   sy   X     1.20       2  180.00
+dihe X    os   p2   X     2.00       1  180.00
+dihe X    os   pe   X     2.00       1  180.00
+dihe X    os   pf   X     2.00       1  180.00
+dihe X    os   p3   X     2.20       2    0.00
+dihe X    os   p4   X     1.05       2  180.00
+dihe X    os   px   X     1.05       2  180.00
+dihe X    os   p5   X     0.80       2    0.00
+dihe X    os   py   X     0.80       2    0.00
+dihe X    p2   p2   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    p2   pe   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    p2   pf   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    p2   pc   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    p2   pd   X     6.60       2  180.00 ! estimated, intrpol.
+dihe X    pe   pe   X     1.20       2  180.00 ! single bond
+dihe X    pf   pf   X     1.20       2  180.00 ! single bond
+dihe X    pc   pc   X     7.20       2  180.00 ! estimated, intrpol.
+dihe X    pd   pd   X     7.20       2  180.00 ! estimated, intrpol.
+dihe X    pc   pd   X     7.20       2  180.00 ! estimated, intrpol.
+dihe X    p2   p3   X     1.20       1    0.00
+dihe X    pe   p3   X     1.20       1    0.00
+dihe X    pf   p3   X     1.20       1    0.00
+dihe X    p2   p4   X     6.65       2  180.00 ! estimated  !!!
+dihe X    pe   px   X     2.45       2    0.00
+dihe X    pf   px   X     2.45       2    0.00
+dihe X    p2   p5   X     6.67       2  180.00 ! estimated  !!!
+dihe X    pe   py   X     1.90       1    0.00
+dihe X    pf   py   X     1.90       1    0.00
+dihe X    p2   sh   X     1.40       2  180.00
+dihe X    pe   sh   X     1.40       2  180.00
+dihe X    pf   sh   X     1.40       2  180.00
+dihe X    p2   ss   X     1.40       2  180.00
+dihe X    pe   ss   X     1.40       2  180.00
+dihe X    pf   ss   X     1.40       2  180.00
+dihe X    p2   s4   X     6.65       2  180.00 ! estimated !!!
+dihe X    pe   sx   X     1.50       2    0.00
+dihe X    pf   sx   X     1.50       2    0.00
+dihe X    p2   s6   X     6.67       2  180.00 ! estimated !!!
+dihe X    pe   sy   X     0.40       3  180.00
+dihe X    pf   sy   X     0.40       3  180.00
+dihe X    p3   p3   X     0.50       3    0.00
+dihe X    p3   p4   X     0.90       1    0.00
+dihe X    p3   px   X     0.90       1    0.00
+dihe X    p3   p5   X     1.83       2  180.00
+dihe X    p3   py   X     1.83       2  180.00
+dihe X    p3   sh   X     4.60       2    0.00
+dihe X    p3   ss   X     1.15       3    0.00
+dihe X    p3   ss   X     1.15       3    0.00
+dihe X    p3   s4   X     3.85       2    0.00
+dihe X    p3   sx   X     3.85       2    0.00
+dihe X    p3   s6   X     0.27       3    0.00
+dihe X    p3   sy   X     0.27       3    0.00
+dihe X    p4   p4   X     6.65       2  180.00 ! estimated   !!!
+dihe X    px   px   X     1.45       2  180.00
+dihe X    p4   p5   X     6.65       2  180.00 ! estimated   !!!
+dihe X    px   py   X     0.32       2  180.00
+dihe X    p4   s4   X     6.65       2  180.00 ! estimated   !!!
+dihe X    px   sx   X     0.85       1    0.00
+dihe X    p4   s6   X     6.65       2  180.00 ! estimated   !!!
+dihe X    px   sy   X     0.12       3    0.00
+dihe X    p4   sh   X     0.25       1  180.00
+dihe X    px   sh   X     0.25       1  180.00
+dihe X    p4   ss   X     0.60       2  180.00
+dihe X    px   ss   X     0.60       2  180.00
+dihe X    p5   p5   X     6.67       2  180.00 ! estimated   !!!
+dihe X    py   py   X     0.60       2    0.00
+dihe X    p5   sh   X     0.30       3    0.00
+dihe X    py   sh   X     0.30       3    0.00
+dihe X    p5   ss   X     3.80       2  180.00
+dihe X    py   ss   X     3.80       2  180.00
+dihe X    p5   s4   X     6.67       2  180.00 ! estimated  !!!
+dihe X    py   sx   X     0.27       3    0.00
+dihe X    p5   s6   X     6.67       2  180.00 ! estimated  !!!
+dihe X    py   sy   X     0.28       3    0.00
+dihe X    sh   sh   X     5.60       3    0.00
+dihe X    sh   ss   X     5.30       3    0.00
+dihe X    sh   s4   X     0.70       3    0.00
+dihe X    sh   sx   X     0.70       3    0.00
+dihe X    sh   s6   X     4.67       2  180.00
+dihe X    sh   sy   X     4.67       2  180.00
+dihe X    ss   ss   X     0.00       3    0.00
+dihe X    ss   s4   X     0.30       3    0.00
+dihe X    ss   sx   X     0.30       3    0.00
+dihe X    ss   s6   X     3.07       2  180.00
+dihe X    ss   sy   X     3.07       2  180.00
+dihe X    s4   s4   X     6.65       2  180.00 ! estimated !!!
+dihe X    sx   sx   X     0.62       3    0.00
+dihe X    s4   s6   X     6.67       2  180.00 ! estimated !!!
+dihe X    sx   sy   X     4.33       2  180.00
+dihe X    s6   s6   X     6.67       2  180.00 ! estimated !!!
+dihe X    sy   sy   X     0.16       2  180.00
+dihe c3   c    sh   hs    1.30       1  180.00
+dihe c2   c2   ss   c3    0.70       3  180.00
+dihe c2   c2   n    c     1.20       1  180.00
+dihe c    n    p2   c2    1.90       1  180.00
+dihe n    c3   c    n     2.00       2  180.00
+dihe c    n    c3   c     0.80       1    0.00
+dihe c3   c3   n    c     0.53       1    0.00 ! phi,psi,parm94
+dihe c3   c3   c    n     0.07       2    0.00 ! phi,psi,parm94
+dihe c2   ne   p5   o     2.30       1    0.00 ! phi,psi,parm94
+dihe c2   nf   p5   o     2.30       1    0.00 ! phi,psi,parm94
+dihe ce   ne   p5   o     2.30       1    0.00 ! phi,psi,parm94
+dihe ce   nf   p5   o     2.30       1    0.00 ! phi,psi,parm94
+dihe cf   ne   p5   o     2.30       1    0.00 ! phi,psi,parm94
+dihe cf   nf   p5   o     2.30       1    0.00 ! phi,psi,parm94
+dihe hn   n    c    o     2.00       1    0.00 ! J.C.cistrans-NMA DE
+dihe c3   ss   ss   c3    0.60       3    0.00 ! JCC,7,(1986),230
+dihe c3   n3   nh   ca    1.90       3    0.00
+dihe c3   n3   p5   o     2.30       3    0.00
+dihe ca   nh   oh   ho    1.50       2    0.00
+dihe oh   p5   os   c3    1.20       2    0.00 ! gg&gt ene.631g*/mp2
+dihe os   p5   os   c3    1.20       2    0.00 ! gg&gt ene.631g*/mp2
+dihe h1   c3   c    o     0.08       3  180.00 ! Junmei et al, 1999
+dihe hc   c3   c    o     0.08       3  180.00 ! Junmei et al, 1999
+dihe hc   c3   c3   hc    0.15       3    0.00 ! Junmei et al, 1999
+dihe hc   c3   c3   c3    0.16       3    0.00 ! Junmei et al, 1999
+dihe hc   c3   c2   c2    1.15       1    0.00 ! Junmei et al, 1999
+dihe ho   oh   c3   c3    0.25       1    0.00 ! Junmei et al, 1999
+dihe ho   oh   c    o     1.90       1    0.00 ! Junmei et al, 1999
+dihe c2   c2   c    o     0.30       3    0.00 ! Junmei et al, 1999
+dihe c3   c2   c2   c3    1.90       1  180.00 ! Junmei et al, 1999
+dihe c3   c3   c3   c3    0.20       1  180.00 ! Junmei et al, 1999
+dihe c3   c3   n3   c3    0.48       2  180.00 ! Junmei et al, 1999
+dihe c3   c3   os   c3    0.10       2  180.00
+dihe c3   c3   os   c     0.80       1  180.00 ! Junmei et al, 1999
+dihe c3   os   c3   os    1.35       1  180.00 ! Junmei et al, 1999
+dihe c3   os   c3   na    0.65       2    0.00 ! Piotr et al.
+dihe o    c    os   c3    1.40       1  180.00 ! Junmei et al, 1999
+dihe os   c3   na   c2    2.50       1    0.00 ! parm98, TC,PC,PAK
+dihe os   c3   c3   os    1.18       2    0.00 ! Piotr et al.
+dihe os   c3   c3   oh    1.18       2    0.00 ! parm98, TC,PC,PAK
+dihe oh   c3   c3   oh    1.18       2    0.00 ! parm98, TC,PC,PAK
+dihe f    c3   c3   f     1.20       1  180.00 ! Junmei et al, 1999
+dihe cl   c3   c3   cl    0.45       1  180.00 ! Junmei et al, 1999
+dihe br   c3   c3   br    0.00       1  180.00 ! Junmei et al, 1999
+dihe h1   c3   c3   os    0.25       1    0.00 ! Junmei et al, 1999
+dihe h1   c3   c3   oh    0.25       1    0.00 ! Junmei et al, 1999
+dihe h1   c3   c3   f     0.19       1    0.00 ! Junmei et al, 1999
+dihe h1   c3   c3   cl    0.25       1    0.00 ! Junmei et al, 1999
+dihe h1   c3   c3   br    0.55       1    0.00 ! Junmei et al, 1999
+dihe hc   c3   c3   os    0.25       1    0.00 ! Junmei et al, 1999
+dihe hc   c3   c3   oh    0.25       1    0.00 ! Junmei et al, 1999
+dihe hc   c3   c3   f     0.19       1    0.00 ! Junmei et al, 1999
+dihe hc   c3   c3   cl    0.25       1    0.00 ! Junmei et al, 1999
+dihe hc   c3   c3   br    0.55       1    0.00 ! Junmei et al, 1999
+
+impr X    o    c    o     1.10       2  180.00 ! JCC,7,(1986),230
+impr X    X    c    o    10.50       2  180.00 ! JCC,7,(1986),230
+impr X    X    ca   ha    1.10       2  180.00 ! bsd.on C6H6 nmodes
+impr X    X    n    hn    1.10       2  180.00 ! JCC,7,(1986),230
+impr X    X    n2   hn    1.10       2  180.00 ! JCC,7,(1986),230
+impr X    X    na   hn    1.10       2  180.00 ! JCC,7,(1986),230
+impr X    c3   n    c3    1.10       2  180.00 ! JCC,7,(1986),230
+impr X    n2   ca   n2   10.50       2  180.00 ! JCC,7,(1986),230
+impr c    c2   c2   c3    1.10       2  180.00 ! dac guess, 9/94
+impr c    ca   ca   c3    1.10       2  180.00 ! dac guess, 9/94
+impr c    c3   n    hn    1.10       2  180.00 ! Junmei et al.1999
+impr c    c3   n    o     1.10       2  180.00 ! Junmei et al.1999
+impr c2   c2   na   c3    1.10       2  180.00
+impr c2   c    c2   c3    1.10       2  180.00
+impr c3   o    c    oh    1.10       2  180.00
+impr c2   c3   c2   hc    1.10       2  180.00 ! Junmei et al.1999
+impr c2   c3   ca   hc    1.10       2  180.00 ! Junmei et al.1999
+impr c2   hc   c    o     1.10       2  180.00 ! Junmei et al.1999
+impr c3   o    c    oh    1.10       2  180.00
+impr c3   c2   c2   n2    1.10       2  180.00
+impr c3   c2   c2   na    1.10       2  180.00
+impr c3   ca   ca   n2    1.10       2  180.00
+impr c3   ca   ca   na    1.10       2  180.00
+impr ca   ca   ca   c2    1.10       2  180.00
+impr ca   ca   ca   c3    1.10       2  180.00
+impr ca   ca   ca   f     1.10       2  180.00 ! Junmei et al.1999
+impr ca   ca   ca   cl    1.10       2  180.00 ! Junmei et al.1999
+impr ca   ca   ca   br    1.10       2  180.00 ! Junmei et al.1999
+impr ca   ca   ca   i     1.10       2  180.00 ! Junmei et al.1999
+impr ca   ca   c    oh    1.10       2  180.00 ! (not used in tyr!)
+impr ca   ca   na   c3    1.10       2  180.00
+impr ca   c    ca   c3    1.10       2  180.00
+impr ca   hc   c    o     1.10       2  180.00 ! Junmei et al.1999
+impr ca   n2   ca   n2    1.10       2  180.00 ! dac, 10/94
+impr hc   o    c    oh    1.10       2  180.00 ! Junmei et al.1999
+impr hc   o    c    os    1.10       2  180.00
+impr n2   c2   ca   n2    1.10       2  180.00 ! dac guess, 9/94
+impr n2   ca   ca   c3    1.10       2  180.00
+impr n2   ca   ca   n2    1.10       2  180.00 ! dac guess, 9/94
+impr na   ca   ca   c3    1.10       2  180.00
+impr na   n2   ca   n2    1.10       2  180.00 ! dac, 10/94
+
diff --git a/Scripts/gamess.rb b/Scripts/gamess.rb
new file mode 100755 (executable)
index 0000000..5439dde
--- /dev/null
@@ -0,0 +1,310 @@
+#
+#  gamess.rb
+#
+#  Created by Toshi Nagata on 2009/11/22.
+#  Copyright 2009 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+class Molecule
+
+  def Molecule.read_gamess_basis_sets(fname)
+    $gamess_basis = Hash.new unless $gamess_basis
+       $gamess_ecp = Hash.new unless $gamess_ecp
+       basename = File.basename(fname, ".*")
+       keys = []
+    File.open(fname, "r") { |fp|
+         while (s = fp.gets)
+               ss, bas = (s.split)[0..1]     #  Tokens delimited by whitespaces
+               next if ss == nil || ss == ""
+               if ss == "$ECP"
+               #  Read the ECP section
+                 ecp = $gamess_ecp[basename]
+                 if !ecp
+                   ecp = []
+                       $gamess_ecp[basename] = ecp
+                 end
+                 keys << basename unless keys.include?(basename)
+                 while (s = fp.gets)
+                   break if s=~ /\$END/
+                       ary = s.split  #  (PNAME, PTYPE, IZCORE, LMAX+1)
+                       elem = ary[0].match(/\w{1,2}/).to_a[0]  #  ary[0] should begin with an element name
+                       ap = Parameter.atoms.find { |p| p.name.casecmp(elem) == 0 }
+                       raise MolbyError, "the ECP definition does not begin with an element name: #{s}" if !ap
+                       ecpdef = s
+                       ln = 1
+                       (0..Integer(ary[3])).each {
+                         s = fp.gets
+                         raise MolbyError, "the ECP definition ends unexpectedly at line #{ln} for: #{s}" if !s
+                         ln += 1
+                         ary = s.split  #  (NGPOT, comment)
+                         ecpdef += s
+                         (1..Integer(ary[0])).each {
+                           s = fp.gets
+                               raise MolbyError, "the ECP definition ends unexpectedly at line #{ln} for: #{s}" if !s
+                               ln += 1
+                               ecpdef += s
+                         }
+                       }
+                   ecp[ap.index] = ecpdef
+                 end
+               elsif ss =~ /\W/
+                 #  Comments or other unrecognizable lines
+                 next
+               elsif (ap = Parameter.atoms.find { |p| p.name.casecmp(ss) == 0 || p.fullname.casecmp(ss) == 0 })
+                 #  Valid basis definition
+                 if bas == nil || bas =~ /\W/
+                   bas = basename
+                 end
+                 basis = $gamess_basis[bas]
+                 if !basis
+                   basis = []
+                       $gamess_basis[bas] = basis
+                 end
+                 keys << bas unless keys.include?(bas)
+                 basdef = ""
+                 while (s = fp.gets) && s =~ /\S/
+                   basdef += s
+                 end
+                 basis[ap.index] = basdef
+               else
+                 raise MolbyError, "ss is not a valid element symbol or name: #{s}"
+               end
+         end
+    }
+  end
+  
+  def export_gamess(fname, hash)
+
+       now = Time.now.to_s
+       basename = File.basename(fname, ".*")
+       
+       #  Various settings
+       icharg = hash["charge"]
+       mult = hash["mult"]
+       runtyp = ["ENERGY", "PROP", "OPTIMIZE"][Integer(hash["runtype"])]
+       scftyp = ["RHF", "ROHF", "UHF"][Integer(hash["scftype"])]
+       bssname = hash["basis"]
+       bssname2 = hash["secondary_basis"]
+       if hash["use_secondary_basis"] != 0 && bssname2 != bssname
+         use_2nd = true
+         element2 = hash["secondary_elements"].split(/[\s,]+/).map { |name| name.capitalize }
+       else
+         use_2nd = false
+         bssname2 = bssname
+       end
+       basis = $gamess_basis[bssname]
+       basis2 = $gamess_basis[bssname2]
+       if !basis || !basis2
+         raise MolbyError, "Unknown basis set name??? \"#{bssname}\" or \"#{bssname2}\""
+       end
+
+       #  Use effective core potentials?
+       ecp = $gamess_ecp[bssname]
+       ecp2 = $gamess_ecp[bssname2]
+       ecp_read = (ecp || ecp2 ? "ECP=READ " : "")
+
+       #  Use only one built-in basis set?
+       gbasis = nil
+       if !use_2nd
+         case bssname
+         when "PM3"
+               gbasis = "PM3"
+         when "STO3G"
+               gbasis = "STO NGAUSS=3"
+         when "321G"
+               gbasis = "N21 NGAUSS=3"
+         when "631G"
+               gbasis = "N31 NGAUSS=6"
+         when "631Gd"
+               gbasis = "N31 NGAUSS=6 NDFUNC=1"
+         when "631Gdp"
+               gbasis = "N31 NGAUSS=6 NDFUNC=1 NPFUNC=1"
+         when "6311G"
+               gbasis = "N311 NGAUSS=6"
+         when "6311Gdp"
+               gbasis = "N311 NGAUSS=6 NDFUNC=1 NPFUNC=1"
+         end
+       end
+
+    File.open(fname, "wb") { |fp|
+         fp.print "!  GAMESS input\n"
+         fp.print "!  Generated by Molby at #{now}\n"
+         fp.print " $CONTRL COORD=UNIQUE EXETYP=RUN ICHARG=#{icharg}\n"
+         fp.print "         ICUT=20 INTTYP=HONDO ITOL=30\n"
+         fp.print "         MAXIT=200 MOLPLT=.T. MPLEVL=0\n"
+         fp.print "         MULT=#{mult} QMTTOL=1e-08 RUNTYP=#{runtyp}\n"
+         if hash["dft"] != 0
+           dfttyp = hash["dfttype"]
+           fp.print "         DFTTYP=#{dfttyp}\n"
+         end
+         fp.print "         SCFTYP=#{scftyp} #{ecp_read}UNITS=ANGS $END\n"
+         fp.print " $SCF    CONV=1.0E-06 DIRSCF=.T. FDIFF=.T. DAMP=.T. $END\n"
+         fp.print " $STATPT NSTEP=400 OPTTOL=1.0E-06               $END\n"
+         fp.print " $SYSTEM MEMDDI=0 MWORDS=16 TIMLIM=50000        $END\n"
+         fp.print " $GUESS  GUESS=HUCKEL                           $END\n"
+      if gbasis
+           fp.print " $BASIS  GBASIS=#{gbasis} $END\n"
+         end
+         if hash["esp"] != 0
+           fp.print " $ELPOT  IEPOT=1 OUTPUT=PUNCH WHERE=PDC         $END\n"
+               fp.print " $PDC    CONSTR=NONE PTSEL=CONNOLLY             $END\n"
+         end
+         fp.print " $DATA\n#{basename}\nC1 0\n"
+         secondary = []
+         each_atom { |ap|
+               fp.printf "%-6s %4d %10.6f %10.6f %10.6f\n", ap.name, ap.atomic_number, ap.r.x, ap.r.y, ap.r.z
+               if use_2nd && element2.include?(ap.element)
+                 secondary[ap.index] = true
+               end
+           if !gbasis
+                 #  Basis specification followed by a blank line
+                 bas = (secondary[ap.index] ? basis2 : basis)
+                 if bas.is_a?(Array)
+                   bas = bas[ap.atomic_number]
+                 end
+                 if !bas
+                   raise MolbyError, "Basis set is not defined for atom #{ap.index}, element #{ap.element}"
+                 end
+                 fp.print bas
+                 fp.print "\n"
+               end
+         }
+         fp.print " $END\n"
+         ecp_ary = []
+         if ecp || ecp2
+           fp.print " $ECP\n"
+               each_atom { |ap|
+                 an = ap.atomic_number
+                 ecpp = (secondary[ap.index] ? ecp2 : ecp)
+                 e = ecp_ary[an] || (ecpp && ecpp[an])
+                 if e
+                   #  Cache the PNAME of the $ECP entry and re-use it
+                   ecp_ary[an] ||= (e.split)[0] + "\n"
+                 else
+                   e = ap.element.upcase + "-ECP NONE\n"
+                 end
+                 fp.print e
+               }
+           fp.print " $END\n"
+         end
+       }
+       fname
+  end
+  
+  def cmd_create_gamess_input
+    if natoms == 0
+      raise MolbyError, "cannot create GAMESS input; the molecule is empty"
+    end
+
+       #  Descriptive text and internal string for popup menus
+    bset_desc = ["PM3", "STO-3G", "3-21G", "6-31G", "6-31G(d)", "6-31G(d,p)", "6-311G", "6-311G(d,p)", "LanL2DZ"]
+       bset_internal = ["PM3", "STO3G", "321G", "631G", "631Gd", "631Gdp", "6311G", "6311Gdp", "LanL2DZ"]
+       dft_desc = ["B3LYP"]
+       dft_internal = ["B3LYP"]
+
+       defaults = {"scftype"=>0, "runtype"=>0, "charge"=>"0", "mult"=>"1",
+         "basis"=>4, "use_secondary_basis"=>0, "secondary_elements"=>"",
+         "secondary_basis"=>8, "esp"=>0}
+
+    hash = RubyDialog.run("GAMESS Export") {
+         def action(n)
+           if n == 0 || n == 1
+             each_item { |i|
+               tag = attr(i, :tag)
+                       if tag
+                         set_global_settings("gamess.#{tag}", attr(i, :value))
+                       end
+                 }
+               end
+               super
+         end
+         layout(4,
+               item(:text, :title=>"SCF type"),
+               item(:popup, :subitems=>["RHF", "ROHF", "UHF"], :tag=>"scftype"),
+           item(:text, :title=>"Run type"),
+               item(:popup, :subitems=>["Energy", "Property", "Optimize"], :tag=>"runtype"),
+
+               item(:text, :title=>"Charge"),
+               item(:textfield, :width=>80, :tag=>"charge"),
+               item(:text, :title=>"Multiplicity"),
+               item(:textfield, :width=>80, :tag=>"mult"),
+
+               item(:checkbox, :title=>"Use DFT", :tag=>"dft",
+                 :action=>proc { |n| set_attr("dfttype", :enabled=>(attr(n, :value) != 0)) } ),
+               -1,
+               item(:text, :title=>"DFT type"),
+               item(:popup, :subitems=>dft_desc, :tag=>"dfttype"),
+               
+               item(:line),
+               -1, -1, -1,
+
+               item(:text, :title=>"Basis set"),
+               item(:popup, :subitems=>bset_desc, :tag=>"basis"),
+               -1, -1,
+
+               item(:checkbox, :title=>"Use secondary basis set", :tag=>"use_secondary_basis",
+                 :action=>proc { |n|
+                   flag = (attr(n, :value) != 0)
+                       set_attr("secondary_elements", :enabled=>flag)
+                       set_attr("secondary_basis", :enabled=>flag)
+                 }),
+               -1, -1, -1,
+
+               item(:text, :title=>"   Elements"),
+               item(:textfield, :width=>80, :tag=>"secondary_elements"),
+               item(:text, :title=>"Basis set"),
+               item(:popup, :subitems=>bset_desc, :tag=>"secondary_basis"),
+               
+               item(:line),
+               -1, -1, -1,
+               
+               item(:checkbox, :title=>"Calculate electrostatic potential (ESP)", :tag=>"esp"),
+               -1, -1, -1,
+       
+               item(:line),
+               -1, -1, -1
+         )
+         values = Hash.new
+         each_item { |i|
+           tag = attr(i, :tag)
+               if tag
+                 values[tag] = (get_global_settings("gamess.#{tag}") || defaults[tag])
+                 set_attr(i, :value=>values[tag])
+               end
+         }
+         set_attr("secondary_elements", :enabled=>(values["use_secondary_basis"] == 1))
+         set_attr("secondary_basis", :enabled=>(values["use_secondary_basis"] == 1))
+         set_attr("dfttype", :enabled=>(values["dft"] == 1))
+       }
+       if hash
+         #  Specify basis by internal keys
+         hash["basis"] = bset_internal[hash["basis"]]
+         hash["secondary_basis"] = bset_internal[hash["secondary_basis"]]
+         hash["dfttype"] = dft_internal[hash["dfttype"]]
+         basename = (self.path ? File.basename(self.path, ".*") : self.name)
+      fname = RubyDialog.save_panel("GAMESS input file name", self.dir, basename + ".inp", "GAMESS input file (*.inp)|*.inp|All files|*.*")
+         return nil if !fname
+         export_gamess(fname, hash)
+       else
+         nil
+       end
+  end
+  
+end
+
+Molecule.read_gamess_basis_sets("LanL2DZ.txt")
+Molecule.read_gamess_basis_sets("631Gd.txt")
+Molecule.read_gamess_basis_sets("631Gdp.txt")
+Molecule.read_gamess_basis_sets("6311Gdp.txt")
+$gamess_basis["PM3"]   = " PM3 0\n"
+$gamess_basis["STO3G"] = " STO 3\n"
+$gamess_basis["321G"]  = " N21 3\n"
+$gamess_basis["631G"]  = " N31 6\n"
diff --git a/Scripts/lib/scanf.rb b/Scripts/lib/scanf.rb
new file mode 100644 (file)
index 0000000..d083019
--- /dev/null
@@ -0,0 +1,702 @@
+# scanf for Ruby
+#
+# $Revision: 1.1 $
+# $Id: scanf.rb,v 1.1 2010/01/21 16:11:05 tngrp Exp $
+# $Author: tngrp $
+# $Date: 2010/01/21 16:11:05 $
+#
+# A product of the Austin Ruby Codefest (Austin, Texas, August 2002)
+
+=begin
+
+=scanf for Ruby
+
+==Description
+
+scanf for Ruby is an implementation of the C function scanf(3),
+modified as necessary for Ruby compatibility.
+
+The methods provided are String#scanf, IO#scanf, and
+Kernel#scanf. Kernel#scanf is a wrapper around STDIN.scanf.  IO#scanf
+can be used on any IO stream, including file handles and sockets.
+scanf can be called either with or without a block.
+
+scanf for Ruby scans an input string or stream according to a
+<b>format</b>, as described below ("Conversions"), and returns an
+array of matches between the format and the input.  The format is
+defined in a string, and is similar (though not identical) to the
+formats used in Kernel#printf and Kernel#sprintf.
+
+The format may contain <b>conversion specifiers</b>, which tell scanf
+what form (type) each particular matched substring should be converted
+to (e.g., decimal integer, floating point number, literal string,
+etc.)  The matches and conversions take place from left to right, and
+the conversions themselves are returned as an array.
+
+The format string may also contain characters other than those in the
+conversion specifiers.  White space (blanks, tabs, or newlines) in the
+format string matches any amount of white space, including none, in
+the input.  Everything else matches only itself.
+
+Scanning stops, and scanf returns, when any input character fails to
+match the specifications in the format string, or when input is
+exhausted, or when everything in the format string has been
+matched. All matches found up to the stopping point are returned in
+the return array (or yielded to the block, if a block was given).
+
+
+==Basic usage
+
+   require 'scanf.rb'
+
+   # String#scanf and IO#scanf take a single argument (a format string)
+   array = aString.scanf("%d%s")
+   array = anIO.scanf("%d%s")
+
+   # Kernel#scanf reads from STDIN
+   array = scanf("%d%s")
+
+==Block usage
+
+When called with a block, scanf keeps scanning the input, cycling back
+to the beginning of the format string, and yields a new array of
+conversions to the block every time the format string is matched
+(including partial matches, but not including complete failures).  The
+actual return value of scanf when called with a block is an array
+containing the results of all the executions of the block. 
+
+   str = "123 abc 456 def 789 ghi"
+   str.scanf("%d%s") { |num,str| [ num * 2, str.upcase ] }
+     # => [[246, "ABC"], [912, "DEF"], [1578, "GHI"]]
+
+==Conversions
+
+The single argument to scanf is a format string, which generally
+includes one or more conversion specifiers. Conversion specifiers
+begin with the percent character ('%') and include information about
+what scanf should next scan for (string, decimal number, single
+character, etc.).
+
+There may be an optional maximum field width, expressed as a decimal
+integer, between the % and the conversion. If no width is given, a
+default of `infinity' is used (with the exception of the %c specifier;
+see below).  Otherwise, given a field width of <em>n</em> for a given
+conversion, at most <em>n</em> characters are scanned in processing
+that conversion.  Before conversion begins, most conversions skip
+white space in the input string; this white space is not counted
+against the field width.
+
+The following conversions are available. (See the files EXAMPLES
+and <tt>tests/scanftests.rb</tt> for examples.)
+
+[%]
+  Matches a literal `%'. That is, `%%' in the format string matches a
+  single input `%' character. No conversion is done, and the resulting
+  '%' is not included in the return array.
+
+[d]
+  Matches an optionally signed decimal integer.
+
+[u]
+  Same as d.
+
+[i] 
+  Matches an optionally signed integer. The integer is read in base
+  16 if it begins with `0x' or `0X', in base 8 if it begins with `0',
+  and in base 10 other- wise. Only characters that correspond to the
+  base are recognized.
+
+[o]
+  Matches an optionally signed octal integer.
+
+[x,X]
+  Matches an optionally signed hexadecimal integer,
+
+[f,g,e,E]
+  Matches an optionally signed floating-point number.
+
+[s]
+  Matches a sequence of non-white-space character. The input string stops at
+  white space or at the maximum field width, whichever occurs first.
+
+[c]
+  Matches a single character, or a sequence of <em>n</em> characters if a
+  field width of <em>n</em> is specified. The usual skip of leading white
+  space is suppressed. To skip white space first, use an explicit space in
+  the format.
+
+[<tt>[</tt>]
+  Matches a nonempty sequence of characters from the specified set
+  of accepted characters.  The usual skip of leading white space is
+  suppressed.  This bracketed sub-expression is interpreted exactly like a
+  character class in a Ruby regular expression.  (In fact, it is placed as-is
+  in a regular expression.)  The matching against the input string ends with
+  the appearance of a character not in (or, with a circumflex, in) the set,
+  or when the field width runs out, whichever comes first.
+
+===Assignment suppression
+
+To require that a particular match occur, but without including the result
+in the return array, place the <b>assignment suppression flag</b>, which is
+the star character ('*'), immediately after the leading '%' of a format
+specifier (just before the field width, if any).
+
+==Examples
+
+See the files <tt>EXAMPLES</tt> and <tt>tests/scanftests.rb</tt>.
+
+==scanf for Ruby compared with scanf in C
+
+scanf for Ruby is based on the C function scanf(3), but with modifications,
+dictated mainly by the underlying differences between the languages.
+
+===Unimplemented flags and specifiers
+
+* The only flag implemented in scanf for Ruby is '<tt>*</tt>' (ignore
+  upcoming conversion). Many of the flags available in C versions of scanf(4)
+  have to do with the type of upcoming pointer arguments, and are literally
+  meaningless in Ruby.
+
+* The <tt>n</tt> specifier (store number of characters consumed so far in
+  next pointer) is not implemented.
+
+* The <tt>p</tt> specifier (match a pointer value) is not implemented.
+
+===Altered specifiers
+
+[o,u,x,X]
+  In scanf for Ruby, all of these specifiers scan for an optionally signed
+  integer, rather than for an unsigned integer like their C counterparts.
+
+===Return values
+
+scanf for Ruby returns an array of successful conversions, whereas
+scanf(3) returns the number of conversions successfully
+completed. (See below for more details on scanf for Ruby's return
+values.)
+
+==Return values
+
+Without a block, scanf returns an array containing all the conversions
+it has found. If none are found, scanf will return an empty array. An
+unsuccesful match is never ignored, but rather always signals the end
+of the scanning operation. If the first unsuccessful match takes place
+after one or more successful matches have already taken place, the
+returned array will contain the results of those successful matches.
+
+With a block scanf returns a 'map'-like array of transformations from
+the block -- that is, an array reflecting what the block did with each
+yielded result from the iterative scanf operation.  (See "Block
+usage", above.)
+
+==Test suite
+
+scanf for Ruby includes a suite of unit tests (requiring the
+<tt>TestUnit</tt> package), which can be run with the command <tt>ruby
+tests/scanftests.rb</tt> or the command <tt>make test</tt>.
+
+==Current limitations and bugs
+
+When using IO#scanf under Windows, make sure you open your files in
+binary mode:
+
+    File.open("filename", "rb")
+
+so that scanf can keep track of characters correctly.
+
+Support for character classes is reasonably complete (since it
+essentially piggy-backs on Ruby's regular expression handling of
+character classes), but users are advised that character class testing
+has not been exhaustive, and that they should exercise some caution
+in using any of the more complex and/or arcane character class
+idioms.
+
+
+==Technical notes
+
+===Rationale behind scanf for Ruby
+
+The impetus for a scanf implementation in Ruby comes chiefly from the fact
+that existing pattern matching operations, such as Regexp#match and
+String#scan, return all results as strings, which have to be converted to
+integers or floats explicitly in cases where what's ultimately wanted are
+integer or float values.
+
+===Design of scanf for Ruby
+
+scanf for Ruby is essentially a <format string>-to-<regular
+expression> converter.
+
+When scanf is called, a FormatString object is generated from the
+format string ("%d%s...") argument. The FormatString object breaks the
+format string down into atoms ("%d", "%5f", "blah", etc.), and from
+each atom it creates a FormatSpecifier object, which it
+saves.
+
+Each FormatSpecifier has a regular expression fragment and a "handler"
+associated with it. For example, the regular expression fragment
+associated with the format "%d" is "([-+]?\d+)", and the handler
+associated with it is a wrapper around String#to_i. scanf itself calls
+FormatString#match, passing in the input string. FormatString#match
+iterates through its FormatSpecifiers; for each one, it matches the
+corresponding regular expression fragment against the string. If
+there's a match, it sends the matched string to the handler associated
+with the FormatSpecifier.
+
+Thus, to follow up the "%d" example: if "123" occurs in the input
+string when a FormatSpecifier consisting of "%d" is reached, the "123"
+will be matched against "([-+]?\d+)", and the matched string will be
+rendered into an integer by a call to to_i.
+
+The rendered match is then saved to an accumulator array, and the
+input string is reduced to the post-match substring. Thus the string
+is "eaten" from the left as the FormatSpecifiers are applied in
+sequence.  (This is done to a duplicate string; the original string is
+not altered.)
+
+As soon as a regular expression fragment fails to match the string, or
+when the FormatString object runs out of FormatSpecifiers, scanning
+stops and results accumulated so far are returned in an array.
+
+==License and copyright
+
+Copyright:: (c) 2002-2003 David Alan Black
+License:: Distributed on the same licensing terms as Ruby itself
+
+==Warranty disclaimer
+
+This software is provided "as is" and without any express or implied
+warranties, including, without limitation, the implied warranties of
+merchantibility and fitness for a particular purpose.
+
+==Credits and acknowledgements
+
+scanf for Ruby was developed as the major activity of the Austin
+Ruby Codefest (Austin, Texas, August 2002).
+
+Principal author:: David Alan Black (mailto:dblack@superlink.net)
+Co-author:: Hal Fulton (mailto:hal9000@hypermetrics.com)
+Project contributors:: Nolan Darilek, Jason Johnston
+
+Thanks to Hal Fulton for hosting the Codefest.
+
+Thanks to Matz for suggestions about the class design.  
+
+Thanks to Gavin Sinclair for some feedback on the documentation.
+
+The text for parts of this document, especially the Description and
+Conversions sections, above, were adapted from the Linux Programmer's
+Manual manpage for scanf(3), dated 1995-11-01.
+
+==Bugs and bug reports
+
+scanf for Ruby is based on something of an amalgam of C scanf
+implementations and documentation, rather than on a single canonical
+description. Suggestions for features and behaviors which appear in
+other scanfs, and would be meaningful in Ruby, are welcome, as are
+reports of suspicious behaviors and/or bugs. (Please see "Credits and
+acknowledgements", above, for email addresses.)
+
+=end
+
+module Scanf
+
+  class FormatSpecifier
+
+    attr_reader :re_string, :matched_string, :conversion, :matched
+
+    private
+
+    def skip;  /^\s*%\*/.match(@spec_string); end
+
+    def extract_float(s); s.to_f if s &&! skip; end
+    def extract_decimal(s); s.to_i if s &&! skip; end
+    def extract_hex(s); s.hex if s &&! skip; end
+    def extract_octal(s); s.oct if s &&! skip; end
+    def extract_integer(s); Integer(s) if s &&! skip; end
+    def extract_plain(s); s unless skip; end
+
+    def nil_proc(s); nil; end
+
+    public
+
+    def to_s
+      @spec_string
+    end
+
+    def count_space?
+      /(?:\A|\S)%\*?\d*c|\[/.match(@spec_string)
+    end
+
+    def initialize(str)
+      @spec_string = str
+      h = '[A-Fa-f0-9]'
+
+      @re_string, @handler = 
+        case @spec_string
+
+          # %[[:...:]]
+        when /%\*?(\[\[:[a-z]+:\]\])/
+          [ "(#{$1}+)", :extract_plain ]
+
+          # %5[[:...:]]
+        when /%\*?(\d+)(\[\[:[a-z]+:\]\])/
+          [ "(#{$2}{1,#{$1}})", :extract_plain ]
+
+          # %[...]
+        when /%\*?\[([^\]]*)\]/
+          yes = $1
+          if /^\^/.match(yes) then no = yes[1..-1] else no = '^' + yes end
+          [ "([#{yes}]+)(?=[#{no}]|\\z)", :extract_plain ]
+
+          # %5[...]
+        when /%\*?(\d+)\[([^\]]*)\]/
+          yes = $2
+          w = $1
+          [ "([#{yes}]{1,#{w}})", :extract_plain ]
+
+          # %i
+        when /%\*?i/
+          [ "([-+]?(?:(?:0[0-7]+)|(?:0[Xx]#{h}+)|(?:[1-9]\\d*)))", :extract_integer ]
+
+          # %5i
+        when /%\*?(\d+)i/
+          n = $1.to_i
+          s = "("
+          if n > 1 then s += "[1-9]\\d{1,#{n-1}}|" end
+          if n > 1 then s += "0[0-7]{1,#{n-1}}|" end
+          if n > 2 then s += "[-+]0[0-7]{1,#{n-2}}|" end
+          if n > 2 then s += "[-+][1-9]\\d{1,#{n-2}}|" end
+          if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
+          if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
+          s += "\\d"
+          s += ")"
+          [ s, :extract_integer ]
+
+          # %d, %u
+        when /%\*?[du]/
+          [ '([-+]?\d+)', :extract_decimal ]
+
+          # %5d, %5u
+        when /%\*?(\d+)[du]/
+          n = $1.to_i
+          s = "("
+          if n > 1 then s += "[-+]\\d{1,#{n-1}}|" end
+          s += "\\d{1,#{$1}})"
+          [ s, :extract_decimal ]
+
+          # %x
+        when /%\*?[Xx]/
+          [ "([-+]?(?:0[Xx])?#{h}+)", :extract_hex ]
+
+          # %5x
+        when /%\*?(\d+)[Xx]/
+          n = $1.to_i
+          s = "("
+          if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
+          if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
+          if n > 1 then s += "[-+]#{h}{1,#{n-1}}|" end
+          s += "#{h}{1,#{n}}"
+          s += ")"
+          [ s, :extract_hex ]
+
+          # %o
+        when /%\*?o/
+          [ '([-+]?[0-7]+)', :extract_octal ]
+
+          # %5o
+        when /%\*?(\d+)o/
+          [ "([-+][0-7]{1,#{$1.to_i-1}}|[0-7]{1,#{$1}})", :extract_octal ]
+
+          # %f
+        when /%\*?f/
+          [ '([-+]?((\d+(?>(?=[^\d.]|$)))|(\d*(\.(\d*([eE][-+]?\d+)?)))))', :extract_float ]
+
+          # %5f
+        when /%\*?(\d+)f/
+          [ "(\\S{1,#{$1}})", :extract_float ]
+
+          # %5s
+        when /%\*?(\d+)s/
+          [ "(\\S{1,#{$1}})", :extract_plain ]
+
+          # %s
+        when /%\*?s/
+          [ '(\S+)', :extract_plain ]
+
+          # %c
+        when /\s%\*?c/
+          [ "\\s*(.)", :extract_plain ]
+
+          # %c
+        when /%\*?c/
+          [ "(.)", :extract_plain ]
+
+          # %5c (whitespace issues are handled by the count_*_space? methods)
+        when /%\*?(\d+)c/
+          [ "(.{1,#{$1}})", :extract_plain ]
+
+          # %%
+        when /%%/
+          [ '(\s*%)', :nil_proc ]
+
+          # literal characters
+        else
+          [ "(#{Regexp.escape(@spec_string)})", :nil_proc ]
+        end
+
+      @re_string = '\A' + @re_string
+    end
+
+    def to_re
+      Regexp.new(@re_string,Regexp::MULTILINE)
+    end
+
+    def match(str)
+      @matched = false
+      s = str.dup
+      s.sub!(/\A\s+/,'') unless count_space?
+      res = to_re.match(s)
+      if res
+        @conversion = send(@handler, res[1])
+        @matched_string = @conversion.to_s
+        @matched = true
+      end
+      res
+    end
+
+    def letter
+      /%\*?\d*([a-z\[])/.match(@spec_string).to_a[1]
+    end
+
+    def width
+      w = /%\*?(\d+)/.match(@spec_string).to_a[1]
+      w && w.to_i
+    end
+
+    def mid_match?
+      return false unless @matched
+      cc_no_width    = letter == '[' &&! width
+      c_or_cc_width  = (letter == 'c' || letter == '[') && width
+      width_left     = c_or_cc_width && (matched_string.size < width)
+
+      return width_left || cc_no_width
+    end
+    
+  end
+
+  class FormatString
+
+    attr_reader :string_left, :last_spec_tried,
+                :last_match_tried, :matched_count, :space
+
+    SPECIFIERS = 'diuXxofeEgsc'
+    REGEX = /
+        # possible space, followed by...
+          (?:\s*
+          # percent sign, followed by...
+            %
+            # another percent sign, or...
+              (?:%|
+                # optional assignment suppression flag
+                \*?
+                # optional maximum field width
+                \d*
+                  # named character class, ...
+                  (?:\[\[:\w+:\]\]|
+                  # traditional character class, or...
+                     \[[^\]]*\]|
+                  # specifier letter.
+                     [#{SPECIFIERS}])))|
+            # or miscellaneous characters
+              [^%\s]+/ix
+
+    def initialize(str)
+      @specs = []
+      @i = 1
+      s = str.to_s
+      return unless /\S/.match(s)
+      @space = true if /\s\z/.match(s)
+      @specs.replace s.scan(REGEX).map {|spec| FormatSpecifier.new(spec) }
+    end
+
+    def to_s
+      @specs.join('')
+    end
+
+    def prune(n=matched_count)
+      n.times { @specs.shift }
+    end
+
+    def spec_count
+      @specs.size
+    end
+
+    def last_spec
+      @i == spec_count - 1
+    end
+
+    def match(str)
+      accum = []
+      @string_left = str
+      @matched_count = 0
+
+      @specs.each_with_index do |spec,@i|
+        @last_spec_tried = spec
+        @last_match_tried = spec.match(@string_left)
+        break unless @last_match_tried
+        @matched_count += 1
+
+        accum << spec.conversion
+
+        @string_left = @last_match_tried.post_match
+        break if @string_left.empty?
+      end
+      return accum.compact
+    end
+  end
+end
+
+class IO
+
+# The trick here is doing a match where you grab one *line*
+# of input at a time.  The linebreak may or may not occur
+# at the boundary where the string matches a format specifier.
+# And if it does, some rule about whitespace may or may not
+# be in effect...
+#
+# That's why this is much more elaborate than the string
+# version.
+#
+# For each line:
+# Match succeeds (non-emptily)
+# and the last attempted spec/string sub-match succeeded:
+#
+#   could the last spec keep matching?
+#     yes: save interim results and continue (next line)
+#
+# The last attempted spec/string did not match:
+#
+# are we on the next-to-last spec in the string?
+#   yes:
+#     is fmt_string.string_left all spaces?
+#       yes: does current spec care about input space?
+#         yes: fatal failure
+#         no: save interim results and continue
+#   no: continue  [this state could be analyzed further]
+#
+#
+
+  def scanf(str,&b)
+    return block_scanf(str,&b) if b
+    return [] unless str.size > 0
+
+    start_position = pos rescue 0
+    matched_so_far = 0
+    source_buffer = ""
+    result_buffer = []
+    final_result = []
+
+    fstr = Scanf::FormatString.new(str)
+
+    loop do
+      if eof || (tty? &&! fstr.match(source_buffer))
+        final_result.concat(result_buffer)
+        break
+      end
+
+      source_buffer << gets
+
+      current_match = fstr.match(source_buffer)
+
+      spec = fstr.last_spec_tried
+
+      if spec.matched
+        if spec.mid_match?
+          result_buffer.replace(current_match)
+          next
+        end
+
+      elsif (fstr.matched_count == fstr.spec_count - 1)
+        if /\A\s*\z/.match(fstr.string_left)
+          break if spec.count_space?
+          result_buffer.replace(current_match)
+          next
+        end
+      end
+
+      final_result.concat(current_match)
+
+      matched_so_far += source_buffer.size
+      source_buffer.replace(fstr.string_left)
+      matched_so_far -= source_buffer.size
+      break if fstr.last_spec
+      fstr.prune
+    end
+    seek(start_position + matched_so_far, IO::SEEK_SET) rescue Errno::ESPIPE
+    soak_up_spaces if fstr.last_spec && fstr.space
+
+    return final_result
+  end
+
+  private
+
+  def soak_up_spaces
+    c = getc
+    ungetc(c) if c
+    until eof ||! c || /\S/.match(c.chr)
+      c = getc
+    end
+    ungetc(c) if (c && /\S/.match(c.chr))
+  end
+
+  def block_scanf(str)
+    final = []
+# Sub-ideal, since another FS gets created in scanf.
+# But used here to determine the number of specifiers.
+    fstr = Scanf::FormatString.new(str)
+    last_spec = fstr.last_spec
+    begin
+      current = scanf(str)
+      break if current.empty?
+      final.push(yield(current))
+    end until eof || fstr.last_spec_tried == last_spec
+    return final
+  end
+end
+
+class String
+
+  def scanf(fstr,&b)
+    if b
+      block_scanf(fstr,&b)
+    else
+      fs = 
+        if fstr.is_a? Scanf::FormatString
+          fstr 
+        else 
+          Scanf::FormatString.new(fstr)
+        end
+      fs.match(self)
+    end
+  end
+
+  def block_scanf(fstr,&b)
+    fs = Scanf::FormatString.new(fstr)
+    str = self.dup
+    final = []
+    begin
+      current = str.scanf(fs)
+      final.push(yield(current)) unless current.empty?
+      str = fs.string_left
+    end until current.empty? || str.empty?
+    return final
+  end
+end
+
+module Kernel
+  private
+  def scanf(fs,&b)
+    STDIN.scanf(fs,&b)
+  end
+end
diff --git a/Scripts/loadsave.rb b/Scripts/loadsave.rb
new file mode 100755 (executable)
index 0000000..71785d7
--- /dev/null
@@ -0,0 +1,549 @@
+#
+#  loadsave.rb
+#
+#  Created by Toshi Nagata.
+#  Copyright 2008 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+require "scanf.rb"
+
+class Molecule
+
+  def loadcrd(filename)
+    if natoms == 0
+      raise MolbyError, "cannot load crd; the molecule is empty"
+    end
+       fp = open(filename, "rb")
+    count = 0
+       frame = 0
+       coords = (0...natoms).collect { Vector3D[0, 0, 0] }
+       show_progress_panel("Loading a crd file...")
+#    puts "sframe = #{sframe}, pos = #{fp.pos}"
+    while 1
+      line = fp.gets
+      if line == nil
+           if (count > 0)
+                 raise MolbyError, sprintf("crd format error - file ended in the middle of frame %d", frame)
+               end
+           fp.close
+               break
+      end
+      next if line.match(/^TITLE/)
+      line.chomp
+      values = line.split(' ')
+      if count + values.size > natoms * 3
+        raise MolbyError, sprintf("crd format error - too many values at line %d in file %s; number of atoms = %d, current frame = %d", fp.lineno, fp.path, natoms, frame)
+      end
+         values.each { |v|
+           coords[count / 3][count % 3] = Float(v)
+           count += 1
+         }
+      if count == natoms * 3
+           #  End of frame
+               if frame == 0 && atoms.all? { |ap| ap.r.x == 0.0 && ap.r.y == 0.0 && ap.r.z == 0.0 }
+                       #  Do not create a new frame
+                       atoms.each_with_index { |ap, i| ap.r = coords[i] }
+               else
+                       create_frame([coords])
+               end
+        count = 0
+        frame += 1
+               if frame % 5 == 0
+                 set_progress_message("Loading a crd file...\n(#{frame} frames completed)")
+               end
+      end
+    end
+       hide_progress_panel
+       if frame > 0
+         self.frame = self.nframes - 1
+         return true
+       else
+         return false
+       end
+  end
+    
+  def savecrd(filename)
+    if natoms == 0
+      raise MolbyError, "cannot save crd; the molecule is empty"
+    end
+    fp = open(filename, "wb")
+       show_progress_panel("Saving a crd file...")
+       fp.printf("TITLE: %d atoms\n", natoms)
+       cframe = self.frame
+       nframes = self.nframes
+       j = 0
+       self.update_enabled = false
+       begin
+               (0...nframes).each { |i|
+                 select_frame(i)
+                 j = 0
+                 while (j < natoms * 3)
+                       w = atoms[j / 3].r[j % 3]
+                       fp.printf(" %7.3f", w)
+                       fp.print("\n") if (j % 10 == 9 || j == natoms * 3 - 1)
+                       j += 1
+                 end
+                 if i % 5 == 0
+                       set_progress_message("Saving a crd file...\n(#{i} frames completed)")
+                 end
+               }
+       ensure
+               self.update_enabled = true
+       end
+       select_frame(cframe)
+       fp.close
+       hide_progress_panel
+       true
+  end
+
+  def loadlog(filename)
+
+    if natoms == 0
+               new_unit = true
+ #     raise MolbyError, "cannot load crd; the molecule is empty"
+       else
+               new_unit = false
+    end
+       save_undo_enabled = self.undo_enabled?
+       self.undo_enabled = false
+       self.update_enabled = false
+       begin
+               fp = open(filename, "rb")
+               if nframes > 0
+                       create_frame
+                       frame = nframes - 1
+               end
+               n = 0
+               while 1
+                       line = fp.gets
+                       if line == nil
+                               fp.close
+                               break
+                       end
+                       line.chomp
+                       if line =~ /ATOM\s+ATOMIC\s+COORDINATES/ || line =~ /COORDINATES OF ALL ATOMS ARE/
+                               first_line = (line =~ /ATOMIC/)
+                               line = fp.gets    #  Skip one line
+                               n = 0
+                               coords = []
+                               names = []
+                               while (line = fp.gets) != nil
+                                       break if line =~ /^\s*$/ || line =~ /END OF ONE/
+                                       next if line =~ /-----/
+                                       name, charge, x, y, z = line.split
+                                       v = Vector3D[x, y, z]
+                                       coords.push(v * (first_line ? 0.529177 : 1.0))  #  Bohr to angstrom
+                                       names.push([name, charge])
+                               end
+                               if new_unit
+                                       #  Build a new molecule
+                                       names.each_index { |i|
+                                               ap = add_atom(names[i][0])
+                                               ap.atomic_number = names[i][1].to_i
+                                               ap.atom_type = ap.element
+                                               ap.r = coords[i]
+                                       }
+                                       #  Find bonds
+                                       guess_bonds
+                               #       atoms.each { |ap|
+                               #               j = ap.index
+                               #               (j + 1 ... natoms).each { |k|
+                               #                       if calc_bond(j, k) < 1.7
+                               #                               create_bond(j, k)
+                               #                       end
+                               #               }
+                               #       }
+                                       new_unit = false
+                                       create_frame
+                               else
+                                       create_frame([coords])  #  Should not be (coords)
+                               end
+                       elsif line =~ /EQUILIBRIUM GEOMETRY LOCATED/i
+                               fp.gets; fp.gets; fp.gets
+                               n = 0
+                               while (line = fp.gets) != nil
+                                       break if line =~ /^\s*$/
+                                       line.chomp
+                                       atom, an, x, y, z = line.split
+                                       ap = atoms[n]
+                                       ap.r = Vector3D[x, y, z]
+                                       n += 1
+                                       break if n >= natoms
+                               end
+                       end
+               end
+       ensure
+               self.undo_enabled = save_undo_enabled
+               self.update_enabled = true
+       end
+       (n > 0 ? true : false)
+  end
+  
+  def loadxyz(filename)
+       save_undo_enabled = self.undo_enabled?
+       self.undo_enabled = false
+       fp = open(filename, "rb")
+       n = 0
+       coords = []
+       names = []
+       cell = nil
+       while 1
+         line = fp.gets
+         if line == nil
+               fp.close
+               break
+         end
+         line.chomp
+         toks = line.split
+         if coords.length == 0
+           #  Allow "number of atoms" line or "number of atoms and crystallographic cell parameter"
+               #  (Chem3D xyz format)
+               next if toks.length == 1
+               if toks.length == 7
+                 cell = toks[1..6].map { |s| Float(s.sub(/\(\d+\)/, "")) }  #  Remove (xx) and convert to float
+                 next
+               end
+         end
+         name, x, y, z = line.split
+         next if z == nil
+         x = Float(x.sub(/\(\d+\)/, ""))
+         y = Float(y.sub(/\(\d+\)/, ""))
+         z = Float(z.sub(/\(\d+\)/, ""))
+         r = Vector3D[x, y, z]
+         coords.push(r)
+         names.push(name)
+         n += 1
+       end
+       celltr = nil
+       if cell
+         self.cell = cell
+         celltr = self.cell_transform
+       end
+       names.each_index { |i|
+         ap = add_atom(names[i])
+         names[i] =~ /^([A-Za-z]{1,2})/
+         element = $1.capitalize
+         ap.element = element
+         ap.atom_type = element
+         if celltr
+           ap.r = celltr * coords[i]
+         else
+           ap.r = coords[i]
+         end
+       }
+       guess_bonds
+       #  Find bonds
+#      atoms.each { |ap|
+#        j = ap.index
+#        (j + 1 ... natoms).each { |k|
+#              if calc_bond(j, k) < 1.7
+#              #  create_bond(j, k)
+#              end
+#        }
+#      }
+       self.undo_enabled = save_undo_enabled
+       (n > 0 ? true : false)
+  end
+  
+  def loadout(filename)
+
+    if natoms == 0
+               new_unit = true
+ #     raise MolbyError, "cannot load crd; the molecule is empty"
+       else
+               new_unit = false
+    end
+       save_undo_enabled = undo_enabled?
+       self.undo_enabled = false
+       fp = open(filename, "rb")
+       if nframes > 0
+               create_frame
+               frame = nframes - 1
+       end
+       n = 0
+       nf = 0
+       use_input_orientation = false
+       while 1
+               line = fp.gets
+               if line == nil
+                       fp.close
+                       break
+               end
+               line.chomp
+               if line =~ /(Input|Standard) orientation/
+                       match = $1
+                       if match == "Input"
+                               use_input_orientation = true if nf == 0
+                               next if !use_input_orientation
+                       else
+                               next if use_input_orientation
+                       end
+                       4.times { line = fp.gets }    #  Skip four lines
+                       n = 0
+                       coords = []
+                       anums = []
+                       while (line = fp.gets) != nil
+                               break if line =~ /-----/
+                               num, charge, type, x, y, z = line.split
+                               coords.push(Vector3D[x, y, z])
+                               anums.push(charge)
+                       end
+                       if new_unit
+                               #  Build a new molecule
+                               anums.each_index { |i|
+                                       ap = add_atom("X")
+                                       ap.atomic_number = anums[i]
+                                       ap.atom_type = ap.element
+                                       ap.name = sprintf("%s%d", ap.element, i)
+                                       ap.r = coords[i]
+                               }
+                               #  Find bonds
+                               atoms.each { |ap|
+                                       j = ap.index
+                                       (j + 1 ... natoms).each { |k|
+                                               if calc_bond(j, k) < 1.7
+                                                       create_bond(j, k)
+                                               end
+                                       }
+                               }
+                               new_unit = false
+                               create_frame
+                       else
+                               create_frame([coords])  #  Should not be (coords)
+                       end
+               end
+       end
+       self.undo_enabled = save_undo_enabled
+       (n > 0 ? true : false)
+  end
+
+  def loadcom(filename)
+       save_undo_enabled = self.undo_enabled?
+       self.undo_enabled = false
+       self.remove(All)
+       fp = open(filename, "rb")
+       section = 0
+       while (line = fp.gets)
+         line.chomp!
+         if section == 0
+           section = 1 if line =~ /^\#/
+               next
+         elsif section == 1 || section == 2
+           section += 1 if line =~ /^\s*$/
+               next
+         else
+           #  The first line is skipped (charge and multiplicity)
+               while (line = fp.gets)
+                 line.chomp!
+                 break if line =~ /^\s*$/
+                 a = line.split(/\s*[ \t,\/]\s*/)
+                 r = Vector3D[Float(a[1]), Float(a[2]), Float(a[3])]
+                 ap = add_atom(a[0])
+                 a[0] =~ /^([A-Za-z]{1,2})/
+                 element = $1.capitalize
+             ap.element = element
+             ap.atom_type = element
+             ap.r = r
+               end
+               break
+         end
+       end
+       fp.close
+       self.undo_enabled = save_undo_enabled
+       return true
+  end
+  
+  def loadinp(filename)
+       save_undo_enabled = self.undo_enabled?
+       self.undo_enabled = false
+       self.remove(All)
+       fp = open(filename, "rb")
+       section = 0
+       has_basis = false
+       while (line = fp.gets)
+         if line =~ /\A \$BASIS/
+           has_basis = true
+               next
+         end
+         next if line !~ /\A \$DATA/
+         line = fp.gets   #  Title line
+         line = fp.gets   #  Symmetry line
+         while (line = fp.gets)
+#          puts line
+           line.chomp!
+               break if line =~ /\$END/
+               a = line.split
+               ap = add_atom(a[0])
+               ap.atomic_number = Integer(a[1])
+               ap.atom_type = ap.element
+               r = Vector3D[Float(a[2]), Float(a[3]), Float(a[4])]
+               ap.r = r;
+               if !has_basis
+                 #  Skip until one blank line is detected
+                 while (line = fp.gets) && line =~ /\S/
+                 end
+               end
+      end
+         break
+       end
+       fp.close
+       guess_bonds
+       self.undo_enabled = save_undo_enabled
+       return true
+  end
+  
+  def saveinp(filename)
+    if natoms == 0
+      raise MolbyError, "cannot save GAMESS input; the molecule is empty"
+    end
+    fp = open(filename, "wb")
+       now = Time.now.to_s
+       fp.print <<end_of_header
+!  GAMESS input
+!  Generated by Molby at #{now}
+ $CONTRL COORD=UNIQUE EXETYP=RUN ICHARG=0
+         ICUT=20 INTTYP=HONDO ITOL=30
+         MAXIT=200 MOLPLT=.T. MPLEVL=0
+         MULT=1 QMTTOL=1e-08 RUNTYP=OPTIMIZE
+         SCFTYP=RHF UNITS=ANGS                  $END
+ $SCF    CONV=1.0E-06 DIRSCF=.T.                $END
+ $STATPT NSTEP=400 OPTTOL=1.0E-06               $END
+ $SYSTEM MEMDDI=0 MWORDS=16 TIMLIM=50000        $END
+ $BASIS  GBASIS=N31 NDFUNC=1 NGAUSS=6           $END
+ $GUESS  GUESS=HUCKEL                           $END
+!
+ $DATA
+ #{name}
+ C1
+end_of_header
+       each_atom { |ap|
+               fp.printf " %-6s %4d %10.6f %10.6f %10.6f\n", ap.name, ap.atomic_number, ap.r.x, ap.r.y, ap.r.z
+       }
+       fp.print " $END\n"
+       fp.close
+  end
+
+  def savecom(filename)
+    if natoms == 0
+      raise MolbyError, "cannot save Gaussian input; the molecule is empty"
+    end
+    fp = open(filename, "wb")
+       base = File.basename(filename, ".*")
+       fp.print <<end_of_header
+%Chk=#{base}.chk
+\# PM3 Opt
+
+ #{name}; created by Molby at #{Time.now.to_s}
+
+ 0 1
+end_of_header
+       each_atom { |ap|
+               fp.printf "%-6s %10.6f %10.6f %10.6f\n", ap.element, ap.r.x, ap.r.y, ap.r.z
+       }
+       fp.print "\n"
+       fp.close
+  end
+
+  alias :loadgjf :loadcom
+  alias :savegjf :savecom
+  
+  def dump(group = nil)
+    def quote(str)
+         if str == ""
+           str = "\"\""
+         else
+           str = str.gsub(/%/, "%%")
+               str.gsub!(/ /, "%20")
+               str.gsub!(/\t/, "%09")
+         end
+         str
+       end
+       group = atom_group(group ? group : 0...natoms)
+       s = ""
+       group.each { |i|
+         ap = atoms[i]
+         s += sprintf("%4d %-7s %-4s %-4s %-2s %7.3f %7.3f %7.3f %6.3f [%s]\n",
+               ap.index, sprintf("%3s.%d", ap.res_name, ap.res_seq),
+               quote(ap.name), quote(ap.atom_type), quote(ap.element),
+               ap.r.x, ap.r.y, ap.r.z, ap.charge,
+               ap.connects.join(","))
+       }
+       print s
+  end
+
+  def from_dump(arg)
+    if natoms > 0
+         raise "Molecule must be empty"
+       end
+    format = "index residue name atom_type element rx ry rz charge connects"
+       keys = []
+       resAtoms = Hash.new
+       newBonds = []
+    arg.each { |line|
+      #  arg can be either a String or an array of String. If it is a string,
+         #  arg.each iterates for each line in the string. If it is an array,
+         #  arg.each iterates for each member of the array.
+         if line =~ /^\#/
+           format = line[1..-1]
+               keys = []
+         end
+         if keys.length == 0
+           keys = format.split(" ").collect { |key| key.to_sym }
+         end
+         values = line.chomp.split(" ")
+         next if values == nil || values.length == 0
+         ap = create_atom(sprintf("X%03d", natoms))
+         r = Vector3D[0, 0, 0]
+         keys.each_index { |i|
+           break if (value = values[i]) == nil
+               if value == "\"\""
+                 value = ""
+               else
+                 value.gsub(/%09/, "\t")
+                 value.gsub(/%20/, " ")
+                 value.gsub(/%%/, "%")
+               end
+               key = keys[i]
+               if key == :residue
+                 if resAtoms[value] == nil
+                   resAtoms[value] = []
+                 end
+                 resAtoms[value].push(ap.index)
+               elsif key == :rx
+                 r.x = value.to_f
+               elsif key == :ry
+                 r.y = value.to_f
+               elsif key == :rz
+                 r.z = value.to_f
+               elsif key == :connects
+                 value.scan(/\d+/).each { |i|
+                   i = i.to_i
+                   if ap.index < i
+                         newBonds.push(ap.index)
+                         newBonds.push(i)
+                       end
+                 }
+               elsif key == :index
+                 next
+               else
+                 ap.set_attr(key, value)
+               end
+         }
+         ap.r = r
+       }
+       resAtoms.each_key { |key|
+         assign_residue(atom_group(resAtoms[key]), key)
+       }
+       create_bond(*newBonds)
+       self
+  end
+
+end
diff --git a/Scripts/md.rb b/Scripts/md.rb
new file mode 100755 (executable)
index 0000000..e46a9b8
--- /dev/null
@@ -0,0 +1,844 @@
+#
+#  md.rb
+#
+#  Created by Toshi Nagata.
+#  Copyright 2009 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+class Molecule
+
+  def cmd_md_sub
+    arena = self.md_arena
+    keys = arena.to_hash.keys
+       #  Sort the keys so that they are (a little more) readable
+       [:timestep, :temperature, :cutoff, :electro_cutoff, :pairlist_distance,
+        :scale14_vdw, :scale14_elect, :use_xplor_shift, :dielectric,
+        :andersen_freq, :andersen_coupling, :random_seed, :relocate_center,
+        :use_graphite, :surface_probe_radius, :surface_tension, :surface_potential_freq,
+        :gradient_convergence, :coordinate_convergence,
+        :log_file, :coord_file, :vel_file, :force_file, :debug_file, :debug_output_level,
+        :coord_output_freq, :energy_output_freq].each_with_index { |k, i|
+         keys.delete(k)
+         keys.insert(i, k)
+       }
+       #  Arrange the items in vertical direction
+       n = (keys.count + 1) / 2
+       i = 0
+       keys = keys.sort_by { |k| i += 1; (i > n ? i - n + 0.5 : i) }
+       #  Do dialog
+    hash = RubyDialog.run("Molecular Dynamics Advanced Settings") {
+         items = []
+         keys.each { |k|
+           enabled = (k == :step || k == :coord_frame ? false : true)
+           items.push(item(:text, :title=>k.to_s))
+               it = item(:textfield, :width=>120, :value=>arena[k].to_s)
+               if enabled
+                 set_attr(it, :tag=>k)
+               else
+                 set_attr(it, :enabled=>false)
+               end
+               items.push(it)
+         }
+         layout(4, *items)
+       }
+       if hash
+         hash.keys.each { |k|
+           arena[k] = hash[k]
+         }
+#        arena.prepare
+         return hash
+       else
+         return nil
+       end
+  end
+  
+  def prepare_arena
+  
+       #  Get the MDArena
+       arena = self.md_arena
+       if !arena.prepare(true)   #  Parameter check only
+         RubyDialog.run("MD Error") {
+               layout(1,
+                 item(:text, :title=>"Some parameters are missing. Please open\nthe 'parameters' table and examine."))
+                 set_attr(1, :hidden=>true)  #  Hide the cancel button
+         }
+         return nil
+    end
+
+    #  Initialize some fields at first invocation
+    if !@arena_created
+         if arena.log_file == nil && self.dir != nil
+           arena.log_file = self.dir + "/" + self.name.gsub(/\.\w+$/, ".log")
+         end
+         @md_spf = 500
+         @md_nf = 200
+         @arena_created = true
+       end
+       
+    return arena
+
+  end
+
+  def cmd_md(minimize)
+
+       #  Create arena
+       arena = self.prepare_arena
+    if !arena
+         return -1
+       end
+       
+    #  Basic parameters
+       spf = @md_spf
+       nf = @md_nf
+       mol = self
+       files_save = Hash.new
+       [:log_file, :coord_file, :vel_file, :force_file, :debug_file].each { |k|
+         s = arena[k]
+         files_save[k] = s
+         if s != nil
+           arena[k] = File.basename(s)
+         end
+       }
+       title = (minimize ? "Minimize" : "Molecular Dynamics")
+       hash = RubyDialog.run(title) {
+         items = []
+         if !minimize
+           items.push item(:text, :title=>"Timestep (fs)")
+           items.push item(:textfield, :width=>120, :value=>arena.timestep.to_s, :tag=>"timestep")
+           items.push item(:text, :title=>"Target temperature (K)")
+           items.push item(:textfield, :width=>120, :value=>arena.temperature.to_s, :tag=>"temperature")
+         end
+         items.push item(:text, :title=>"Steps per frame")
+         items.push item(:textfield, :width=>120, :value=>arena.coord_output_freq.to_s, :tag=>"steps_per_frame")
+         items.push item(:text, :title=>"Number of frames")
+         items.push item(:textfield, :width=>120, :value=>nf.to_s, :tag=>"number_of_frames")
+         items.push item(:text, :title=>"Log file")
+         items.push item(:textfield, :width=>120, :value=>(arena.log_file || ""), :tag=>"log_file")
+         items.push item(:button, :title=>"Advanced...",
+                    :action=>proc {
+                          if mol.cmd_md_sub
+                            if !minimize
+                              set_value("timestep", arena[:timestep].to_s)
+                                  set_value("temperature", arena[:temperature].to_s)
+                                end
+                                set_value("log_file", arena[:log_file])
+                          end
+                        }
+                       )
+         layout(2, *items)
+       }
+       dirstr = (self.dir || document_home)
+       if hash
+         arena[:log_file] = hash[:log_file]
+       end
+       [:log_file, :coord_file, :vel_file, :force_file, :debug_file].each { |k|
+         if hash
+           s = arena[k]
+           if s != nil && s != ""
+             arena[k] = dirstr + "/" + s
+               else
+                 arena[k] = nil
+               end
+         else
+           arena[k] = files_save[k]
+      end
+       }
+       if !hash
+         return -1
+       end
+       if !minimize
+         arena.temperature = Float(hash["temperature"])
+         arena.timestep = Float(hash["timestep"])
+       end
+       arena.coord_output_freq = Integer(hash["steps_per_frame"])
+       arena.energy_output_freq = arena.coord_output_freq
+       return Integer(hash["number_of_frames"])
+  end
+  
+  def cmd_define_unit_cell
+    mol = self
+    hash = RubyDialog.run("Define Unit Cell") {
+         @mol = mol
+         def set_box_value(n)
+           h = Hash.new
+               ["o0", "o1", "o2", "a0", "a1", "a2", "b0", "b1", "b2", "c0", "c1", "c2"].each { |k|
+                 begin
+                   s = value(k)
+                       if s == nil || s == ""
+                         h[k] = 0.0
+                       else
+                     h[k] = Float(eval(s))
+                         set_value(k, h[k].to_s)
+                       end
+                 rescue
+                   mes = "Cannot evaluate #{value(k)}: " + $!.to_s
+                       RubyDialog.run("Value Error") {
+                         layout(1, item(:text, :title=>mes))
+                         set_attr(1, :hidden=>true)
+                       }
+                       return nil
+                 end
+               }
+               ax = Vector3D[h["a0"], h["a1"], h["a2"]]
+               bx = Vector3D[h["b0"], h["b1"], h["b2"]]
+               cx = Vector3D[h["c0"], h["c1"], h["c2"]]
+               ox = Vector3D[h["o0"], h["o1"], h["o2"]]
+               @mol.set_box(ax, bx, cx, ox)
+               return @mol
+         end
+         def action(n)
+           if n == 0
+                 if !set_box_value(n)
+                   return  #  Cannot set box: dialog is not dismissed
+                 end
+               end
+               super
+         end
+         box = @mol.box
+         layout(4,
+           item(:text, :title=>"Unit cell:"),
+               -1, -1, -1,
+           item(:text, :title=>"origin"),
+               item(:textfield, :width=>140, :tag=>"o0", :value=>(box ? box[3].x.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"o1", :value=>(box ? box[3].y.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"o2", :value=>(box ? box[3].z.to_s : "")),
+           item(:text, :title=>"a-axis"),
+               item(:textfield, :width=>140, :tag=>"a0", :value=>(box ? box[0].x.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"a1", :value=>(box ? box[0].y.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"a2", :value=>(box ? box[0].z.to_s : "")),
+           item(:text, :title=>"b-axis"),
+               item(:textfield, :width=>140, :tag=>"b0", :value=>(box ? box[1].x.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"b1", :value=>(box ? box[1].y.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"b2", :value=>(box ? box[1].z.to_s : "")),
+           item(:text, :title=>"c-axis"),
+               item(:textfield, :width=>140, :tag=>"c0", :value=>(box ? box[2].x.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"c1", :value=>(box ? box[2].y.to_s : "")),
+               item(:textfield, :width=>140, :tag=>"c2", :value=>(box ? box[2].z.to_s : "")),
+               item(:button, :title=>"Set", :action=>:set_box_value),
+               item(:text, :title=>"(Ruby expressions are allowed as the values)"),
+               -1, -1)
+       }
+  end
+
+  def cmd_show_periodic_image
+    mol = self
+    hash = RubyDialog.run("Show Periodic Image") {
+         @mol = mol
+         def set_periodic_image(n)
+           a = []
+           ["amin", "amax", "bmin", "bmax", "cmin", "cmax"].each_with_index { |k, i|
+                 s = value(k)
+                 if s == nil || s == ""
+                   a[i] = 0
+                 else
+                   a[i] = Integer(s)
+                 end
+               }
+           @mol.show_periodic_image(a)
+         end
+         def action(n)
+           if n == 0
+                 set_periodic_image(n)
+               end
+               super
+         end
+         pimage = @mol.show_periodic_image
+         layout(4,
+           item(:text, :title=>"Show Periodic Image:"),
+               -1, -1, -1,
+               item(:text, :title=>"a-axis"),
+               item(:textfield, :width=>80, :tag=>"amin", :value=>pimage[0].to_s),
+               item(:text, :title=>"to"),
+               item(:textfield, :width=>80, :tag=>"amax", :value=>pimage[1].to_s),
+               item(:text, :title=>"b-axis"),
+               item(:textfield, :width=>80, :tag=>"bmin", :value=>pimage[2].to_s),
+               item(:text, :title=>"to"),
+               item(:textfield, :width=>80, :tag=>"bmax", :value=>pimage[3].to_s),
+               item(:text, :title=>"c-axis"),
+               item(:textfield, :width=>80, :tag=>"cmin", :value=>pimage[4].to_s),
+               item(:text, :title=>"to"),
+               item(:textfield, :width=>80, :tag=>"cmax", :value=>pimage[5].to_s),
+               item(:button, :title=>"Set", :action=>:set_periodic_image))
+       }
+  end
+
+  def cmd_pressure_control
+  
+    if box == nil
+         RubyDialog.run("Pressure Control Error") {
+               layout(1,
+                 item(:text, :title=>"Unit cell is not defined. Please open 'Unit Cell...' \nfrom the MM/MD menu and set up the unit cell."))
+               set_attr(1, :hidden=>true)  #  Hide the cancel button
+         }
+         return nil
+       end
+       
+       #  Create arena
+       arena = self.prepare_arena
+    if !arena
+         return nil
+       end
+    mol = self
+       
+       #  Create pressure arena
+       if arena.pressure_freq == nil
+         arena.pressure_freq = 20   #  This assignment will automatically create pressure arena
+       end
+       
+       hash = RubyDialog.run("Pressure Control") {
+         @mol = mol
+         layout(2,
+           item(:text, :title=>"Frequency"),
+               item(:textfield, :width=>100, :tag=>"freq", :value=>arena.pressure_freq.to_s),
+               item(:text, :title=>"Coupling"),
+               item(:textfield, :width=>100, :tag=>"coupling", :value=>arena.pressure_coupling.to_s),
+           item(:text, :title=>"Pressure a"),
+               item(:textfield, :width=>100, :tag=>"pa", :value=>arena.pressure[0].to_s),
+           item(:text, :title=>"Pressure b"),
+               item(:textfield, :width=>100, :tag=>"pb", :value=>arena.pressure[1].to_s),
+           item(:text, :title=>"Pressure c"),
+               item(:textfield, :width=>100, :tag=>"pc", :value=>arena.pressure[2].to_s),
+           item(:text, :title=>"Cell flexibility a"),
+               item(:textfield, :width=>100, :tag=>"fa", :value=>arena.pressure_cell_flexibility[0].to_s),
+           item(:text, :title=>"Cell flexibility b"),
+               item(:textfield, :width=>100, :tag=>"fb", :value=>arena.pressure_cell_flexibility[1].to_s),
+           item(:text, :title=>"Cell flexibility c"),
+               item(:textfield, :width=>100, :tag=>"fc", :value=>arena.pressure_cell_flexibility[2].to_s),
+           item(:text, :title=>"Cell flexibility bc"),
+               item(:textfield, :width=>100, :tag=>"fbc", :value=>arena.pressure_cell_flexibility[3].to_s),
+           item(:text, :title=>"Cell flexibility ca"),
+               item(:textfield, :width=>100, :tag=>"fca", :value=>arena.pressure_cell_flexibility[4].to_s),
+           item(:text, :title=>"Cell flexibility ab"),
+               item(:textfield, :width=>100, :tag=>"fab", :value=>arena.pressure_cell_flexibility[5].to_s),
+           item(:text, :title=>"Cell flexibility origin"),
+               item(:textfield, :width=>100, :tag=>"forig", :value=>arena.pressure_cell_flexibility[6].to_s),
+           item(:text, :title=>"Cell flexibility orientation"),
+               item(:textfield, :width=>100, :tag=>"forient", :value=>arena.pressure_cell_flexibility[7].to_s),
+           item(:text, :title=>"Fluctuate cell origin"),
+               item(:textfield, :width=>100, :tag=>"orig", :value=>arena.pressure_fluctuate_cell_origin.to_s),
+           item(:text, :title=>"Fluctuate cell orientation"),
+               item(:textfield, :width=>100, :tag=>"orient", :value=>arena.pressure_fluctuate_cell_orientation.to_s))
+    }
+       if hash
+         arena.pressure_freq = hash["freq"]
+         arena.pressure_coupling = hash["coupling"]
+         arena.pressure = [hash["pa"], hash["pb"], hash["pc"]]
+         arena.pressure_cell_flexibility = [hash["fa"], hash["fb"], hash["fc"], hash["fbc"], hash["fca"], hash["fab"], hash["forig"], hash["forient"]]
+         arena.pressure_fluctuate_cell_origin = hash["orig"]
+         arena.pressure_fluctuate_cell_orientation = hash["orient"]
+       end
+  end
+  
+  def ambertools_dialog(tool)
+       ante_dir = get_global_settings("antechamber.ante_dir")
+       log_dir = get_global_settings("antechamber.log_dir")
+       if !ante_dir
+         ante_dir = ($platform == "mac" ? "/Applications/amber10" : ($platform == "win" ? "c:/opt/amber10" : ""))
+       end
+       if !log_dir
+         log_dir = document_home + "/amber10"
+       end
+       if $platform == "win"
+         suffix = ".exe"
+       else
+         suffix = ""
+       end
+       log_level = (get_global_settings("antechamber.log_level") || "none")
+    hash = RubyDialog.run("Run " + tool.capitalize) {
+         @toolname = tool + suffix
+      def valid_antechamber_dir(s)
+           FileTest.exist?(s + "/" + @toolname)
+         end
+         def action(n)
+           if n == 4
+                 if valid_antechamber_dir(attr(n, :value))
+                   set_attr(0, :enabled=>true)
+                 end
+               elsif n == 0 || n == 1
+                 #  Settings are stored in the global settings (even if Cancel is pressed)
+                 self.each_item { |i|
+                   if (tag = attr(i, :tag))
+                         value = attr(i, :value)
+                 v = [["log_none", "none"], ["log_error_only", "error_only"], ["log_keep_latest", "latest"], ["log_all", "all"]].assoc(tag)
+                         if v 
+                           next if value != 1
+                           tag = "log_level"
+                               value = v[1]
+                         end
+                         set_global_settings("antechamber.#{tag}", value)
+                       end
+                 }
+               end
+               super
+         end
+         layout(2,
+               item(:text, :title=>"Ambertools directory:"),
+               [ item(:button, :title=>"Choose...",
+                       :action=>proc { |n|
+                       #  print "action method called\n"
+                         dir = RubyDialog.open_panel(nil, nil, nil, true)
+                         if dir
+                               if valid_antechamber_dir(dir)
+                                 set_value("ante_dir", dir)
+                         set_attr(0, :enabled=>true)
+                               else
+                                 RubyDialog.run {
+                                   layout(1,
+                                         item(:text, :title=>"Cannot find #{tool} in #{dir}."))
+                                 }
+                                 set_attr(0, :enabled=>false)
+                               end
+                         end
+                       }
+                 ), {:align=>:right} ],
+               item(:textfield, :width=>360, :height=>40, :tag=>"ante_dir", :value=>ante_dir),
+               -1,
+               item(:text, :title=>"Log directory:"),
+               [ item(:button, :title=>"Choose...",
+                       :action=>proc { |n|
+                         dir = RubyDialog.open_panel(nil, nil, nil, true)
+                         if dir
+                               set_value("log_dir", dir)
+                         end
+                       }
+                 ), {:align=>:right} ],
+               item(:textfield, :width=>360, :height=>40, :tag=>"log_dir", :value=>log_dir),
+               -1,
+               item(:text, :title=>"Log handling"),
+               -1,
+               layout(1,
+                 item(:radio, :title=>"Do not keep logs", :tag=>"log_none", :value=>(log_level == "none" ? 1 : 0)),
+                 item(:radio, :title=>"Keep only when error occurred", :tag=>"log_error_only", :value=>(log_level == "error_only" ? 1 : 0)),
+                 layout(3,
+                   item(:radio, :title=>"Keep latest ", :tag=>"log_keep_latest", :value=>(log_level == "latest" ? 1 : 0)),
+                   item(:textfield, :width=>80, :tag=>"log_keep_number", :value=>(get_global_settings("antechamber.log_keep_number") || "10")),
+                   item(:text, :title=>"logs"),
+                   {:margin=>0, :padding=>0}),
+                 item(:radio, :title=>"Keep All", :tag=>"log_all", :value=>(log_level == "all" ? 1 : 0)),
+                 {:margin=>0, :padding=>0}
+               ),
+               -1,
+               item(:text, :title=>"Net Molecular Charge:"),
+               item(:textfield, :width=>"80", :tag=>"nc", :value=>(get_global_settings("antechamber.nc") || "0"))
+         )
+         set_attr(0, :enabled=>valid_antechamber_dir(ante_dir))
+    }
+       #  The hash values are set in the action() method
+#      print "retval = #{hash ? 1 : 0}\n"
+       return (hash ? 1 : 0)
+  end
+  
+  def create_ante_log_dir(name, key)
+    log_dir = get_global_settings("antechamber.log_dir")
+       msg = ""
+       begin
+         if !FileTest.directory?(log_dir)
+           Dir.mkdir(log_dir) rescue ((msg = "Cannot create directory #{log_dir}") && raise)
+         end
+         n = 1
+         while FileTest.exist?(dname = log_dir + "/#{name}_#{key}.#{n}")
+           n += 1
+         end
+         Dir.mkdir(dname) rescue ((msg = "Cannot create directory #{dname}") && raise)
+         return dname
+       rescue
+         error_message_box(msg + ": " + $!.to_s)
+         return
+       end
+  end
+  
+  def clean_ante_log_dir(nkeep)
+    def rm_recursive(f)
+         if FileTest.directory?(f)
+           Dir.entries(f).each { |file|
+                 next if file == "." || file == ".."
+                 rm_recursive(f + "/" + file)
+               }
+               Dir.rmdir(f)
+         else
+           File.delete(f)
+         end
+       end
+    log_dir = get_global_settings("antechamber.log_dir")
+       cwd = Dir.pwd
+       count = 0
+       begin
+         Dir.chdir(log_dir)
+         #  Get subdirectories and sort by last modified time
+         dirs = Dir.entries(log_dir).reject! { |x|
+           !FileTest.directory?(x) || x == "." || x == ".."
+         }.sort_by { |x| 
+           File.mtime(x)
+         }
+         dirs[0..-(nkeep + 1)].each { |d|
+           rm_recursive(d)
+               count += 1
+         }
+       rescue
+         error_message_box $!.to_s
+         Dir.chdir(cwd)
+         return
+       end
+       Dir.chdir(cwd)
+       return count
+  end
+  
+  def cmd_antechamber
+    return ambertools_dialog("antechamber")
+  end
+  
+  def import_ac(acfile)
+    open(acfile, "r") { |fp|
+         while (s = fp.gets)
+           next if s !~ /^ATOM/
+               s.chomp!
+               idx = Integer(s[4..11]) - 1
+               charge = Float(s[54..63])
+               type = s[72..-1]
+               type.gsub!(/ /, "")
+               ap = atoms[idx]
+               ap.charge = charge
+               ap.atom_type = type
+         end
+       }
+  end
+  
+  def import_frcmod(file)
+    self.md_arena.prepare(true)  #  Clean up existing parameters
+       par = self.parameter
+    open(file, "r") { |fp|
+         wtable = Hash.new
+         state = 0
+         while (s = fp.gets)
+           s.chomp!
+               case s
+               when /^MASS/
+                 state = 1
+                 next
+               when /^BOND/
+                 state = 2
+                 next
+               when /^ANGLE/
+                 state = 3
+                 next
+               when /^DIHE/
+                 state = 4
+                 next
+               when /^IMPR/
+                 state = 5
+                 next
+               when /^NONB/
+                 state = 6
+                 next
+               when ""
+                 state = 0
+                 next
+               else
+                 case state
+                 when 1
+                       name, weight = s.split
+                       wtable[name] = Float(weight)
+                 when 2
+                   types, k, r0, com = s.split(nil, 4)
+                   pp = par.bonds.lookup(types, :local, :missing) || par.bonds.insert
+                       pp.atom_types = types
+                       pp.k = k
+                       pp.r0 = r0
+                       pp.comment = com
+                 when 3
+                   types, k, a0, com = s.split(nil, 4)
+                       pp = par.angles.lookup(types, :local, :missing) || par.angles.insert
+                       pp.atom_types = types
+                       pp.k = k
+                       pp.a0 = a0
+                       pp.comment = com
+                 when 4
+                   types, n, k, phi0, period, com = s.split(nil, 6)
+                       pp = par.dihedrals.lookup(types, :local, :missing) || par.dihedrals.insert
+                       pp.atom_types = types
+                       pp.mult = 1
+                       pp.k = k
+                       pp.phi0 = phi0
+                       pp.period = Float(period).round
+                       pp.comment = com
+                 when 5
+                   types, k, phi0, period, com = s.split(nil, 5)
+                       pp = par.impropers.lookup(types, :local, :missing) || par.impropers.insert
+                       pp.atom_types = types
+                       pp.mult = 1
+                       pp.k = k
+                       pp.phi0 = phi0
+                       pp.period = Float(period).round
+                       pp.comment = com
+                 when 6
+                   name, r_eq, eps, com = s.split(nil, 4)
+                       pp = par.vdws.lookup(name, :local, :missing) || par.vdws.insert
+                       pp.atom_type = name
+                       pp.r_eq = r_eq
+                       pp.r_eq14 = r_eq
+                       pp.eps = eps
+                       pp.eps14 = eps
+                       if wtable[name]
+                         pp.weight = wtable[name]
+                       end
+                       pp.comment = com
+                 end
+               end
+         end
+    }
+  end
+  
+  def count_elements
+    elements = []
+       each_atom { |ap|
+         if (p = elements.assoc(ap.atomic_number))
+           p[1] += 1
+         else
+           elements.push [ap.atomic_number, 1]
+         end
+       }
+       elements.sort_by { |p| p[0] }
+  end
+  
+  def cmd_run_resp
+    if natoms == 0
+         error_message_box "Molecule is empty"
+         return
+       elsif nelpots == 0
+         error_message_box "No ESP information is loaded"
+         return
+       end
+       return unless ambertools_dialog("resp")
+       nc = get_global_settings("antechamber.nc")
+       ante_dir = get_global_settings("antechamber.ante_dir")
+
+       #  Create the temporary directory
+       dname = create_ante_log_dir((self.path ? File.basename(self.path, ".*") : self.name), "rs")
+       return unless dname
+       cwd = Dir.pwd
+       Dir.chdir(dname)
+
+       if true
+         eq = search_equivalent_atoms
+         #  search for methyl (and methylene??) hydrogens
+         methyl = []
+         each_atom { |ap|
+           if ap.element == "C" && ap.connects.length == 4
+                 hs = ap.connects.select { |n| atoms[n].element == "H" }
+                 if hs.length == 3
+                   mc = hs.min
+                   hs.each { |n| methyl[n] = mc }  #  methyl hydrogens
+                       methyl[ap.index] = -1           #  methyl carbon
+                 end
+               end
+         }
+         for i in [1,2]
+           open("resp.input#{i}", "w") { |fp|
+                 fp.print " #{self.name} \n"
+                 fp.print " &cntrl\n"
+                 fp.print " ioutput=1, IQOPT=#{i}, nmol=1, ihfree=1, irstrnt=1, qwt=#{i*0.0005},\n"
+                 fp.print " &end\n"
+                 fp.print "  1.0\n"
+                 fp.print " #{self.name} \n"
+                 fp.printf "%4d%5d\n", nc, self.natoms
+                 each_atom { |ap|
+                   idx = ap.index
+                       if i == 1
+                         n = (methyl[idx] ? 0 : eq[idx] + 1)
+                         n = 0 if n == idx + 1
+                       else
+                         n = (methyl[idx] ? methyl[idx] + 1 : -1)
+                         n = 0 if n == idx + 1
+                       end
+                       fp.printf "%4d%5d\n", ap.atomic_number, n
+                 }
+                 fp.print "\n\n\n\n\n\n"
+               }
+         end
+       else
+      #  Export as an antechamber format
+         c = count_elements
+         formula = c.map { |p| Parameter.atoms[p[0]].name + p[1].to_s }.join(" ")
+         open("respgen_in.ac", "w") { |fp|
+           fp.printf("CHARGE %9.2f ( %d )\n", nc, nc)
+           fp.printf("Formula: #{formula}\n")
+           each_atom { |ap|
+             fp.printf("ATOM %6d  %-4s%-3s%6d%12.3f%8.3f%8.3f%10.6f%8s\n", ap.index + 1, ap.name, ap.res_name, ap.res_seq, ap.r.x, ap.r.y, ap.r.z, ap.charge, ap.atom_type)
+           }
+           bonds.each_with_index { |b, i|
+             fp.printf("BOND%5d%5d%5d%5d  %5s%5s\n", i + 1, b[0] + 1, b[1] + 1, 0, atoms[b[0]].name, atoms[b[1]].name)
+           }
+         }
+       
+         #  Create resp input by respgen
+         if !system("#{ante_dir}/respgen -i respgen_in.ac -o resp.input1 -f resp1") \
+         || !system("#{ante_dir}/respgen -i respgen_in.ac -o resp.input2 -f resp2")
+           error_message_box("Cannot run respgen.")
+           Dir.chdir(cwd)
+           return
+         end
+       end
+       
+       #  Create ESP file
+       a2b = 1.8897259885   #  angstrom to bohr
+       open("resp.esp", "w") { |fp|
+         fp.printf("%5d%5d%5d\n", natoms, nelpots, nc)
+         each_atom { |ap|
+           fp.printf("                %16.7E%16.7E%16.7E\n", ap.r.x * a2b, ap.r.y * a2b, ap.r.z * a2b)
+         }
+         nelpots.times { |i|
+           pos, esp = elpot(i)
+           fp.printf("%16.7E%16.7E%16.7E%16.7E\n", esp, pos.x, pos.y, pos.z)
+         }
+         fp.print "\n\n"
+       }
+
+    #  Run resp
+       if !system("#{ante_dir}/resp -O -i resp.input1 -o resp.output1 -e resp.esp -t qout_stage1") \
+       || !system("#{ante_dir}/resp -O -i resp.input2 -o resp.output2 -e resp.esp -q qout_stage1 -t qout_stage2")
+         error_message_box("Cannot run resp.")
+         Dir.chdir(cwd)
+         return 
+       end
+       
+       #  Import resp output
+       open("punch", "r") { |fp|
+         while (s = fp.gets)
+           next unless s =~ /Point charges/ && s =~ /after optimization/
+               s = fp.gets
+               i = 0
+               while (s = fp.gets)
+                 ary = s.split
+                 break if ary.count == 0
+                 if Integer(ary[1]) != atoms[i].atomic_number
+                   error_message_box(sprintf("The atom %d has inconsistent atomic number (%d in mol, %d in resp output)", i, atoms[i].atomic_number, ary[1]))
+                       Dir.chdir(cwd)
+                       return
+                 end
+                 atoms[i].charge = Float(ary[3])
+                 i += 1
+               end
+               break
+         end
+    }
+       Dir.chdir(cwd)
+       return true
+  end
+  
+  def cmd_gamess_resp
+       if natoms == 0
+         error_message_box "Molecule is empty"
+         return
+       end
+       mol = self;
+       RubyDialog.run("#{name}:GAMESS/RESP", nil, nil) {
+         layout(1,
+           item(:text, :title=>"Step 1:\nCreate GAMESS input for ESP calculation"),
+               [item(:button, :title=>"Create GAMESS Input...",
+                 :action=>proc {
+                   esp_save = get_global_settings("gamess.esp")
+                       set_global_settings("gamess.esp", 1)
+                       mol.cmd_create_gamess_input
+                       set_global_settings("gamess.esp", esp_save)
+                 }),
+                 {:align=>:right}],
+               item(:line),
+               item(:text, :title=>"Step 2:\nImport GAMESS .dat file"),
+               [item(:button, :title=>"Import GAMESS dat...",
+                 :action=>proc {
+                   fname = RubyDialog.open_panel("Select GAMESS .dat file", nil, "*.dat")
+                       if fname
+                         errmsg = nil
+                         begin
+                           mol.loaddat(fname)
+                               errmsg = "Cannot find ESP results in the dat file." if mol.nelpots == 0
+                         rescue
+                           errmsg = "Error reading the dat file."
+                         end
+                         if errmsg
+                           message_box(errmsg + "\nYou may want to try another .dat file.", "GAMESS Import Error", :ok, :warning)
+                         else
+                           set_attr("resp", :enabled=>true)
+                         end
+                       end
+                 }),
+                 {:align=>:right}],
+               item(:line),
+               item(:text, :title=>"Step 3:\nRun RESP for charge fitting"),
+               [item(:button, :title=>"Run RESP...", :tag=>"resp",
+                 :action=>proc {
+                   if mol.cmd_run_resp
+                         if get_global_settings("antechamber.log_level") == "latest"
+                           mol.clean_ante_log_dir(Integer(get_global_settings("antechamber.log_keep_number")))
+                         end
+                         action(0)
+                       end
+                 }),
+                 {:align=>:right}],
+               item(:line),
+               [item(:button, :title=>"Close", :action=>proc { action(1) }),
+                 {:align=>:center}]
+         )
+         if mol.nelpots == 0
+           set_attr("resp", :enabled=>false)
+         end
+       }
+  end
+  
+  def cmd_edit_local_parameter_in_mainview(ptype, names, types, value, params)
+    #  The parameters are given as space separated strings
+       #  e.g. "1.862 200.000"
+       case ptype
+       when "bond"
+         k = ["r0", "k"]
+         pen = self.parameter.bonds
+       when "angle"
+         k = ["a0", "k"]
+         pen = self.parameter.angles
+       when "dihedral"
+         k = ["k", "period", "phi0"]
+         pen = self.parameter.dihedrals
+       when "improper"
+         k = ["k", "period", "phi0"]
+         pen = self.parameter.impropers
+       else
+         return
+       end
+       p = params.split
+       hash = RubyDialog.run("Edit local parameter") {
+      layout(1,
+           item(:text, :title=>"Edit #{ptype} parameter for #{names} (#{types})"),
+               item(:text, :title=>"(Current value = #{value})"),
+           layout(4,
+             [item(:text, :title=>"types"), {:align=>:center}],
+             [item(:text, :title=>k[0]), {:align=>:center}],
+             [item(:text, :title=>k[1]), {:align=>:center}],
+             (k[2] ? [item(:text, :title=>k[2]), {:align=>:center}] : -1),
+               
+                 item(:textfield, :width=>100, :value=>types, :tag=>"types"),
+                 item(:textfield, :width=>100, :value=>p[0], :tag=>k[0]),
+                 item(:textfield, :width=>100, :value=>p[1], :tag=>k[1]),
+                 (k[2] ? item(:textfield, :width=>100, :value=>p[2], :tag=>k[2]) : -1)
+               )
+         )
+       }
+       if hash
+         pref = pen.lookup(hash["types"], :create, :local, :missing, :nobasetype, :nowildcard)
+         raise "Cannot create new #{ptype} parameter" if !pref
+         k.each { |key| pref.set_attr(key, hash[key]) }
+         self.md_arena.prepare(true)  #  Check parameter only
+         return 1
+       else
+         return 0
+       end
+  end
+  
+end
diff --git a/Scripts/molecule.rb b/Scripts/molecule.rb
new file mode 100755 (executable)
index 0000000..3ca9394
--- /dev/null
@@ -0,0 +1,633 @@
+#
+#  molecule.rb
+#
+#  Created by Toshi Nagata.
+#  Copyright 2008 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+require "scanf.rb"
+
+class Molecule
+
+  def min(group = nil)
+    rv = Vector3D[1e20, 1e20, 1e20]
+    group = atom_group(group ? group : 0...natoms)
+    group.each { |i|
+      v = atoms[i].r
+      rv.x = v.x if v.x < rv.x
+      rv.y = v.y if v.y < rv.y
+      rv.z = v.z if v.z < rv.z
+    }
+    rv
+  end
+  
+  def max(group = nil)
+    rv = Vector3D[-1e20, -1e20, -1e20]
+    group = atom_group(group ? group : 0...natoms)
+    group.each { |i|
+      v = atoms[i].r
+      rv.x = v.x if v.x > rv.x
+      rv.y = v.y if v.y > rv.y
+      rv.z = v.z if v.z > rv.z
+    }
+    rv
+  end
+
+  def neutralize(charge = 0.0, group = nil)
+    group = atom_group(group ? group : 0...natoms)
+    csum = 0.0
+    group.each { |i| csum += atoms[i].charge }
+    if csum != 0.0
+      csum = -csum / group.length
+      group.each { |i| atoms[i].charge += csum }
+    end
+  end
+
+  def all
+    return atom_group
+  end
+  
+  def rotate_with_axis(xv, yv, cv = nil, group = nil)
+    cv = Vector3D[0, 0, 0] if !cv
+    v1 = (xv - cv).normalize
+    v3 = v1.cross(yv - cv).normalize
+    v2 = v3.cross(v1).normalize
+#    tr = Transform[v1, v2, v3, cv - Transform[v1, v2, v3, [0, 0, 0]] * cv]
+    tr = Transform[v1, v2, v3, [0,0,0]].inverse * Transform.translation(cv * (-1))
+    transform(tr, group)
+  end
+
+  #  Get the fragment including the atom n1. If additional atoms are given,
+  #  those atoms will not be counted during the search.
+#  def fragment(n1, *n2)
+#    def fragment_sub(idx, g, gx)
+#        return if gx.include?(idx)
+#      g << idx
+#      for i in atoms[idx].connects
+#        next if g.include?(i)
+#        fragment_sub(i, g, gx)
+#      end
+#    end
+#      n1 = atom_index(n1)
+#      g = atom_group(n1)
+#      gx = atom_group(*n2)
+#    for i in atoms[n1].connects
+#      fragment_sub(i, g, gx)
+#    end
+#    g
+#  end
+
+  #  Rotate the molecular fragment. The fragment given by "fragment(n2, n1)" is rotated,
+  #  with the atom n1 as the center and n2->n1 as the axis.
+  def rotate_fragment(n1, n2, angle)
+    frag = fragment(n2, n1)
+    cen = atoms[n1].r
+    axis = (atoms[n2].r - cen).normalize
+    rotate(axis, angle, cen, frag)
+  end
+  
+  #  Calculate the bond length defined by two vectors.
+  def Molecule.calc_bond(v1, v2)
+    (v1 - v2).length
+  end
+  
+  #  Calculate the angle defined by three vectors.
+  def Molecule.calc_angle(v1, v2, v3)
+    r21 = v1 - v2
+    r23 = v3 - v2
+    w1 = r21.length
+    w2 = r23.length
+    if (w1 < 1e-5 || w2 < 1e-5)
+      return 0.0
+    end
+    cs = r21.dot(r23) / (w1 * w2)
+    Math.atan2(Math.sqrt(1 - cs*cs), cs)
+  end
+  
+  #  Calculate the dihedral angle defined by four vectors.
+  def Molecule.calc_dihedral(v1, v2, v3, v4)
+    r21 = v1 - v2
+    r32 = v2 - v3
+    r43 = v3 - v4
+    cr1 = r21.cross(r32)
+    cr2 = r32.cross(r43)
+    cr3 = r32.cross(cr1)
+    w1 = cr1.length
+    w2 = cr2.length
+    w3 = cr3.length
+    if (w1 < 1e-5 || w2 < 1e-5 || w3 < 1e-5)
+      return 9999.0
+    end
+    sn = cr3.dot(cr2) / (w3 * w2)
+    cs = cr1.dot(cr2) / (w1 * w2)
+    Math.atan2(-sn, cs)
+  end
+  
+  #  Calculate the bond length between the two atoms.
+  def calc_bond(n1, n2)
+    n1 = atoms[n1].r if !n1.is_a?(Vector3D)
+    n2 = atoms[n2].r if !n2.is_a?(Vector3D)
+    self.class.calc_bond(n1, n2)
+  end
+
+  #  Calculate the bond angle defined by the three atoms.
+  def calc_angle(n1, n2, n3)
+    n1 = atoms[n1].r if !n1.is_a?(Vector3D)
+    n2 = atoms[n2].r if !n2.is_a?(Vector3D)
+    n3 = atoms[n3].r if !n3.is_a?(Vector3D)
+    self.class.calc_angle(n1, n2, n3)
+  end
+
+  #  Calculate the dihedral angle defined by the four atoms
+  def calc_dihedral(n1, n2, n3, n4)
+    n1 = atoms[n1].r if !n1.is_a?(Vector3D)
+    n2 = atoms[n2].r if !n2.is_a?(Vector3D)
+    n3 = atoms[n3].r if !n3.is_a?(Vector3D)
+    n4 = atoms[n4].r if !n4.is_a?(Vector3D)
+    self.class.calc_dihedral(n1, n2, n3, n4)
+  end
+
+  #  Set the dihedral angle n1-n2-n3-n4 to the specified value. The fragment
+  #  including n3 and n4 are rotated.
+  def set_dihedral(n1, n2, n3, n4, angle)
+    dihed = calc_dihedral(n1, n2, n3, n4)
+    if (dihed < 9999.0)
+      rotate_fragment(n2, n3, dihed - angle)
+    end
+  end
+
+  #  Intended for use internally. Find the bond vector to be created. If atom to remove (rem) is specified, it is
+  #  the vector base->rem. If rem is not specified, look for the "dummy" atom if present.
+  #  Otherwise, no atom is removed, and the new bond vector is defined as the most distant
+  #  direction from any existing bonds to base. Returns [n, v] where n is the atom to be
+  #  removed (or nil), and v is the bond vector.
+
+  def find_dock_vec(base, rem)
+    if (!rem)
+      atoms.each { |ap|
+        if ap.name[0] == ?#
+          rem = ap.index
+          break
+        end
+      }
+    end
+    if (!rem)
+      v1 = atoms[base].r
+      v2 = Vector3D[0, 0, 0]
+      for i in atoms[base].connects
+        v2 += (atoms[i].r - v1).normalize
+      end
+      v2 = (v2.length > 0 ? v2.normalize * (-1.0) : Vector3D[1, 0, 0])
+    else
+      v2 = (atoms[rem].r - atoms[base].r).normalize
+    end
+    [rem, v2]
+  end
+
+  #  Combine two molecules. A new bond is created between base1 and base2. rem1 and rem2
+  #  specify the atom (or fragment) to be removed when creating the new bond. If nil is
+  #  specified for either of them, a dummy atom (that has a '#' at the top of its name)
+  #  is looked for, and if none is found then no atom is removed and the new bond is
+  #  directed so that it is most remote from any of the existing bonds.
+  #  rem1 and rem2 can also be Vector3D's that specify the direction of the new bond.
+  #  In this case, no atoms are removed.
+  #  If len is specified, then the length of the new bond is set to this value. Otherwise,
+  #  it is set to 1.5.
+  #  If dihed is specified, then the dihedral angle def1-base1-base2-def2 is set to this
+  #  value (in radian). If nil is specified for either def1 or def2 (or both), then
+  #  the atom which is connected to base1 (base2) and has the smallest index is used.
+  #  Returns the group of atom indices that have been added.
+  
+  def dock(mol, base1, base2, rem1 = nil, rem2 = nil, len = nil, dihed = nil, def1 = nil, def2 = nil)
+
+       if rem1.is_a?(Vector3D)
+         v1 = rem1.normalize
+         rem1 = nil
+       else
+      rem1, v1 = find_dock_vec(base1, rem1)
+       end
+       if rem2.is_a?(Vector3D)
+         v2 = rem2.normalize
+         rem2 = nil
+       else
+      rem2, v2 = mol.find_dock_vec(base2, rem2)
+       end
+    if (!len)
+      #  If new bond length is not specified:
+      if (rem1)
+        #  If the atom to remove is specified, then the old bond length is kept
+        len = (atoms[rem1].r - atoms[base1].r).length
+      else
+        #  Otherwise, an arbitrary bond length is used
+        len = 1.5
+      end
+    end
+    #  Duplicate the second molecule
+    mol = mol.dup
+    #  Rotate the second molecule so that v2 becomes antiparallel to v1
+    cs = -(v1.dot(v2))
+    if (cs < 1 - 1e-6)
+      if (cs < -1 + 1e-6)
+        #  v1 == v2 (180 deg rotation)
+        v3 = v2.cross([0, 1, 0])
+        if (v3.length < 1e-6)
+          v3 = v2.cross([0, 0, 1])
+        end
+        v3 = v3.normalize
+      else
+        v3 = v2.cross(v1).normalize
+      end
+      angle = Math.atan2(Math.sqrt(1.0 - cs*cs), cs)
+      mol.rotate(v3, angle, mol.atoms[base2].r)
+    end
+    #  Move the second molecule so that the atom 'base2' is located at atoms[base1].r+v1*len
+    mol.translate(atoms[base1].r + v1 * len - mol.atoms[base2].r)
+    if (dihed)
+      #  Rotate the bond base1-base2 so that the dihedral angle becomes dihed
+      #  Find atom of lowest index if def1/2 is not specified
+      def1 = (atoms[base1].connects - (rem1 ? [atom_index(rem1)] : [])).min if !def1
+      def2 = (mol.atoms[base2].connects - (rem2 ? [mol.atom_index(rem2)] : [])).min if !def2
+         if def1 && def2
+        dihed1 = self.calc_dihedral(atoms[def1].r, atoms[base1].r, mol.atoms[base2].r, mol.atoms[def2].r)
+        mol.rotate(v1, dihed1 - dihed, mol.atoms[base2].r) if dihed1 < 9999.0
+         end
+    end
+    #  Calculate the atom indices for the combined molecule
+       natoms1 = natoms
+       natoms2 = mol.natoms
+    base1 = atom_index(base1)
+    rem1 = atom_index(rem1) if rem1
+    base2 = mol.atom_index(base2) + natoms1
+    rem2 = mol.atom_index(rem2) + natoms1 if rem2
+       #  Fragment to remove from self
+    g = IntGroup[]
+       if rem1
+         f1 = fragment(rem1, base1)
+         natoms1 -= f1.length
+         g += f1
+         assign_residue(f1, 0)
+       end
+       ofs = self.max_residue_number
+       self.nresidues = ofs + 1
+#      mol.offset_residue(mol.all, ofs)
+    #  Merge two molecules, create a bond, and remove fragments if necessary
+    add(mol)
+    create_bond(base1, base2)
+       #  Fragment to remove from added molecule
+       if rem2
+         f2 = fragment(rem2, base2)
+         natoms2 -= f2.length
+         g += f2
+       end
+    remove(g)
+#      mol.offset_residue(mol.all, -ofs)
+       #  Returns the group of appended atom indices
+       IntGroup[natoms1...(natoms1+natoms2)]
+  end
+
+  #  Add a new atom. (bond, base1, angle, base2, dihed, base3) defines
+  #  the position of the new atom in the Z-matrix style. (Note: angle 
+  #  and dihed should be given in *degree*, not in radian). If bond/base1
+  #  are specified, a new bond is also created between the new atom and
+  #  base1. If bond/base1 are specified but angle/base2 are not, then
+  #  the direction of the new bond is assumed as the most distant
+  #  direction from the existing bonds.
+  #  Returns the reference to the new atom.
+  def add_atom(name, atom_type = "c3", element = "C", bond = nil, base1 = nil, angle = nil, base2 = nil, dihed = nil, base3 = nil)
+    ap = create_atom(name)
+    ap.atom_type = atom_type
+    ap.element = element
+    if (base1 != nil)
+      bp1 = atoms[base1].r
+      if ap.res_seq == 0 && (res_seq = atoms[base1].res_seq) > 0
+        group = atom_group { |p| p.res_seq == res_seq }
+        group << ap.index
+        assign_residue(group, atoms[base1].res_name + ".#{res_seq}")
+      end
+      if base2 == nil
+        n, r = find_dock_vec(base1, nil)
+        r = bp1 + r.normalize * bond
+      else
+        v2 = atoms[base2].r - bp1
+        if base3 != nil
+          v3 = atoms[base3].r - bp1
+        else
+          v3 = Vector3D[0, 0, 1]
+          dihed = 180
+        end
+        angle *= 3.1415927 / 180.0
+        dihed *= 3.1415927 / 180.0
+        vx = v2.normalize
+        vy = v3.cross(v2)
+        if vy.length < 1e-8
+          n = Math::floor(angle / 3.1415927 + 0.5)
+          if ((angle - n * 3.1415927).abs < 1e-8)
+            vd = vx * (bond * Math::cos(angle))
+          else
+            raise "Cannot define dihedral angle for atom #{name}-#{base1}-#{base2}-#{base3}"
+          end
+        else
+          vy = vy.normalize
+          vz = vx.cross(vy).normalize
+          x = bond * Math::cos(angle)
+          y = bond * Math::sin(angle) * Math::sin(dihed)
+          z = bond * Math::sin(angle) * Math::cos(dihed)
+          vd = Transform[vx, vy, vz, [0, 0, 0]] * Vector3D[x, y, z]
+        end
+        r = bp1 + vd
+      end
+      ap.r = r
+      create_bond(base1, ap.index)
+    end
+    return ap
+  end
+
+  #  Clean up atom names. All atoms in the group (if specified) are named
+  #  with the sequential numbers.
+  def guess_names(group = nil)
+    count = Hash.new(0)
+       atoms.each { |ap|
+         next if group != nil && !group.member?(ap.index)
+         e = ap.element
+         if (e == "Du")
+           ap.element = e = "H"
+         end
+         count[e] += 1
+         ap.name = sprintf("%s%d", e, count[e])
+       }
+       self
+  end
+  
+  def guess_type_sub(idx)
+    ap = atoms[idx]
+       if ap.atom_type != ""
+         return ap.atom_type
+       end
+       e = ap.element
+       n = ap.connects.length
+       t = nil
+       if e == "C"
+         if n == 2
+               t = "c1"
+         elsif n == 3
+               ap.connects.each { |i|
+                 if atoms[i].element == "O" && atoms[i].connects.length == 1
+                       #  Carbonyl carbon
+                       t = "c"
+                       break
+                 end
+               }
+               if t == nil
+                 #  Not carbonyl carbon: may be other type, but it is difficult to 
+                 #  guess the atom type so assign as "CA"
+                 t = "ca"
+               end
+         else
+               t = "c3"
+         end
+       elsif e == "H"
+         tt = guess_type_sub(ap.connects[0])
+         #  Count the number of electronegative atoms connected to the parent atom
+         ne = 0
+         atoms[ap.connects[0]].connects.each { |i|
+               ee = atoms[i].element
+               if ["N", "O", "F", "P", "S", "Cl", "As", "Se", "Br", "Sb", "Te", "I"].index(ee) != nil
+                 ne += 1
+               end
+         }
+         te = atoms[ap.connects[0]].element
+         if te == "C"
+           if tt == "ca"
+             if ne == 1
+                   t = "h4"
+                 elsif ne == 2
+                   t = "h5"
+                 else
+               t = "ha"
+                 end
+           elsif tt == "cz"
+             t = "ha"
+           elsif tt == "c3"
+                 if ne == 1
+                   t = "h1"
+                 elsif ne == 2
+                   t = "h2"
+                 elsif ne == 3
+                   t = "h3"
+                 else
+                   t = "hc"
+                 end
+               else
+                 t = "hc"
+           end
+         elsif te == "N"
+           t = "hn"
+         elsif te == "O"
+               t = "ho"
+         elsif te == "S"
+               t = "hs"
+         else
+               t = "hc"
+         end
+       elsif e == "N"
+         i = ap.connects.find { |j|
+           atoms[j].element == "C" && atoms[j].connects.find { |k|
+                 atoms[k].element == "O" && atoms[k].connects.length == 1
+               }
+         }
+         if i != nil
+           t = "n"   #  Amide NH
+         elsif n == 4
+           t = "n4"  #  Ammonium group
+         else
+           t = "n3"  #  Amino group
+         end
+       elsif e == "O"
+         if n == 1
+           #  Count the number of oxygens connected to the root atom
+               no = 0
+               atoms[ap.connects[0]].connects.each { |i|
+                 if atoms[i].element == "O"
+                   no += 1
+                 end
+               }
+               if no == 1
+                 t = "o"  #  Carbonyl
+               else
+                 t = "o"  #  Carboxyl or phosphate
+               end
+         else
+           #  Count the number of hydrogens connected to this atom
+               nh = 0
+               ap.connects.each { |i|
+                 if atoms[i].element == "H"
+                   nh += 1
+                 end
+               }
+               if nh == 0
+                 t = "os"
+               elsif nh == 1
+                 t = "oh"
+               else
+                 t = "ow"
+               end
+         end
+       else
+         t = e.upcase
+       end
+    ap.atom_type = t
+  end
+
+  #  Clean up atom types.
+  def guess_types(group = nil)
+       atoms.each { |ap|
+         next if group != nil && !group.member?(ap.index)
+         next if ap.atom_type != ""
+         guess_type_sub(ap.index)
+       }
+  end
+
+  #  Solvate the molecule with the given solvent box.
+  #  The first argument (box) must be a Molecule containing a unit cell information.
+  #  The second argument defines the size of the solvated system. A positive number represents
+  #  an offset to the bounding box of the solute, and a negative number represents an absolute size.
+  #  If it is given as an Array (or a Vector), the elements define the sizes in the x/y/z directions.
+  #  The third argument represents the limit distance to avoid conflict between the solute and
+  #  solvent. The solvent molecule containing atoms within this limit from the solute is removed.
+  #  The atom group containing added solvent molecule is returned.
+  def solvate(sbox, size = [10.0, 10.0, 10.0], limit = 3.0)
+
+       #  Sanity check
+    if sbox.box == nil
+         raise MolbyError, "the solvent box does not have a unit cell information"
+    end
+       flags = sbox.box[4]
+       if flags[0] == 0 || flags[1] == 0 || flags[2] == 0
+         raise MolbyError, "the solvent box does not have three-dimensional periodicity"
+       end
+
+       #  Calculate the box size
+       b = self.bounds
+       bsize = b[1] - b[0]
+       if size.kind_of?(Numeric)
+         size = [size, size, size]
+       end
+       size = size.collect { |s| Float(s) }
+       limit = Float(limit)
+       (0..2).each do |i|
+         d = (size[i] >= 0 ? bsize[i] + size[i] * 2 : -size[i])
+         if d < bsize[i]
+           raise MolbyError, "the box size is too small"
+         end
+         bsize[i] = d;
+       end
+#      puts "Box size = #{bsize}"
+
+       #  Translate the solute to the center of the box
+       translate((b[0] + b[1]) * -0.5)
+       solute_natoms = self.natoms
+
+       #  Add solvents so that the target box is fully covered
+       rtr = sbox.box_transform.inverse
+       min = Vector3D[1e30, 1e30, 1e30]
+       max = Vector3D[-1e30, -1e30, -1e30]
+       [[0,0,0],[1,0,0],[0,1,0],[0,0,1],[1,1,0],[1,0,1],[0,1,1],[1,1,1]].each do |pt|
+         pt = Vector3D[(pt[0] - 0.5) * bsize[0], (pt[1] - 0.5) * bsize[1], (pt[2] - 0.5) * bsize[2]]
+         rpt = rtr * pt
+         min.x = rpt.x if min.x > rpt.x
+         min.y = rpt.y if min.y > rpt.y
+         min.z = rpt.z if min.z > rpt.z
+         max.x = rpt.x if max.x < rpt.x
+         max.y = rpt.y if max.y < rpt.y
+         max.z = rpt.z if max.z < rpt.z
+       end
+       xmin = (min.x + 0.5).floor
+       xmax = (max.x + 0.5).floor
+       ymin = (min.y + 0.5).floor
+       ymax = (max.y + 0.5).floor
+       zmin = (min.z + 0.5).floor
+       zmax = (max.z + 0.5).floor
+       sbox_natoms = sbox.natoms
+       xv, yv, zv, ov, flags = sbox.box
+#      puts "xmin = #{xmin}, ymin = #{ymin}, zmin = #{zmin}"
+#      puts "xmax = #{xmax}, ymax = #{ymax}, zmax = #{zmax}"
+       (xmin..xmax).each do |x|
+         (ymin..ymax).each do |y|
+           (zmin..zmax).each do |z|
+                 add(sbox)
+                 translate(xv * x + yv * y + zv * z, IntGroup[self.natoms - sbox_natoms..self.natoms - 1])
+               end
+         end
+       end
+       
+       #  Remove out-of-bounds molecules
+       g = atom_group do |ap|
+         r = ap.r
+         r.x < -bsize[0] * 0.5 || r.y < -bsize[1] * 0.5 || r.z < -bsize[2] * 0.5 || r.x > bsize[0] * 0.5 || r.y > bsize[1] * 0.5 || r.z > bsize[2] * 0.5
+       end
+       g = fragment(g)  #  expand by fragment
+       remove(g)
+#      puts "Removed atoms by bounds: #{g}"
+       
+       #  Find conflicts
+       conf = find_conflicts(limit, IntGroup[0..solute_natoms - 1], IntGroup[solute_natoms..self.natoms - 1])
+       g = atom_group(conf.map { |c| c[1] } )    #  atom group containing conflicting atoms
+       g = fragment(g)                           #  expand by fragment
+       remove(g)
+#      puts "Removed atoms by conflicts: #{g}"
+       
+       #  Renumber residue numbers of solvent molecules
+       rseq = max_residue_number(0..solute_natoms - 1)
+       rseq = 0 if rseq == nil
+       each_fragment do |g|
+         next if g[0] < solute_natoms
+         rseq += 1
+         assign_residue(g, atoms[g[0]].res_name + ".#{rseq}")
+       end
+
+    #  Set the unit cell information
+       set_box(bsize[0], bsize[1], bsize[2])
+       
+       return IntGroup[solute_natoms..self.natoms - 1]
+
+  end  
+  
+  #  Count the (minimum) number of bonds between atoms n1 and n2
+  def count_bonds(n1, n2)
+    if n1 == n2
+         return 0
+    end
+    table = Array.new(natoms, 0) #  Results for each atom (offset by +1)
+    table[n1] = 1                #  count_bonds(n1, n1) should be 0
+    shell = [n1]                 #  The group of atoms with equal count_bonds
+    n = 2                        #  Next shell number
+    while 1
+         nshell = []
+         shell.each { |i|
+           atoms[i].connects.each { |j|
+                 if table[j] == 0
+                   if j == n2
+                         return n - 1
+                   end
+                   table[j] = n
+                   nshell.push(j)
+                 end
+           }
+         }
+         if nshell.length == 0
+           return -1   #  No connection between n1 and n2
+         end
+         shell = nshell
+         n += 1
+    end
+  end
+
+end
diff --git a/Scripts/parm99.par b/Scripts/parm99.par
new file mode 100644 (file)
index 0000000..737067b
--- /dev/null
@@ -0,0 +1,636 @@
+! PARM99 for DNA,RNA,AA, organic molecules, TIP3P wat. Polariz.& LP incl.02/04/99
+! 
+! Original file was taken from the AMBER home page: http://ambermd.org/ (as of Nov 13, 2009)
+! Converted for Molby by amberparm2molby.pl by Toshi Nagata.
+! Thanks to the AMBER development team, the force field parameters are in the public domain. Hereby I acknowledge their great scientific contribution, with references to the literature as below.
+! 1. D.A. Case, T.E. Cheatham, III, T. Darden, H. Gohlke, R. Luo, K.M. Merz, Jr., A. Onufriev, C. Simmerling, B. Wang and R. Woods. The Amber biomolecular simulation programs. J. Computat. Chem. 26, 1668-1688 (2005).
+! 2. J.W. Ponder and D.A. Case. Force fields for protein simulations. Adv. Prot. Chem. 66, 27-85 (2003). Similar information for nucleic acids is given by T.E. Cheatham, III and M.A. Young. Molecular dynamics simulation of nucleic acids: Successes, limitations and promise. Biopolymers 56, 232-256 (2001).
+! 3. D.A. Case, T.A. Darden, T.E. Cheatham, III, C.L. Simmerling, J. Wang, R.E. Duke, R. Luo, M. Crowley, Ross C. Walker,W. Zhang, K.M. Merz, B.Wang, S. Hayik, A. Roitberg, G. Seabra, I. Kolossváry, K.F.Wong, F. Paesani, J. Vanicek, X.Wu, S.R. Brozell, T. Steinbrecher, H. Gohlke, L. Yang, C. Tan, J. Mongan, V. Hornak, G. Cui, D.H. Mathews, M.G. Seetin, C. Sagui, V. Babin, and P.A. Kollman (2008), AMBER 10, University of California, San Francisco.
+!
+bond OW   HW     553.0  0.96 ! TIP3P water
+bond HW   HW     553.0  1.51 ! TIP3P water
+bond C    C      310.0  1.52 ! Junmei et al, 1999
+bond C    CA     469.0  1.41 ! JCC,7,(1986),230; (not used any more in TYR)
+bond C    CB     447.0  1.42 ! JCC,7,(1986),230; GUA
+bond C    CM     410.0  1.44 ! JCC,7,(1986),230; THY,URA
+bond C    CT     317.0  1.52 ! JCC,7,(1986),230; AA
+bond C    N      490.0  1.33 ! JCC,7,(1986),230; AA
+bond C    N*     424.0  1.38 ! JCC,7,(1986),230; CYT,URA
+bond C    NA     418.0  1.39 ! JCC,7,(1986),230; GUA.URA
+bond C    NC     457.0  1.36 ! JCC,7,(1986),230; CYT
+bond C    O      570.0  1.23 ! JCC,7,(1986),230; AA,CYT,GUA,THY,URA
+bond C    O2     656.0  1.25 ! JCC,7,(1986),230; GLU,ASP
+bond C    OH     450.0  1.36 ! JCC,7,(1986),230; (not used any more for TYR)
+bond C    OS     450.0  1.32 ! Junmei et al, 1999
+bond C    H4     367.0  1.08 ! Junmei et al, 1999
+bond C    H5     367.0  1.08 ! Junmei et al, 1999
+bond CA   CA     469.0  1.40 ! JCC,7,(1986),230; BENZENE,PHE,TRP,TYR
+bond CA   CB     469.0  1.40 ! JCC,7,(1986),230; ADE,TRP
+bond CA   CM     427.0  1.43 ! JCC,7,(1986),230; CYT
+bond CA   CN     469.0  1.40 ! JCC,7,(1986),230; TRP
+bond CA   CT     317.0  1.51 ! JCC,7,(1986),230; PHE,TYR
+bond CA   HA     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; PHE,TRP,TYR
+bond CA   H4     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; no assigned
+bond CA   N2     481.0  1.34 ! JCC,7,(1986),230; ARG,CYT,GUA
+bond CA   NA     427.0  1.38 ! JCC,7,(1986),230; GUA
+bond CA   NC     483.0  1.34 ! JCC,7,(1986),230; ADE,CYT,GUA
+bond CA   OH     450.0  1.36 ! substituted for C-OH in tyr
+bond CB   CB     520.0  1.37 ! JCC,7,(1986),230; ADE,GUA
+bond CB   N*     436.0  1.37 ! JCC,7,(1986),230; ADE,GUA
+bond CB   NB     414.0  1.39 ! JCC,7,(1986),230; ADE,GUA
+bond CB   NC     461.0  1.35 ! JCC,7,(1986),230; ADE,GUA
+bond CD   HA     367.0  1.08 ! Junmei et al, 1999
+bond CD   CD     469.0  1.40 ! Junmei et al, 1999
+bond CD   CM     549.0  1.35 ! Junmei et al, 1999
+bond CD   CT     317.0  1.51 ! Junmei et al, 1999
+bond CK   H5     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; ADE,GUA
+bond CK   N*     440.0  1.37 ! JCC,7,(1986),230; ADE,GUA
+bond CK   NB     529.0  1.30 ! JCC,7,(1986),230; ADE,GUA
+bond CM   CM     549.0  1.35 ! JCC,7,(1986),230; CYT,THY,URA
+bond CM   CT     317.0  1.51 ! JCC,7,(1986),230; THY
+bond CM   HA     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; CYT,URA
+bond CM   H4     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; CYT,URA
+bond CM   H5     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; not assigned
+bond CM   N*     448.0  1.36 ! JCC,7,(1986),230; CYT,THY,URA
+bond CM   OS     480.0  1.24 ! Junmei et al, 1999
+bond CQ   H5     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; ADE
+bond CQ   NC     502.0  1.32 ! JCC,7,(1986),230; ADE
+bond CT   CT     310.0  1.53 ! JCC,7,(1986),230; AA, SUGARS
+bond CT   HC     340.0  1.09 ! changed from 331 bsd on NMA nmodes; AA, SUGARS
+bond CT   H1     340.0  1.09 ! changed from 331 bsd on NMA nmodes; AA, RIBOSE
+bond CT   H2     340.0  1.09 ! changed from 331 bsd on NMA nmodes; SUGARS
+bond CT   H3     340.0  1.09 ! changed from 331 bsd on NMA nmodes; not assigned
+bond CT   HP     340.0  1.09 ! changed from 331; AA-lysine, methyl ammonium cation
+bond CT   N*     337.0  1.48 ! JCC,7,(1986),230; ADE,CYT,GUA,THY,URA
+bond CT   N2     337.0  1.46 ! JCC,7,(1986),230; ARG
+bond CT   OH     320.0  1.41 ! JCC,7,(1986),230; SUGARS
+bond CT   OS     320.0  1.41 ! JCC,7,(1986),230; NUCLEIC ACIDS
+bond C*   HC     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes, not needed AA
+bond C*   CB     388.0  1.46 ! JCC,7,(1986),230; TRP
+bond C*   CT     317.0  1.50 ! JCC,7,(1986),230; TRP
+bond C*   CW     546.0  1.35 ! JCC,7,(1986),230; TRP
+bond CB   CN     447.0  1.42 ! JCC,7,(1986),230; TRP
+bond CC   CT     317.0  1.50 ! JCC,7,(1986),230; HIS
+bond CC   CV     512.0  1.38 ! JCC,7,(1986),230; HIS(delta)
+bond CC   CW     518.0  1.37 ! JCC,7,(1986),230; HIS(epsilon)
+bond CC   NA     422.0  1.39 ! JCC,7,(1986),230; HIS
+bond CC   NB     410.0  1.39 ! JCC,7,(1986),230; HIS
+bond CN   NA     428.0  1.38 ! JCC,7,(1986),230; TRP
+bond CR   H5     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes;HIS
+bond CR   NA     477.0  1.34 ! JCC,7,(1986),230; HIS
+bond CR   NB     488.0  1.33 ! JCC,7,(1986),230; HIS
+bond CT   N      337.0  1.45 ! JCC,7,(1986),230; AA
+bond CT   N3     367.0  1.47 ! JCC,7,(1986),230; LYS
+bond CT   NT     367.0  1.47 ! for neutral amines
+bond CT   S      227.0  1.81 ! changed from 222.0 based on dimethylS nmodes
+bond CT   SH     237.0  1.81 ! changed from 222.0 based on methanethiol nmodes
+bond CT   CY     400.0  1.46 ! Howard et al JCC.16,243,1995
+bond CT   CZ     400.0  1.46 ! Howard et al JCC,16,243,1995
+bond CV   H4     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes; HIS
+bond CV   NB     410.0  1.39 ! JCC,7,(1986),230; HIS
+bond CW   H4     367.0  1.08 ! changed from 340. bsd on C6H6 nmodes;HIS(epsilon,+)
+bond CW   NA     427.0  1.38 ! JCC,7,(1986),230; HIS,TRP
+bond CY   NY     600.0  1.15 ! Howard et al JCC,16,243,1995
+bond CZ   CZ     600.0  1.21 ! Howard et al JCC,16,243,1995
+bond CZ   HZ     400.0  1.06 ! Howard et al JCC,16,243,1995
+bond O2   P      525.0  1.48 ! JCC,7,(1986),230; NA PHOSPHATES
+bond OH   P      230.0  1.61 ! JCC,7,(1986),230; NA PHOSPHATES
+bond OS   P      230.0  1.61 ! JCC,7,(1986),230; NA PHOSPHATES
+bond H    N2     434.0  1.01 ! JCC,7,(1986),230; ADE,CYT,GUA,ARG
+bond H    N*     434.0  1.01 ! for plain unmethylated bases ADE,CYT,GUA,ARG
+bond H    NA     434.0  1.01 ! JCC,7,(1986),230; GUA,URA,HIS
+bond H    N      434.0  1.01 ! JCC,7,(1986),230; AA
+bond H    N3     434.0  1.01 ! JCC,7,(1986),230; LYS
+bond H    NT     434.0  1.01 ! for neutral amines
+bond HO   OH     553.0  0.96 ! JCC,7,(1986),230; SUGARS,SER,TYR
+bond HO   OS     553.0  0.96 ! JCC,7,(1986),230; NUCLEOTIDE ENDS
+bond HS   SH     274.0  1.34 ! JCC,7,(1986),230; CYS
+bond S    S      166.0  2.04 ! JCC,7,(1986),230; CYX   (SCHERAGA)
+bond F    CT     367.0  1.38 ! JCC,13,(1992),963;CF4; R0=1.332 FOR CHF3
+bond Cl   CT     232.0  1.77 ! 6-31g* opt
+bond Br   CT     159.0  1.94 ! Junmei et al,99
+bond I    CT     148.0  2.17 ! Junmei et al,99
+bond F    CA     386.0  1.36 ! Junmei et al,99
+bond Cl   CA     193.0  1.73 ! Junmei et al,99
+bond I    CA     171.0  2.08 ! Junmei et al,99
+bond Br   CA     172.0  1.89 ! Junmei et al,99
+bond LP   O      600.0  0.20 ! or 0.35
+bond LP   OH     600.0  0.20 ! or 0.35
+bond LP   OS     600.0  0.20 ! or 0.35
+bond LP   N3     600.0  0.20 ! or 0.35
+bond LP   NT     600.0  0.20 ! or 0.35
+bond LP   NB     600.0  0.20 ! or 0.35 histidines, nucleic acids
+bond LP   NC     600.0  0.20 ! or 0.35 nucleic acids
+bond LP   S      600.0  0.70 ! cys,cyx,met
+bond LP   SH     600.0  0.70 ! cys,cyx
+
+angle HW   OW   HW     100.0 104.52 ! TIP3P water
+angle HW   HW   OW       0.0 127.74 ! (found in crystallographic water with 3 bonds)
+angle C    C    O       80.0 120.00 ! Junmei et al, 1999 acrolein
+angle C    C    OH      80.0 120.00 ! Junmei et al, 1999
+angle CA   C    CA      63.0 120.00 ! changed from 85.0  bsd on C6H6 nmodes; AA
+angle CA   C    OH      70.0 120.00 ! AA (not used in tyr)
+angle CB   C    NA      70.0 111.30 ! NA
+angle CB   C    O       80.0 128.80
+angle CM   C    NA      70.0 114.10
+angle CM   C    O       80.0 125.30
+angle CT   C    O       80.0 120.40
+angle CT   C    O2      70.0 117.00
+angle CT   C    N       70.0 116.60 ! AA general
+angle CT   C    CT      63.0 117.00 ! Junmei et al, 1999
+angle CT   C    OS      80.0 115.00 ! Junmei et al, 1999
+angle CT   C    OH      80.0 110.00 ! Junmei et al, 1999
+angle N*   C    NA      70.0 115.40
+angle N*   C    NC      70.0 118.60
+angle N*   C    O       80.0 120.90
+angle NA   C    O       80.0 120.60
+angle NC   C    O       80.0 122.50
+angle N    C    O       80.0 122.90 ! AA general
+angle O    C    O       80.0 126.00 ! AA COO- terminal residues
+angle O    C    OH      80.0 120.00 ! (check with Junmei for: theta0:120.0?)
+angle O    C    OS      80.0 125.00 ! Junmei et al, 1999
+angle O2   C    O2      80.0 126.00 ! AA GLU            (SCH JPC 79,2379)
+angle H4   C    C       50.0 120.00 ! Junmei et al, 1999
+angle H4   C    CM      50.0 115.00 ! Junmei et al, 1999
+angle H4   C    CT      50.0 115.00 ! Junmei et al, 1999
+angle H4   C    O       50.0 120.00 ! Junmei et al, 1999
+angle H4   C    OH      50.0 120.00 ! Junmei et al, 1999
+angle H5   C    N       50.0 120.00 ! Junmei et al, 1999
+angle H5   C    O       50.0 119.00 ! Junmei et al, 1999
+angle H5   C    OH      50.0 107.00 ! Junmei et al, 1999
+angle H5   C    OS      50.0 107.00 ! Junmei et al, 1999
+angle C    CA   CA      63.0 120.00 ! changed from 85.0  bsd on C6H6 nmodes
+angle C    CA   HA      50.0 120.00 ! AA (not used in tyr)
+angle CA   CA   CA      63.0 120.00 ! changed from 85.0  bsd on C6H6 nmodes
+angle CA   CA   CB      63.0 120.00 ! changed from 85.0  bsd on C6H6 nmodes
+angle CA   CA   CT      70.0 120.00
+angle CA   CA   HA      50.0 120.00
+angle CA   CA   H4      50.0 120.00
+angle CA   CA   OH      70.0 120.00 ! replacement in tyr
+angle CA   CA   CN      63.0 120.00 ! changed from 85.0  bsd on C6H6 nmodes; AA trp
+angle CB   CA   HA      50.0 120.00
+angle CB   CA   H4      50.0 120.00
+angle CB   CA   N2      70.0 123.50
+angle CB   CA   NC      70.0 117.30
+angle CM   CA   N2      70.0 120.10
+angle CM   CA   NC      70.0 121.50
+angle CN   CA   HA      50.0 120.00 ! AA trp
+angle NA   CA   NC      70.0 123.30
+angle N2   CA   NA      70.0 116.00
+angle N2   CA   NC      70.0 119.30
+angle N2   CA   N2      70.0 120.00 ! AA arg
+angle F    CA   CA      70.0 121.00 ! Junmei et al,99
+angle Cl   CA   CA      70.0 118.80 ! Junmei et al,99
+angle Br   CA   CA      70.0 118.80 ! Junmei et al,99
+angle I    CA   CA      70.0 118.80 ! Junmei et al,99
+angle C    CB   CB      63.0 119.20 ! changed from 85.0  bsd on C6H6 nmodes; NA gua
+angle C    CB   NB      70.0 130.00
+angle CA   CB   CB      63.0 117.30 ! changed from 85.0  bsd on C6H6 nmodes; NA ade
+angle CA   CB   NB      70.0 132.40
+angle CB   CB   N*      70.0 106.20
+angle CB   CB   NB      70.0 110.40
+angle CB   CB   NC      70.0 127.70
+angle C*   CB   CA      63.0 134.90 ! changed from 85.0  bsd on C6H6 nmodes; AA trp
+angle C*   CB   CN      63.0 108.80 ! changed from 85.0  bsd on C6H6 nmodes; AA trp
+angle CA   CB   CN      63.0 116.20 ! changed from 85.0  bsd on C6H6 nmodes; AA trp
+angle N*   CB   NC      70.0 126.20
+angle CD   CD   CM      63.0 120.00 ! Junmei et al, 1999
+angle CD   CD   CT      70.0 120.00 ! Junmei et al, 1999
+angle CM   CD   CT      70.0 120.00 ! Junmei et al, 1999
+angle HA   CD   HA      35.0 119.00 ! Junmei et al, 1999
+angle HA   CD   CD      50.0 120.00 ! Junmei et al, 1999
+angle HA   CD   CM      50.0 120.00 ! Junmei et al, 1999
+angle H5   CK   N*      50.0 123.05
+angle H5   CK   NB      50.0 123.05
+angle N*   CK   NB      70.0 113.90
+angle C    CM   CM      63.0 120.70 ! changed from 85.0  bsd on C6H6 nmodes; NA thy
+angle C    CM   CT      70.0 119.70
+angle C    CM   HA      50.0 119.70
+angle C    CM   H4      50.0 119.70
+angle CA   CM   CM      63.0 117.00 ! changed from 85.0  bsd on C6H6 nmodes; NA cyt
+angle CA   CM   HA      50.0 123.30
+angle CA   CM   H4      50.0 123.30
+angle CM   CM   CT      70.0 119.70
+angle CM   CM   HA      50.0 119.70
+angle CM   CM   H4      50.0 119.70
+angle CM   CM   N*      70.0 121.20
+angle CM   CM   OS      80.0 125.00 ! Junmei et al, 1999
+angle H4   CM   N*      50.0 119.10
+angle H4   CM   OS      50.0 113.00 ! Junmei et al, 1999
+angle HA   CM   HA      35.0 120.00 ! Junmei et al, 1999
+angle HA   CM   CD      50.0 120.00 ! Junmei et al, 1999
+angle HA   CM   CT      50.0 120.00 ! Junmei et al, 1999
+angle NC   CQ   NC      70.0 129.10
+angle H5   CQ   NC      50.0 115.45
+angle H1   CT   H1      35.0 109.50
+angle H1   CT   N*      50.0 109.50 ! changed based on NMA nmodes
+angle H1   CT   OH      50.0 109.50 ! changed based on NMA nmodes
+angle H1   CT   OS      50.0 109.50 ! changed based on NMA nmodes
+angle H1   CT   CM      50.0 109.50 ! Junmei et al, 1999
+angle H1   CT   CY      50.0 110.00 ! Junmei et al, 1999
+angle H1   CT   CZ      50.0 110.00 ! Junmei et al, 1999
+angle H1   CT   N       50.0 109.50 ! AA general  changed based on NMA nmodes
+angle H1   CT   S       50.0 109.50 ! AA cys     changed based on NMA nmodes
+angle H1   CT   SH      50.0 109.50 ! AA cyx     changed based on NMA nmodes
+angle H1   CT   N2      50.0 109.50 ! AA arg     changed based on NMA nmodes
+angle H1   CT   NT      50.0 109.50 ! neutral amines
+angle H2   CT   H2      35.0 109.50 ! AA lys
+angle H2   CT   N*      50.0 109.50 ! changed based on NMA nmodes
+angle H2   CT   OS      50.0 109.50 ! changed based on NMA nmodes
+angle HP   CT   HP      35.0 109.50 ! AA lys, ch3nh4+
+angle HP   CT   N3      50.0 109.50 ! AA lys, ch3nh3+, changed based on NMA nmodes
+angle HC   CT   HC      35.0 109.50
+angle HC   CT   CM      50.0 109.50 ! changed based on NMA nmodes
+angle HC   CT   CD      50.0 109.50 ! Junmei et al, 1999
+angle HC   CT   CZ      50.0 110.00 ! Junmei et al, 1999
+angle C    CT   H1      50.0 109.50 ! AA general  changed based on NMA nmodes
+angle C    CT   HP      50.0 109.50 ! AA zwitterion  changed based on NMA nmodes
+angle C    CT   HC      50.0 109.50 ! AA gln      changed based on NMA nmodes
+angle C    CT   N       63.0 110.10 ! AA general
+angle C    CT   N3      80.0 111.20 ! AA amino terminal residues
+angle C    CT   CT      63.0 111.10 ! AA general
+angle C    CT   OS      60.0 109.50 ! Junmei et al, 1999
+angle CA   CT   HC      50.0 109.50 ! AA tyr     changed based on NMA nmodes
+angle CC   CT   CT      63.0 113.10 ! AA his
+angle CC   CT   HC      50.0 109.50 ! AA his     changed based on NMA nmodes
+angle CM   CT   CT      63.0 111.00 ! Junmei et al, 1999 (last change: Mar24,99)
+angle CM   CT   OS      50.0 109.50 ! Junmei et al, 1999
+angle CT   CT   CT      40.0 109.50
+angle CT   CT   HC      50.0 109.50 ! changed based on NMA nmodes
+angle CT   CT   H1      50.0 109.50 ! changed based on NMA nmodes
+angle CT   CT   H2      50.0 109.50 ! changed based on NMA nmodes
+angle CT   CT   HP      50.0 109.50 ! changed based on NMA nmodes
+angle CT   CT   N*      50.0 109.50
+angle CT   CT   OH      50.0 109.50
+angle CT   CT   OS      50.0 109.50
+angle CT   CT   S       50.0 114.70 ! AA cyx            (SCHERAGA  JPC 79,1428)
+angle CT   CT   SH      50.0 108.60 ! AA cys
+angle CT   CT   CA      63.0 114.00 ! AA phe tyr          (SCH JPC  79,2379)
+angle CT   CT   N2      80.0 111.20 ! AA arg             (JCP 76, 1439)
+angle CT   CT   N       80.0 109.70 ! AA ala, general    (JACS 94, 2657)
+angle CT   CT   N3      80.0 111.20 ! AA lys             (JCP 76, 1439)
+angle CT   CT   NT      80.0 111.20 ! neutral amines
+angle CT   CT   CY      63.0 110.00 ! Junmei et al, 1999
+angle CT   CT   CZ      63.0 110.00 ! Junmei et al, 1999
+angle C*   CT   CT      63.0 115.60 ! AA trp
+angle C*   CT   HC      50.0 109.50 ! AA trp    changed based on NMA nmodes
+angle OS   CT   OS     160.0 101.00 ! Junmei et al, 1999
+angle OS   CT   CY      50.0 110.00 ! Junmei et al, 1999
+angle OS   CT   CZ      50.0 110.00 ! Junmei et al, 1999
+angle OS   CT   CZ      50.0 110.00 ! Junmei et al, 1999
+angle OS   CT   CY      50.0 110.00 ! Junmei et al, 1999
+angle OS   CT   N*      50.0 109.50
+angle F    CT   F       77.0 109.10 ! JCC,13,(1992),963;
+angle F    CT   H1      50.0 109.50 ! JCC,13,(1992),963;
+angle F    CT   CT      50.0 109.00
+angle F    CT   H2      50.0 109.50
+angle Cl   CT   CT      50.0 108.50 ! (6-31g* opt value)
+angle Cl   CT   H1      50.0 108.50 ! (6-31g* opt value)
+angle Br   CT   CT      50.0 108.00 ! Junmei et al 99
+angle Br   CT   H1      50.0 106.50 ! Junmei et al 99
+angle I    CT   CT      50.0 106.00 ! Junmei et al,99
+angle CT   CC   NA      70.0 120.00 ! AA his
+angle CT   CC   CV      70.0 120.00 ! AA his
+angle CT   CC   NB      70.0 120.00 ! AA his
+angle CV   CC   NA      70.0 120.00 ! AA his
+angle CW   CC   NA      70.0 120.00 ! AA his
+angle CW   CC   NB      70.0 120.00 ! AA his
+angle CT   CC   CW      70.0 120.00 ! AA his
+angle H5   CR   NA      50.0 120.00 ! AA his
+angle H5   CR   NB      50.0 120.00 ! AA his
+angle NA   CR   NA      70.0 120.00 ! AA his
+angle NA   CR   NB      70.0 120.00 ! AA his
+angle CC   CV   H4      50.0 120.00 ! AA his
+angle CC   CV   NB      70.0 120.00 ! AA his
+angle H4   CV   NB      50.0 120.00 ! AA his
+angle CC   CW   H4      50.0 120.00 ! AA his
+angle CC   CW   NA      70.0 120.00 ! AA his
+angle C*   CW   H4      50.0 120.00 ! AA trp
+angle C*   CW   NA      70.0 108.70 ! AA trp
+angle H4   CW   NA      50.0 120.00 ! AA his
+angle CB   C*   CT      70.0 128.60 ! AA trp
+angle CB   C*   CW      63.0 106.40 ! changed from 85.0  bsd on C6H6 nmodes; AA trp
+angle CT   C*   CW      70.0 125.00 ! AA trp
+angle CA   CN   CB      63.0 122.70 ! changed from 85.0  bsd on C6H6 nmodes; AA trp
+angle CA   CN   NA      70.0 132.80 ! AA trp
+angle CB   CN   NA      70.0 104.40 ! AA trp
+angle CT   CY   NY      80.0 180.00 ! Junmei et al, 1999
+angle CT   CZ   CZ      80.0 180.00 ! Junmei et al, 1999
+angle CZ   CZ   HZ      50.0 180.00 ! Junmei et al, 1999
+angle C    N    CT      50.0 121.90 ! AA general
+angle C    N    H       50.0 120.00 ! AA general, gln, asn,changed based on NMA nmodes
+angle CT   N    H       50.0 118.04 ! AA general,     changed based on NMA nmodes
+angle CT   N    CT      50.0 118.00 ! AA pro             (DETAR JACS 99,1232)
+angle H    N    H       35.0 120.00 ! ade,cyt,gua,gln,asn     **
+angle C    N*   CM      70.0 121.60
+angle C    N*   CT      70.0 117.60
+angle C    N*   H       50.0 119.20 ! changed based on NMA nmodes
+angle CB   N*   CK      70.0 105.40
+angle CB   N*   CT      70.0 125.80
+angle CB   N*   H       50.0 125.80 ! for unmethylated n.a. bases,chngd bsd NMA nmodes
+angle CK   N*   CT      70.0 128.80
+angle CK   N*   H       50.0 128.80 ! for unmethylated n.a. bases,chngd bsd NMA nmodes
+angle CM   N*   CT      70.0 121.20
+angle CM   N*   H       50.0 121.20 ! for unmethylated n.a. bases,chngd bsd NMA nmodes
+angle CA   N2   H       50.0 120.00
+angle CA   N2   CT      50.0 123.20 ! AA arg
+angle CT   N2   H       50.0 118.40 ! AA arg
+angle H    N2   H       35.0 120.00
+angle CT   N3   H       50.0 109.50 ! AA lys,     changed based on NMA nmodes
+angle CT   N3   CT      50.0 109.50 ! AA pro/nt
+angle H    N3   H       35.0 109.50 ! AA lys, AA(end)
+angle CT   NT   H       50.0 109.50 ! neutral amines
+angle CT   NT   CT      50.0 109.50 ! neutral amines
+angle H    NT   H       35.0 109.50 ! neutral amines
+angle C    NA   C       70.0 126.40
+angle C    NA   CA      70.0 125.20
+angle C    NA   H       50.0 116.80 ! changed based on NMA nmodes
+angle CA   NA   H       50.0 118.00 ! changed based on NMA nmodes
+angle CC   NA   CR      70.0 120.00 ! AA his
+angle CC   NA   H       50.0 120.00 ! AA his,    changed based on NMA nmodes
+angle CR   NA   CW      70.0 120.00 ! AA his
+angle CR   NA   H       50.0 120.00 ! AA his,    changed based on NMA nmodes
+angle CW   NA   H       50.0 120.00 ! AA his,    changed based on NMA nmodes
+angle CN   NA   CW      70.0 111.60 ! AA trp
+angle CN   NA   H       50.0 123.10 ! AA trp,    changed based on NMA nmodes
+angle CB   NB   CK      70.0 103.80
+angle CC   NB   CR      70.0 117.00 ! AA his
+angle CR   NB   CV      70.0 117.00 ! AA his
+angle C    NC   CA      70.0 120.50
+angle CA   NC   CB      70.0 112.20
+angle CA   NC   CQ      70.0 118.60
+angle CB   NC   CQ      70.0 111.00
+angle C    OH   HO      50.0 113.00 ! (not used in tyr anymore)
+angle CA   OH   HO      50.0 113.00 ! replacement in tyr
+angle CT   OH   HO      55.0 108.50
+angle HO   OH   P       45.0 108.50
+angle C    OS   CT      60.0 117.00 ! Junmei et al, 1999
+angle CM   OS   CT      60.0 117.00 ! Junmei et al, 1999
+angle CT   OS   CT      60.0 109.50
+angle CT   OS   P      100.0 120.50
+angle P    OS   P      100.0 120.50
+angle O2   P    OH      45.0 108.23
+angle O2   P    O2     140.0 119.90
+angle O2   P    OS     100.0 108.23
+angle OH   P    OS      45.0 102.60
+angle OS   P    OS      45.0 102.60
+angle CT   S    CT      62.0 98.90 ! AA met
+angle CT   S    S       68.0 103.70 ! AA cyx             (SCHERAGA  JPC 79,1428)
+angle CT   SH   HS      43.0 96.00 ! changed from 44.0 based on methanethiol nmodes
+angle HS   SH   HS      35.0 92.07 ! AA cys
+angle CB   NB   LP     150.0 126.00 ! NA
+angle CC   NB   LP     150.0 126.00 ! his,NA
+angle CK   NB   LP     150.0 126.00 ! NA
+angle CR   NB   LP     150.0 126.00 ! his,NA
+angle CV   NB   LP     150.0 126.00 ! his,NA
+angle C    NC   LP     150.0 120.00 ! NA
+angle CA   NC   LP     150.0 120.00 ! NA
+angle CB   NC   LP     150.0 120.00 ! NA
+angle CQ   NC   LP     150.0 120.00 ! NA
+angle CT   N3   LP     150.0 109.50 ! in neutral lysine
+angle H    N3   LP     150.0 109.50 ! in neutral lysine
+angle CT   NT   LP     150.0 109.50
+angle H    NT   LP     150.0 109.50
+angle C    O    LP     150.0 120.00
+angle LP   O    LP     150.0 120.00
+angle C    OH   LP     150.0 120.00
+angle CT   OH   LP     150.0 109.50
+angle HO   OH   LP     150.0 109.50
+angle LP   OH   LP     150.0 109.50
+angle C    OS   LP     150.0 109.50
+angle CM   OS   LP     150.0 109.50 ! methyl vinyl ether
+angle CT   OS   LP     150.0 109.50
+angle LP   OS   LP     150.0 109.50
+angle CT   S    LP     150.0 90.00 ! cys,cyx,met
+angle CT   SH   LP     150.0 90.00 ! cys,cyx,met
+angle P    OS   LP     150.0 109.50 ! NA
+angle LP   S    LP     150.0 180.00 ! cys,cyx,met
+angle LP   SH   LP     150.0 180.00 ! cys,cyx,met
+angle HS   SH   LP     150.0 90.00 ! cys
+
+dihe X    C    C    X     3.62       2  180.00 ! Junmei et al, 1999
+dihe X    C    CA   X     3.62       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    C    CB   X     3.00       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    C    CM   X     2.17       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    C    CT   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    C    N    X     2.50       2  180.00 ! AA,NMA
+dihe X    C    N*   X     1.45       2  180.00 ! JCC,7,(1986),230
+dihe X    C    NA   X     1.35       2  180.00 ! JCC,7,(1986),230
+dihe X    C    NC   X     4.00       2  180.00 ! JCC,7,(1986),230
+dihe X    C    O    X     2.80       2  180.00 ! Junmei et al, 1999
+dihe X    C    OH   X     2.30       2  180.00 ! Junmei et al, 1999
+dihe X    C    OS   X     2.70       2  180.00 ! Junmei et al, 1999
+dihe X    CA   CA   X     3.62       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CA   CB   X     3.50       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CA   CM   X     2.55       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CA   CN   X     3.62       2  180.00 ! reinterpolated 93'
+dihe X    CA   CT   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    CA   N2   X     2.40       2  180.00 ! reinterpolated 93'
+dihe X    CA   NA   X     1.50       2  180.00 ! JCC,7,(1986),230
+dihe X    CA   NC   X     4.80       2  180.00 ! JCC,7,(1986),230
+dihe X    CA   OH   X     0.90       2  180.00 ! Junmei et al, 99
+dihe X    CB   CB   X     5.45       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CB   CN   X     3.00       2  180.00 ! reinterpolated 93'
+dihe X    CB   N*   X     1.65       2  180.00 ! JCC,7,(1986),230
+dihe X    CB   NB   X     2.55       2  180.00 ! JCC,7,(1986),230
+dihe X    CB   NC   X     4.15       2  180.00 ! JCC,7,(1986),230
+dihe X    CC   CT   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    CC   CV   X     5.15       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CC   CW   X     5.38       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CC   NA   X     1.40       2  180.00 ! JCC,7,(1986),230
+dihe X    CC   NB   X     2.40       2  180.00 ! JCC,7,(1986),230
+dihe X    CD   CD   X     1.00       2  180.00 ! Junmei et al, 1999
+dihe X    CD   CT   X     0.00       2    0.00 ! Junmei et al, 1999
+dihe X    CD   CM   X     6.65       2  180.00 ! Junmei et al, 1999
+dihe X    CK   N*   X     1.70       2  180.00 ! JCC,7,(1986),230
+dihe X    CK   NB   X    10.00       2  180.00 ! JCC,7,(1986),230
+dihe X    CM   CM   X     6.65       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CM   CT   X     0.00       3    0.00 ! JCC,7,(1986),230
+dihe X    CM   N*   X     1.85       2  180.00 ! JCC,7,(1986),230
+dihe X    CM   OS   X     1.05       2  180.00 ! Junmei et al, 1999
+dihe X    CN   NA   X     1.52       2  180.00 ! reinterpolated 93'
+dihe X    CQ   NC   X     6.80       2  180.00 ! JCC,7,(1986),230
+dihe X    CT   CT   X     0.16       3    0.00 ! JCC,7,(1986),230
+dihe X    CT   CY   X     0.00       1    0.00 ! Junmei et al, 1999
+dihe X    CT   CZ   X     0.00       1    0.00 ! Junmei et al, 1999
+dihe X    CT   N    X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    CT   N*   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    CT   N2   X     0.00       3    0.00 ! JCC,7,(1986),230
+dihe X    CT   NT   X     0.30       3    0.00 ! Junmei et al, 1999
+dihe X    CT   N3   X     0.16       3    0.00 ! JCC,7,(1986),230
+dihe X    CT   OH   X     0.17       3    0.00 ! JCC,7,(1986),230
+dihe X    CT   OS   X     0.38       3    0.00 ! JCC,7,(1986),230
+dihe X    CT   S    X     0.33       3    0.00 ! JCC,7,(1986),230
+dihe X    CT   SH   X     0.25       3    0.00 ! JCC,7,(1986),230
+dihe X    C*   CB   X     1.68       2  180.00 ! intrpol.bsd.onC6H6aa
+dihe X    C*   CT   X     0.00       2    0.00 ! JCC,7,(1986),230
+dihe X    C*   CW   X     6.53       2  180.00 ! intrpol.bsd.on C6H6
+dihe X    CR   NA   X     2.33       2  180.00 ! JCC,7,(1986),230
+dihe X    CR   NB   X     5.00       2  180.00 ! JCC,7,(1986),230
+dihe X    CV   NB   X     2.40       2  180.00 ! JCC,7,(1986),230
+dihe X    CW   NA   X     1.50       2  180.00 ! JCC,7,(1986),230
+dihe X    OH   P    X     0.25       3    0.00 ! JCC,7,(1986),230
+dihe X    OS   P    X     0.25       3    0.00 ! JCC,7,(1986),230
+dihe N    CT   C    N     2.00       2  180.00
+dihe C    N    CT   C     0.80       1    0.00
+dihe CT   CT   N    C     0.53       1    0.00 ! phi,psi,parm94
+dihe CT   CT   C    N     0.07       2    0.00 ! phi,psi,parm94
+dihe H    N    C    O     2.00       1    0.00 ! J.C.cistrans-NMA DE
+dihe CT   S    S    CT    0.60       3    0.00 ! JCC,7,(1986),230
+dihe OH   P    OS   CT    1.20       2    0.00 ! gg&gt ene.631g*/mp2
+dihe OS   P    OS   CT    1.20       2    0.00 ! gg&gt ene.631g*/mp2
+dihe H1   CT   C    O     0.08       3  180.00 ! Junmei et al, 1999
+dihe HC   CT   C    O     0.08       3  180.00 ! Junmei et al, 1999
+dihe HC   CT   CT   HC    0.15       3    0.00 ! Junmei et al, 1999
+dihe HC   CT   CT   CT    0.16       3    0.00 ! Junmei et al, 1999
+dihe HC   CT   CM   CM    1.15       1    0.00 ! Junmei et al, 1999
+dihe HO   OH   CT   CT    0.25       1    0.00 ! Junmei et al, 1999
+dihe HO   OH   C    O     1.90       1    0.00 ! Junmei et al, 1999
+dihe CM   CM   C    O     0.30       3    0.00 ! Junmei et al, 1999
+dihe CT   CM   CM   CT    1.90       1  180.00 ! Junmei et al, 1999
+dihe CT   CT   CT   CT    0.20       1  180.00 ! Junmei et al, 1999
+dihe CT   CT   NT   CT    0.48       2  180.00 ! Junmei et al, 1999
+dihe CT   CT   OS   CT    0.10       2  180.00
+dihe CT   CT   OS   C     0.80       1  180.00 ! Junmei et al, 1999
+dihe CT   OS   CT   OS    1.35       1  180.00 ! Junmei et al, 1999
+dihe CT   OS   CT   N*    0.65       2    0.00 ! Piotr et al.
+dihe CT   CZ   CZ   HZ    0.00       1    0.00 ! Junmei et al, 1999
+dihe O    C    OS   CT    1.40       1  180.00 ! Junmei et al, 1999
+dihe OS   CT   N*   CK    2.50       1    0.00 ! parm98, TC,PC,PAK
+dihe OS   CT   N*   CM    2.50       1    0.00 ! parm98, TC,PC,PAK
+dihe OS   CT   CT   OS    1.18       2    0.00 ! Piotr et al.
+dihe OS   CT   CT   OH    1.18       2    0.00 ! parm98, TC,PC,PAK
+dihe OH   CT   CT   OH    1.18       2    0.00 ! parm98, TC,PC,PAK
+dihe F    CT   CT   F     1.20       1  180.00 ! Junmei et al, 1999
+dihe Cl   CT   CT   Cl    0.45       1  180.00 ! Junmei et al, 1999
+dihe Br   CT   CT   Br    0.00       1  180.00 ! Junmei et al, 1999
+dihe H1   CT   CT   OS    0.25       1    0.00 ! Junmei et al, 1999
+dihe H1   CT   CT   OH    0.25       1    0.00 ! Junmei et al, 1999
+dihe H1   CT   CT   F     0.19       1    0.00 ! Junmei et al, 1999
+dihe H1   CT   CT   Cl    0.25       1    0.00 ! Junmei et al, 1999
+dihe H1   CT   CT   Br    0.55       1    0.00 ! Junmei et al, 1999
+dihe HC   CT   CT   OS    0.25       1    0.00 ! Junmei et al, 1999
+dihe HC   CT   CT   OH    0.25       1    0.00 ! Junmei et al, 1999
+dihe HC   CT   CT   F     0.19       1    0.00 ! Junmei et al, 1999
+dihe HC   CT   CT   Cl    0.25       1    0.00 ! Junmei et al, 1999
+dihe HC   CT   CT   Br    0.55       1    0.00 ! Junmei et al, 1999
+dihe H1   CT   NT   LP    0.00       3    0.00
+dihe CT   CT   NT   LP    0.00       3    0.00
+dihe CT   C    N    LP    0.00       2  180.00
+dihe O    C    N    LP    0.00       2  180.00
+dihe H1   CT   OH   LP    0.00       3    0.00
+dihe CT   CT   OH   LP    0.00       3    0.00
+dihe H1   CT   OS   LP    0.00       3    0.00
+dihe H2   CT   OS   LP    0.00       3    0.00
+dihe CT   CT   OS   LP    0.00       3    0.00
+dihe CM   CM   OS   LP    0.00       2  180.00
+dihe HA   CM   OS   LP    0.00       2  180.00
+dihe H4   CM   OS   LP    0.00       2  180.00
+
+impr X    X    C    O    10.50       2  180.00 ! JCC,7,(1986),230
+impr X    O2   C    O2   10.50       2  180.00 ! JCC,7,(1986),230
+impr X    X    N    H     1.00       2  180.00 ! JCC,7,(1986),230
+impr X    X    N2   H     1.00       2  180.00 ! JCC,7,(1986),230
+impr X    X    NA   H     1.00       2  180.00 ! JCC,7,(1986),230
+impr X    N2   CA   N2   10.50       2  180.00 ! JCC,7,(1986),230
+impr X    CT   N    CT    1.00       2  180.00 ! JCC,7,(1986),230
+impr X    X    CA   HA    1.10       2  180.00 ! bsd.on C6H6 nmodes
+impr X    X    CW   H4    1.10       2  180.00
+impr X    X    CR   H5    1.10       2  180.00
+impr X    X    CV   H4    1.10       2  180.00
+impr X    X    CQ   H5    1.10       2  180.00
+impr X    X    CK   H5    1.10       2  180.00
+impr X    X    CM   H4    1.10       2  180.00
+impr X    X    CM   HA    1.10       2  180.00
+impr X    X    CA   H4    1.10       2  180.00 ! bsd.on C6H6 nmodes
+impr X    X    CA   H5    1.10       2  180.00 ! bsd.on C6H6 nmodes
+impr CK   CB   N*   CT    1.00       2  180.00
+impr CM   C    N*   CT    1.00       2  180.00 ! dac guess, 9/94
+impr CM   C    CM   CT    1.10       2  180.00
+impr CT   O    C    OH   10.50       2  180.00
+impr NA   CV   CC   CT    1.10       2  180.00
+impr NB   CW   CC   CT    1.10       2  180.00
+impr NA   CW   CC   CT    1.10       2  180.00
+impr CW   CB   C*   CT    1.10       2  180.00
+impr CA   CA   CA   CT    1.10       2  180.00
+impr C    CM   CM   CT    1.10       2  180.00 ! dac guess, 9/94
+impr NC   CM   CA   N2    1.10       2  180.00 ! dac guess, 9/94
+impr CB   NC   CA   N2    1.10       2  180.00 ! dac, 10/94
+impr NA   NC   CA   N2    1.10       2  180.00 ! dac, 10/94
+impr CA   CA   C    OH    1.10       2  180.00 ! (not used in tyr!)
+impr CA   CA   CA   OH    1.10       2  180.00 ! in tyr
+impr H5   O    C    OH    1.10       2  180.00 ! Junmei et al.1999
+impr H5   O    C    OS    1.10       2  180.00
+impr CM   CT   CM   HA    1.10       2  180.00 ! Junmei et al.1999
+impr CA   CA   CA   Br    1.10       2  180.00 ! Junmei et al.1999
+impr CM   H4   C    O     1.10       2  180.00 ! Junmei et al.1999
+impr C    CT   N    H     1.10       2  180.00 ! Junmei et al.1999
+impr C    CT   N    O     1.10       2  180.00 ! Junmei et al.1999
+
+vdw H   0.0157  0.6000  0.0157  0.6000 0   1.008 ! H bonded to nitrogen atoms
+vdw HO  0.0000  0.0000  0.0000  0.0000 0   1.008 ! hydroxyl group
+vdw HS  0.0157  0.6000  0.0157  0.6000 0   1.008 ! hydrogen bonded to sulphur (pol?)
+vdw HC  0.0157  1.4870  0.0157  1.4870 0   1.008 ! H aliph. bond. to C without electrwd.group
+vdw H1  0.0157  1.3870  0.0157  1.3870 0   1.008 ! H aliph. bond. to C with 1 electrwd. group
+vdw H2  0.0157  1.2870  0.0157  1.2870 0   1.008 ! H aliph. bond. to C with 2 electrwd.groups
+vdw H3  0.0157  1.1870  0.0157  1.1870 0   1.008 ! H aliph. bond. to C with 3 eletrwd.groups
+vdw HP  0.0157  1.1000  0.0157  1.1000 0   1.008 ! H bonded to C next to positively charged gr
+vdw HA  0.0150  1.4590  0.0150  1.4590 0   1.008 ! H arom. bond. to C without elctrwd. groups
+vdw H4  0.0150  1.4090  0.0150  1.4090 0   1.008 ! H arom. bond. to C with 1 electrwd. group
+vdw H5  0.0150  1.3590  0.0150  1.3590 0   1.008 ! H arom.at C with 2 elctrwd. gr,+HCOO group
+vdw HW  0.0000  0.0000  0.0000  0.0000 0   1.008 ! H in TIP3P water
+vdw HZ  0.0150  1.4590  0.0150  1.4590 0   1.008 ! H bond sp C (Howard et al.JCC,16,243,1995)
+vdw O   0.2100  1.6612  0.2100  1.6612 0  16.000 ! carbonyl group oxygen
+vdw O2  0.2100  1.6612  0.2100  1.6612 0  16.000 ! carboxyl and phosphate group oxygen
+vdw OW  0.1520  1.7683  0.1520  1.7683 0  16.000 ! oxygen in TIP3P water
+vdw OH  0.2104  1.7210  0.2104  1.7210 0  16.000 ! oxygen in hydroxyl group
+vdw OS  0.1700  1.6837  0.1700  1.6837 0  16.000 ! ether and ester oxygen
+vdw C*  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 arom. 5 memb.ring w/1 subst. (TRP)
+vdw CA  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 C pure aromatic (benzene)
+vdw CB  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 aromatic C, 5&6 membered ring junction
+vdw CC  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 aromatic C, 5 memb. ring HIS
+vdw CD  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 C atom in the middle of: C=CD-CD=C
+vdw CK  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 C 5 memb.ring in purines
+vdw CM  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 C  pyrimidines in pos. 5 & 6
+vdw CN  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 C aromatic 5&6 memb.ring junct.(TRP)
+vdw CQ  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 C in 5 mem.ring of purines between 2 N
+vdw CR  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 arom as CQ but in HIS
+vdw CV  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 arom. 5 memb.ring w/1 N and 1 H (HIS)
+vdw CW  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 arom. 5 memb.ring w/1 N-H and 1 H (HIS)
+vdw CX  0.0860  1.9080  0.0860  1.9080 0   0.000
+vdw CY  0.0860  1.9080  0.0860  1.9080 0  12.010 ! nitrile C (Howard et al.JCC,16,243,1995)
+vdw CZ  0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp C (Howard et al.JCC,16,243,1995)
+vdw CT  0.1094  1.9080  0.1094  1.9080 0  12.010 ! sp3 aliphatic C
+vdw C   0.0860  1.9080  0.0860  1.9080 0  12.010 ! sp2 C carbonyl group
+vdw N   0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp2 nitrogen in amide groups
+vdw NA  0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp2 N in 5 memb.ring w/H atom (HIS)
+vdw N2  0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp2 N in amino groups
+vdw N*  0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp2 N
+vdw NC  0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp2 N in 6 memb.ring w/LP (ADE,GUA)
+vdw NB  0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp2 N in 5 memb.ring w/LP (HIS,ADE,GUA)
+vdw N3  0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp3 N for charged amino groups (Lys, etc)
+vdw NT  0.1700  1.8240  0.1700  1.8240 0  14.010 ! sp3 N for amino groups amino groups
+vdw NP  0.1700  1.8240  0.1700  1.8240 0   0.000
+vdw NO  0.1700  1.8240  0.1700  1.8240 0   0.000
+vdw NY  0.1700  1.8240  0.1700  1.8240 0  14.010 ! nitrile N (Howard et al.JCC,16,243,1995)
+vdw NY  0.1700  1.8240  0.1700  1.8240 0  14.010 ! nitrile N (Howard et al.JCC,16,243,1995)
+vdw S   0.2500  2.0000  0.2500  2.0000 0  32.060 ! S in disulfide linkage,pol:JPC,102,2399,98
+vdw SH  0.2500  2.0000  0.2500  2.0000 0  32.060 ! S in cystine
+vdw P   0.2000  2.1000  0.2000  2.1000 0  30.970 ! phosphate,pol:JACS,112,8543,90,K.J.Miller
+vdw IM  0.1000  2.4700  0.1000  2.4700 0  35.450 ! assumed to be Cl- (ion minus)
+vdw Li  0.0183  1.1370  0.0183  1.1370 0   6.940 ! lithium, ions pol:J.PhysC,11,1541,(1978)
+vdw IP  0.0028  1.8680  0.0028  1.8680 0  22.990 ! assumed to be Na+ (ion plus)
+vdw Na  0.0028  1.8680  0.0028  1.8680 0  22.990 ! Na+, ions pol:J.PhysC,11,1541,(1978)
+vdw K   0.0003  2.6580  0.0003  2.6580 0  39.100 ! potassium
+vdw Rb  0.0002  2.9560  0.0002  2.9560 0  85.470 ! rubidium
+vdw Cs  0.0001  3.3950  0.0001  3.3950 0 132.910 ! cesium
+vdw MG  0.8947  0.7926  0.8947  0.7926 0  24.305 ! magnesium
+vdw C0  0.4598  1.7131  0.4598  1.7131 0  40.080 ! calcium
+vdw Zn  0.0125  1.1000  0.0125  1.1000 0  65.400 ! Zn2+
+vdw F   0.0610  1.7500  0.0610  1.7500 0  19.000 ! fluorine
+vdw Cl  0.2650  1.9480  0.2650  1.9480 0  35.450 ! chlorine  (Applequist)
+vdw Br  0.3200  2.2200  0.3200  2.2200 0  79.900 ! bromine  (Applequist)
+vdw I   0.4000  2.3500  0.4000  2.3500 0 126.900 ! iodine   (Applequist)
+vdw IB  0.1000  5.0000  0.1000  5.0000 0 131.000 ! 'big ion w/ waters' for vacuum (Na+, 6H2O)
+vdw LP  0.0000  0.0000  0.0000  0.0000 0   3.000 ! lone pair
+
diff --git a/Scripts/startup.rb b/Scripts/startup.rb
new file mode 100755 (executable)
index 0000000..32fab30
--- /dev/null
@@ -0,0 +1,45 @@
+#
+#  startup.rb
+#
+#  Created by Toshi Nagata.
+#  Copyright 2008 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+include Math
+
+$startup_dir = Dir.pwd
+case RUBY_PLATFORM
+  when /mswin|mingw|cygwin|bccwin/
+    $platform = "win"
+  when /darwin/
+    $platform = "mac"
+  else
+    $platform = "other"
+end
+
+$backtrace = nil
+
+def backtrace
+  if $backtrace
+    print $backtrace.join("\n")
+  end
+end
+
+load "transform.rb"
+load "molecule.rb"
+load "loadsave.rb"
+load "formula.rb"
+load "dialog.rb"
+load "commands.rb"
+load "md.rb"
+load "gamess.rb"
+
+GC.start
diff --git a/Scripts/transform.rb b/Scripts/transform.rb
new file mode 100755 (executable)
index 0000000..5262b99
--- /dev/null
@@ -0,0 +1,107 @@
+#
+#  transform.rb
+#
+#  Created by Toshi Nagata.
+#  Copyright 2008 Toshi Nagata. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+class Transform
+
+  #  Transform matrix that translates cx to origin and rotates ax->x
+  #  and ay->y. Ax and ay will be normalized, and if ax and ay are
+  #  not perpendicular, (ax.cross(ay)).cross(ax).normalize is used
+  #  instead of ay.
+  def self.rotation_with_axis(ax, ay, cx = Vector3D[0,0,0])
+    ax = ax.normalize
+    az = ax.cross(ay).normalize
+    ay = az.cross(ax).normalize
+    Transform[ax, ay, az, [0,0,0]].inverse * Transform.translation(cx * (-1))
+  end
+
+  #  Decompose a rotation matrix to the rotation axis and angle.
+  #  Returns a list [axis, angle]
+  def to_rot
+    xx = self[0,0]
+    yy = self[1,1]
+    zz = self[2,2]
+    ww = xx + yy + zz + 1.0
+    if (ww >= 1.0)
+      w = Math.sqrt(0.25 * ww)
+      ww = 0.25 / w;
+      x = (self[1,2] - self[2,1]) * ww
+      y = (self[2,0] - self[0,2]) * ww
+      z = (self[0,1] - self[1,0]) * ww
+    elsif (xx >= yy && xx >= zz)
+      x = Math.sqrt(0.25 * (1.0 + xx - yy - zz))
+      ww = 0.25 / x
+      y = (self[1,0] + self[0,1]) * ww
+      z = (self[2,0] + self[0,2]) * ww
+      w = (self[1,2] - self[2,1]) * ww
+    elsif (yy >= zz)
+      y = Math.sqrt(0.25 * (1.0 + yy - xx - zz))
+      ww = 0.25 / y
+      x = (self[1,0] + self[0,1]) * ww
+      z = (self[1,2] + self[2,1]) * ww
+      w = (self[2,0] - self[0,2]) * ww
+    else
+      z = Math.sqrt(0.25 * (1.0 + zz - xx - yy))
+      ww = 0.25 / z
+      x = (self[2,0] + self[0,2]) * ww
+      y = (self[2,1] + self[1,2]) * ww
+      w = (self[0,1] - self[1,0]) * ww
+    end
+#    puts "#{x}, #{y}, #{z}, #{w}"
+    ww = Math.sqrt(x*x + y*y + z*z)
+    ang = Math.atan2(ww, w) * 2
+    v = Vector3D[x/ww, y/ww, z/ww]
+    if (ang > Math::PI)
+      ang = 2*Math::PI - ang
+      v = v * (-1)
+    end
+    return [v, ang]
+  end
+
+  #  Decompose a rotation matrix to spiral components
+  #  Returns a list [axis, angle, center, pitch]
+  def to_spiral
+    r, ang = to_rot   #  axis and angle
+    d = column(3)     #  translation vector
+    #  Calculate psuedoinverse (Moore-Penrose inverse) matrix of (I-R)
+    #  and solve d = (I-R)c + (d.r)*r for c
+    r0 = Vector3D[1,0,0].cross(r)
+    if r0.length < 1e-4
+      r0 = Vector3D[0,1,0].cross(r)
+    end
+    r0 = r0.normalize
+    r1 = r.cross(r0).normalize
+    q0 = r0 - (self * r0 - d)
+    k0 = q0.length
+    q0 = q0.normalize
+    q1 = r1 - (self * r1 - d)
+    k1 = q1.length
+    q1 = q1.normalize
+    u = Transform[q0, q1, r, [0,0,0]]
+    uinv = u.inverse
+    udet = u.determinant
+#    puts "u = #{u}, udet = #{udet}, uinv = #{uinv}"
+    usv = u * Transform.diagonal(k0, k1, 0) * (Transform[r0, r1, r, [0,0,0]].inverse)
+#    puts "usv = #{usv}, rot = #{rot}"
+    mpinv = Transform[r0 * (1.0/k0), r1 * (1.0/k1), [0,0,0], [0,0,0]] * (Transform[q0, q1, r, [0,0,0]].inverse)
+#    mm = ir - ir * mpinv * ir
+#    mm2 = mpinv - mpinv * ir * mpinv
+#    puts "mpinv = #{mpinv}, mm = #{mm}, mm2 = #{mm2}"
+    pitch = d.dot(r)
+    c = mpinv * (d - r * pitch)
+#    c2 = (rot2 * c - c).cross(r)
+#    puts "c = #{c}, c2 = #{c2}"
+    return [r, ang, c, pitch]
+  end
+end
diff --git a/Version b/Version
new file mode 100644 (file)
index 0000000..48e41e4
--- /dev/null
+++ b/Version
@@ -0,0 +1,2 @@
+version = "0.5.0"
+date = "20100121"
diff --git a/amberparm2molby.pl b/amberparm2molby.pl
new file mode 100644 (file)
index 0000000..a115cb0
--- /dev/null
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+#  amberparm2molby: read amber .dat file and convert to a Molby parameter file
+#  input (stdin): a .dat file
+#  output (stdout): a Molby parm file
+#  Written as amberparm2namd.pl by Toshi Nagata, Dec 2 2004
+#  Updated for Molby, Nov 13 2009
+
+$title = <>;
+print "! ", $title;
+
+$blanks = ' ' x 100;
+
+sub format_com {
+       my ($com) = @_;
+       $com =~ s/(^\s*)|(\s*$)//g;
+       $com =~ s/^\!\s*//;
+       if ($com eq "!") {
+               $com = "";
+       } elsif ($com ne "") {
+               $com = " ! " . $com;
+       }
+       return $com;
+}
+
+while (<>) {
+       chomp;
+       last if /^\s*$/;
+       ($aname, $molw, $pol, $com) = unpack("A2 x1 A10 A10 A77", $_ . $blanks);
+       push @atoms, $aname;
+       $molws{$aname} = $molw;
+       $comments{$aname} = format_com($com);
+}
+
+#  Hydrophilic atoms
+$line = <>;
+
+#  Bonds
+while (<>) {
+       chomp;
+       last if /^\s*$/;
+       ($a1, $a2, $fc, $len, $com) = unpack("A2 x1 A2 A10 A10 A75", $_ . $blanks);
+       printf "bond %-2s   %-2s   %7.1f %5.2f%s\n", $a1, $a2, $fc, $len, format_com($com);
+}
+print "\n";
+
+#  Angles
+while (<>) {
+       chomp;
+       last if /^\s*$/;
+       ($a1, $a2, $a3, $fc, $ang, $com) = unpack("A2 x1 A2 x1 A2 A10 A10 A72", $_ . $blanks);
+       printf "angle %-2s   %-2s   %-2s   %7.1f %5.2f%s\n", $a1, $a2, $a3, $fc, $ang, format_com($com);
+}
+print "\n";
+
+#  Dihedrals
+while (<>) {
+       chomp;
+       last if /^\s*$/;
+       ($a1, $a2, $a3, $a4, $div, $fc, $ang, $per, $com) = unpack("A2 x1 A2 x1 A2 x1 A2 A4 A15 A15 A15 A40", $_ . $blanks);
+       if ($div > 0) {
+               $fc /= $div;
+       }
+       next if $per < 0;
+       printf "dihe %-2s   %-2s   %-2s   %-2s %7.2f %7d %7.2f%s\n", $a1, $a2, $a3, $a4, $fc, $per, $ang, format_com($com);
+}
+print "\n";
+
+#  Impropers
+while (<>) {
+       chomp;
+       last if /^\s*$/;
+       ($a1, $a2, $a3, $a4, $div, $fc, $ang, $per, $com) = unpack("A2 x1 A2 x1 A2 x1 A2 A4 A15 A15 A15 A40", $_ . $blanks);
+       printf "impr %-2s   %-2s   %-2s   %-2s %7.2f %7d %7.2f%s\n", $a1, $a2, $a3, $a4, $fc, $per, $ang, format_com($com);
+}
+print "\n";
+
+$line = <>;  #  H-bond param
+$line = <>;  #  blank
+
+#  Atom equivalences
+while (<>) {
+       chomp;
+       last if /^\s*$/;
+       $label = "";
+       foreach $i (unpack("A2 x2" x 20, $_ . $blanks)) {
+               if ($label eq "") {
+                       $label = $i;
+               } elsif ($i !~ /^\s*$/) {
+                       push @{$equil{$label}}, $i;
+               }
+       }
+}
+
+while (<>) {
+       chomp;
+       last if /^\s*$/;
+       ($label, $kind) = unpack("A4 x6 A2", $_. $blanks);
+       if ($label eq "END") {
+               last;
+       }
+       if ($kind ne "RE") {
+               print STDERR "The vdW parameter other than 'RE' format ($kind) is not supported yet.\n";
+               print STDERR "\$_='$_'\n\$label='$label'\n\$kind='$kind'\n";
+               exit(1);
+       }
+       while (<>) {
+               chomp;
+               last if /^\s*$/;
+               ($sym, $r, $edep) = unpack("x2 A2 x6 A10 A10", $_ . $blanks);
+               $sigma = 2 * $r * 0.890899;  #  sigma = 2R * (1/2)**(1/6)
+               printf "vdw %-2s %7.4f %7.4f %7.4f %7.4f %d %7.3f%s\n", $sym, $edep, $r, $edep, $r, 0, $molws{$sym}, $comments{$sym};
+               foreach $sym2 (@{$equil{$sym}}) {
+                       printf "vdw %-2s %7.4f %7.4f %7.4f %7.4f %d %7.3f%s\n", $sym2, $edep, $r, $edep, $r, 0, $molws{$sym2}, $comments{$sym2};
+               }
+       }
+}
+print "\n";
diff --git a/bitmaps/chart.xpm b/bitmaps/chart.xpm
new file mode 100755 (executable)
index 0000000..413c8f9
--- /dev/null
@@ -0,0 +1,45 @@
+/* XPM */
+static const char * chart_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 7 1",
+"  c black",
+". c blue",
+"X c #008000",
+"o c #800000",
+"O c red",
+"+ c #808080",
+"@ c gray100",
+/* pixels */
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
+"@@                          @@@@",
+"@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@@@",
+"@@ @@+@@+@@+@@++++@++++@@@@ +@@@",
+"@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@@@",
+"@@ @++@@+@@++@+@++@@+@@+@@@ +@@@",
+"@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@@@",
+"@@ @++@@++@@+++@@+++@@+++@@ +@@@",
+"@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@@@",
+"@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@@@",
+"@@ @                          @@",
+"@@ @ @@@@@@@@@@@@@@@@@@@@@@@@ +@",
+"@@ @ @@@@@@@@@@@@X@@@@@@@@..@ +@",
+"@@ @ @@@@@@@@@O@@X@@@@@@.@.@@ +@",
+"@@ @ @@@@@O@@@O@XX@@@@..@.@@@ +@",
+"@@ @ @O@@@O@@.O@@X@...@@@@OO@ +@",
+"@@ @ @O@@@O@@.O@XX@@@@O@@O@@@ +@",
+"@@   .O@@.O@@.O@@X@OOO@OO@@@@ +@",
+"@@@  .O@@.O@@.O@@X@@@@@@@@@@@ +@",
+"@@@@ .O@@.O@@.O@XXXXXXXXXXXX@ +@",
+"@@@@ @@@@@@@@@@@@X@@X@@X@@X@@ +@",
+"@@@@ oo@@oo@@oo@@@@@@@@@@@@@@ +@",
+"@@@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@",
+"@@@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@",
+"@@@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@",
+"@@@@    @@   @  @@@   @ @@ @@ +@",
+"@@@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@",
+"@@@@ @@@@@@@@@@@@@@@@@@@@@@@@ +@",
+"@@@@                          +@",
+"@@@@@++++++++++++++++++++++++++@",
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+};
diff --git a/bitmaps/copy.xpm b/bitmaps/copy.xpm
new file mode 100755 (executable)
index 0000000..b14b414
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static const char * copy_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 23 1",
+"+ c #769CDA",
+": c #DCE6F6",
+"X c #3365B7",
+"* c #FFFFFF",
+"o c #9AB6E4",
+"< c #EAF0FA",
+"# c #B1C7EB",
+". c #6992D7",
+"3 c #F7F9FD",
+", c #F0F5FC",
+"$ c #A8C0E8",
+"  c None",
+"- c #FDFEFF",
+"& c #C4D5F0",
+"1 c #E2EAF8",
+"O c #89A9DF",
+"= c #D2DFF4",
+"4 c #FAFCFE",
+"2 c #F5F8FD",
+"; c #DFE8F7",
+"% c #B8CCEC",
+"> c #E5EDF9",
+"@ c #648FD6",
+/* pixels */
+" .....XX        ",
+" .oO+@X#X       ",
+" .$oO+X##X      ",
+" .%$o........   ",
+" .&%$.*=&#o.-.  ",
+" .=&%.*;=&#.--. ",
+" .:=&.*>;=&.... ",
+" .>:=.*,>;=&#o. ",
+" .<1:.*2,>:=&#. ",
+" .2<1.*32,>:=&. ",
+" .32<.*432,>:=. ",
+" .32<.*-432,>:. ",
+" .....**-432,>. ",
+"     .***-432,. ",
+"     .......... "
+};
diff --git a/bitmaps/cut.xpm b/bitmaps/cut.xpm
new file mode 100755 (executable)
index 0000000..83ff3bb
--- /dev/null
@@ -0,0 +1,46 @@
+/* XPM */
+static const char * cut_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 25 1",
+"6 c #D8BDC0",
+": c #C3C3C4",
+"- c #FFFFFF",
+". c #6C6D70",
+"2 c #AD3A45",
+"o c #DBDBDB",
+"# c #939495",
+"< c #E42234",
+"& c #C3C5C8",
+"; c #C6CCD3",
+"% c #B7B7B8",
+"  c None",
+"* c #DFE0E2",
+"5 c #B69596",
+"3 c #9C2A35",
+"1 c #CFCFD0",
+", c #AB5C64",
+"+ c #D2D3D4",
+"$ c #BCBDBE",
+"@ c #C6C8CA",
+"> c #CDC0C1",
+"O c #826F72",
+"X c #979BA0",
+"4 c #9B8687",
+"= c #9FA0A0",
+/* pixels */
+"   .X     .o    ",
+"   O.+   @.     ",
+"    O.   ..     ",
+"    O#$ %.&     ",
+"     O.*..      ",
+"     #%#..      ",
+"     O=-..      ",
+"     #%#;.      ",
+"     OO:=O      ",
+"  >,,<, ,<,,1   ",
+" ><23<1 1<32<1  ",
+" ,2 4<   <5 2,  ",
+" <, ,2   2, ,<  ",
+" 23,<5   5<,32  ",
+" 6225     522>  "
+};
diff --git a/bitmaps/help.xpm b/bitmaps/help.xpm
new file mode 100755 (executable)
index 0000000..5af6f74
--- /dev/null
@@ -0,0 +1,71 @@
+/* XPM */
+static const char * help_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 50 1",
+"j c #4E7FD0",
+"8 c #EDF2FB",
+", c #7198D9",
+"e c #DCE6F6",
+"y c #FFFFFF",
+"5 c #95B2E3",
+"$ c #9AB6E4",
+"g c #EAF0FA",
+"1 c #2D59A3",
+"@ c #B1C7EB",
+"> c #6992D7",
+"a c #D9E4F5",
+"r c #356AC1",
+"6 c #9BB7E5",
+"= c #F7F9FD",
+"+ c #BED0EE",
+"z c #F0F5FC",
+"f c #ADC4E9",
+"# c #A8C0E8",
+"7 c #CBD9F1",
+"u c #366BC2",
+"  c None",
+"c c #FDFEFF",
+"w c #274D8D",
+"t c #C4D5F0",
+"% c #7CA0DC",
+"h c #E2EAF8",
+"p c #487BCE",
+"o c #4377CD",
+"4 c #2A549A",
+"< c #254A87",
+"O c #CCDAF2",
+"& c #89A9DF",
+"9 c #2B559B",
+"* c #D2DFF4",
+". c #3366BB",
+": c #2E5CA8",
+"x c #FAFCFE",
+"l c #F5F8FD",
+"2 c #799EDB",
+"d c #DFE8F7",
+"; c #A6BFE8",
+"3 c #638ED5",
+"- c #5282D0",
+"X c #2A5398",
+"0 c #B8CCEC",
+"s c #376EC9",
+"q c #2D5AA5",
+"i c #285092",
+"k c #8CACE0",
+/* pixels */
+"   .......Xo    ",
+"   .O+@#$%.&o   ",
+"   .*O+@#$.=&-  ",
+"  ;:::>#@#.==&: ",
+" ,<1234<>@....: ",
+"5<X67869<&0#$&. ",
+"q99wwe;9Xrt0#$. ",
+"<99X&yu99iOt0#. ",
+"<99wyp999<aOt0. ",
+"<99X<u99XsdaOt. ",
+",w9<y;99<fghaO. ",
+" j<X<XX<klzgha. ",
+"  5:::j7x=lzgh. ",
+"   .yyyccx=lzg. ",
+"   ............ "
+};
diff --git a/bitmaps/jump_to_end.xpm b/bitmaps/jump_to_end.xpm
new file mode 100755 (executable)
index 0000000..3b8c834
--- /dev/null
@@ -0,0 +1,22 @@
+/* XPM */
+static char *jump_to_end_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"9 9 7 1",
+/* colors */
+"A c None",
+"B c #DEDEDEDEDEDE",
+"C c #BDBDBDBDBDBD",
+"D c #B5B5B5B5B5B5",
+"E c #ADADADADADAD",
+"F c #A5A5A5A5A5A5",
+"G c #000000000000",
+/* pixels */
+"AAAAAAAAA",
+"AGEAAAGCA",
+"AGGFAAGDA",
+"AGGGFAGCA",
+"AGGGGBGCA",
+"AGGGEAGDA",
+"AGGDAAGCA",
+"AGCAAAGCA",
+"AAAAAAAAA"};
diff --git a/bitmaps/jump_to_start.xpm b/bitmaps/jump_to_start.xpm
new file mode 100755 (executable)
index 0000000..febe404
--- /dev/null
@@ -0,0 +1,23 @@
+/* XPM */
+static char *jump_to_start_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"9 9 8 1",
+/* colors */
+"A c None",
+"B c #DEDEDEDEDEDE",
+"C c #BDBDBDBDBDBD",
+"D c #B5B5B5B5B5B5",
+"E c #ADADADADADAD",
+"F c #A5A5A5A5A5A5",
+"G c #848484848484",
+"H c #000000000000",
+/* pixels */
+"AAAAAAAAA",
+"AHGAAAEHA",
+"AHGAAFHHA",
+"AHGAFHHHA",
+"AHGBHHHHA",
+"AHGAEHHHA",
+"AHGAADHHA",
+"AHGAAACHA",
+"AAAAAAAAA"};
diff --git a/bitmaps/molby_icon.icns b/bitmaps/molby_icon.icns
new file mode 100644 (file)
index 0000000..e81a493
Binary files /dev/null and b/bitmaps/molby_icon.icns differ
diff --git a/bitmaps/molby_icon.ico b/bitmaps/molby_icon.ico
new file mode 100644 (file)
index 0000000..87719b3
Binary files /dev/null and b/bitmaps/molby_icon.ico differ
diff --git a/bitmaps/molby_icon.tif b/bitmaps/molby_icon.tif
new file mode 100644 (file)
index 0000000..cfc21ea
Binary files /dev/null and b/bitmaps/molby_icon.tif differ
diff --git a/bitmaps/new.xpm b/bitmaps/new.xpm
new file mode 100755 (executable)
index 0000000..7402207
--- /dev/null
@@ -0,0 +1,52 @@
+/* XPM */
+static const char * new_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 31 1",
+". c #7198D9",
+"2 c #DCE6F6",
+", c #FFFFFF",
+"= c #9AB6E4",
+"6 c #EAF0FA",
+"w c #6992D7",
+"0 c #5886D2",
+"7 c #F7F9FD",
+"5 c #F0F5FC",
+"* c #A8C0E8",
+"  c None",
+"8 c #FDFEFF",
+"% c #C4D5F0",
+"3 c #E2EAF8",
+"+ c #4377CD",
+"O c #487BCE",
+"; c #6B94D7",
+"- c #89A9DF",
+": c #5584D1",
+"# c #3569BF",
+"@ c #3A70CA",
+"1 c #D2DFF4",
+"> c #3366BB",
+"$ c #2E5CA8",
+"9 c #FAFCFE",
+"4 c #F5F8FD",
+"q c #638ED5",
+"o c #5282D0",
+"& c #B8CCEC",
+"X c #376EC9",
+"< c #ACE95B",
+/* pixels */
+"   .XoO+@#$.    ",
+"   .%%&*=-O;:   ",
+"  >>>>%&*=O,=o  ",
+"  ><<>%%&*O,,=o ",
+">>><<>>>%&OOo+@ ",
+"><<<<<<>1%&*=-@ ",
+"><<<<<<>21%&*=@ ",
+">>><<>>>321%&*+ ",
+"  ><<>456321%&O ",
+"  >>>>7456321%o ",
+"   .,8974563210 ",
+"   .,,897456320 ",
+"   .,,,8974563q ",
+"   .,,,,897456w ",
+"   ............ "
+};
diff --git a/bitmaps/open.xpm b/bitmaps/open.xpm
new file mode 100755 (executable)
index 0000000..8604ce5
--- /dev/null
@@ -0,0 +1,57 @@
+/* XPM */
+static const char * open_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 36 1",
+", c #AAC1E8",
+"1 c #9AEA53",
+"q c #DCE6F6",
+". c #295193",
+"; c #A9D066",
+"r c #C6D6F0",
+"$ c #4A7CCE",
+"9 c #779DDB",
+"8 c #EAF0FA",
+"O c #6E96D8",
+"X c #214279",
+"e c #BED0EE",
+"6 c #85A7DF",
+"5 c #F0F5FC",
+"t c #ADC4E9",
+": c #3161B1",
+"  c None",
+"u c #274D8B",
+"& c #BFDC9B",
+"> c #9FB9E5",
+"y c #5584D1",
+"w c #3569BF",
+"% c #3A70CA",
+"+ c #305FAC",
+"3 c #5D89D3",
+"0 c #D2DFF4",
+"@ c #CAE2AA",
+"= c #B2D58C",
+"2 c #FAFCFE",
+"# c #638ED5",
+"* c #CEDCF2",
+"4 c #90AFE2",
+"< c #B3C8EB",
+"7 c #E5EDF9",
+"- c #A2BCE6",
+"o c #DFF0D0",
+/* pixels */
+"                ",
+"     ....       ",
+"XXXXX .oo.      ",
+"XOOOO+ .@o.     ",
+"XOOOO#$%.&*XXX  ",
+"XOOOOOOO.=*X-X  ",
+"XOXXXX...;*XXXX:",
+"XOX>,<.11111*X2:",
+"X3X4>,<.111*X5-:",
+"XX#64>,,.1*X78: ",
+"XXO964>,,.X0q7: ",
+"Xw3O964>,,er0t: ",
+"X%y3O964>,<er:  ",
+"uXXXXXXXXXXXX:  ",
+"                "
+};
diff --git a/bitmaps/paste.xpm b/bitmaps/paste.xpm
new file mode 100755 (executable)
index 0000000..028fa48
--- /dev/null
@@ -0,0 +1,46 @@
+/* XPM */
+static const char * paste_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 25 1",
+"< c #FEECE4",
+"> c #FEE3D7",
+"O c #FFFFFF",
+"o c #7B767D",
+"% c #F79586",
+"& c #CAE1F3",
+"@ c #F08B62",
+"# c #FCCBB8",
+"- c #FDD8C9",
+"4 c #FFF8F4",
+"5 c #FFF5F0",
+"  c None",
+"$ c #F8AA8F",
+", c #EFF6FC",
+"1 c #F7FBFD",
+"2 c #FAFCFE",
+"; c #DAEAF7",
+": c #E9F3FA",
+"6 c #FFFAF8",
+". c #3C78A6",
+"3 c #FFF1ED",
+"X c #9B8687",
+"+ c #FBBCA4",
+"* c #B6D5EE",
+"= c #F4F9FD",
+/* pixels */
+"   ......       ",
+" .XoOOOOoo.     ",
+".+XOOOOOOX@.    ",
+".+XXXXXXXX@.    ",
+".#++$$%@.....   ",
+".##++$$%.&*.=.  ",
+".-##++$$.;&.==. ",
+".--##++$.:;.... ",
+".>--##++.,:;&*. ",
+".<>--##+.1,:;&. ",
+".<<>--##.21,:;. ",
+".3<<>--#.O21=:. ",
+".45<<>--....... ",
+".6453<>----.    ",
+"............    "
+};
diff --git a/bitmaps/play_backward.xpm b/bitmaps/play_backward.xpm
new file mode 100755 (executable)
index 0000000..ad4db8a
--- /dev/null
@@ -0,0 +1,27 @@
+/* XPM */
+static char *play_backward_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"9 9 12 1",
+/* colors */
+"A c None",
+"B c #DEDEDEDEDEDE",
+"C c #CECECECECECE",
+"D c #BDBDBDBDBDBD",
+"E c #ADADADADADAD",
+"F c #9C9C9C9C9C9C",
+"G c #737373737373",
+"H c #6B6B6B6B6B6B",
+"I c #5A5A5A5A5A5A",
+"J c #424242424242",
+"K c #212121212121",
+"L c #000000000000",
+/* pixels */
+"AAAAAAAAA",
+"AAAAACILA",
+"AAAAFKLLA",
+"AADHLLLLA",
+"ABJLLLLLA",
+"AACGLLLLA",
+"AAAAEKLLA",
+"AAAAABILA",
+"AAAAAAAAA"};
diff --git a/bitmaps/play_forward.xpm b/bitmaps/play_forward.xpm
new file mode 100755 (executable)
index 0000000..0acea49
--- /dev/null
@@ -0,0 +1,27 @@
+/* XPM */
+static char *play_forward_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"9 9 12 1",
+/* colors */
+"A c None",
+"B c #DEDEDEDEDEDE",
+"C c #CECECECECECE",
+"D c #BDBDBDBDBDBD",
+"E c #ADADADADADAD",
+"F c #9C9C9C9C9C9C",
+"G c #6B6B6B6B6B6B",
+"H c #5A5A5A5A5A5A",
+"I c #525252525252",
+"J c #424242424242",
+"K c #212121212121",
+"L c #000000000000",
+/* pixels */
+"AAAAAAAAA",
+"ALICAAAAA",
+"ALLKFAAAA",
+"ALLLLGDAA",
+"ALLLLLJBA",
+"ALLLLGCAA",
+"ALLKEAAAA",
+"ALHBAAAAA",
+"AAAAAAAAA"};
diff --git a/bitmaps/preview.xpm b/bitmaps/preview.xpm
new file mode 100755 (executable)
index 0000000..0111238
--- /dev/null
@@ -0,0 +1,51 @@
+/* XPM */
+static const char * preview_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 30 1",
+"1 c Black",
+"= c #97C4E7",
+"< c #FFFFFF",
+", c #69A1CF",
+". c #5A89A6",
+"# c #3A749C",
+"+ c #538BB1",
+"> c #D1E5F5",
+"o c #6792AE",
+"% c #C3DDF1",
+"8 c #749BB5",
+"; c #619BC4",
+"X c #6B97B6",
+"@ c #4B82A8",
+"  c None",
+"O c #5F8FB0",
+"q c #84A5BB",
+": c #71A8D1",
+"- c #85BBE2",
+"9 c #EFF6FC",
+"7 c #F7FBFD",
+"6 c #FAFCFE",
+"2 c #7FA6C0",
+"5 c #C00000",
+"0 c #FDFDFE",
+"3 c #E2EFF8",
+"$ c #8EA9BC",
+"& c #B6D5EE",
+"* c #A5CCEA",
+"4 c #F4F9FD",
+/* pixels */
+"  ..XoO+@#$     ",
+"  .%%&*=-;:;    ",
+"  .>>%&*=,<=X   ",
+"  $%%%1112<<=X  ",
+"  $3-1<<<1oXO+  ",
+"  $314<<<<1=-+  ",
+"  $31<<<<<1-=+  ",
+"  $31<<<<<1&*O  ",
+"  $3-1<<<51>%X  ",
+"  $367111551>8  ",
+"  $3-----95518  ",
+"  $3<067466551  ",
+"  $3--------551 ",
+"  $3<<<0666<<55 ",
+"  $$$$$$$$$$q$  "
+};
diff --git a/bitmaps/print.xpm b/bitmaps/print.xpm
new file mode 100755 (executable)
index 0000000..6cc757a
--- /dev/null
@@ -0,0 +1,56 @@
+/* XPM */
+static const char * print_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 35 1",
+"y c #EDF2FB",
+"9 c #4E7FD0",
+"> c #B9CDED",
+"# c #3365B7",
+"r c #FFFFFF",
+"8 c #2C58A0",
+"X c #779DDB",
+"7 c #7FA2DD",
+"t c #3263B4",
+"q c #6E96D8",
+"= c #6992D7",
+"* c #D9E4F5",
+"0 c #356AC1",
+"& c #CBD9F1",
+"; c #97B4E3",
+"e c #3161B1",
+"  c None",
+"1 c #B5C9EB",
+", c #7CA0DC",
+"4 c #487BCE",
+"- c #4377CD",
+". c #5584D1",
+"6 c #3A70CA",
+"2 c #305FAC",
+"< c #5685D2",
+"w c #4075CC",
+"5 c #638ED5",
+"3 c #3467BC",
+"% c #2F5DA9",
+"o c #ECF1FA",
+": c #D6E1F4",
+"@ c #376EC9",
+"$ c #2D5AA5",
+"+ c #A2BCE6",
+"O c #CFDDF3",
+/* pixels */
+"   .XXXXXXXX    ",
+"   .oooooooX    ",
+"   .OOOOOOOX    ",
+"   .+++++++X    ",
+" @@#$%%%%%$%@@  ",
+"#&*=-=;:>,<#*&# ",
+".11.2345.63#11. ",
+"4;;;;;;;;;;;;;4 ",
+"677588888889776 ",
+"0qq60wwwwwweqq0 ",
+"3553rrrrrrr$553 ",
+"3<<tyrrrrrr$<<3 ",
+"XXX%rrrrrrr$XXX ",
+"   3rrrrrrr$    ",
+"   #XXXXXXX@    "
+};
diff --git a/bitmaps/rotate_bond.xpm b/bitmaps/rotate_bond.xpm
new file mode 100755 (executable)
index 0000000..b130a37
--- /dev/null
@@ -0,0 +1,54 @@
+/* XPM */
+static char *rotate_bond_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"18 18 30 2",
+/* colors */
+"AA c None",
+"BA c #F7F7F7F7F7F7",
+"CA c #EFEFEFEFEFEF",
+"DA c #E7E7E7E7E7E7",
+"EA c #DEDEDEDEDEDE",
+"FA c #D6D6D6D6D6D6",
+"GA c #CECECECECECE",
+"HA c #C6C6C6C6C6C6",
+"IA c #B5B5B5B5B5B5",
+"JA c #ADADADADADAD",
+"KA c #A5A5A5A5A5A5",
+"LA c #9C9C9C9C9C9C",
+"MA c #949494949494",
+"NA c #8C8C8C8C8C8C",
+"OA c #848484848484",
+"PA c #7B7B7B7B7B7B",
+"AB c #737373737373",
+"BB c #6B6B6B6B6B6B",
+"CB c #636363636363",
+"DB c #5A5A5A5A5A5A",
+"EB c #525252525252",
+"FB c #4A4A4A4A4A4A",
+"GB c #424242424242",
+"HB c #393939393939",
+"IB c #313131313131",
+"JB c #292929292929",
+"KB c #212121212121",
+"LB c #181818181818",
+"MB c #080808080808",
+"NB c #000000000000",
+/* pixels */
+"AAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAA",
+"AAAAAAAAAAAAAAAAAAAACAOAAAAACAAAAAAA",
+"AAAAAAAAAAAAAAAAAADAIBIBGBLBKBHBBAAA",
+"AAAAAAAAAAAAAAAAAAEBNBNBBBAAAAFBJAAA",
+"AAAAAAAAAAAAAABAJACBOAOANAHAAAHBGAAA",
+"AAAAAAAAAACAEBNBMBNBJBGAAAAAFAKBAAAA",
+"AAAAAAAAHALBPAEAAADALAKBMABAIBMAAAAA",
+"AAAAAAAAHBLAAAAAAAAAAAFADBOAIBBAAAAA",
+"AAAAAADAKBAAAAAAAAAAAAAAKAJBEAAAAAAA",
+"AAAABAGAHBAAAAAAAAAAAAMAKBFAAAAAAAAA",
+"AACAKBCALBDAAAAAAAAAMALBGAGAAAAAAAAA",
+"AAGBPAAAPAHBEAAABABBJBKAEBBAAAAAAAAA",
+"IALBCAAAAAABGBBADBGBDAFBMBHAAAAAAAAA",
+"BBOAAAAAAAAAJAKBCBBAGABAJAKBJAAAAAAA",
+"FBIAAAAAHAGBKBIAAAAAAAAAAAHAMBLAAAAA",
+"NAKBEBJBIBMACAAAAAAAAAAAAAAAFAJBOABA",
+"AAFAIAEAAAAAAAAAAAAAAAAAAAAAAADAJBPA",
+"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAEA"};
diff --git a/bitmaps/rotate_x.xpm b/bitmaps/rotate_x.xpm
new file mode 100755 (executable)
index 0000000..a290bb0
--- /dev/null
@@ -0,0 +1,53 @@
+/* XPM */
+static char *rotate_x_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"18 18 29 2",
+/* colors */
+"AA c None",
+"BA c #F7F7F7F7F7F7",
+"CA c #EFEFEFEFEFEF",
+"DA c #DEDEDEDEDEDE",
+"EA c #D6D6D6D6D6D6",
+"FA c #CECECECECECE",
+"GA c #C6C6C6C6C6C6",
+"HA c #BDBDBDBDBDBD",
+"IA c #B5B5B5B5B5B5",
+"JA c #ADADADADADAD",
+"KA c #A5A5A5A5A5A5",
+"LA c #949494949494",
+"MA c #8C8C8C8C8C8C",
+"NA c #848484848484",
+"OA c #7B7B7B7B7B7B",
+"PA c #737373737373",
+"AB c #6B6B6B6B6B6B",
+"BB c #5A5A5A5A5A5A",
+"CB c #525252525252",
+"DB c #4A4A4A4A4A4A",
+"EB c #424242424242",
+"FB c #393939393939",
+"GB c #313131313131",
+"HB c #292929292929",
+"IB c #212121212121",
+"JB c #181818181818",
+"KB c #101010101010",
+"LB c #080808080808",
+"MB c #000000000000",
+/* pixels */
+"AAAAAAAAAAAAAAAAHADBLAAAAAAAAAAAAAAA",
+"AAAAAAAAAAAAAACAIBIAIBJAAAAAAAAAAAAA",
+"AAAAAAAAAAAAAAPAOAAAGAEBAAAAAAAAAAAA",
+"AAAAAAAAAAAAAAKBEAAAAAHBCAAAAAAAAAAA",
+"AAAAAAAAAAAAFAJBAAAAAAABKAAAAAAAAAAA",
+"AAAAAAAAAAAAJACBAAAAAAMAPAAAAAAAAAAA",
+"AAAAAAAAAAAADAGAAAAAAAJACBAAAAAAAAAA",
+"AAAAAAAAAAAAAAAAAAAAAAHAFBAAAAAAAAAA",
+"LAGBGBGBGBGBGBGBGBCBAAFAHBMAGBGBGBIA",
+"AAAAAAAAAABACAAAAAAAAAGAHBAAAAAAAAAA",
+"AAAAAAAAAAAAOAFAAAAAAAHAEBAAAAAAAAAA",
+"AAAAAAAAAAAADBFBBAAAAAIADBAAAAAAAAAA",
+"AAAAAAAAAAAAIBMBKAAAAALAABAAAAAAAAAA",
+"AAAAAAAAAACAIBLBBBBAAAABKAAAAAAAAAAA",
+"AAAAAAAAAAHADAKBFACAAAGBCABAAAAAAAAA",
+"AAAAAAAAAABAAAABNAAAGAFBAAAAAAAAAAAA",
+"AAAAAAAAAAAAAACAIBHAIBJAAAAAAAAAAAAA",
+"AAAAAAAAAAAAAAAAIAEBLAAAAAAAAAAAAAAA"};
diff --git a/bitmaps/rotate_y.xpm b/bitmaps/rotate_y.xpm
new file mode 100755 (executable)
index 0000000..3cf16ca
--- /dev/null
@@ -0,0 +1,50 @@
+/* XPM */
+static char *rotate_y_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"18 18 26 1",
+/* colors */
+"A c None",
+"B c #EFEFEFEFEFEF",
+"C c #E7E7E7E7E7E7",
+"D c #DEDEDEDEDEDE",
+"E c #D6D6D6D6D6D6",
+"F c #CECECECECECE",
+"G c #C6C6C6C6C6C6",
+"H c #BDBDBDBDBDBD",
+"I c #B5B5B5B5B5B5",
+"J c #ADADADADADAD",
+"K c #9C9C9C9C9C9C",
+"L c #949494949494",
+"M c #737373737373",
+"N c #6B6B6B6B6B6B",
+"O c #636363636363",
+"P c #5A5A5A5A5A5A",
+"Q c #525252525252",
+"R c #4A4A4A4A4A4A",
+"S c #424242424242",
+"T c #393939393939",
+"U c #313131313131",
+"V c #292929292929",
+"W c #212121212121",
+"X c #181818181818",
+"Y c #080808080808",
+"Z c #000000000000",
+/* pixels */
+"AAAAAAAAOAAAAAAAAA",
+"AAAAAAAAVAAAAAAAAA",
+"AAAAAAAAVAAAAAAAAA",
+"AAAAAAAAUAAAAAAAAA",
+"AAAAAAAAVAAAAAAAAA",
+"AAAAAAAAVAAAACHCAA",
+"AAABHLEAVBMTXYIAAA",
+"ACPYTMFAUABOZYYQDA",
+"IWLCAAAAVAAAGOHLWJ",
+"RIAAAAAAPAAAAABAHR",
+"LWGAAAAAAAAAAAAGWL",
+"AJSTNKIGEFGHKMUSJA",
+"AAACKNRUWVUSOKCAAA",
+"AAAAAAAALAAAAAAAAA",
+"AAAAAAAAVAAAAAAAAA",
+"AAAAAAAAVAAAAAAAAA",
+"AAAAAAAAUAAAAAAAAA",
+"AAAAAAAALAAAAAAAAA"};
diff --git a/bitmaps/sample.xpm b/bitmaps/sample.xpm
new file mode 100755 (executable)
index 0000000..47b6038
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static const char * sample_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 6 1",
+"  c black",
+". c navy",
+"X c red",
+"o c yellow",
+"O c gray100",
+"+ c None",
+/* pixels */
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++              ++++++++++",
+"++++++++ ............ ++++++++++",
+"++++++++ ............ ++++++++++",
+"++++++++ .OO......... ++++++++++",
+"++++++++ .OO......... ++++++++++",
+"++++++++ .OO......... ++++++++++",
+"++++++++ .OO......              ",
+"++++++++ .OO...... oooooooooooo ",
+"         .OO...... oooooooooooo ",
+" XXXXXXX .OO...... oOOooooooooo ",
+" XXXXXXX .OO...... oOOooooooooo ",
+" XOOXXXX ......... oOOooooooooo ",
+" XOOXXXX ......... oOOooooooooo ",
+" XOOXXXX           oOOooooooooo ",
+" XOOXXXXXXXXX ++++ oOOooooooooo ",
+" XOOXXXXXXXXX ++++ oOOooooooooo ",
+" XOOXXXXXXXXX ++++ oOOooooooooo ",
+" XOOXXXXXXXXX ++++ oooooooooooo ",
+" XOOXXXXXXXXX ++++ oooooooooooo ",
+" XXXXXXXXXXXX ++++              ",
+" XXXXXXXXXXXX ++++++++++++++++++",
+"              ++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++"
+};
diff --git a/bitmaps/save.xpm b/bitmaps/save.xpm
new file mode 100755 (executable)
index 0000000..e4e6570
--- /dev/null
@@ -0,0 +1,42 @@
+/* XPM */
+static const char * save_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"16 15 21 1",
+": c #AAC1E8",
+"1 c #B9CDED",
+"O c #FFFFFF",
+"  c #2C58A0",
+"* c #B0C6EA",
+"; c #2D59A3",
+"X c #1C3866",
+"= c #C3D4EF",
+"2 c #CBD9F1",
+"- c #DAE5F6",
+"# c #97B4E3",
+". c None",
+"$ c #274D8B",
+"& c #9FB9E5",
+"@ c #5584D1",
+"% c #82A5DE",
+"o c #3A70CA",
+"< c #A5BEE7",
+", c #D2DFF4",
+"+ c #3467BC",
+"> c #C0D1EE",
+/* pixels */
+"               .",
+" XoOOOOOOOOO+X .",
+" @oO#######O+@ .",
+" @oOOOOOOOOO+@ .",
+" @oO#######O+@ .",
+" @oOOOOOOOOO+@ .",
+" @@+++++++++@@ .",
+" @@@@@@@@@@@@@ .",
+" @@@$$$$$$$$@@ .",
+" @@$%%%&*=-O$@ .",
+" @@$%X;;*=-O$@ .",
+" @@$%X;;:>,O$@ .",
+" @@$%X;;<12O$@ .",
+" @@$<<2OOOOO$@ .",
+".             .."
+};
diff --git a/clapack-3.1.1.1-mingw.patch b/clapack-3.1.1.1-mingw.patch
new file mode 100755 (executable)
index 0000000..de0004c
--- /dev/null
@@ -0,0 +1,255 @@
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/F2CLIBS/libf2c/Makefile CLAPACK-3.1.1.1-mingw/F2CLIBS/libf2c/Makefile
+--- CLAPACK-3.1.1.1/F2CLIBS/libf2c/Makefile    Thu Sep  4 04:29:25 2008
++++ CLAPACK-3.1.1.1-mingw/F2CLIBS/libf2c/Makefile      Fri Nov  7 01:58:05 2008
+@@ -181,8 +181,8 @@
+ xwsne.o:      fmt.h
+ arith.h: arithchk.c
+-      $(CC) $(CFLAGS) -DNO_FPINIT arithchk.c -lm ||\
+-       $(CC) -DNO_LONG_LONG $(CFLAGS) -DNO_FPINIT arithchk.c -lm
++      $(CC) $(CFLAGS) -DNO_FPINIT arithchk.c -lm -o a.out ||\
++       $(CC) -DNO_LONG_LONG $(CFLAGS) -DNO_FPINIT arithchk.c -lm -o a.out
+       ./a.out >arith.h
+       rm -f a.out arithchk.o
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/F2CLIBS/libf2c/dtime_.c CLAPACK-3.1.1.1-mingw/F2CLIBS/libf2c/dtime_.c
+--- CLAPACK-3.1.1.1/F2CLIBS/libf2c/dtime_.c    Thu Sep  4 04:29:25 2008
++++ CLAPACK-3.1.1.1-mingw/F2CLIBS/libf2c/dtime_.c      Fri Nov  7 23:01:21 2008
+@@ -1,6 +1,6 @@
+ #include "time.h"
+-#ifdef MSDOS
++#if defined(MSDOS) || defined(MINGW)
+ #undef USE_CLOCK
+ #define USE_CLOCK
+ #endif
+@@ -14,9 +14,10 @@
+ #define _INCLUDE_XOPEN_SOURCE /* for HP-UX */
+ #include "sys/types.h"
+ #include "sys/times.h"
++#endif
++
+ #ifdef __cplusplus
+ extern "C" {
+-#endif
+ #endif
+ #undef Hz
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/F2CLIBS/libf2c/etime_.c CLAPACK-3.1.1.1-mingw/F2CLIBS/libf2c/etime_.c
+--- CLAPACK-3.1.1.1/F2CLIBS/libf2c/etime_.c    Thu Sep  4 04:29:25 2008
++++ CLAPACK-3.1.1.1-mingw/F2CLIBS/libf2c/etime_.c      Fri Nov  7 02:07:20 2008
+@@ -1,6 +1,6 @@
+ #include "time.h"
+-#ifdef MSDOS
++#if defined(MSDOS) || defined(MINGW)
+ #undef USE_CLOCK
+ #define USE_CLOCK
+ #endif
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/INCLUDE/clapack.h CLAPACK-3.1.1.1-mingw/INCLUDE/clapack.h
+--- CLAPACK-3.1.1.1/INCLUDE/clapack.h  Thu Sep  4 04:29:25 2008
++++ CLAPACK-3.1.1.1-mingw/INCLUDE/clapack.h    Fri Nov  7 23:41:46 2008
+@@ -1,6 +1,10 @@
+ #ifndef __CLAPACK_H
+ #define __CLAPACK_H
+- 
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
+ /* Subroutine */ int cbdsqr_(char *uplo, integer *n, integer *ncvt, integer *
+       nru, integer *ncc, real *d__, real *e, complex *vt, integer *ldvt, 
+       complex *u, integer *ldu, complex *c__, integer *ldc, real *rwork, 
+@@ -1569,7 +1573,7 @@
+       doublereal *wi, doublereal *z__, integer *ldz, doublereal *work, 
+       integer *lwork, integer *info);
+-/* Subroutine */ int dlabad_(doublereal *small, doublereal *large);
++/* Subroutine */ int dlabad_(doublereal *small__, doublereal *large__);
+ /* Subroutine */ int dlabrd_(integer *m, integer *n, integer *nb, doublereal *
+       a, integer *lda, doublereal *d__, doublereal *e, doublereal *tauq, 
+@@ -3027,7 +3031,7 @@
+        integer *ihi, real *h__, integer *ldh, real *wr, real *wi, real *z__,   
+       integer *ldz, real *work, integer *lwork, integer *info);
+-/* Subroutine */ int slabad_(real *small, real *large);
++/* Subroutine */ int slabad_(real *small__, real *large__);
+ /* Subroutine */ int slabrd_(integer *m, integer *n, integer *nb, real *a, 
+       integer *lda, real *d__, real *e, real *tauq, real *taup, real *x, 
+@@ -5429,6 +5433,10 @@
+ /* Subroutine */ int zupmtr_(char *side, char *uplo, char *trans, integer *m, 
+       integer *n, doublecomplex *ap, doublecomplex *tau, doublecomplex *c__,
+        integer *ldc, doublecomplex *work, integer *info);
++
++#ifdef __cplusplus
++}
++#endif
+ #endif /* __CLAPACK_H */
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/INCLUDE/f2c.h CLAPACK-3.1.1.1-mingw/INCLUDE/f2c.h
+--- CLAPACK-3.1.1.1/INCLUDE/f2c.h      Thu Sep  4 04:29:25 2008
++++ CLAPACK-3.1.1.1-mingw/INCLUDE/f2c.h        Fri Nov  7 23:33:56 2008
+@@ -7,6 +7,10 @@
+ #ifndef F2C_INCLUDE
+ #define F2C_INCLUDE
++#ifdef __cplusplus
++extern "C" {
++#endif
++
+ typedef long int integer;
+ typedef unsigned long int uinteger;
+ typedef char *address;
+@@ -220,4 +224,9 @@
+ #undef unix
+ #undef vax
+ #endif
++
++#ifdef __cplusplus
++}
++#endif
++
+ #endif
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/INSTALL/dsecnd.c CLAPACK-3.1.1.1-mingw/INSTALL/dsecnd.c
+--- CLAPACK-3.1.1.1/INSTALL/dsecnd.c   Thu Sep  4 04:29:25 2008
++++ CLAPACK-3.1.1.1-mingw/INSTALL/dsecnd.c     Sun Nov  9 15:22:19 2008
+@@ -1,7 +1,15 @@
+ #include "blaswrap.h"
+ #include "f2c.h"
++
++#if defined(MSDOS) || defined(MINGW)
++#undef USE_CLOCK
++#define USE_CLOCK
++#endif
++
++#ifndef USE_CLOCK
+ #include <sys/times.h>
+ #include <sys/types.h>
++#endif
+ #include <time.h>
+ #ifndef CLK_TCK
+@@ -10,10 +18,17 @@
+ doublereal dsecnd_()
+ {
++#ifdef USE_CLOCK
++    
++  double t = clock();
++  return (doublereal)t / CLK_TCK;
++    
++#else
+   struct tms rusage;
+   times(&rusage);
+   return (doublereal)(rusage.tms_utime) / CLK_TCK;
+-
++#endif
++    
+ } /* dsecnd_ */
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/INSTALL/second.c CLAPACK-3.1.1.1-mingw/INSTALL/second.c
+--- CLAPACK-3.1.1.1/INSTALL/second.c   Thu Sep  4 04:29:25 2008
++++ CLAPACK-3.1.1.1-mingw/INSTALL/second.c     Sun Nov  9 15:22:01 2008
+@@ -1,6 +1,14 @@
+ #include "f2c.h"
++
++#if defined(MSDOS) || defined(MINGW)
++#undef USE_CLOCK
++#define USE_CLOCK
++#endif
++
++#ifndef USE_CLOCK
+ #include <sys/times.h>
+ #include <sys/types.h>
++#endif
+ #include <time.h>
+ #ifndef CLK_TCK
+@@ -9,10 +17,17 @@
+ doublereal second_()
+ {
++#ifdef USE_CLOCK
++    
++  double t = clock();
++  return (doublereal)t / CLK_TCK;
++    
++#else
+   struct tms rusage;
+   times(&rusage);
+   return (doublereal)(rusage.tms_utime) / CLK_TCK;
++#endif
+ } /* second_ */
+diff --exclude=.* -Naur CLAPACK-3.1.1.1/make.inc CLAPACK-3.1.1.1-mingw/make.inc
+--- CLAPACK-3.1.1.1/make.inc   Thu Jan  1 09:00:00 1970
++++ CLAPACK-3.1.1.1-mingw/make.inc     Sun Nov  9 14:31:50 2008
+@@ -0,0 +1,61 @@
++####################################################################
++#  CLAPACK make include file.                                      #
++#  CLAPACK, Version 3.1.1.1                                        #
++#  October 2008                                                    #
++####################################################################
++#
++#  The machine (platform) identifier to append to the library names
++#
++PLAT = MinGW
++#  
++#  Modify the CC and CFLAGS definitions to refer to the
++#  compiler and desired compiler options for your machine.  NOOPT
++#  refers to the compiler options desired when NO OPTIMIZATION is
++#  selected.  Define LOADER and LOADOPTS to refer to the loader and
++#  desired load options for your machine.
++#
++#######################################################
++# This is used to compile C libary
++CC        = gcc
++# if no wrapping of the blas library is needed, uncomment next line
++#CC        = gcc -DNO_BLAS_WRAP
++CFLAGS    = -DMINGW -O2
++LOADER    = gcc
++LOADOPTS  = -Wl,--stack=33554432
++NOOPT     = -O0 
++DRVCFLAGS = $(CFLAGS)
++F2CCFLAGS = $(CFLAGS)
++#######################################################################
++#
++#  The archiver and the flag(s) to use when building archive (library)
++#  If you system has no ranlib, set RANLIB = echo.
++#
++ARCH     = ar
++ARCHFLAGS= cr
++RANLIB   = ranlib
++#
++#  The location of the libraries to which you will link.  (The 
++#  machine-specific, optimized BLAS library should be used whenever
++#  possible.)
++#
++
++# Using reference BLAS
++BLASLIB      = ../../blas$(PLAT).a
++# Using Vectib
++#BLASLIB = -framework vecLib
++# Using Fortran BLAS interface of ATLAS without wrapper (CLAPACK has to be compiled with -DNO_BLAS_WRAP)
++#BLASLIB = -lf77blas -latlas
++# Using Fortran BLAS interface of ATLAS with wrapper
++#BLASLIB = ../../libfblaswr.a -lf77blas -latlas
++# Using CBLAS interface of ATLAS with wrapper
++#BLASLIB = ../../libcblaswr.a -lcblas -latlas
++# Using Goto without wrapper (CLAPACK has to be compiled with -DNO_BLAS_WRAP)
++#BLASLIB = -lgoto -lpthread
++# Using Goto with wrapper
++#BLASLIB = ../../libfblaswr.a -lgoto -lpthread 
++LAPACKLIB    = lapack$(PLAT).a
++F2CLIB       = ../../F2CLIBS/libf2c.a
++TMGLIB       = tmglib$(PLAT).a
++EIGSRCLIB    = eigsrc$(PLAT).a
++LINSRCLIB    = linsrc$(PLAT).a
++
diff --git a/memo.txt b/memo.txt
new file mode 100755 (executable)
index 0000000..88e1815
--- /dev/null
+++ b/memo.txt
@@ -0,0 +1,315 @@
+2008.10.24.
+ wxWidgets のサンプル docvwmdi を元に改造していこうとしている。
+TODO: ドキュメントは1種類でよい。ただし、1枚のウィンドウに複数のペインを持って、その中でグラフィック表示やリスト表示をする。
+-> wxView を1種類にすること。
+
+DrawingDocument -> MoleculeDocument, DrawingView -> MoleculeView にする。
+TextDocument, TextView? は廃止。
+
+Carbon emacs: マウスドラッグで自動的にコピーされるのを止める
+(setq mouse-drag-copy-region nil)
+マックのショートカットを使う
+(mac-key-mode 1)
+
+docview.cpp 中の MyFrame::CreateCanvas で MyCanvas を作っている。MyCanvas の定義を書き換えて、wxGLCanvas のサブクラスを作れば良い。
+
+MainView は Cocoa 版では MyWindowController の windowDidLoad で作られ、dealloc で破棄される。一方、Molecule は MyDocument の init で空のものが作られるが、ファイルから読み込むと新しい Molecule が作られ、[self setMolecule: mol] で更新される。
+
+UTF8 -> wxString:  wxString string(ascii_string, wxConvUTF8);
+wxString -> UTF8:  strncpy(buf, (const char*)string.mb_str(wxConvUTF8), sizeof buf - 1); buf[sizeof buf - 1] = 0;
+かなりめんどくさいがしょうがない。NSString だって似たようなもんだよな。
+
+wxDocument でファイルオープン、保存のオーバーロード。ストリームを使いたくない場合は、
+ protected:
+  virtual bool DoSaveDocument(const wxString& file);
+  virtual bool DoOpenDocument(const wxString& file);
+をオーバーライドすればよい。ドキュメントに書いてないけど、隠し機能なの?
+
+wxGLCanvas がうまく動かなくて苦労している。作ったあと、サイズを改めて指定してやらないと中身が表示されないらしい。これは wxWidgets のサンプルでも #if __X__ として入れてあるコード。MacOSX でもこれが必要みたい。
+    int x, y;
+    frame->GetSize(&x, &y);
+    frame->SetSize(wxDefaultCoord, wxDefaultCoord, x, y);
+
+Undo/redo はどう実装するか。これは相当面倒くさい。Cocoa の場合、UndoManager は任意のタイミングで任意の "undo用メッセージ" を受け取って溜めておき、アイドル状態になったらそれをまとめて Undo スタックか redo スタック(後者は自分が Undo メッセージを送っている最中)に積む。wxWidgets は、自分ですべてのメッセージを1つの wxCommand にまとめ、それを wxCommandProcessor に送ることで Do/undo を実現する。大きな違いは、最初に "Do" がなされた地点では Do の情報はメッセージ中には入っていないこと。
+1つのアイデア:MainView の中で、undo 用メッセージを自前で溜めておく。アイドル状態になったら、それをまとめて自分の undo スタックに積み、wxCommandProcessor にダミーの Submit を送る。wxCommandProcessor から undo が指示されたら、undo フラグを立てて undo 用メッセージを最初と逆順に送る。このとき送られて来る "undo 用メッセージ" は redo のための物なので、同じように溜めてアイドル状態になったら redo スタックに積む。(またはスタックは1つにして、「現在の undo アクション」を表すポインタを前後させてもよい。)
+重要:wxCommandProcessor の ClearCommands() (と、たぶん MarkAsSaved())はオーバーライドして、MainView_clearCommands() を呼び出す必要がある。
+もう1つのアイデア:最初の Submit の前は、上と同様に undo 用メッセージを溜めておく。アイドル状態になったらこのメッセージを新しい wxCommand にまとめて Submit する。Do が呼ばれるが、これはそのままスルーする。wxCommand::Undo が呼ばれたら、上と同様に undo 用メッセージを溜めておくが、アイドル状態になったら溜めたメッセージを同じ wxCommand の Redo バッファに格納する。これなら wxCommandProcessor は素のままでよさそう。また、undo 用メッセージを溜めておくのは MainView よりも MyDocument (wxDocument の派生クラス) の方が適当か。
+
+「アイドル状態になったら」の実装方法。
+wxEvent って notification 目的で使えるのかなあ。→できそうだけど、けっこう複雑なので要注意。有用なドキュメント: http://wiki.wxwidgets.org/Custom_Events
+
+ 関係ありそうなところを引用。
+// This goes somewhere in a cpp file, before you use it (unless you've declared it elsewhere)
+const wxEventType MyExcitingEvent = wxNewEventType(); // You get to choose the name yourself
+// In the posting method:
+...
+wxCommandEvent MyEvent( MyExcitingEvent ); // Still keeping it simple, don't give a specific event ID
+wxPostEvent(this, MyEvent); // This posts to ourselves: it'll be caught and sent to a different method
+wxPostEvent(pBar, MyEvent); // Posts to a different class, Bar, where pBar points to an instance of Bar 
+The event table would be:
+BEGIN_EVENT_TABLE(MyFoo, wxSomething)
+  EVT_COMMAND(wxID_ANY, MyExcitingEvent, MyFoo::OnMyEvent)
+  ...
+END_EVENT_TABLE()
+
+The handler's signature is still void MyFoo::OnMyEvent(wxCommandEvent&)
+
+Most of the time all of this will be happening within the same cpp file. However if you need to use your eventtype elsewhere, you'll need to declare it there as an extern:
+
+extern const wxEventType MyExcitingEvent;
+
+The individual ID case
+
+What if you want to send several messages? Well, you could create several eventtypes: they're only ints after all, there isn't a world shortage. But it's a bit easier to have just one eventtype, and use different IDs for different destinations. It's easier to read if you do that with an enum.
+// The values don't matter, as they're only used with this eventtype
+enum { Foo_DoFirstThing = 1, Foo_DoSecondThing, Foo_DoThirdThing };
+// In the posting method:
+...
+// The first two events are posted to ourselves
+wxCommandEvent MyEvent1( MyExcitingEvent, Foo_DoFirstThing ); wxPostEvent(this, MyEvent1);
+wxCommandEvent MyEvent2( MyExcitingEvent, Foo_DoSecondThing, ); wxPostEvent(this, MyEvent2);
+// Just for variety, let's send the third one elsewhere:
+wxCommandEvent MyEvent3( MyExcitingEvent, Foo_DoThirdThing ); wxPostEvent(pBar, MyEvent3);
+The MyFoo event table would be:
+BEGIN_EVENT_TABLE(MyFoo, wxSomething)
+  EVT_COMMAND(Foo_DoFirstThing, MyExcitingEvent, MyFoo::OnMyFirstEvent)
+  EVT_COMMAND(Foo_DoSecondThing,, MyExcitingEvent, MyFoo::OnMySecondEvent)
+  ...
+END_EVENT_TABLE()
+
+Alternatively you could catch everything as before with wxID_ANY, and discriminate between them in the handler with event.GetId().
+(引用おわり)
+
+ しかし、notification の代わりにはならないかな。各 wxEvtHandler ごとに wxPostEvent しないといけないみたいだし。→もしかして wxEvtHandler::Connect() 使ったらいけるか?
+    m_child->Connect(wxID_ANY, wxEVT_LEAVE_WINDOW,
+                     wxMouseEventHandler(MyFrame::OnMouseLeave),
+                     NULL,  // unused extra data parameter
+                     this); // this indicates the object to connect to
+  notification 用の wxEvent は MyDocument 宛に送る。その notification を聞きたいオブジェクトは、 myDocument->Connect(ID, TYPE, HANDLER, NULL, this) でハンドラを登録し、ハンドラ中では event.Skip() で処理を中断しないようにする。→うまくいった!
+  
+
+何となく DECLARE_DYNAMIC_CLASS(class) / IMPLEMENT_DYNAMIC_CLASS(class, superclass) を使うとエラーが出てしまった。最初は、IMPLEMENT_DYNAMIC_CLASS を忘れていて、"vtable not defined" という不思議なエラーが出ていたし、IMPLEMENT_DYNAMIC_CLASS を書くと今度は "no matching member function" というようなエラーが出た。正確なエラーメッセージは、
+1つめ:
+Undefined symbols:
+  "vtable for ConsoleFrame", referenced from:
+      __ZTV12ConsoleFrame$non_lazy_ptr in ConsoleFrame.o
+2つめ:
+no matching function for call to 'ConsoleFrame::ConsoleFrame()
+note: candidates are: ConsoleFrame::ConsoleFrame(wxMDIParentFrame*, const wxString&, const wxPoint&, const wxSize&, long int)
+note:                 ConsoleFrame::ConsoleFrame(const ConsoleFrame&)
+両方とも削除するとコンパイルは通った。難しいねぇ。
+
+デバッグ用に wxString を表示しようとしてハマる。cout << string はダメ(ポインタの値が表示される)。cout << string.c_str() でもダメ。printf("%s", (const char*)string.mb_str(wxConvUTF8)); でいいみたい。
+
+wxTextCtrl は EVT_TEXT_ENTER(id, func) でリターンキーの押下を検出して横取り(TextCtrl に "\n" が入力される前に処理)できるが、wxRichTextCtrl は横取りできず、"\n" が入力されてからイベントハンドラが呼ばれてしまう。wxRichTextCtrl のサブクラスを作って EVT_CHAR イベントをつかまえれば処理できそうだが、ほかの手はないものか? -> これも Connect() を使って解決できた。
+textCtrl->Connect(-1, wxEVT_CHAR, wxKeyEventHandler(ConsoleFrame::OnChar), NULL, this);
+void
+ConsoleFrame::OnChar(wxKeyEvent &event)
+{
+       int code = event.GetKeyCode();
+       printf("OnChar: %d\n", code);
+       if (code == WXK_RETURN || code == WXK_NUMPAD_ENTER)
+               OnEnterPressed(event);
+       else
+               event.Skip();
+}
+でちゃんとリターンキーだけを横取りできる。このイベントメカニズムはなかなか便利だな。
+
+テキストを色分けしたいとき、(極度に重い)wxRichTextCtrl を使わなくとも wxTextCtrl で十分。(バックグラウンドに色をつけることはできないみたい)。wxMSW では wxTE_RICH2 あたりが必要かもしれない。
+
+色指定で、wxColour(red, green, blue) の引数は unsigned char なので注意。うっかり Cocoa の感覚で wxColour redColor(1, 0, 0) なんてやって「おかしい、色がつかない」と1時間ぐらい悩んでいた。
+
+Ruby で実行するスクリプトと結果表示を色分けするととたんに見やすくなった。よしよし。
+
+MSW でもビルドできるようになった。Mac で走っていたコードが MSW では BAD ADDRESS で落ちたりする。
+
+画面レイアウトを作っていくため、Life のソースを読んでいる。アイコンは xpm (ASCII Bitmap) で作っておくのが無難みたい。GraphicConverter でも作れる。知らなかったよ。
+
+wxBoxSizer の使い方。ウィンドウ内のある領域に、部品を「縦に上から」または「横に左から」順に並べていく、という考え方でレイアウトする。
+左から並べる時は、こんな風に使う。
+wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);  //  Sizer を作る
+wxButton *button1 = new wxButton(frame, ...);      //  部品1を作る。部品1の親は sizer ではなく親ウィンドウ。
+sizer->Add(button1, 1, wxALL | wxEXPAND, 3);       //  "1" は proportion で、水平方向にリサイズしない部品は0、リサイズする部品は正の値を入れる。同じサイザー内に複数の部品があるとき、この値が大きいものほど伸び縮みが大きくなる。"3" は部品の枠とサイザーの枠の間にとる余白のピクセル数を指定する。そして、どちらの方向に余白を置くかを wxALL, wxTOP, wxBOTTOM, wxRIGHT, wxLEFT で指定する。
+wxEXPAND は、この部品を垂直方向に一杯までリサイズする指定。縦方向は固定サイズなら、これは指定しない。
+sizer の高さを固定するには、どれかの部品に wxEXPAND を無指定とし、その部品の縦サイズを固定値で指定すればよい。
+部品を Add() で加えたあと、sizer->Layout() を呼ぶと、指示したようにレイアウトされる。複数のサイザーが入れ子になっている時は、一番外側のサイザーについて Layout() を呼べば十分。
+
+マウスドラッグを実装していて気がついたのだが、ドラッグ中に画面からマウスが外に出ると、そこでボタンを離してもウィンドウにマウスアップのイベントが来ないみたい。ドラッグ途中の状態で画面が残ってしまう。これはなんとかしないとまずいな。あんまりきれいな解決法がない。とりあえず、MouseEnter イベントをつかまえて、ドラッグ中でかつボタンが離されていれば MouseUp の処理をすることにした。
+-> wxWindow のメソッド CaptureMouse/ReleaseMouse を使えばよい。マウスダウンのハンドラで CaptureMouse() を呼べば、すべてのマウスイベントがこのウィンドウで処理される。マウスアップの際に ReleaseMouse() を呼べばよい。CaptureMouse() を使うウィンドウは wxMouseCaptureLostevent に反応しないといけない。
+
+C++ ってほんまわけわからんなあ。
+MyGLCanvas *MoleculeView::CreateCanvas(wxMDIChildFrame *parent, const wxSize &size);
+のつもりで、
+MyGLCanvas *MoleculeView::CreateCanvas(wxMDIChildFrame *parent, wxSize &size);
+と宣言・定義してしまい、次のように呼び出すと、
+CreateCanvas(frame, wxSize(100,100));
+こんなエラーが出る。
+error: no matching function for call to 'MoleculeView::CreateCanvas(wxMDIChildFrame*&, wxSize)'
+note: candidates are: MyGLCanvas* MoleculeView::CreateCanvas(wxMDIChildFrame*, wxSize&)
+このエラーメッセージから「あ、const が抜けてる」って思わなくちゃいけないの? 難しいねぇ。
+
+キーイベントの扱いが Mac と MSW で違う? ウィンドウ内でフォーカスが当たっているコントロールがないとき、Mac では キーイベントを wxView で受け取れるのだが、MSW では受け取れない。どこ行っちゃってるんだろ?
+MyApp で EVT_KEY_DOWN をつかまえることはできた。しかし、これは逆に、フォーカスが当たっているコントロールがあるときもここを通るので、Skip() すべきかどうかの判断が難しい。
+キーボードフォーカスを誰が持っているかは wxWindow::FindFocus() で調べられることがわかった。しかし、たとえば MyGLCanvas がフォーカスを受け取れるようにするにはどうすればいいんだ? wxWANTS_CHARS ではだめだった。→なんだ、簡単じゃないか。MyGLCanvas::OnMouseEvent() の中で this->SetFocus() すればいいんだ。そうすると、上のボタンが押された時とか、MySlider が操作された時も(ユーザーの意識はたぶん MyGLCanvas に向いているから)同じ処理をすればよいな。
+
+MySlider の実装。マウスイベントをつかまえたあと、コントロールごとの独自イベントを投げるやり方。
+
+const wxEventType MySliderEvent = wxNewEventType();
+void
+MySlider::OnMouseEvent(wxMouseEvent &event)
+{
+       ...
+    wxCommandEvent event(MySliderEvent, GetId());
+    event.SetEventObject(this);
+       GetEventHandler()->ProcessEvent(event);
+}
+
+wxCommandEvent は "propagate" されるので、wxView のイベントテーブルを書いておけばつかまえることができる。
+
+BEGIN_EVENT_TABLE(MoleculeView, wxView)
+       ...
+       EVT_COMMAND(wxID_ANY, MySliderEvent, MoleculeView::OnSliderAction)
+END_EVENT_TABLE()
+
+wxID_ANY の代わりに、MySlider の ID を指定すれば、スライダーごとに別のハンドラを呼び出すこともできる。
+
+command-. の処理だけど、もしかして ::wxGetKeyState() と ::wxGetMouseState() の組み合わせでいける? イベントを取りにいかなくていいんだったら楽なのだが。
+(ところで、このキーは Windows では何を使うのがいいんだろう?)
+
+Ruby のリンクについて。Windows では、One-click Ruby Installer を使ってもらうことにする。そうなると、Mac OS 10.4 でも One-click Ruby Installer OSX を使ってもらうことにすれば、例のインタラプトの問題が解決するのでは??
+One-click Ruby Installer OSX を試しに使ってみたが、/usr/local が散らかるのであまりよろしくない。それよりは、Ruby.framework を自前で作る方がまだよろしい。
+
+Ruby.framework の作り方。
+$ export ARCH_FLAG='-isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -arch i386 -arch ppc'
+$ CFLAGS="$ARCH_FLAG" ./configure --prefix=/Library/Frameworks/Ruby.framework/Versions/1.8/usr --enable-shared --disable-pthread
+$ make
+$ make install
+$ sh ruby-createframework.sh
+
+ruby-createframework.sh は ~/Development/UNIX/ に置いてある。本当はもうちょっとスマートにしたいところだが。
+
+開発マシンでは、こうやってできた Ruby.framework を /Library/Frameworks に置いておく。SDK の Library/Frameworks は実は /Library/Frameworks へのシンボリックリンクなので、SDK を使っていてもサーチパスに /Library/Frameworks を入れておけばちゃんとリンクできる。
+
+実行マシンでも、同じ Ruby.framework を /Library/Frameworks に置いておけば、これをリンクしてくれる。なお、素の Leopard では Ruby.framework は /System/Library/Frameworks にあるのだが、リンク先が /Library/Frameworks であっても、そこに見つからなければ /System/Library/Frameworks を探しにいってくれるので大丈夫。
+
+実際、Molby のビルドで、ruby.h のサーチパスを /Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/ruby/1.8/universal-darwin8.0 (長っ!)、-lruby のサーチパスを /Library/Frameworks/Ruby.framework/Versions/Current/usr/lib にしてビルドしてみる。コンソールで puts RUBY_VERSION とすると、
+/Library/Frameworks に Ruby.framework あり:1.8.7
+/Library/Frameworks に Ruby.framework なし:1.8.6
+となる。
+上のサーチパスだが、実はビルドすると /Library/.../ruby/1.8/i686-darwin9.5.1/ というパスになっており、RUBY_PLATFORM も i686-darwin9.5.1 を返す。これを universal-darwin8.0 に変える方法をいろいろ考えたのだがどうしても思いつかなかった。Apple はどうやってるんだろう?
+
+2008.12.7.
+ テーブルビューを使おうと思い、wxGrid を入れてみたら、リンクエラーが出て wxGrid::wxGrid がないと言われた。サンプルプログラムの grid は通るので、wxMac のビルドが悪い訳ではない。grid の make が実行するコマンドを参考にして、リンカフラグに -lwx_macu_adv-2.8 を加えて解決。
+
+2009.7.21.
+ 結局テーブルビューは wxListCtrl を使っているが、一番左のカラムしか編集できない。なんじゃそりゃ、と思ったが、要は Finder 系のリストビューを実現するためのクラスなので仕方がない。wxDataViewCtrl というのが開発中のようだが、まだ枯れている感じじゃないので、あまり使いたくない。結局、wxGenericListCtrl をベースにして、セル編集を独自にコーディングしてしのいでいる。まだあちこち変な動作をしているが。
+ wxMSW では、普通にコンパイルすると wxGenericListCtrl が使えないので困ってしまった。generic/listctrl.cpp をコピーしてきて、wx/generic/listctrl.h を include するように書き換えると一応動作する。wxWidgets の本体に修正を加えると GPL で配布しないといけなくなるから困るんだよな。なんとかならないものかな。→これはどうやら勘違いみたい。まだLGPLの解釈が今ひとつわかってない。
+
+2009.10.16.
+ 大高さんに渡すために突貫工事でいろいろ実装して、なんとか単体で MD ができるようになった。現時点で明らかに残っている問題点:
+  MD 中に GUI が生きている。分子をいろいろ回したりして見られるのはいいのだが、編集までできてしまうのはまずい。また、MD を二重に走らせることができてしまうかもしれない。Ruby インタプリタはリエントラントでないので、排他処理をしないと危ない。そもそも、MD run を Ruby で実装しているのがまずいと思う。Ruby から md や minimize ができるのはいいのだが、メニューコマンドから走らせる md/minimize は C レベルで実装した方がいい。(できればマルチスレッド化したいし。)
+  パラメータを見ることができない。MDを初期化したあとだけでいいから、テーブルでパラメータを見たい。
+  MyListCtrl は MoleculeView 決めうちじゃなくて、独立したクラスにしておきたい。RubyDialog でも使えるといい。
+  まあ今はいろいろと忙しいので、11月ぐらいになってからかな。
+
+2009.10.20.
+ といいつつプログラムを修正していたりする。
+  Ruby スクリプトを走らせるところを少し整理中。いくつか必要なインターフェイスがある。
+・MolAction を経由するもの。引数を(C レベルの値で)与えることができ、戻り値を(C レベルで)受け取ることもできる。引数・戻り値の型は MolAction のメッセージ中に指定する。
+Int ival; Double dval; char *sval;
+n = MolActionCreateAndPerform(mol, SCRIPT_ACTION_WITH_INTERRUPT("id;s"), "script", ival, dval, &sval);
+"script" のところに来るのは、メソッド名または Proc を与える文字列。メソッド名は Molecule[mol] のメソッドとして、Proc 文字列は Molecule[mol].instance_eval で Proc オブジェクトを作った上で call(arguments) を呼ぶ。戻り値は rb_protect の status。
+・引数を与えず、任意のスクリプトを実行するもの。スクリプトの戻り値は RubyValue として返され、エラーステータスは *status に返される。mol != NULL なら Molecule[mol].instance_eval で、mol == NULL ならトップレベルで実行される。
+val = Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, int *status);
+
+インタラプトの有無を厳密に区別する必要もないかなあ。前は Ruby レベルでスレッドを使ったりしてトリッキーなコーディングをしていたが、1.8.7 をスタティックリンクすることにしたのでこのへんは関係なくなったしな。Ruby_SetInterrupt(val), Ruby_GetInterrupt() で十分対応できる。
+
+2009.10.21.
+ MD を C++ レベルで実行するようにした。排他処理は未実装。むしろ、もうマルチスレッド化していいんじゃないかと思う。現状では、2つ同時に MD を走らせると GUI がおかしくなり、終了時にフリーズ。理由は不明だが、gdb で見ると、pthread でデッドロックを起こしていた。
+ マルチスレッド化するには、Molecule に対するアクセスについて排他処理すればいい。サブスレッドは分子1つにつき1本に限り、走らせるのは MD と分子のアニメーションに限定すれば、それほど難しくはないんじゃないか。
+ メインスレッドから Molecule にアクセスするのは、メニューコマンドの実行・GUI 操作の時(Ruby スクリプトの実行を含む)。
+ MDArena の中に Molecule のローカルコピーを作るようにした。MM/MD の計算はローカルコピーに対して行われる。必要に応じて mb_copyback で明示的にコピーする。
+  arena->xmol は残しておく必要ないんじゃないか?(要検討)
+ 一応動いているみたい。2つ分子を開いてそれぞれ MD を走らせると、むちゃくちゃ遅いけどちゃんと2つ同時に走る。なかなかのもんじゃないか。
+
+2009.10.30.
+ wxThread を使ったら MSW でビルドできなくなってしまった。当たり前で、wxMSW を --disable-thread でビルドしていたから。mingwm10.dll という DLL が必要になるので避けていたのだが、インストーラを使うようにしたのだから別に DLL 必要でもいいだろう。(参考:http://0xcc.net/pub/uu-2004-08/)
+インストーラ Inno Setup の解説。http://www.sutosoft.com/mt/devdiary/archives/000056.html(ちょっと古い)
+ 今テストしたら、MD のログが Molby.exe のディレクトリに作成されてしまう、というバグを発見。これはなんとかしないと。→なんとかできた、と思う。ファイルが確定していない (unnamed の) ドキュメントについてはホームディレクトリに、それ以外はドキュメントと同じディレクトリに作る。ホームディレクトリだが、Windows では c:\Documents and Settings\username\ だが、このディレクトリはアクセスしづらいので、この上の My Documents の方がいい。Mac OS X はホームディレクトリでよい。
+ Windows で、DLL 依存関係の調べ方:objdump -p XXXX.exe | grep dll
+
+2009.11.6.
+ ドキュメントを書き始めた。効率よく書くためにいろいろと工夫。たくさんの html ファイルに分けて相互リンクしないといけないのだが、一部自動化してみたい。
+・それぞれのファイルに収めるべき内容を <div file="...."></div> の中に入れる。
+・リンクを明示するため、次の疑似命令を使う。
+  <link rel="Up" href="...">   [Up] リンク先。
+  <link rel="Prev" href="..."> [Prev] リンク先。[Next] は [Prev] の逆として自動生成。
+  <link id="#toc('....')"> '....' の一つ下のファイルのリストを生成。'....' が省略された場合は、すべてのファイル(入れ子になった下層のファイルもすべて含める)のリストを生成。
+  <link id="#header"> パンくずリストを生成。
+  <link id="#navigation"> ナビゲーション ([Up: ....][Prev: ....]等) を生成。
+  <div .... lang="en|ja"> と指定されている場合は、その節は英語版(日本語版)のみ生成する。
+  Ruby の REXML パッケージを使って、なんとか実装できた。Documents/ 以下がソースファイルで、cd Documents; ruby makedoc.rb とすると、../doc/ 以下に en, ja の2つのディレクトリができて、そこに html ファイルが生成される。
+  
+2009.11.11.
+wxStandardPaths::GetUserConfigDir() で初期設定のディレクトリが取得できるのだが、wxMac では最後に "/" がつくのに wxMSW では "\" がつかない。これは wxMac のバグじゃないかな。
+
+2009.11.14.
+ MM/MD の時の missing parameter の扱いを整理しよう。
+・md_prepare() では、すべての必要なパラメータを arena->par に登録する。mol 由来のものは src フィールド=0、未定義のものは -1。→実装した。
+・md_prepare() の終了時に、mol->par を再構築する。mol 由来のものはそのまま残し、その他のものはいったん全部削除して arena->par からコピーし直す。後ろのものの方が優先されるので、グローバル→mol定義→未定義の順に並べる。→実装した。
+・Parameter テーブルでは、未定義パラメータは目立つように背景を赤か何かで表示する。→実装した。
+・Parameter テーブルの編集をサポート。グローバルパラメータは編集不可。mol由来、未定義のもののみ。→実装した
+行を選択して、パラメータごと削除する機能もつける。複製や新しいパラメータの作成も可能で、作成したものは未定義扱いになる。【未実装】
+・グローバルパラメータをファイルから読み込むときは、ファイル名を gGlobalParInfo.files/gGlobalParInfo.count に登録。ただし、最初の読み込み(gBuiltinParameters == NULL のとき)は特別扱いで、このファイル名は登録されない(default.par のはず)。また、このファイルは include 文のみを含んでいるはずで、その読み込みが終わった時点で gGlobalParInfo.builtinCount を設定。グローバル変数 gGlobalParInfo は Parameter.h で宣言する。→実装した
+・gGlobalParInfo.files の要素は、struct { char *name; char *dir; int src; }。src フィールドは、ソース名に対応するコメントインデックス。ソース名はファイル名から ".par" を除いたもので、同じものがあってはいけない(同じファイル名のものを読み込もうとするとエラーが出る)。→実装した
+・ドキュメント保存のときは、グローバルパラメータはソース名をコメント欄に埋め込んで保存する。読み込むときは、ソースが指定されているときはその時点でのグローバルパラメータを検索して、同じものがあればそれをソースとして置き換える。なければコメント欄にもとのソースをそのまま残す。→実装した。
+・ParEnumerable の定義が変。ローカルパラメータを見ているつもりでも、グローバルパラメータにフォールバックするようになっている。便利さを追求したつもりらしいが、わかりにくいよ。【要改善】
+・Parameter 編集の undo はどうであるべきか。グローバルパラメータの編集を禁止したので、普通のドキュメントレベルの undo でよい。
+
+2009.11.16.
+ コピーしたあと、同じドキュメント内でペーストすると、コピーしたのと全く同じ位置に原子がペーストされるので何が起きたかわからない。少しずらすべき。→実装した
+
+2009.11.20.
+ antechamber とのインターフェイスを作成中。OSX では一応動くようになった。
+
+2009.11.22.
+ GAMESS とのインターフェイス。GamessQ がなかなか使いやすそうなので、インターフェイス自体はこれに任せて、inp ファイルのエキスポートと log/dat ファイルのインポートに徹する。
+  3-21G  ->  GBASIS=N21 NGAUSS=3 ; H-Xe
+  6-31G  ->  GBASIS=N31 NGAUSS=6 ; H-Ar (Ga-Kr: BC)
+  6-31G* = 6-31G(d) ->  GBASIS=N31 NGAUSS=6 NDFUNC=1
+  6-31G** = 6-31G(d,p) -> GBASIS=N31 NGAUSS=6 NDFUNC=1 NPFUNC=1
+  6-311G -> GBASIS=N311 NGAUSS=6 ; H-Ne (Na-Ar: MC)
+  6-311G(d,p) -> GBASIS=N311 NGAUSS=6 NDFUNC=1 NPFUNC=1
+
+  LanL2DZ を使うときは、ECP が必要。対称性を持つと面倒だが、当面対称性は無視して常に "C1 0" でいく。$BASIS セクションは書かずに、全部の原子にそれぞれ基底関数を記述する。(こうすれば extbas が要らない。ただし見通しは悪くなる)混合基底を使うときも同様。
+
+  gamess.rb に Molecule.read_gamess_basis_sets(fname) というメソッドを作成。グローバル変数 $gamess_basis に基底関数の記述、$gamess_ecp に ECP の記述を保存する。$gamess_{ecp,basis} はハッシュで、その要素は(原子番号を添字とする)配列。値は複数行にわたる文字列で、最後に "\n" がついている。
+  GAMESS で 6-31G(d), 6-31G(d,p), 6-311G(d,p) の計算を行い、$DATA セクションの記述を出力しておいた。(d),(d,p) がついているものは、分極関数の係数が必要なので、ファイルにしておくのが便利。分極関数を使わないなら "N31 6" とかだけでよいので、特にファイルにしておく必要はない。STO-3G は "STO 3", PM3 は "PM3 0"。
+ 一応動くようになった。「ZnだけLanL2DZ, 他は6-31G(d)」とかがダイアログ一発でできるのはたいへん便利。といっても、Gaussian では昔から普通に簡単にできたのだが。
+
+2009.12.13.
+  公開に向けていろいろと整理していて、MinGW 上で CLAPACK を再コンパイルしたら、テストの時に SIGSEGV で落ちる。gdb で見てみると、__do_global_dtors() 中の probe() という関数でクラッシュしている。dtors って C++? C++ なんてどこにも使ってないぜ? おかしいなと思って Google で探してみたら、こんな記事に遭遇。
+
+Re: [uClinux-dev] do_global_dtors problem
+From: David McCullough (davidm@snapgear.com)
+Date: Sun Nov 04 2001 - 17:37:38 EST
+(中略)
+> Has anyone else seen this behavior?  Isn't do_global_dtors
+> a c++ function?  Any enlightenment on the constructor/
+> destructor functions would be appreciated.
+
+It is unlikely that the problem is with do_global_dtors.   What happens
+is in the later version of gcc (ie 2.95) main calls __main which processes
+do_global_[cd]tors lists.  So you get them with 'C' as well.  Actually,  I
+think that atexit is processed this way,  but I may be wrong.
+
+The fact that the program has run to completion and then crashed would make
+me look at the stack size and see if it could be overflowing into the top
+of the BSS/data sections.
+
+  ひぇー、スタックサイズか。そんな落とし穴があったんか。ちなみに、MinGW でのデフォルトスタックサイズは 2MB らしい。FORTRAN プログラムを f2c でコンバートしたら、2MB を越える大量の自動変数を使うことになった、というのはあり得る話だ(確認してないけど)。そこで、make.inc に
+LOADOPTS = -Wl,--stack=33554432
+という1行を加えてみたら、見事にテスト通過。いやはや、これだから FORTRAN は。(というか f2c を使うというのがそもそも筋悪という話も。)
+
diff --git a/msw-build/Makefile b/msw-build/Makefile
new file mode 100755 (executable)
index 0000000..6b92398
--- /dev/null
@@ -0,0 +1,2 @@
+TARGET_PLATFORM = MSW
+include ../Makefile
diff --git a/msw-build/molby.iss b/msw-build/molby.iss
new file mode 100755 (executable)
index 0000000..0774a95
--- /dev/null
@@ -0,0 +1,16 @@
+[Setup]
+AppName = Molby
+AppVerName = Molby (v0.5.0 build 20100121)
+DefaultDirName = {pf}\Molby
+DefaultGroupName = Molby
+UninstallDisplayIcon = {app}\Molby.exe
+OutputBaseFileName = SetupMolby
+
+[Files]
+Source: "build\Molby\Molby.exe"; DestDir: {app}
+Source: "build\Molby\mingwm10.dll"; DestDir: {app}
+Source: "build\Molby\Scripts\*"; DestDir: {app}\Scripts
+Source: "build\Molby\Scripts\lib\*"; DestDir: {app}\Scripts\lib
+
+[Icons]
+Name: "{group}\Molby"; Filename: "{app}\Molby.exe"
diff --git a/msw-build/molby.rc b/msw-build/molby.rc
new file mode 100644 (file)
index 0000000..61d8583
--- /dev/null
@@ -0,0 +1,2 @@
+molbyicon ICON "../bitmaps/molby_icon.ico"
+#include "wx/msw/wx.rc"
diff --git a/osx-build/Info.plist b/osx-build/Info.plist
new file mode 100644 (file)
index 0000000..f4495f2
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.yourcompany.Molby</string>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>Molby</string>
+       <key>CFBundleName</key>
+       <string>Molby</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>0.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>0.0.0</string>
+       <key>CFBundleGetInfoString</key>
+       <string>Molby version 0.0.0, (c) 2008</string>
+       <key>CFBundleLongVersionString</key>
+       <string>0.0.0, (c) 2008</string>
+       <key>NSHumanReadableCopyright</key>
+       <string>Copyright 2008</string>
+       <key>LSRequiresCarbon</key>
+       <true/>
+       <key>CSResourcesFileMapped</key>
+       <true/>
+</dict>
+</plist>
diff --git a/osx-build/Makefile b/osx-build/Makefile
new file mode 100755 (executable)
index 0000000..7055396
--- /dev/null
@@ -0,0 +1,2 @@
+TARGET_PLATFORM = MAC
+include ../Makefile
diff --git a/ruby-1.8.7-p160-tn.patch b/ruby-1.8.7-p160-tn.patch
new file mode 100644 (file)
index 0000000..ac866d0
--- /dev/null
@@ -0,0 +1,33 @@
+--- ruby-1.8.7-p160/eval.c     2009-03-23 18:28:31.000000000 +0900
++++ ruby-1.8.7-p160-tn/eval.c  2009-09-22 18:53:47.000000000 +0900
+@@ -3145,6 +3145,9 @@
+               goto while_out;
+           do {
+             while_redo:
++                  EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
++                                  ruby_frame->last_func,
++                                  ruby_frame->last_class); /* 20090829 TN */
+               rb_eval(self, node->nd_body);
+             while_next:
+               ;
+@@ -3180,6 +3183,9 @@
+               goto until_out;
+           do {
+             until_redo:
++                  EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
++                                  ruby_frame->last_func,
++                                  ruby_frame->last_class); /* 20090829 TN */
+               rb_eval(self, node->nd_body);
+             until_next:
+               ;
+--- ruby-1.8.7-p160/gc.c       2009-03-27 19:25:23.000000000 +0900
++++ ruby-1.8.7-p160-tn/gc.c    2009-09-23 21:16:13.000000000 +0900
+@@ -1215,7 +1215,7 @@
+       if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
+     }
+     malloc_increase = 0;
+-    if (freed < free_min) {
++    if (freed < free_min || freelist == 0) {
+       add_heap();
+     }
+     during_gc = 0;
diff --git a/update_version.rb b/update_version.rb
new file mode 100644 (file)
index 0000000..7234e38
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/ruby
+
+require 'kconv'
+
+#  Get the version string
+#  version = "X.X.X"
+#  date = "yyyymmdd"
+version = nil
+date = nil
+eval IO.read("Version")
+ver = version
+t = Time.now
+year = t.year
+month = t.month
+day = t.day
+d = sprintf("%04d%02d%02d", year, month, day)
+exit 0 if date == d
+File.open("Version", "w") { |fp|
+  fp.print "version = \"#{version}\"\n"
+  fp.print "date = \"#{d}\"\n"
+}
+build = "build " + d
+verstr = "v#{ver} #{build}"
+yrange = (year > 2008 ? "2008-#{year}" : "2008")
+
+def modify_file(name, &block)
+  ary = IO.readlines(name)
+  modified = false
+  ary.each_with_index { |s, i|
+    s = block.call(s)
+    if s
+      ary[i] = s
+      modified = true
+    end
+  }
+  if modified
+    File.rename(name, name + "~")
+    open(name, "wb") { |fp|
+      ary.each { |s| fp.write(s) }
+    }
+  end
+end
+
+#  Modify Info.plist
+nm = "xcode-build/Info.plist"
+version = false
+modify_file(nm) { |s|
+  if version
+    version = false
+    "\t<string>#{verstr}</string>\n"
+  else
+    version = (s =~ /\bCFBundleVersion\b/)
+    nil
+  end
+}
+
+#  Modify InfoPlist.strings
+Dir["xcode-build/*.lproj/InfoPlist.strings"].each { |nm|
+  modify_file(nm) { |s|
+    s = s.kconv(Kconv::UTF8, Kconv::UTF16)
+    if s =~ /Copyright/ && s.sub!(/Toshi Nagata, [-0-9]+/, "Toshi Nagata, #{yrange}")
+      s = s.kconv(Kconv::UTF16, Kconv::UTF8)
+    else
+      nil
+    end
+  }
+}
+
+#  Modify Molby.iss
+modify_file("msw-build/molby.iss") { |s|
+  if s =~ /AppVerName/ && s.sub!(/\(.*\)*/, "(#{verstr})")
+    s
+  else
+    nil
+  end
+}
+
+#  Modify MyVersion.c
+modify_file("wxSources/MyVersion.c") { |s|
+  if s =~ /Version/ && s.sub!(/\".*\"/, "\"#{verstr}\"")
+    s
+  else
+    nil
+  end
+}
+
+#  Modify doc_source.html
+modify_file("Documents/src/doc_source.html") { |s|
+  if s =~ /Version/ && s.sub!(/[Vv][-.0-9 A-Za-z_]*/, "Version #{ver} #{build}")
+    s
+  else
+    nil
+  end
+}
diff --git a/wxSources/ConsoleFrame.cpp b/wxSources/ConsoleFrame.cpp
new file mode 100755 (executable)
index 0000000..205d45e
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ *  ConsoleFrame.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/27.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "ConsoleFrame.h"
+
+#include "wx/menu.h"
+#include "wx/regex.h"
+#include "wx/colour.h"
+
+#include "MyApp.h"
+#include "../MolLib/Ruby_bind/Molby_extern.h"
+
+BEGIN_EVENT_TABLE(ConsoleFrame, wxMDIChildFrame)
+       EVT_UPDATE_UI(wxID_CLOSE, ConsoleFrame::OnUpdateUI)
+       EVT_CLOSE(ConsoleFrame::OnCloseWindow)
+       EVT_MENU(wxID_CLOSE, ConsoleFrame::OnClose)
+       EVT_MENU(wxID_CUT, ConsoleFrame::OnCut)
+       EVT_MENU(wxID_COPY, ConsoleFrame::OnCopy)
+       EVT_MENU(wxID_PASTE, ConsoleFrame::OnPaste)
+       EVT_MENU(wxID_CLEAR, ConsoleFrame::OnClear)
+END_EVENT_TABLE()
+
+ConsoleFrame::ConsoleFrame(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, long type):
+       wxMDIChildFrame(parent, wxID_ANY, title, pos, size, type)
+{
+}
+
+ConsoleFrame::~ConsoleFrame()
+{
+       wxGetApp().DocManager()->FileHistoryRemoveMenu(file_history_menu);
+}
+
+void
+ConsoleFrame::OnCreate()
+{
+       //  Make a text view
+#warning "TODO: Set the sizer for the text control properly"
+       int width, height;
+       GetClientSize(&width, &height);
+       textCtrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxPoint(0, 0), wxSize(width, height), wxTE_MULTILINE | wxTE_RICH);
+       
+       //  Connect "OnKeyDown" event handler
+       textCtrl->Connect(-1, wxEVT_KEY_DOWN, wxKeyEventHandler(ConsoleFrame::OnKeyDown), NULL, this);
+       
+       wxMenuBar *menu_bar = wxGetApp().CreateMenuBar(2, &file_history_menu, &edit_menu);
+       
+       //// Associate the menu bar with the frame
+       SetMenuBar(menu_bar);
+}
+
+ConsoleFrame *
+ConsoleFrame::CreateConsoleFrame(wxMDIParentFrame *parent)
+{
+#ifdef __WXMSW__
+       wxPoint origin(0, 0);
+       wxSize size(640, 200);
+#else
+       wxPoint origin(10, 24);
+       wxSize size(640, 200);
+#endif
+       ConsoleFrame *frame = new ConsoleFrame(parent, _T("Console"), origin, size, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
+       
+       frame->OnCreate();
+       return frame;
+}
+
+bool
+GetLineIncludingPosition(wxTextCtrl *ctrl, int pos, int *start, int *end)
+{
+       int pos1, pos2, posend;
+       wxChar posChar;
+       
+       if (ctrl == NULL)
+               return false;
+       if (pos == 0)
+               pos1 = 0;
+       else {
+               pos1 = pos;
+               while (pos1 > 0) {
+                       posChar = ctrl->GetRange(pos1 - 1, pos1).GetChar(0);
+                       if (posChar == '\n')
+                               break;
+                       pos1--;
+               }
+       }
+       posend = ctrl->GetLastPosition();
+       posChar = ctrl->GetRange(posend - 1, posend).GetChar(0);
+       if (pos >= posend)
+               pos2 = pos;
+       else {
+               pos2 = pos;
+               while (pos2 < posend) {
+                       posChar = ctrl->GetRange(pos2, pos2 + 1).GetChar(0);
+                       if (posChar == '\n') {
+                               pos2++;
+                               break;
+                       }
+                       pos2++;
+               }
+       }
+       if (start != NULL)
+               *start = pos1;
+       if (end != NULL)
+               *end = pos2;
+       return true;
+}
+
+void
+ConsoleFrame::OnCloseWindow(wxCloseEvent &event)
+{
+       //  Do not delete this window; it may be reopened later
+       this->Hide();
+}
+
+void
+ConsoleFrame::OnClose(wxCommandEvent &event)
+{
+       this->Close();
+}
+
+void
+ConsoleFrame::OnCut(wxCommandEvent &event)
+{
+       textCtrl->Cut();
+}
+
+void
+ConsoleFrame::OnCopy(wxCommandEvent &event)
+{
+       textCtrl->Copy();
+}
+
+void
+ConsoleFrame::OnPaste(wxCommandEvent &event)
+{
+       textCtrl->Paste();
+}
+
+void
+ConsoleFrame::OnClear(wxCommandEvent &event)
+{
+       textCtrl->Clear();
+}
+
+void
+ConsoleFrame::OnUpdateUI(wxUpdateUIEvent& event)
+{
+       //  Why this is not automatically done??
+       int uid = event.GetId();
+       if (uid == wxID_CLOSE)
+               event.Enable(true);
+}
+
+void
+ConsoleFrame::OnEnterPressed(wxKeyEvent& event)
+{
+       if (::wxGetKeyState(WXK_ALT)) {
+               textCtrl->WriteText(wxT("\n> "));
+               return;
+       }
+       
+       int start, pos, end, veryend, lastpos;
+       wxChar startChar;
+
+       //  Get the block of script to be executed
+       pos = textCtrl->GetInsertionPoint();
+       lastpos = textCtrl->GetLastPosition();
+       veryend = -1;
+       while (pos >= 0) {
+               if (!GetLineIncludingPosition(textCtrl, pos, &start, &end) || start == end) {
+                       start = end = veryend = pos;
+                       break;
+               }
+               if (veryend < 0)
+                       veryend = end;
+               startChar = textCtrl->GetRange(start, start + 1).GetChar(0);
+               if (startChar == '%') {
+                       start++;
+                       break;
+               } else if (startChar == '>') {
+                       pos = start - 1;
+                       continue;
+               } else {
+                       start = end = veryend = pos;
+                       break;
+               }
+       }
+       while (start < end && veryend < lastpos) {
+               pos = veryend + 1;
+               if (!GetLineIncludingPosition(textCtrl, pos, &pos, &end) || pos == end) {
+                       break;
+               }
+               startChar = textCtrl->GetRange(pos, pos + 1).GetChar(0);
+               if (startChar != '>')
+                       break;
+               veryend = end;
+       }
+
+       wxString string = textCtrl->GetRange(start, veryend);
+       int len = string.Len();
+       
+       //  Is there any non-whitespace characters?
+       wxChar ch;
+       int i;
+       for (i = 0; i < len; i++) {
+               ch = string[i];
+               if (ch != ' ' && ch != '\t' && ch != '\n' && ch != 'r')
+                       break;
+       }
+       if (i < len) {
+               //  Input is not empty
+               if (veryend < lastpos) {
+                       // Enter is pressed in the block not at the end
+                       // -> Insert the text at the end
+                       wxRegEx re1(wxT("[ \t\n]*$"));
+                       re1.ReplaceFirst(&string, wxT(""));
+                       wxRegEx re2(wxT("^[ \t\n]*"));
+                       re2.ReplaceFirst(&string, wxT(""));
+                       if (textCtrl->GetRange(lastpos - 1, lastpos).GetChar(0) != '\n')
+                               textCtrl->AppendText(wxT("\n"));
+                       MyAppCallback_showRubyPrompt();
+                       MyAppCallback_setConsoleColor(3);
+                       textCtrl->AppendText(string);
+               } else {
+                       wxTextAttr scriptAttr(*wxBLUE);
+                       textCtrl->SetStyle(start, veryend, scriptAttr);
+               }
+               string.Append(wxT("\n"));  //  To avoid choking Ruby interpreter
+               wxRegEx re3(wxT("\n>"));
+               re3.Replace(&string, wxT("\n"));
+               if (textCtrl->GetRange(lastpos - 1, lastpos).GetChar(0) != '\n')
+                       textCtrl->AppendText(wxT("\n"));
+               MyAppCallback_setConsoleColor(0);
+               textCtrl->Update();
+               
+               //  Invoke ruby interpreter
+               int status;
+               RubyValue val;
+               Molecule *mol = MoleculeCallback_currentMolecule();
+               MoleculeLock(mol);
+               val = Molby_evalRubyScriptOnMolecule(string.mb_str(wxConvUTF8), MoleculeCallback_currentMolecule(), &status);
+               MoleculeUnlock(mol);
+               if (status != -1) {  /*  Status -1 is already handled  */
+                       MyAppCallback_setConsoleColor(1);
+                       if (status != 0) {
+                               Molby_showError(status);
+                       } else {
+                               textCtrl->AppendText(wxT("-->"));
+                               Molby_showRubyValue(val);
+                               textCtrl->AppendText(wxT("\n"));
+                               MyAppCallback_showRubyPrompt();
+                       }
+                       MyAppCallback_setConsoleColor(0);
+               }
+       } else {
+               textCtrl->AppendText(wxT("\n"));
+               MyAppCallback_showRubyPrompt();
+       }
+}
+
+void
+ConsoleFrame::OnKeyDown(wxKeyEvent &event)
+{
+       int code = event.GetKeyCode();
+       //      printf("OnChar: %d\n", code);
+       if (code == WXK_RETURN || code == WXK_NUMPAD_ENTER)
+               OnEnterPressed(event);
+       else
+               event.Skip();
+}
diff --git a/wxSources/ConsoleFrame.h b/wxSources/ConsoleFrame.h
new file mode 100755 (executable)
index 0000000..112b1cb
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  ConsoleFrame.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/27.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __ConsoleFrame_h__
+#define __ConsoleFrame_h__
+
+#include "wx/mdi.h"
+//#include "wx/richtext/richtextctrl.h"
+#include "wx/textctrl.h"
+
+class wxMenu;
+
+class ConsoleFrame: public wxMDIChildFrame
+{
+
+public:
+       wxTextCtrl *textCtrl;
+       wxMenu *file_history_menu;
+       wxMenu *edit_menu;
+
+       ConsoleFrame(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, long type);
+       virtual ~ConsoleFrame();
+
+       void OnCreate();
+       void OnEnterPressed(wxKeyEvent& event);
+       void OnKeyDown(wxKeyEvent &event);
+
+       static ConsoleFrame *CreateConsoleFrame(wxMDIParentFrame *parent);
+       void OnCloseWindow(wxCloseEvent &event);
+       void OnClose(wxCommandEvent &event);
+       void OnUpdateUI(wxUpdateUIEvent& event);
+
+       void OnCut(wxCommandEvent &event);
+       void OnCopy(wxCommandEvent &event);
+       void OnPaste(wxCommandEvent &event);
+       void OnClear(wxCommandEvent &event);
+       
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+#endif /* __ConsoleFrame_h__ */
diff --git a/wxSources/GlobalParameterFilesFrame.cpp b/wxSources/GlobalParameterFilesFrame.cpp
new file mode 100644 (file)
index 0000000..fa1eaef
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ *  GlobalParameterFilesFrame.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/11/14.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "GlobalParameterFilesFrame.h"
+
+#include "wx/menu.h"
+#include "wx/regex.h"
+#include "wx/colour.h"
+#include "wx/button.h"
+#include "wx/filedlg.h"
+#include "wx/msgdlg.h"
+
+#include "MyApp.h"
+#include "MyListCtrl.h"
+#include "../MolLib/MolLib.h"
+#include "../MolLib/Missing.h"
+
+// #include "../MolLib/Ruby_bind/Molby_extern.h"
+
+enum {
+       myID_addFileButton = 500,
+       myID_removeFileButton
+};
+
+BEGIN_EVENT_TABLE(GlobalParameterFilesFrame, wxMDIChildFrame)
+EVT_CLOSE(GlobalParameterFilesFrame::OnCloseWindow)
+EVT_MENU(wxID_CLOSE, GlobalParameterFilesFrame::OnClose)
+EVT_UPDATE_UI(wxID_CLOSE, GlobalParameterFilesFrame::OnUpdateUI)
+EVT_BUTTON(myID_addFileButton, GlobalParameterFilesFrame::OnAddGlobalParameterFile)
+EVT_BUTTON(myID_removeFileButton, GlobalParameterFilesFrame::OnRemoveGlobalParameterFile)
+END_EVENT_TABLE()
+
+#pragma mark ====== Static utility functions ======
+                                                                       
+GlobalParameterFilesFrame::GlobalParameterFilesFrame(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, long type):
+wxMDIChildFrame(parent, wxID_ANY, title, pos, size, type)
+{
+}
+
+GlobalParameterFilesFrame::~GlobalParameterFilesFrame()
+{
+       wxGetApp().DocManager()->FileHistoryRemoveMenu(file_history_menu);
+}
+
+void
+GlobalParameterFilesFrame::OnCreate()
+{
+       //  Frame structure:
+       //  vertical{ listview, horizontal{ plus_button, minus_button } }
+       wxSize size(340, 160);
+       wxBoxSizer *sizer0;
+       sizer0 = new wxBoxSizer(wxVERTICAL);
+       listctrl = new MyListCtrl();
+       listctrl->Create(this, wxID_ANY, wxDefaultPosition, size);
+       listctrl->InsertColumn(0, _T("name"), wxLIST_FORMAT_LEFT, 80);
+       listctrl->InsertColumn(1, _T("directory"), wxLIST_FORMAT_LEFT, 240);
+       sizer0->Add(listctrl, 1, wxLEFT | wxRIGHT | wxEXPAND, 0);
+       {
+               wxBoxSizer *sizer1;
+               sizer1 = new wxBoxSizer(wxHORIZONTAL);
+               add_button = new wxButton(this, myID_addFileButton, _T("Load..."), wxDefaultPosition, wxDefaultSize);
+               remove_button = new wxButton(this, myID_removeFileButton, _T("Unload"), wxDefaultPosition, wxDefaultSize);
+               sizer1->Add(add_button, 0, wxALL | wxEXPAND, 3);
+               sizer1->Add(remove_button, 0, wxALL | wxEXPAND, 3);
+               sizer0->Add(sizer1, 0, wxALL | wxEXPAND, 8);
+       }
+       this->SetSizer(sizer0);
+       listctrl->SetDataSource(this);
+       remove_button->Enable(false);
+
+       /*  Create menu bar  */
+       wxMenuBar *menu_bar = wxGetApp().CreateMenuBar(2, &file_history_menu, &edit_menu);
+       SetMenuBar(menu_bar);
+}
+
+GlobalParameterFilesFrame *
+GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(wxMDIParentFrame *parent)
+{
+       GlobalParameterFilesFrame *frame = new GlobalParameterFilesFrame(parent, _T("Load/Unload Global Parameters"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
+       
+       frame->OnCreate();
+       frame->Centre();
+       return frame;
+}
+
+void
+GlobalParameterFilesFrame::OnCloseWindow(wxCloseEvent &event)
+{
+       //  Do not delete this window; it may be reopened later
+       this->Hide();
+}
+
+void
+GlobalParameterFilesFrame::OnClose(wxCommandEvent &event)
+{
+       this->Close();
+}
+
+void
+GlobalParameterFilesFrame::OnUpdateUI(wxUpdateUIEvent& event)
+{
+       int uid = event.GetId();
+       if (uid == wxID_CLOSE)
+               event.Enable(true);
+}
+
+void
+GlobalParameterFilesFrame::OnAddGlobalParameterFile(wxCommandEvent& event)
+{
+       wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Parameter File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
+       if (dialog->ShowModal() == wxID_OK) {
+               char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
+               char *wbuf;
+               int i;
+               int src_idx = ParameterCommentIndexForGlobalFileName(p);
+               for (i = gGlobalParInfo.count - 1; i >= 0; i--) {
+                       if (gGlobalParInfo.files[i].src == src_idx)
+                               break;
+               }
+               if (i >= 0) {
+                       char *s;
+                       asprintf(&s, "File '%s' has already been read. Do you want to replace the parameters?", ParameterGetComment(src_idx));
+                       wxString mes(s, wxConvUTF8);
+                       if (::wxMessageBox(mes, _T("Reload global parameter file"), wxOK | wxCANCEL | wxICON_EXCLAMATION) != wxOK)
+                               return;
+               }
+               ParameterReadFromFile(NULL, p, &wbuf, NULL);
+               if (wbuf != NULL) {
+                       wxGetApp().SetConsoleColor(1);
+                       wxGetApp().AppendConsoleMessage(wbuf);
+                       wxGetApp().SetConsoleColor(0);
+                       free(wbuf);
+               }
+               free(p);
+               /*  Need to update MD parameters for all molecules  */
+               {
+                       Molecule *mol;
+                       for (i = 0; (mol = MoleculeCallback_moleculeAtIndex(i)) != NULL; i++) {
+                               mol->needsMDRebuild = 1;
+                       }
+               }
+       }
+       dialog->Destroy();
+       listctrl->RefreshTable();
+
+       {
+               MyListCtrl *parameterListCtrl = wxGetApp().GetGlobalParameterListCtrl();
+               if (parameterListCtrl != NULL)
+                       parameterListCtrl->RefreshTable();
+       }
+}
+
+void
+GlobalParameterFilesFrame::OnRemoveGlobalParameterFile(wxCommandEvent& event)
+{
+       int i, n, cn;
+       wxString files;
+       ParFileInfo *ip;
+       const char *cp;
+       char *s;
+
+       n = listctrl->GetItemCount();
+       cn = 0;
+       for (i = 0; i < n; i++) {
+               if (i >= gGlobalParInfo.builtinCount && listctrl->GetItemState(i, wxLIST_STATE_SELECTED)) {
+                       ip = &gGlobalParInfo.files[i];
+                       cp = ParameterGetComment(ip->src);
+                       if (cn > 0)
+                               files << _T(", ");
+                       files << wxString(cp, wxConvUTF8);
+                       cn++;
+               }
+       }
+       if (cn == 0)
+               return;
+       
+       asprintf(&s, "Do you really want to unload the parameter%s %s?", (cn > 1 ? "s" : ""), (const char *)files.mb_str(wxConvUTF8));
+       wxString mes(s, wxConvUTF8);
+       if (::wxMessageBox(mes, _T("Unload global parameter file"), wxOK | wxCANCEL | wxICON_EXCLAMATION) != wxOK)
+               return;
+       for (i = gGlobalParInfo.count - 1; i >= gGlobalParInfo.builtinCount; i--) {
+               if (listctrl->GetItemState(i, wxLIST_STATE_SELECTED)) {
+                       ip = &gGlobalParInfo.files[i];
+                       ParameterDeleteAllEntriesForSource(gBuiltinParameters, ip->src);
+                       listctrl->SetItemState(i, 0, wxLIST_STATE_SELECTED);
+               }
+       }
+       listctrl->RefreshTable();
+
+       /*  Request to update MD parameters for all molecules  */
+       {
+               Molecule *mol;
+               for (i = 0; (mol = MoleculeCallback_moleculeAtIndex(i)) != NULL; i++) {
+                       mol->needsMDRebuild = 1;
+               }
+       }
+       
+       {
+               MyListCtrl *parameterListCtrl = wxGetApp().GetGlobalParameterListCtrl();
+               if (parameterListCtrl != NULL)
+                       parameterListCtrl->RefreshTable();
+       }
+       
+       remove_button->Enable(false);
+}
+
+#pragma mark ====== MyListCtrl data source ======
+
+/*  Get information from gGlobalParInfo (defined in Parameter.c)  */
+int
+GlobalParameterFilesFrame::GetItemCount(MyListCtrl *ctrl)
+{
+       return gGlobalParInfo.count;
+}
+
+wxString
+GlobalParameterFilesFrame::GetItemText(MyListCtrl *ctrl, long row, long column) const
+{
+       ParFileInfo *ip = &gGlobalParInfo.files[row];
+       const char *p = NULL;
+       if (column == 0)
+               p = ParameterGetComment(ip->src);
+       else if (column == 1) {
+               if (row < gGlobalParInfo.builtinCount)
+                       p = "(built-in)";
+               else
+                       p = ip->dir;
+       }
+       wxString str((p ? p : ""), wxConvUTF8);
+       return str;
+}
+
+int
+GlobalParameterFilesFrame::SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value)
+{
+       return 0;
+}
+
+void
+GlobalParameterFilesFrame::DragSelectionToRow(MyListCtrl *ctrl, long row)
+{
+}
+
+bool
+GlobalParameterFilesFrame::IsItemEditable(MyListCtrl *ctrl, long row, long column)
+{
+       return false;
+}
+
+bool
+GlobalParameterFilesFrame::IsDragAndDropEnabled(MyListCtrl *ctrl)
+{
+       return 0;
+}
+
+void
+GlobalParameterFilesFrame::OnSelectionChanged(MyListCtrl *ctrl)
+{
+       int i, n, cn;
+       ctrl->EnableSelectionChangeNotification(false);
+       n = ctrl->GetItemCount();
+       cn = 0;
+       for (i = 0; i < n; i++) {
+               if (ctrl->GetItemState(i, wxLIST_STATE_SELECTED)) {
+                       if (i < gGlobalParInfo.builtinCount)
+                               ctrl->SetItemState(i, 0, wxLIST_STATE_SELECTED);
+                       else cn++;
+               }
+       }
+       ctrl->EnableSelectionChangeNotification(true);
+       if (cn > 0)
+               remove_button->Enable(true);
+       else remove_button->Enable(false);
+}
+
+int
+GlobalParameterFilesFrame::SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg)
+{
+       if (row >= 0 && row < gGlobalParInfo.builtinCount && col == -1) {
+               fg[0] = fg[1] = fg[2] = 0.5;
+               return 1;
+       } else return 0;
+}
diff --git a/wxSources/GlobalParameterFilesFrame.h b/wxSources/GlobalParameterFilesFrame.h
new file mode 100644 (file)
index 0000000..0abe815
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  GlobalParameterFilesFrame.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/11/14.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __GlobalParameterFilesFrame_h__
+#define __GlobalParameterFilesFrame_h__
+
+#include "wx/mdi.h"
+#include "MyListCtrl.h"
+#include "wx/sizer.h"
+
+class wxMenu;
+class GlobalParameterFilesFrame: public wxMDIChildFrame, public MyListCtrlDataSource
+{
+public:
+       MyListCtrl *listctrl;
+       wxButton *add_button, *remove_button;
+       wxMenu *file_history_menu;
+       wxMenu *edit_menu;
+       
+       GlobalParameterFilesFrame(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, long type);
+       virtual ~GlobalParameterFilesFrame();
+       
+       MyListCtrl *GetListCtrl() { return listctrl; }
+       
+       void OnCreate();
+       
+       static GlobalParameterFilesFrame *CreateGlobalParameterFilesFrame(wxMDIParentFrame *parent);
+       
+       void OnCloseWindow(wxCloseEvent &event);
+       void OnClose(wxCommandEvent &event);
+       void OnUpdateUI(wxUpdateUIEvent& event);
+       void OnAddGlobalParameterFile(wxCommandEvent& event);
+       void OnRemoveGlobalParameterFile(wxCommandEvent& event);
+
+       /*  MyListCtrlDataSource functions  */
+       virtual int GetItemCount(MyListCtrl *ctrl);
+       virtual wxString GetItemText(MyListCtrl *ctrl, long row, long column) const;
+       virtual int SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value);
+       virtual void DragSelectionToRow(MyListCtrl *ctrl, long row);
+       virtual bool IsItemEditable(MyListCtrl *ctrl, long row, long column);
+       virtual bool IsDragAndDropEnabled(MyListCtrl *ctrl);
+       virtual void OnSelectionChanged(MyListCtrl *ctrl);
+       virtual int SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg);
+       
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+#endif /* __GlobalParameterFilesFrame_h__ */
diff --git a/wxSources/GlobalParameterFrame.cpp b/wxSources/GlobalParameterFrame.cpp
new file mode 100755 (executable)
index 0000000..fd56e40
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *  GlobalParameterFrame.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/11/05.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "GlobalParameterFrame.h"
+
+#include "wx/menu.h"
+#include "wx/regex.h"
+#include "wx/colour.h"
+
+#include "MyApp.h"
+#include "MyListCtrl.h"
+#include "../MolLib/Ruby_bind/Molby_extern.h"
+
+BEGIN_EVENT_TABLE(GlobalParameterFrame, wxMDIChildFrame)
+       EVT_CLOSE(GlobalParameterFrame::OnCloseWindow)
+       EVT_MENU(wxID_CLOSE, GlobalParameterFrame::OnClose)
+       EVT_UPDATE_UI(wxID_CLOSE, GlobalParameterFrame::OnUpdateUI)
+END_EVENT_TABLE()
+
+GlobalParameterFrame::GlobalParameterFrame(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, long type):
+       wxMDIChildFrame(parent, wxID_ANY, title, pos, size, type)
+{
+}
+
+GlobalParameterFrame::~GlobalParameterFrame()
+{
+       wxGetApp().DocManager()->FileHistoryRemoveMenu(file_history_menu);
+}
+
+void
+GlobalParameterFrame::OnCreate()
+{
+       /*  Make a MyListCtrl view  */
+       int width, height;
+       GetClientSize(&width, &height);
+       listCtrl = new MyListCtrl();
+       listCtrl->Create(this, wxID_ANY, wxPoint(0, 0), wxSize(width, height));
+       listCtrl->SetDataSource(this);
+       wxMenuBar *menu_bar = wxGetApp().CreateMenuBar(2, &file_history_menu, &edit_menu);
+       
+       /*  Associate the menu bar with the frame  */
+       SetMenuBar(menu_bar);
+}
+
+GlobalParameterFrame *
+GlobalParameterFrame::CreateGlobalParameterFrame(wxMDIParentFrame *parent)
+{
+#ifdef __WXMSW__
+       wxPoint origin(16, 16);
+       wxSize size(774, 300);
+#else
+       wxPoint origin(26, 40);
+       wxSize size(774, 300);
+#endif
+       GlobalParameterFrame *frame = new GlobalParameterFrame(parent, _T("Global Parameters"), origin, size, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
+       
+       frame->OnCreate();
+       return frame;
+}
+
+void
+GlobalParameterFrame::OnCloseWindow(wxCloseEvent &event)
+{
+       //  Do not delete this window; it may be reopened later
+       this->Hide();
+}
+
+void
+GlobalParameterFrame::OnClose(wxCommandEvent &event)
+{
+       //  Why this is not automatically connected?
+       this->Close();
+}
+
+void
+GlobalParameterFrame::OnUpdateUI(wxUpdateUIEvent& event)
+{
+       //  Why this is not automatically done??
+       int uid = event.GetId();
+       if (uid == wxID_CLOSE)
+               event.Enable(true);
+}
+
+#pragma mark ====== MyListCtrl data source ======
+
+int
+GlobalParameterFrame::GetItemCount(MyListCtrl *ctrl)
+{
+       return MainView_numberOfRowsInTable(NULL);
+}
+
+wxString
+GlobalParameterFrame::GetItemText(MyListCtrl *ctrl, long row, long column) const
+{
+       char buf[128];
+       MainView_valueForTable(NULL, column, row, buf, sizeof buf);
+       wxString *str = new wxString(buf, wxConvUTF8);
+       return *str;
+}
+
+int
+GlobalParameterFrame::SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value)
+{
+//     MainView_setValueForTable(NULL, column, row, value.mb_str(wxConvUTF8));
+       return 0;
+}
+
+void
+GlobalParameterFrame::DragSelectionToRow(MyListCtrl *ctrl, long row)
+{
+}
+
+bool
+GlobalParameterFrame::IsItemEditable(MyListCtrl *ctrl, long row, long column)
+{
+       return false;
+//     return MainView_isTableItemEditable(NULL, column, row);
+}
+
+bool
+GlobalParameterFrame::IsDragAndDropEnabled(MyListCtrl *ctrl)
+{
+       return 0;
+}
+
+void
+GlobalParameterFrame::OnSelectionChanged(MyListCtrl *ctrl)
+{
+       MainView_setSelectionFromTable(NULL);
+}
+
+int
+GlobalParameterFrame::SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg)
+{
+       if (col == -1) {
+               int src = ParameterTableGetItemSource(gBuiltinParameters, row);
+               if (src == -2) { /* separator row */
+                       bg[0] = bg[1] = bg[2] = 0.6;
+                       return 2;
+               }
+       }
+       return 0;
+}
diff --git a/wxSources/GlobalParameterFrame.h b/wxSources/GlobalParameterFrame.h
new file mode 100755 (executable)
index 0000000..01557dc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  GlobalParameterFrame.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/11/05.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __GlobalParameterFrame_h__
+#define __GlobalParameterFrame_h__
+
+#include "wx/mdi.h"
+#include "MyListCtrl.h"
+
+class wxMenu;
+
+class GlobalParameterFrame: public wxMDIChildFrame, public MyListCtrlDataSource
+{
+
+public:
+       MyListCtrl *listCtrl;
+       wxMenu *file_history_menu;
+       wxMenu *edit_menu;
+
+       GlobalParameterFrame(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, long type);
+       virtual ~GlobalParameterFrame();
+
+       MyListCtrl *GetListCtrl() { return listCtrl; }
+
+       void OnCreate();
+
+       static GlobalParameterFrame *CreateGlobalParameterFrame(wxMDIParentFrame *parent);
+       
+       void OnCloseWindow(wxCloseEvent &event);
+       void OnClose(wxCommandEvent &event);
+       void OnUpdateUI(wxUpdateUIEvent& event);
+
+       /*  MyListCtrlDataSource functions  */
+       virtual int GetItemCount(MyListCtrl *ctrl);
+       virtual wxString GetItemText(MyListCtrl *ctrl, long row, long column) const;
+       virtual int SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value);
+       virtual void DragSelectionToRow(MyListCtrl *ctrl, long row);
+       virtual bool IsItemEditable(MyListCtrl *ctrl, long row, long column);
+       virtual bool IsDragAndDropEnabled(MyListCtrl *ctrl);
+       virtual void OnSelectionChanged(MyListCtrl *ctrl);
+       virtual int SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg);
+
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+#endif /* __GlobalParameterFrame_h__ */
diff --git a/wxSources/MoleculeView.cpp b/wxSources/MoleculeView.cpp
new file mode 100755 (executable)
index 0000000..938b758
--- /dev/null
@@ -0,0 +1,1201 @@
+/*
+ *  MoleculeView.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error "You should have DocView architecture enabled in your wxWidgets installation."
+#endif
+
+#include "MoleculeView.h"
+
+#include "MyApp.h"
+#include "MyDocument.h"
+#include "MyGLCanvas.h"
+#include "MyCommand.h"
+#include "MySlider.h"
+#include "MyListCtrl.h"
+#include "../MolLib/Missing.h"
+
+#include "wx/tglbtn.h"
+#include "wx/listctrl.h"
+#include "wx/splitter.h"
+#include "wx/choice.h"
+#include "wx/font.h"
+
+//#include "../MolLib/Ruby_bind/Molby_extern.h"
+
+enum {
+       myID_RotButton = 500,
+       myID_TransButton,
+       myID_ScaleButton,
+       myID_SelectButton,
+       myID_BondButton,
+       myID_EraseButton,
+       myID_RotateBondSlider,
+       myID_RotateXSlider,
+       myID_RotateYSlider,
+       myID_FrameControlPanel,
+       myID_FrameSlider,
+       myID_JumpToStartButton,
+       myID_PlayBackwardButton,
+       myID_FrameText,
+       myID_PlayForwardButton,
+       myID_JumpToEndButton,
+       myID_Table,
+       myID_TableMenu
+};
+
+IMPLEMENT_DYNAMIC_CLASS(MoleculeView, wxView)
+
+BEGIN_EVENT_TABLE(MoleculeView, wxView)
+       EVT_TOGGLEBUTTON(myID_RotButton, MoleculeView::OnButtonPressed)
+       EVT_TOGGLEBUTTON(myID_TransButton, MoleculeView::OnButtonPressed)
+       EVT_TOGGLEBUTTON(myID_ScaleButton, MoleculeView::OnButtonPressed)
+       EVT_TOGGLEBUTTON(myID_SelectButton, MoleculeView::OnButtonPressed)
+       EVT_TOGGLEBUTTON(myID_BondButton, MoleculeView::OnButtonPressed)
+       EVT_TOGGLEBUTTON(myID_EraseButton, MoleculeView::OnButtonPressed)
+       EVT_COMMAND(wxID_ANY, MySliderEvent, MoleculeView::OnSliderAction)
+       EVT_COMMAND_SCROLL(myID_FrameSlider, MoleculeView::OnFrameSliderAction)
+       EVT_TEXT_ENTER(myID_FrameText, MoleculeView::OnFrameTextAction)
+       EVT_CHOICE(myID_TableMenu, MoleculeView::OnSelectTable)
+END_EVENT_TABLE()
+
+#define ConnectMouseDownEvents(src, func, target) \
+       (src->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(func), NULL, target), \
+       src->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(func), NULL, target))
+                                
+                                
+bool
+MoleculeView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
+{
+       int i;
+
+       // Make a document frame
+       frame = new wxDocMDIChildFrame(doc, this, GetMainFrame(), wxID_ANY, _T("New Molby Document"),
+                                                  wxPoint(10, 24), wxSize(680, 400),
+                                                  wxDEFAULT_FRAME_STYLE |
+                                                  wxNO_FULL_REPAINT_ON_RESIZE);
+
+       canvas = NULL;
+       mview = NULL;
+       listmenu = NULL;
+       listctrl = NULL;
+       file_history_menu = NULL;
+       edit_menu = NULL;
+       memset(tbuttons, 0, sizeof(tbuttons));
+       infotext = NULL;
+       frameControlPanel = NULL;
+       frameSlider = NULL;
+       frameText = NULL;
+       isRebuildingTable = false;
+       
+       wxMenuBar *menu_bar = wxGetApp().CreateMenuBar(1, &file_history_menu, &edit_menu);
+       
+       // Associate the menu bar with the frame
+       frame->SetMenuBar(menu_bar);
+
+       // Associate the edit menu with the command processor
+       doc->GetCommandProcessor()->SetEditMenu(edit_menu);
+       
+    mview = MainView_newMainView(this);
+       
+       // Create the window content
+       
+       //  A splitter window embraces a grid (left) and main screen (right)
+       wxSplitterWindow *splitter = new wxSplitterWindow(frame, -1, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE);
+       
+       //  Create the left half
+       //  A panel containing a popup menu and a list window
+       wxPanel *panel0 = new wxPanel(splitter);
+       {
+               char buf[16];
+               wxBoxSizer *sizer0;
+               sizer0 = new wxBoxSizer(wxVERTICAL);
+               wxArrayString choiceItems;
+               for (i = 0; ; i++) {
+                       MainView_tableTitleForIndex(mview, i, buf, sizeof buf);
+                       if (buf[0] == 0)
+                               break;
+                       wxString itemTitle(buf, wxConvUTF8);
+                       choiceItems.Add(itemTitle);
+               }
+       /*      static wxString choiceItems[] = {
+                       wxT("atoms"), wxT("bonds"), wxT("angles"), wxT("dihedrals"), wxT("impropers"), wxT("MO info")
+               }; */
+               
+               listmenu = new wxChoice(panel0, myID_TableMenu, wxDefaultPosition, wxDefaultSize, choiceItems);
+               sizer0->Add(listmenu, 0, wxALL, 0);
+               
+               listctrl = new MyListCtrl();
+               listctrl->Create(panel0, myID_Table, wxDefaultPosition, wxDefaultSize);
+               sizer0->Add(listctrl, 1, wxALL | wxEXPAND, 0);
+               panel0->SetSizer(sizer0);
+       }
+               
+       //  Create the right half
+       //  A panel containing MyGLCanvas, buttons, sliders, etc.
+       wxPanel *panel1 = new wxPanel(splitter);
+       
+       {       //  Vertical sizer containing [sizer2, sizer3, sizer4]
+               wxBoxSizer *sizer1;
+               sizer1 = new wxBoxSizer(wxVERTICAL);
+               
+               {       //  Horizontal sizer containing [button0, button1, ..., button5, infotext]
+                       wxBoxSizer *sizer2;
+                       sizer2 = new wxBoxSizer(wxHORIZONTAL);
+                       
+                       {       // Button0..5 (Rot/Trans/Scale/Select/Bond/Erase)
+                               wxString labels[] = {
+                                       wxT("Rot"), wxT("Trans"), wxT("Scale"), wxT("Select"), wxT("Bond"), wxT("Erase")
+                               };
+                               wxWindowID ids[] = {
+                                       myID_RotButton, myID_TransButton, myID_ScaleButton, 
+                                       myID_SelectButton, myID_BondButton, myID_EraseButton
+                               };
+                               for (i = 0; i < 6; i++) {
+                                       tbuttons[i] = new wxToggleButton(panel1, ids[i], labels[i], wxDefaultPosition, wxSize(40, 16));
+                                       sizer2->Add(tbuttons[i], 0, wxALL | wxEXPAND, 3);
+                               }
+                               tbuttons[0]->SetValue(true);
+                       }
+                       {       // Information text
+                               infotext = new wxStaticText(panel1, -1, wxT(""), wxDefaultPosition, wxSize(40, 16), wxST_NO_AUTORESIZE | wxBORDER_SUNKEN);
+                               infotext->SetMinSize(wxSize(80, 32));
+                               infotext->SetFont(*wxSMALL_FONT);
+                               sizer2->Add(infotext, 1, wxALL | wxEXPAND, 3);   // Can expand horizontally
+                       }
+                       
+                       sizer1->Add(sizer2, 0, wxALL | wxEXPAND, 0);
+               }
+               
+               {       // Horizontal sizer containing [sizer31, sizer32, sizer33]
+                       wxBoxSizer *sizer3 = new wxBoxSizer(wxHORIZONTAL);
+                       
+                       {       // Vertical sizer containing [button, mySlider]
+                               wxBoxSizer *sizer31 = new wxBoxSizer(wxVERTICAL);
+                               {       // "Rotate bond" button and mySlider
+                                       #include "../bitmaps/rotate_bond.xpm"
+                                       wxBitmap bmp1(rotate_bond_xpm, wxBITMAP_TYPE_XPM);
+                                       wxBitmapButton *button1 = new wxBitmapButton(panel1, -1, bmp1, wxDefaultPosition, wxSize(21, 21));
+                                       sizer31->Add(button1, 0, 0, 0);
+                                       button1->Disable();
+                                       MySlider *slider1 = new MySlider(panel1, myID_RotateBondSlider, wxVERTICAL, wxDefaultPosition, wxSize(21, 21));
+                                       sizer31->Add(slider1, 1, wxEXPAND);
+                               }
+                               sizer3->Add(sizer31, 0, wxALL | wxEXPAND, 0);
+                       }
+                       
+                       {       // Vertical sizer containing [Canvas, [button, mySlider]]
+                               wxBoxSizer *sizer32 = new wxBoxSizer(wxVERTICAL);
+                               {
+                                       canvas = new MyGLCanvas(this, panel1, wxDefaultPosition, wxSize(100, 100));
+                                       sizer32->Add(canvas, 1, wxALL | wxEXPAND, 0);
+                                       
+                                       //  Let the MyGLCanvas pass the keyboard event to this
+                                       canvas->Connect(-1, wxEVT_CHAR, wxKeyEventHandler(MoleculeView::OnChar), NULL, this);
+                               }
+                               {
+                                       wxBoxSizer *sizer321 = new wxBoxSizer(wxHORIZONTAL);
+                                       {
+                                               #include "../bitmaps/rotate_y.xpm"
+                                               wxBitmap bmp2(rotate_y_xpm, wxBITMAP_TYPE_XPM);
+                                               wxBitmapButton *button2 = new wxBitmapButton(panel1, -1, bmp2, wxDefaultPosition, wxSize(21, 21));
+                                               sizer321->Add(button2, 0, 0, 0);
+                                               button2->Disable();
+                                               MySlider *slider2 = new MySlider(panel1, myID_RotateYSlider, wxHORIZONTAL, wxDefaultPosition, wxSize(21, 21));
+                                               sizer321->Add(slider2, 1, wxEXPAND);
+                                       }
+                                       sizer32->Add(sizer321, 0, wxEXPAND);
+                               }
+                               sizer3->Add(sizer32, 1, wxEXPAND);
+                       }
+
+                       {       // Vertical sizer containing [button, mySlider]
+                               wxBoxSizer *sizer33 = new wxBoxSizer(wxVERTICAL);
+                               {       // "Rotate bond" button and mySlider
+                                       #include "../bitmaps/rotate_x.xpm"
+                                       wxBitmap bmp3(rotate_x_xpm, wxBITMAP_TYPE_XPM);
+                                       wxBitmapButton *button3 = new wxBitmapButton(panel1, -1, bmp3, wxDefaultPosition, wxSize(21, 21));
+                                       button3->Disable();
+                                       sizer33->Add(button3, 0, 0, 0);
+                                       
+                                       MySlider *slider3 = new MySlider(panel1, myID_RotateXSlider, wxVERTICAL, wxDefaultPosition, wxSize(21, 21));
+                                       sizer33->Add(slider3, 1, wxEXPAND);
+                               }
+                               sizer3->Add(sizer33, 0, wxALL | wxEXPAND, 0);
+                       }
+                       
+                       sizer1->Add(sizer3, 1, wxALL | wxEXPAND, 0);
+               }
+               
+               {       //  Horizontal sizer containing frame controls
+                       
+                       const int height = 18;
+                       frameControlPanel = new wxPanel(panel1, myID_FrameControlPanel, wxDefaultPosition, wxSize(200, height));
+                       wxBoxSizer *sizer4 = new wxBoxSizer(wxHORIZONTAL);
+                       {
+                               frameSlider = new wxSlider(frameControlPanel, myID_FrameSlider, 0, 0, 0, wxDefaultPosition, wxSize(40, height - 2));
+                               sizer4->Add(frameSlider, 1, wxALL | wxEXPAND, 1);
+                       
+                               #include "../bitmaps/jump_to_start.xpm"
+                               wxBitmap bmp41(jump_to_start_xpm, wxBITMAP_TYPE_XPM);
+                               wxBitmapButton *button41 = new wxBitmapButton(frameControlPanel, myID_JumpToStartButton, bmp41, wxDefaultPosition, wxSize(16, height));
+                               sizer4->Add(button41, 0, wxEXPAND);
+                               ConnectMouseDownEvents(button41, MoleculeView::OnFrameButtonAction, this);
+
+                               #include "../bitmaps/play_backward.xpm"
+                               wxBitmap bmp42(play_backward_xpm, wxBITMAP_TYPE_XPM);
+                               wxBitmapButton *button42 = new wxBitmapButton(frameControlPanel, myID_PlayBackwardButton, bmp42, wxDefaultPosition, wxSize(16, height));
+                               sizer4->Add(button42, 0, wxEXPAND);
+                               ConnectMouseDownEvents(button42, MoleculeView::OnFrameButtonAction, this);
+                               
+                               {
+                                       frameText = new wxTextCtrl(frameControlPanel, myID_FrameText, wxT(""), wxDefaultPosition, wxSize(40, height));
+                                       wxFont font(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
+                                       wxTextAttr attr(*wxBLACK, wxNullColour, font);
+                                       frameText->SetDefaultStyle(attr);
+                                       sizer4->Add(frameText, 0, wxEXPAND);
+                               }
+                       
+                               #include "../bitmaps/play_forward.xpm"
+                               wxBitmap bmp43(play_forward_xpm, wxBITMAP_TYPE_XPM);
+                               wxBitmapButton *button43 = new wxBitmapButton(frameControlPanel, myID_PlayForwardButton, bmp43, wxDefaultPosition, wxSize(16, height));
+                               sizer4->Add(button43, 0, wxEXPAND);
+                               ConnectMouseDownEvents(button43, MoleculeView::OnFrameButtonAction, this);
+
+                               #include "../bitmaps/jump_to_end.xpm"
+                               wxBitmap bmp44(jump_to_end_xpm, wxBITMAP_TYPE_XPM);
+                               wxBitmapButton *button44 = new wxBitmapButton(frameControlPanel, myID_JumpToEndButton, bmp44, wxDefaultPosition, wxSize(16, height));
+                               sizer4->Add(button44, 0, wxEXPAND);
+                               ConnectMouseDownEvents(button44, MoleculeView::OnFrameButtonAction, this);
+                               
+                               wxPanel *spacer = new wxPanel(frameControlPanel, -1, wxDefaultPosition, wxSize(21, height));
+                               sizer4->Add(spacer, 0, wxEXPAND);
+                       }
+                       frameControlPanel->SetSizer(sizer4);
+                       sizer1->Add(frameControlPanel, 0, wxALL | wxEXPAND, 0);
+               //      controls->Disable();
+               }
+
+               panel1->SetSizer(sizer1);
+       }
+
+       splitter->SplitVertically(panel0, panel1);
+
+       wxBoxSizer *mainsizer = new wxBoxSizer(wxHORIZONTAL);
+       mainsizer->Add(splitter, 1, wxEXPAND);
+       frame->SetSizer(mainsizer);
+
+       mainsizer->Layout();
+       splitter->SetSashPosition(240, true);
+       
+       //  Associate the molecule with the main view
+       MainView_setMolecule(mview, ((MyDocument *)doc)->GetMolecule());
+       
+       //  Initialize table view
+       MainView_createColumnsForTableAtIndex(mview, 0);
+
+       //  Select table popup
+       listmenu->SetSelection(0);
+       
+#if defined(__X__) || defined(__WXMAC__)
+    // X seems to require a forced resize
+    int x, y;
+    frame->GetSize(&x, &y);
+    frame->SetSize(wxDefaultCoord, wxDefaultCoord, x, y);
+#endif
+    frame->Show(true);
+    Activate(true);
+
+       //  Connect the notification handler
+       doc->Connect(MyDocumentEvent_documentModified, MyDocumentEvent, wxCommandEventHandler(MoleculeView::OnDocumentModified), NULL, this);
+
+       wxGetApp().Connect(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, wxCommandEventHandler(MoleculeView::OnScriptMenuModified), NULL, this);
+
+       //  Intercept the double-click handler of MyListCtrl
+       listctrl->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(MoleculeView::OnLeftDClickInListCtrl), NULL, this);
+                                       
+    return true;
+}
+
+void
+MoleculeView::OnDraw(wxDC *dc)
+{
+       if (mview != NULL && mview->mol != NULL) {
+               MoleculeLock(mview->mol);
+               MainView_drawModel(mview);
+               MoleculeUnlock(mview->mol);
+       }
+}
+
+void
+MoleculeView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint))
+{
+       if (canvas)
+               canvas->Refresh();
+
+/*  Maybe necessary in some platforms (not in MacOSX and MSW)  */
+#if 0
+  if (canvas) {
+      wxClientDC dc(canvas);
+      dc.Clear();
+      OnDraw(&dc);
+  }
+#endif
+}
+
+bool
+MoleculeView::OnClose(bool deleteWindow)
+{
+       if (!GetDocument()->Close())
+               return false;
+
+       //  Dispose relationship between MainView and Molecule
+       MainView_setMolecule(mview, NULL);
+       //  Release the MainView object
+       MainView_release(mview);
+       mview = NULL;
+
+       //  Dispose Connection between DocManager and file history menu
+       wxGetApp().DocManager()->FileHistoryRemoveMenu(file_history_menu);
+
+       // Clear the canvas in case we're in single-window mode,
+       // and the canvas stays.
+       canvas->ClearBackground();
+       canvas->view = NULL;
+       canvas = NULL;
+
+       wxString s(wxTheApp->GetAppName());
+       if (frame)
+               frame->SetTitle(s);
+
+       SetFrame(NULL);
+
+       Activate(false);
+
+       if (deleteWindow) {
+               delete frame;
+               return true;
+       }
+       return true;
+}
+
+void
+MoleculeView::UpdateFrameControlValues()
+{
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       wxString str;
+       int cframe;
+       cframe = mview->mol->cframe;
+       str.Printf(_T("%d"), cframe);
+       frameText->SetValue(str);
+       frameSlider->SetValue(cframe);
+}
+
+void
+MoleculeView::UpdateFrameControls()
+{
+       int nframes;
+       bool enabled = false;
+       if (mview != NULL && mview->mol != NULL) {
+               MoleculeLock(mview->mol);
+               nframes = MoleculeGetNumberOfFrames(mview->mol);
+               MoleculeUnlock(mview->mol);
+               if (nframes > 0)
+                       enabled = true;
+       }
+       
+       frameControlPanel->Enable(enabled);
+       if (enabled) {
+               frameSlider->SetRange(0, nframes - 1);
+               UpdateFrameControlValues();
+       } else {
+               frameSlider->SetRange(0, 0);
+               frameText->SetValue(_T(""));
+       }
+       frameControlPanel->Update();
+}
+
+void
+MoleculeView::SelectButtonForMode(int mode)
+{
+       int i;
+       if (mode >= 1 && mode <= 6) {
+               for (i = 0; i < 6; i++) {
+                       tbuttons[i]->SetValue((i == mode - 1));
+               }
+       }
+       MainViewCallback_setKeyboardFocus(mview);
+}
+
+void
+MoleculeView::OnButtonPressed(wxCommandEvent& event)
+{
+       int eventId = event.GetId();
+       int mode = eventId - myID_RotButton + kTrackballRotateMode;
+       MainView_setMode(mview, mode);
+       SelectButtonForMode(mode);
+}
+
+void
+MoleculeView::OnSliderAction(wxCommandEvent& event)
+{
+       int eventId = event.GetId();
+       int mode = eventId - myID_RotateBondSlider + 1;
+       MySlider *sender = (MySlider *)event.GetEventObject();
+       float angle = sender->GetFloatValue();
+       int mouseStatus = sender->GetMouseStatus();
+       MoleculeLock(mview->mol);
+       MainView_rotateBySlider(mview, angle * 3.1415927 * 2, mode, mouseStatus, MainViewCallback_modifierFlags(NULL));
+       MoleculeUnlock(mview->mol);
+}
+
+void
+MoleculeView::OnFrameButtonAction(wxMouseEvent &event)
+{
+       int ival, nframes, bid;
+       if (mview == NULL || mview->mol == NULL)
+               goto skip;
+       nframes = MoleculeGetNumberOfFrames(mview->mol);
+       if (nframes == 0)
+               goto skip;
+       bid = event.GetId();
+       if (bid == myID_JumpToStartButton) {
+               ival = 0;
+       } else if (bid == myID_JumpToEndButton) {
+               ival = nframes - 1;
+       } else if (bid == myID_PlayForwardButton) {
+               ival = (mview->mol->cframe + 1) % nframes;
+       } else if (bid == myID_PlayBackwardButton) {
+               ival = (mview->mol->cframe + nframes - 1) % nframes;
+       }
+       //  TODO: implement continuous move
+       if (ival >= 0 && ival < nframes) {
+               MoleculeLock(mview->mol);
+               MoleculeSelectFrame(mview->mol, ival, 1);
+               MoleculeUnlock(mview->mol);
+               MainViewCallback_display(mview);
+               UpdateFrameControlValues();
+       }
+       
+skip:
+       event.Skip();
+}
+
+void
+MoleculeView::OnFrameSliderAction(wxScrollEvent &event)
+{
+       int ival, nframes;
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       MoleculeLock(mview->mol);
+       nframes = MoleculeGetNumberOfFrames(mview->mol);
+       if (nframes != 0) {
+               ival = frameSlider->GetValue();
+               if (ival >= 0 && ival < nframes) {
+                       MoleculeSelectFrame(mview->mol, ival, 1);
+                       MoleculeUnlock(mview->mol);
+                       MainViewCallback_display(mview);
+                       UpdateFrameControlValues();
+                       return;
+               }
+       }
+       MoleculeUnlock(mview->mol);
+}
+
+void
+MoleculeView::OnFrameTextAction(wxCommandEvent &event)
+{
+       int ival, nframes;
+       wxString str;
+       if (mview == NULL || mview->mol == NULL)
+               return;
+       MoleculeLock(mview->mol);
+       nframes = MoleculeGetNumberOfFrames(mview->mol);
+       if (nframes != 0) {
+               str = frameText->GetValue();
+               ival = atoi((const char *)str.mb_str(wxConvUTF8));
+               if (ival >= 0 && ival < nframes) {
+                       MoleculeSelectFrame(mview->mol, ival, 1);
+                       MoleculeUnlock(mview->mol);
+                       MainViewCallback_display(mview);
+                       UpdateFrameControlValues();
+                       return;
+               }
+       }
+       MoleculeUnlock(mview->mol);
+}
+
+void
+MoleculeView::OnDocumentModified(wxCommandEvent& event)
+{
+       if (!mview->freezeScreen) {
+               if (canvas)
+                       canvas->Refresh();
+               UpdateFrameControls();
+               MoleculeLock(mview->mol);
+               MainView_refreshTable(mview);
+               MoleculeUnlock(mview->mol);
+       }
+       printf("MoleculeView::OnDocumentModified invoked\n");
+       event.Skip();  /*  Continue processing of the notification  */
+}
+
+void
+MoleculeView::OnScriptMenuModified(wxCommandEvent& event)
+{
+       wxGetApp().UpdateScriptMenu(frame->GetMenuBar());
+       event.Skip();
+}
+
+void
+MoleculeView::OnChar(wxKeyEvent &event)
+{
+       int code = event.GetKeyCode();
+       int mode = 0;
+       bool noMod = ((event.GetModifiers() | wxMOD_SHIFT) == wxMOD_SHIFT);
+//     MyAppCallback_showScriptMessage("MoleculeView::OnChar invoked\n");
+       if (code == WXK_BACK || code == WXK_DELETE || code == 0x7f || code == 8) {
+               MoleculeLock(mview->mol);
+               MainView_delete(mview);
+               MoleculeUnlock(mview->mol);
+       } else if (noMod && (code == 'r' || code == 'R'))
+               mode = kTrackballRotateMode;
+       else if (noMod && (code == 't' || code == 'T'))
+               mode = kTrackballTranslateMode;
+       else if (noMod && (code == 's' || code == 'S'))
+               mode = kTrackballScaleMode;
+       else if (noMod && (code == ' '))
+               mode = kTrackballSelectionMode;
+       else if (noMod && (code == 'b' || code == 'B'))
+               mode = kTrackballCreateMode;
+       else if (noMod && (code == 'e' || code == 'E'))
+               mode = kTrackballEraseMode;
+       else {
+               event.Skip();
+               return;
+       }
+       if (mode > 0) {
+               MainView_setMode(mview, mode);
+               MainViewCallback_selectMatrixCellForMode(mview, mode);
+       }
+}
+
+void
+MoleculeView::SelectTable(int idx)
+{
+       if (idx >= 0 && idx < listmenu->GetCount()) {
+               isRebuildingTable = true;
+               listmenu->SetSelection(idx);
+               MainView_createColumnsForTableAtIndex(mview, idx);
+               isRebuildingTable = false;
+               MoleculeLock(mview->mol);
+               MainView_refreshTable(mview);
+               MoleculeUnlock(mview->mol);
+       }
+}
+
+void
+MoleculeView::OnSelectTable(wxCommandEvent &event)
+{
+       if (!isRebuildingTable) {
+               MoleculeLock(mview->mol);
+               SelectTable(listmenu->GetSelection());
+               MoleculeUnlock(mview->mol);
+       }
+}
+
+void
+MoleculeView::OnLeftDClickInListCtrl(wxMouseEvent &event)
+{
+       listctrl->OnLeftDClick(event);
+       if (mview->tableIndex >= kMainViewBondTableIndex && mview->tableIndex <= kMainViewImproperTableIndex && mview->mol->par != NULL) {
+               int row, col, i;
+               char names[64], types[64], value[20], params[3][20];
+               char *ptype, *parstr;
+               wxPoint pos = event.GetPosition();
+               if (!listctrl->FindItemAtPosition(pos, &row, &col) || col < 4)
+                       return;
+               /*  Start editing the local parameter; open a separate dialog  */
+               MainView_valueForTable(mview, 1, row, names, sizeof names);
+               MainView_valueForTable(mview, 2, row, types, sizeof types);
+               MainView_valueForTable(mview, 3, row, value, sizeof value);
+               for (i = 0; i < 3; i++) {
+                       MainView_valueForTable(mview, 4 + i, row, params[i], sizeof(params[0]));                        
+               }
+               switch (mview->tableIndex) {
+                       case kMainViewBondTableIndex: ptype = "bond"; break;
+                       case kMainViewAngleTableIndex: ptype = "angle"; break;
+                       case kMainViewDihedralTableIndex: ptype = "dihedral"; break;
+                       case kMainViewImproperTableIndex: ptype = "improper"; break;
+                       default: return;
+               }
+               asprintf(&parstr, "%s %s %s", params[0], params[1], params[2]);
+               MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("sssss"), "cmd_edit_local_parameter_in_mainview", ptype, names, types, value, parstr);
+       }
+}
+
+#pragma mark ====== MyListCtrl data source ======
+
+int
+MoleculeView::GetItemCount(MyListCtrl *ctrl)
+{
+       return MainView_numberOfRowsInTable(mview);
+}
+
+wxString
+MoleculeView::GetItemText(MyListCtrl *ctrl, long row, long column) const
+{
+       char buf[128];
+       MainView_valueForTable(mview, column, row, buf, sizeof buf);
+       wxString *str = new wxString(buf, wxConvUTF8);
+       return *str;
+}
+
+int
+MoleculeView::SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value)
+{
+       MainView_setValueForTable(mview, column, row, value.mb_str(wxConvUTF8));
+       return 0;
+}
+
+void
+MoleculeView::DragSelectionToRow(MyListCtrl *ctrl, long row)
+{
+       MainView_dragTableSelectionToRow(mview, row);
+}
+
+bool
+MoleculeView::IsItemEditable(MyListCtrl *ctrl, long row, long column)
+{
+       return MainView_isTableItemEditable(mview, column, row);
+}
+
+bool
+MoleculeView::IsDragAndDropEnabled(MyListCtrl *ctrl)
+{
+       /*  Only enabled for the atom table  */
+       return (MainView_tableType(mview) == 0);
+}
+
+void
+MoleculeView::OnSelectionChanged(MyListCtrl *ctrl)
+{
+       MainView_setSelectionFromTable(mview);
+}
+
+int
+MoleculeView::SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg)
+{
+       if (mview != NULL && mview->mol != NULL) {
+               if (mview->tableIndex == kMainViewParameterTableIndex && col == -1) {
+                       int src = ParameterTableGetItemSource(mview->mol->par, row);
+                       if (src == -2) {  /* separator line */
+                               bg[0] = bg[1] = bg[2] = 0.6;
+                               return 2;
+                       } else if (src == -1) { /*  undefined parameters  */
+                               bg[0] = 1.0;
+                               bg[1] = bg[2] = 0.2;
+                               return 2;
+                       } else if (src == 0) {  /*  local parameter  */
+                               bg[0] = bg[1] = 1.0;
+                               bg[2] = 0.6;
+                               return 2;
+                       }
+               } else if (mview->tableIndex > 0 && mview->tableIndex < 5) {
+                       return MainView_setColorForTable(mview, col, row, fg, bg);
+               }
+       }
+       return 0;
+}
+
+#pragma mark ====== Plain C interface ======
+
+int
+MainViewCallback_modifierFlags(void *eventRef)
+{
+       int flags = 0;
+       unsigned modifiers;
+       wxMouseState state = ::wxGetMouseState();
+       modifiers = 0;
+       if (state.ShiftDown())
+         flags |= kShiftKeyMask;
+       if (state.AltDown())
+         flags |= kAltKeyMask;
+       return flags;
+}
+
+int
+MainViewCallback_clickCount(void *eventRef)
+{
+  wxMouseEvent *mevent = (wxMouseEvent *)eventRef;
+  if (mevent != NULL) {
+    if (mevent->LeftDClick())
+      return 2;
+    else if (mevent->LeftDown() || mevent->LeftUp())
+      return 1;
+    else return 0;
+  }
+  return 0;
+}
+
+void
+MainViewCallback_lockFocus(MainView *mview)
+{
+       if (mview != NULL && mview->ref != NULL)
+         ((MoleculeView *)(mview->ref))->canvas->SetCurrent();
+}
+
+void
+MainViewCallback_unlockFocus(MainView *mview)
+{
+  //   if (mview != NULL && mview->ref != NULL)
+  //  [[(MyWindowController *)(mview->ref) myOpenGLView] unlockFocus];
+}
+
+void
+MainViewCallback_frame(MainView *mview, float *rect)
+{
+       if (mview != NULL && mview->ref != NULL) {
+         int width, height;
+         ((MoleculeView *)(mview->ref))->canvas->GetClientSize(&width, &height);
+         rect[0] = rect[1] = 0.0;
+         rect[2] = width;
+         rect[3] = height;
+       } else {
+               rect[0] = rect[1] = rect[2] = rect[3] = 0.0;
+       }
+}
+
+void
+MainViewCallback_display(MainView *mview)
+{
+       if (mview != NULL && mview->ref != NULL) {
+         wxWindow *canvas = ((MoleculeView *)(mview->ref))->canvas;
+         canvas->Refresh();
+         canvas->Update();
+       }
+}
+
+void
+MainViewCallback_setNeedsDisplay(MainView *mview, int flag)
+{
+  if (mview != NULL && mview->ref != NULL) {
+    if (flag)
+      ((MoleculeView *)(mview->ref))->canvas->Refresh();
+  }
+}
+
+void
+MainViewCallback_setKeyboardFocus(MainView *mview)
+{
+       if (mview != NULL && mview->ref != NULL) {
+               ((MoleculeView *)(mview->ref))->canvas->SetFocus();
+       }
+}
+
+void
+MainViewCallback_clearLabels(MainView *mview)
+{
+       return;
+/*
+       if (mview != NULL && mview->ref != NULL) {
+               id view = [(MyWindowController *)(mview->ref) myOverlayView];
+               NSRect bounds = [view bounds];
+               [view lockFocus];
+               NSEraseRect(bounds);
+               [[NSColor cyanColor] set];
+               bounds.origin.x = bounds.size.width / 2;
+               NSFrameRect(bounds);
+               [view unlockFocus];
+               [view setNeedsDisplay: YES];
+       }
+*/
+}
+
+void
+MainViewCallback_drawLabel(MainView *mview, const float *pos, const char *label)
+{
+       return;
+/*
+       if (mview != NULL && mview->ref != NULL) {
+               id view = [(MyWindowController *)(mview->ref) myOverlayView];
+               NSString *s = [NSString stringWithUTF8String: label];
+               NSDictionary *attr = [(MyWindowController *)(mview->ref) labelAttributes];
+               [view lockFocus];
+               [s drawAtPoint: NSMakePoint(pos[0], pos[1]) withAttributes: attr];
+               [view unlockFocus];
+               [view setNeedsDisplay: YES];
+       }
+*/
+}
+
+void
+MainViewCallback_drawInfoText(MainView *mview, const char *label)
+{
+       if (mview != NULL && mview->ref != NULL) {
+               wxString labelstr(label, wxConvUTF8);
+               ((MoleculeView *)(mview->ref))->infotext->SetLabel(labelstr);
+       }
+}
+
+int
+MainViewCallback_mouseCheck(MainView *mview)
+{
+       return 0;
+}
+
+void
+MainViewCallback_selectMatrixCellForMode(MainView *mview, int mode)
+{
+       if (mview != NULL && mview->ref != NULL)
+               ((MoleculeView *)(mview->ref))->SelectButtonForMode(mode);
+}
+
+//int
+//MainViewCallback_getTag(MainView *mview)
+//{
+  //   if (mview != NULL && mview->ref != NULL)
+  //   return [(MyWindowController *)(mview->ref) myTag];
+  //else return -1;
+//}
+
+MainView *
+MainViewCallback_viewWithTag(int tag)
+{
+       wxList &doclist = wxGetApp().DocManager()->GetDocuments();
+       wxList::iterator iter;
+       int i = 0;
+       for (i = 0, iter = doclist.begin(); iter != doclist.end(); ++i, ++iter) {
+               if (i == tag) {
+                       return ((MoleculeView *)(((MyDocument *)(*iter))->GetFirstView()))->mview;
+               }
+       }
+       return NULL;
+  //wxList::compatibility_iterator iter = doclist.Item(tag);
+  //if (iter != NULL)
+  //  return (MyDocument *)(*iter)->GetFirstView()->mview;
+  //else
+//    return NULL;
+  //   int i;
+  //   if (sMyWindowControllers == nil)
+  //           return NULL;
+  //   for (i = [sMyWindowControllers count] - 1; i >= 0; i--) {
+  //           id obj = [sMyWindowControllers objectAtIndex: i];
+  //   if ([obj myTag] == tag)
+  //                   return [obj mainView];
+  //   }
+  //   return NULL;
+}
+
+MainView *
+MainViewCallback_activeView(void)
+{
+       MoleculeView *cview = (MoleculeView *)(wxGetApp().DocManager()->GetCurrentView());
+       if (cview == NULL)
+               return NULL;
+       else
+               return cview->mview;
+//     return ((MoleculeView *)(wxGetApp().DocManager()->GetCurrentView()))->mview;
+  //   return MainViewCallback_viewWithTag(sLastMainViewTag);
+}
+
+MainView *
+MainViewCallback_newFromFile(const char *fname)
+{
+  wxDocument *doc;
+  wxDocManager *manager = wxGetApp().DocManager();
+  if (fname == NULL || *fname == 0) {
+    doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
+  } else {
+    wxString fnamestr(fname, wxConvUTF8);  /*  UTF8 -> wxString  */
+    doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
+  }
+  return MainViewCallback_activeView();
+}
+
+int
+MainViewCallback_importFromFile(MainView *mview, const char *fname)
+{
+  MyDocument *doc;
+  if (mview != NULL && mview->ref != NULL && (doc = (((MoleculeView *)(mview->ref))->MolDocument())) != NULL) {
+    wxString fnamestr(fname, wxConvUTF8);  /*  UTF8 -> wxString  */
+    // doc->importFromFile(fnamestr);
+    MainViewCallback_setNeedsDisplay(mview, 1);
+    return 1;
+  }
+  return 0;
+}
+
+void
+MainViewCallback_getFilename(MainView *mview, char *buf, int bufsize)
+{
+  MyDocument *doc;
+  if (mview != NULL && mview->ref != NULL && (doc = (((MoleculeView *)(mview->ref))->MolDocument())) != NULL) {
+    wxString fname;
+    fname = doc->GetFilename();
+    strncpy(buf, (const char*)fname.mb_str(wxConvUTF8), bufsize - 1);  /*  wxString -> UTF8  */
+    buf[bufsize - 1] = 0;
+  } else {
+    buf[0] = 0;
+  }
+}
+
+void
+MainViewCallback_moleculeReplaced(MainView *mview, struct Molecule *mol)
+{
+       if (mview != NULL && mview->ref != NULL) {
+               MyDocument *doc = ((MoleculeView *)(mview->ref))->MolDocument();
+               if (doc != NULL)
+                       doc->SetMolecule(mol);
+               MyListCtrl *listctrl = ((MoleculeView *)(mview->ref))->GetListCtrl();
+               if (listctrl != NULL)
+                       listctrl->SetDataSource((MoleculeView *)(mview->ref));
+       }
+}
+
+typedef struct Label {
+  //   StringTexture *tex;
+} Label;
+
+struct Label *
+MainViewCallback_newLabel(MainView *mview, const char *message, float fontsize, const float *forecolor, const float *backcolor)
+{
+  /*
+       Label *label;
+       NSDictionary *attr;
+       NSColor *textColor, *boxColor;
+       label = (Label *)malloc(sizeof(Label));
+       if (label == NULL)
+               return NULL;
+       memset(label, 0, sizeof(Label));
+//     MainViewCallback_lockFocus(mview);
+       if (forecolor != NULL)
+               textColor = [NSColor colorWithDeviceRed: forecolor[0] green: forecolor[1] blue: forecolor[2] alpha: forecolor[3]];
+       else
+               textColor = [NSColor whiteColor];
+       if (backcolor != NULL)
+               boxColor = [NSColor colorWithDeviceRed: backcolor[0] green: backcolor[1] blue: backcolor[2] alpha: backcolor[3]];
+       else
+               boxColor = [NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:0.0f];
+       attr = [NSDictionary dictionaryWithObjectsAndKeys: [NSFont userFontOfSize: fontsize], NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil];
+       label->tex = [[StringTexture alloc] 
+               initWithString: [NSString stringWithUTF8String: message] 
+               withAttributes: attr
+               withTextColor: textColor
+               withBoxColor: boxColor
+               withBorderColor: [NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:0.0f]];
+//     MainViewCallback_unlockFocus(mview);
+       return label;
+  */
+}
+
+void
+MainViewCallback_releaseLabel(struct Label *label)
+{
+  /*   if (label != NULL) {
+               [label->tex release];
+               free(label);
+       }
+  */
+}
+
+void
+MainViewCallback_drawLabelAtPoint(struct Label *label, const float *pos)
+{
+  /*
+       if (label != NULL && pos != NULL) {
+       //      GLint matrixMode;
+               glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+       //      glGetIntegerv (GL_MATRIX_MODE, &matrixMode);
+       //      glMatrixMode(GL_MODELVIEW);
+       //      glPushMatrix();
+       //      glTranslatef(0.0f, 0.0f, pos[3]);
+               [label->tex drawAtPoint: NSMakePoint(pos[0], pos[1]) withDepth: pos[2]];
+       //      glPopMatrix();
+       //      glMatrixMode (matrixMode);
+       }
+  */
+}
+
+void
+MainViewCallback_labelSize(struct Label *label, float *outSize)
+{
+  /*   if (label != NULL) {
+               NSSize size = [label->tex frameSize];
+               if (outSize != NULL) {
+                       outSize[0] = size.width;
+                       outSize[1] = size.height;
+               }
+       }
+  */
+}
+
+#pragma mark ====== Plain C Interface (MyListCtrl) ======
+
+/*  These interface functions are also used for accessing MyListCtrl of GlobalParameterFrame (when mview == NULL) */
+
+static MyListCtrl *
+s_MyListCtrlFromMainView(MainView *mview)
+{
+       if (mview == NULL)
+               return wxGetApp().GetGlobalParameterListCtrl();
+       if (mview != NULL && mview->ref != NULL)
+               return ((MoleculeView *)(mview->ref))->GetListCtrl();
+       else return NULL;
+}
+
+void
+MainViewCallback_selectTable(MainView *mview, int idx)
+{
+       if (mview != NULL && mview->ref != NULL)
+               ((MoleculeView *)(mview->ref))->SelectTable(idx);
+}
+
+int
+MainViewCallback_numberOfTableColumns(MainView *mview)
+{
+       MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
+       if (listctrl != NULL)
+               return listctrl->GetColumnCount();
+       else return 0;
+}
+
+int
+MainViewCallback_addTableColumn(MainView *mview, const char *name, int width, int editable)
+{
+       int idx;
+       wxString nstr(name, wxConvUTF8);
+       MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
+       if (listctrl == NULL)
+               return 0;
+       idx = listctrl->GetColumnCount();
+       listctrl->InsertColumn(idx, nstr, wxLIST_FORMAT_LEFT);
+       listctrl->SetColumnWidth(idx, width * 10);
+       return idx;
+}
+
+int
+MainViewCallback_removeTableColumnAtIndex(MainView *mview, int idx)
+{
+       int ncolumns;
+       MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
+       if (listctrl == NULL)
+               return 0;
+       ncolumns = listctrl->GetColumnCount();
+       if (idx >= 0 && idx < ncolumns) {
+               listctrl->DeleteColumn(idx);
+               ncolumns--;
+       }
+       return ncolumns;
+}
+
+void
+MainViewCallback_reloadTableData(MainView *mview)
+{
+       MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
+       if (listctrl != NULL)
+               listctrl->RefreshTable();
+/*     
+       int nrows = MainView_numberOfRowsInTable(mview);
+       listctrl->SetItemCount(nrows);
+       if (nrows > 0)
+               listctrl->RefreshItems(0, nrows - 1);
+*/
+}
+
+void
+MainViewCallback_setTableSelection(MainView *mview, IntGroup *selection)
+{
+       int i, n, n1, n2;
+       MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
+       if (listctrl == NULL)
+               return;
+       n = 0;
+       listctrl->EnableSelectionChangeNotification(false);
+       for (i = 0; (n1 = IntGroupGetStartPoint(selection, i)) >= 0; i++) {
+               n2 = IntGroupGetEndPoint(selection, i);
+               while (n < n1) {
+                       listctrl->SetItemState(n, 0, wxLIST_STATE_SELECTED);
+                       n++;
+               }
+               while (n < n2) {
+                       listctrl->SetItemState(n, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+                       n++;
+               }
+       }
+       listctrl->RefreshTable();
+       n1 = MainView_numberOfRowsInTable(mview);
+       while (n < n1) {
+               listctrl->SetItemState(n, 0, wxLIST_STATE_SELECTED);
+               n++;
+       }
+//     listctrl->RefreshItems(0, n1 - 1);
+       {
+               //  EVT_LIST_ITEM_SELECTED is sent by wxPostEvent rather than ProcessEvent,
+               //  so re-enable of table selection event should also be sent by wxPostEvent.
+               //  Otherwise, the enable flag is set to true _before_ EVT_LIST_ITEM_SELECTED is sent.
+               wxCommandEvent myEvent(MyListCtrlEvent, MyListCtrlEvent_enableTableSelectionNotification);
+               wxPostEvent(listctrl, myEvent);
+       }
+}
+
+IntGroup *
+MainViewCallback_getTableSelection(MainView *mview)
+{
+       int i, n;
+       IntGroup *ig;
+       MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
+       if (listctrl == NULL)
+               return NULL;
+       ig = IntGroupNew();
+       n = MainView_numberOfRowsInTable(mview);
+       for (i = 0; i < n; i++) {
+               if (listctrl->GetItemState(i, wxLIST_STATE_SELECTED) != 0)
+                       IntGroupAdd(ig, i, 1);
+       }
+       return ig;
+}
+
+void
+MainViewCallback_showTable(MainView *mview)
+{
+  //   if (mview != NULL && mview->ref != NULL) {
+  //   MyDocument *doc = (MyDocument *)[(id)(mview->ref) document];
+  //   [doc showTable: doc];
+  //}
+}
+
+void
+MainViewCallback_hideTable(MainView *mview)
+{
+  //   if (mview != NULL && mview->ref != NULL) {
+  //   MyDocument *doc = (MyDocument *)[(id)(mview->ref) document];
+  //           [doc hideTable: doc];
+  //   }
+}
diff --git a/wxSources/MoleculeView.h b/wxSources/MoleculeView.h
new file mode 100755 (executable)
index 0000000..b3ecd5e
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  MoleculeView.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MoleculeView_h__
+#define __MoleculeView_h__
+
+#include "wx/docview.h"
+
+#include "../MolLib/MolLib.h"
+#include "MyListCtrl.h"
+
+class MyDocument;
+class MyGLCanvas;
+class wxMenu;
+class wxToggleButton;
+class wxStaticText;
+class wxChoice;
+
+class MoleculeView: public wxView, public MyListCtrlDataSource
+{
+public:
+    wxMDIChildFrame *frame;
+    MyGLCanvas *canvas;
+    MainView *mview;
+       wxChoice *listmenu;
+       MyListCtrl *listctrl;
+       wxMenu *file_history_menu;
+       wxMenu *edit_menu;
+       wxToggleButton *tbuttons[6];
+       wxStaticText *infotext;
+       wxPanel *frameControlPanel;
+       wxSlider *frameSlider;
+       wxTextCtrl *frameText;
+
+       bool isRebuildingTable;
+
+    MoleculeView() { canvas = (MyGLCanvas *) NULL; frame = (wxMDIChildFrame *) NULL; }
+    ~MoleculeView() {}
+
+    MyDocument *MolDocument() { return (MyDocument *)m_viewDocument; }
+       MyListCtrl *GetListCtrl() { return listctrl; }
+
+    bool OnCreate(wxDocument *doc, long flags);
+    void OnDraw(wxDC *dc);
+    void OnUpdate(wxView *sender, wxObject *hint = (wxObject *) NULL);
+    bool OnClose(bool deleteWindow = true);
+
+       void OnButtonPressed(wxCommandEvent &event);
+       void OnSliderAction(wxCommandEvent &event);
+
+       void OnFrameButtonAction(wxMouseEvent &event);
+       void OnFrameSliderAction(wxScrollEvent &event);
+       void OnFrameTextAction(wxCommandEvent &event);
+
+       void OnDocumentModified(wxCommandEvent &event);
+       void OnChar(wxKeyEvent &event);
+       void OnScriptMenuModified(wxCommandEvent& event);
+       void OnLeftDClickInListCtrl(wxMouseEvent& event);
+
+       void SelectButtonForMode(int mode);
+       void UpdateFrameControlValues();
+       void UpdateFrameControls();
+       
+       void SelectTable(int idx);
+       void OnSelectTable(wxCommandEvent &event);
+
+       /*  MyListCtrlDataSource functions  */
+       virtual int GetItemCount(MyListCtrl *ctrl);
+       virtual wxString GetItemText(MyListCtrl *ctrl, long row, long column) const;
+       virtual int SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value);
+       virtual void DragSelectionToRow(MyListCtrl *ctrl, long row);
+       virtual bool IsItemEditable(MyListCtrl *ctrl, long row, long column);
+       virtual bool IsDragAndDropEnabled(MyListCtrl *ctrl);
+       virtual void OnSelectionChanged(MyListCtrl *ctrl);
+       virtual int SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg);
+
+private:
+    DECLARE_DYNAMIC_CLASS(MoleculeView)
+    DECLARE_EVENT_TABLE()
+};
+
+#endif
diff --git a/wxSources/MyApp.cpp b/wxSources/MyApp.cpp
new file mode 100755 (executable)
index 0000000..7628085
--- /dev/null
@@ -0,0 +1,1282 @@
+/*
+ *  MyApp.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error "You should have DocView architecture enabled in your wxWidgets installation."
+#endif
+
+#if !wxUSE_MDI_ARCHITECTURE
+#error "You should have MDI architecture enabled in your wxWidgets installation."
+#endif
+
+#include "wx/filename.h"
+#include "wx/progdlg.h"
+#include "wx/sysopt.h"
+#include "wx/regex.h"
+#include "wx/stdpaths.h"
+#include "wx/textfile.h"
+#include "wx/process.h"
+
+#include "MyApp.h"
+#include "MyDocument.h"
+#include "MoleculeView.h"
+#include "ConsoleFrame.h"
+#include "ProgressFrame.h"
+#include "GlobalParameterFrame.h"
+#include "GlobalParameterFilesFrame.h"
+
+#include "../MolLib/MolLib.h"
+#include "../MolLib/Ruby_bind/Molby.h"
+#include "../MolLib/Missing.h"
+
+#include <wchar.h>
+#include <stdio.h>
+
+#if defined(__WXMAC__)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#pragma mark ====== MyApp ======
+
+MyFrame *frame = (MyFrame *) NULL;
+
+IMPLEMENT_APP(MyApp)
+
+//IMPLEMENT_CLASS(MyApp, wxApp)
+
+BEGIN_EVENT_TABLE(MyApp, wxApp)
+       //EVT_KEY_DOWN(MyApp::OnChar)
+       //EVT_MOUSE_EVENTS(MyApp::OnMouseEvent)
+       EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
+       EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
+       EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
+       EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
+//     EVT_MENU(myMenuID_ReadParameters, MyApp::OnReadParameters)
+       EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
+       EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
+       EVT_END_PROCESS(-1, MyApp::OnEndProcess)
+END_EVENT_TABLE()
+
+//  Find the path of the directory where the relevant resources are to be found.
+//  Mac: the "Resources" directory in the application bundle.
+//  Windows: the directory in which the application executable is located.
+//  UNIX: ?
+static wxString
+wxFindResourcePath()
+{
+#if defined(__WXMAC__)
+       CFBundleRef mainBundle = CFBundleGetMainBundle();
+       CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
+       if (ref != NULL) {
+               UInt8 buffer[256];
+               if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
+                       wxString dirname((const char *)buffer, wxConvUTF8);
+                       CFRelease(ref);
+                       return dirname;
+               }
+               CFRelease(ref);
+       }
+       return wxEmptyString;
+#elif defined(__WXMSW__)
+    wxString str;
+       wxString argv0 = wxTheApp->argv[0];
+       //  Is it an absolute path?
+    if (wxIsAbsolutePath(argv0)) {
+        return wxPathOnly(argv0);
+    } else {
+        //  Is it a relative path?
+        wxString currentDir = wxGetCwd();
+        if (currentDir.Last() != wxFILE_SEP_PATH)
+            currentDir += wxFILE_SEP_PATH;             
+        str = currentDir + argv0;
+        if (wxFileExists(str))
+            return wxPathOnly(str);
+    }
+       //  Search PATH
+    wxPathList pathList;
+    pathList.AddEnvList(wxT("PATH"));
+    str = pathList.FindAbsoluteValidPath(argv0);
+    if (!str.IsEmpty())
+        return wxPathOnly(str);
+    return wxEmptyString;
+#else
+#error "wxFindResourcePath is not defined for UNIXes."
+#endif
+}
+
+MyApp::MyApp(void)
+{
+    m_docManager = NULL;
+       m_progressFrame = NULL;
+       m_processTerminated = false;
+       m_processExitCode = 0;
+       countScriptMenu = 0;
+       scriptMenuCommands = NULL;
+       scriptMenuTitles = NULL;
+       scriptMenuModifiedEventPosted = false;
+       parameterFrame = NULL;
+       parameterFilesFrame = NULL;
+       consoleFrame = NULL;
+}
+
+bool MyApp::OnInit(void)
+{
+
+       //  Set defaults
+#ifdef __WXMAC__
+       wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
+#endif
+
+#if __WXMSW__
+       {
+               //  Check if the same application is already running
+               char *buf;
+               asprintf(&buf, "Molby-%s", wxGetUserId().c_str());
+               wxString name(buf, wxConvUTF8);
+               free(buf);
+               m_checker = new wxSingleInstanceChecker(name);
+               if (m_checker->IsAnotherRunning()) {
+                       wxLogError(_T("Molby is already running."));
+                       return false;
+               }
+       }
+#endif
+       
+       // Create a document manager
+       m_docManager = new MyDocManager;
+
+       // Create templates relating drawing documents to their views
+       new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Gaussian Output File"), _T("*.out"), _T(""), _T("out"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("GAMESS Output File"), _T("*.log"), _T(""), _T("log"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+
+       // Create the main frame window
+       frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
+                      _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
+                      wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
+
+       // Give it an icon (this is ignored in MDI mode: uses resources)
+#ifdef __WXMSW__
+       frame->SetIcon(wxIcon(_T("doc")));
+#endif
+#ifdef __X__
+       frame->SetIcon(wxIcon(_T("doc.xbm")));
+#endif
+
+       wxMenuBar *menu_bar = CreateMenuBar(0);
+       
+#ifdef __WXMAC__
+       wxMenuBar::MacSetCommonMenuBar(menu_bar);
+#endif
+
+       // Associate the menu bar with the frame
+       frame->SetMenuBar(menu_bar);
+
+       frame->Centre(wxBOTH);
+
+#if defined(__WXMAC__)
+       frame->Move(-10000, -10000);  //  Set invisible
+#else
+       frame->Show(true);
+#endif
+
+       SetTopWindow(frame);
+
+       //  Load default settings from the preference file
+       LoadDefaultSettings();
+       
+       //  Create a console window
+       consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
+       consoleFrame->Show(true);
+
+       /*  Initialize Ruby interpreter with the startup script  */
+       {
+               static const char fname[] = "startup.rb";
+               wxString dirname = wxFindResourcePath();
+               char *wbuf;
+       
+               dirname += wxFILE_SEP_PATH;
+               dirname += wxT("Scripts");
+               wxString cwd = wxGetCwd();
+               wxSetWorkingDirectory(dirname);
+
+               /*  Read atom display parameters  */
+               if (AtomParameterInitialize("dispatom.par", &wbuf) != 0) {
+                       SetConsoleColor(1);
+                       AppendConsoleMessage(wbuf);
+                       SetConsoleColor(0);
+                       free(wbuf);
+               }
+               
+               /*  Read default parameters  */
+               ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
+               if (wbuf != NULL) {
+                       SetConsoleColor(1);
+                       AppendConsoleMessage(wbuf);
+                       SetConsoleColor(0);
+                       free(wbuf);
+               }
+
+               wxString fnamestr(fname, wxConvUTF8);
+               Molby_startup(wxFileExists(fnamestr) ? fname : NULL, dirname.mb_str(wxConvUTF8));
+               
+               wxSetWorkingDirectory(cwd);
+               MyAppCallback_showScriptMessage("%% ");
+       }
+       
+       /*  Open given files as MyDocument  */
+       if (argc == 1) {
+#if __WXMSW__
+               m_docManager->CreateDocument(_T(""), wxDOC_NEW);
+#endif
+       } else {
+               while (argc > 1) {
+                       wxString file(argv[1]);
+                       m_docManager->CreateDocument(file, wxDOC_SILENT);
+                       argc--;
+                       argv++;
+               }
+       }
+       
+       return true;
+}
+
+//  Create Menu Bars
+//  kind == 0: main menu
+//  kind == 1: molecule window
+//  kind == 2: console window
+wxMenuBar *
+MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
+{
+       
+       //// Make a menubar
+       wxMenu *file_menu = new wxMenu;
+
+       file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
+       file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
+       if (out_file_history_menu != NULL) {
+               *out_file_history_menu = new wxMenu;
+               file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
+               m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
+               m_docManager->FileHistoryUseMenu(*out_file_history_menu);  //  Should be removed when menu is discarded
+       }
+
+       file_menu->AppendSeparator();
+       file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
+       file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
+       file_menu->Append(wxID_SAVEAS, _T("Save &As..."));      
+       
+       file_menu->AppendSeparator();
+       file_menu->Append(myMenuID_Import, _T("Import..."));    
+       file_menu->Append(myMenuID_Export, _T("Export..."));    
+       
+       file_menu->AppendSeparator();
+       file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
+       file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
+       file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
+       
+       file_menu->AppendSeparator();
+#if defined(__WXMAC__)
+       file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
+#else
+       file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
+#endif
+
+       wxMenu *edit_menu = new wxMenu;
+       edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
+       edit_menu->Append(wxID_REDO, _T("&Redo"));
+       edit_menu->AppendSeparator();
+       edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
+       edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
+       edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
+       edit_menu->Append(wxID_CLEAR, _T("Clear"));
+       edit_menu->AppendSeparator();
+       edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
+       edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
+       edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
+       edit_menu->AppendSeparator();
+       wxMenu *add_hydrogen_menu = new wxMenu;
+       add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
+       add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
+       add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
+       add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
+       add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
+       edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
+       
+       if (out_edit_menu != NULL)
+               *out_edit_menu = edit_menu;     // Should be associated with the command processor if available
+       
+       wxMenu *show_menu = new wxMenu;
+       show_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
+       show_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
+       show_menu->AppendSeparator();
+       show_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
+/*     show_menu->Append(myMenuID_ShowPeriodicBox, _T("Show Periodic Box"), _T(""), wxITEM_CHECK); */
+       show_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
+       show_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
+       show_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
+       show_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
+       show_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK);
+       show_menu->AppendSeparator();
+       show_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
+       show_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
+       show_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
+       show_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
+       show_menu->AppendSeparator();
+       show_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
+       show_menu->AppendSeparator();
+       show_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
+
+       wxMenu *md_menu = new wxMenu;
+       md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
+       md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
+       md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
+       md_menu->AppendSeparator();
+//     md_menu->Append(myMenuID_ReadParameters, _T("Read Parameters..."));     
+       md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
+       md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
+       md_menu->AppendSeparator();
+       md_menu->Append(myMenuID_DefinePeriodicBox, _T("Define Unit Cell..."));
+       md_menu->Append(myMenuID_ShowPeriodicImage, _T("Show Periodic Image..."));
+       md_menu->Append(myMenuID_PressureControl, _T("Pressure Control..."));
+/*     md_menu->Append(myMenuID_DefineSymmetry, _T("Define Symmetry Operations..."));
+       md_menu->Append(myMenuID_ExpandBySymmetry, _T("Expand by Symmetry...")); */
+       md_menu->AppendSeparator();
+       wxMenu *md_tools_menu = new wxMenu;
+       md_tools_menu->Append(myMenuID_RunAntechamber, _T("Antechamber/parmchk..."));
+       md_tools_menu->Append(myMenuID_RunResp, _T("GAMESS/RESP..."));
+       md_menu->Append(myMenuID_MDTools, _T("Tools"), md_tools_menu);
+
+       wxMenu *qc_menu = new wxMenu;
+       qc_menu->Append(myMenuID_CreateGamessInput, _T("Create GAMESS input..."));
+       qc_menu->Append(myMenuID_CreateMOCube, _T("Create MO cube..."));
+       
+       wxMenu *script_menu = new wxMenu;
+       script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
+       script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window..."));
+       script_menu->AppendSeparator();
+       countNonCustomScriptMenu = script_menu->GetMenuItemCount();
+
+       wxMenu *help_menu = new wxMenu;
+       help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
+       
+       wxMenuBar *menu_bar = new wxMenuBar;
+       
+       menu_bar->Append(file_menu, _T("&File"));
+       menu_bar->Append(edit_menu, _T("&Edit"));
+       menu_bar->Append(show_menu, _T("Show"));
+       menu_bar->Append(md_menu, _T("MM/MD"));
+       menu_bar->Append(qc_menu, _T("QChem"));
+       menu_bar->Append(script_menu, _T("&Script"));
+       menu_bar->Append(help_menu, _T("&Help"));
+       
+       UpdateScriptMenu(menu_bar);
+       
+       return menu_bar;
+}
+
+#if __WXMAC__
+/*  When the application is launched without any documents, an empty document is opened.
+    This should be implemented by overriding this special method; parsing argc/argv does
+    not work, because the list of files is passed through an Apple Event.  */
+void
+MyApp::MacNewFile()
+{
+       m_docManager->CreateDocument(_T(""), wxDOC_NEW);
+}
+#endif
+
+int MyApp::OnExit(void)
+{
+       SaveDefaultSettings();
+    delete m_docManager;
+#if __WXMSW__
+       delete m_checker;
+#endif
+    return 0;
+}
+
+int
+MyApp::AppendConsoleMessage(const char *mes)
+{
+       wxTextCtrl *textCtrl;
+       if (consoleFrame != NULL && (textCtrl = consoleFrame->textCtrl) != NULL) {
+               wxString string(mes, wxConvUTF8);
+               textCtrl->AppendText(string);
+               return string.Len();
+       } else return 0;
+}
+
+void
+MyApp::FlushConsoleMessage()
+{
+       wxTextCtrl *textCtrl = consoleFrame->textCtrl;
+       textCtrl->Refresh();
+       textCtrl->Update();
+}
+
+void
+MyApp::SetConsoleColor(int color)
+{
+       wxTextCtrl *textCtrl = consoleFrame->textCtrl;
+       static wxTextAttr *col[4];
+       if (col[0] == NULL) {
+               col[0] = new wxTextAttr(*wxBLACK);
+               col[1] = new wxTextAttr(*wxRED);
+               col[2] = new wxTextAttr(*wxGREEN);
+               col[3] = new wxTextAttr(*wxBLUE);
+       }
+       textCtrl->SetDefaultStyle(*(col[color % 4]));
+}
+
+void
+MyApp::ShowProgressPanel(const char *mes)
+{
+       wxString string((mes ? mes : ""), wxConvUTF8);
+       if (m_progressFrame == NULL) {
+#if __WXMAC__
+               {
+                       wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
+                       wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
+                       if (quitMenuItem != NULL)
+                               quitMenuItem->Enable(false);
+                       mbar->Enable(false);
+               }
+#endif
+               m_progressFrame = new ProgressFrame(_T("Progress"), string);
+               m_progressCanceled = false;
+               m_progressValue = -1;
+       }
+}
+
+void
+MyApp::HideProgressPanel()
+{
+       if (m_progressFrame != NULL) {
+               m_progressFrame->Hide();
+               m_progressFrame->Destroy();
+               m_progressFrame = NULL;
+#if __WXMAC__
+               {
+                       wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
+                       mbar->Enable(true);
+                       wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
+                       if (quitMenuItem != NULL)
+                               quitMenuItem->Enable(true);
+               }
+#endif
+       }
+}
+
+void
+MyApp::SetProgressValue(double dval)
+{
+       if (m_progressFrame != NULL) {
+               m_progressFrame->SetProgressValue(dval);
+       }
+}
+
+void
+MyApp::SetProgressMessage(const char *mes)
+{
+       if (m_progressFrame != NULL) {
+               wxString string((mes ? mes : ""), wxConvUTF8);
+               m_progressFrame->SetProgressMessage(string);
+       }
+}
+
+int
+MyApp::IsInterrupted()
+{
+       return m_progressFrame->CheckInterrupt();
+}
+
+#warning "TODO: Move this to MyDocument and 'import parameters' "
+/*
+ void
+MyApp::OnReadParameters(wxCommandEvent& event)
+{
+       wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Parameter File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
+       if (dialog->ShowModal() == wxID_OK) {
+               char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
+               char *wbuf;
+               ParameterReadFromFile(NULL, p, &wbuf, NULL);
+               if (wbuf != NULL) {
+                       SetConsoleColor(1);
+                       AppendConsoleMessage(wbuf);
+                       SetConsoleColor(0);
+                       free(wbuf);
+               }
+               free(p);
+       }
+       dialog->Destroy();
+}
+*/
+
+void
+MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
+{
+       consoleFrame->Show(true);
+       consoleFrame->Raise();
+}
+
+void
+MyApp::OnViewGlobalParameters(wxCommandEvent& event)
+{
+       if (parameterFrame == NULL) {
+               parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
+               MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
+       }
+       MainView_refreshTable(NULL);
+       parameterFrame->Show(true);
+       parameterFrame->Raise();
+}
+
+void
+MyApp::OnViewParameterFilesList(wxCommandEvent &event)
+{
+       if (parameterFilesFrame == NULL) {
+               parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
+       }
+       parameterFilesFrame->Show(true);
+       parameterFilesFrame->Raise();
+}
+
+void
+MyApp::RegisterScriptMenu(const char *cmd, const char *title)
+{
+       int i;
+       if (cmd[0] == 0 && title[0] == 0)
+               i = countScriptMenu;  /*  A sepearator */
+       else {
+               for (i = 0; i < countScriptMenu; i++) {
+                       if (strcmp(cmd, scriptMenuCommands[i]) == 0) {
+                               free(scriptMenuTitles[i]);
+                               scriptMenuTitles[i] = strdup(title);
+                               break;
+                       } else if (strcmp(title, scriptMenuTitles[i]) == 0) {
+                               free(scriptMenuCommands[i]);
+                               scriptMenuCommands[i] = strdup(cmd);
+                               break;
+                       }
+               }
+       }
+       if (i >= countScriptMenu) {
+               if (countScriptMenu == 0) {
+                       scriptMenuTitles = (char **)malloc(sizeof(char *));
+                       scriptMenuCommands = (char **)malloc(sizeof(char *));
+               } else {
+                       scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
+                       scriptMenuCommands = (char **)realloc(scriptMenuCommands, sizeof(char *) * (countScriptMenu + 1));
+               }
+               scriptMenuTitles[countScriptMenu] = strdup(title);
+               scriptMenuCommands[countScriptMenu] = strdup(cmd);
+               countScriptMenu++;
+       }
+       if (!scriptMenuModifiedEventPosted) {
+               wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
+               wxPostEvent(this, myEvent);     
+               scriptMenuModifiedEventPosted = true;
+       }
+}
+
+void
+MyApp::UpdateScriptMenu(wxMenuBar *mbar)
+{
+       int i;
+
+       wxMenu *smenu = mbar->GetMenu(myMenuIndex_Script);
+       if (smenu == NULL)
+               return;
+       
+       //  Remove all custom items
+       for (i = smenu->GetMenuItemCount() - 1; i >= countNonCustomScriptMenu; i--) {
+               wxMenuItem *item = smenu->FindItemByPosition(i);
+               if (!item->IsSeparator()) {
+                       int n = item->GetId();
+                       Disconnect(n, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
+               }
+               smenu->Remove(item);
+               delete item;
+       }
+       
+       //  Build script menu from internal array
+       for (i = 0; i < countScriptMenu; i++) {
+               const char *title = scriptMenuTitles[i];
+               if (title == NULL || title[0] == 0) {
+                       smenu->AppendSeparator();
+               } else {
+                       wxString stitle(scriptMenuTitles[i], wxConvUTF8);
+                       wxMenuItem *item = new wxMenuItem(smenu, myMenuID_CustomScript + i, stitle);
+                       smenu->Append(item);
+               }
+       }
+       Connect(myMenuID_CustomScript, myMenuID_CustomScript + countScriptMenu - 1, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
+}
+
+void
+MyApp::OnScriptMenuModified(wxCommandEvent& event)
+{
+       scriptMenuModifiedEventPosted = false;
+       UpdateScriptMenu(GetMainFrame()->GetMenuBar());
+       UpdateScriptMenu(consoleFrame->GetMenuBar());
+       event.Skip();
+}
+
+void
+MyApp::OnScriptMenuSelected(wxCommandEvent& event)
+{
+       char *cmd;
+       int methodType;
+       MainView *mview;
+       Molecule *mol;
+       int index = event.GetId() - myMenuID_CustomScript;
+       if (index < 0 || index >= countScriptMenu)
+               return;
+       cmd = scriptMenuCommands[index];
+       methodType = Ruby_methodType("Molecule", cmd);
+       if (methodType == 0)
+               return;
+       mview = MainViewCallback_activeView();
+       if (mview == NULL)
+               mol = NULL;
+       else mol = mview->mol;
+       if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
+               MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), cmd);
+       else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
+               MolActionCreateAndPerform(NULL, SCRIPT_ACTION("M"), cmd, mol);
+       else return;
+}
+
+void
+MyApp::OnUpdateUI(wxUpdateUIEvent& event)
+{
+       int uid = event.GetId();
+       MainView *mview = MainViewCallback_activeView();
+       if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
+               //  Check the script menu
+               char *cmd;
+               int methodType;
+               Molecule *mol;
+               int index = uid - myMenuID_CustomScript;
+               cmd = scriptMenuCommands[index];
+               methodType = Ruby_methodType("Molecule", cmd);
+               event.Enable(false);
+               if (methodType != 0) {
+                       if (mview == NULL)
+                               mol = NULL;
+                       else mol = mview->mol;
+                       if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
+                               event.Enable(true);
+                       else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
+                               event.Enable(true);
+               }
+       } else if (uid == myMenuID_ExecuteScript || uid == myMenuID_OpenConsoleWindow || uid == myMenuID_ViewParameterFilesList || uid == myMenuID_ViewGlobalParameters) {
+               event.Enable(true);
+       } else {
+               if (mview == NULL)
+                       event.Enable(false);
+               else
+                       event.Skip();
+       }
+}
+
+void
+MyApp::OnExecuteScript(wxCommandEvent &event)
+{
+       wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
+       if (dialog->ShowModal() == wxID_OK) {
+               int status;
+               wxString path = dialog->GetPath();
+               
+               //  Command line: execute_script('pathname')
+               wxString cline(path);
+               wxRegEx re(_T("[\\\\']"));   //  A backslash and a single-quote
+               re.Replace(&cline, _T("\\\\\\0"));  //  A backslash followed by "\0"
+               cline.Prepend(_T("execute_script('"));
+               cline += _T("')");
+               MyAppCallback_setConsoleColor(3);
+               wxGetApp().AppendConsoleMessage((const char *)(cline.mb_str(wxConvFile)));
+               wxGetApp().AppendConsoleMessage("\n");
+               MyAppCallback_setConsoleColor(0);
+
+               MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
+               if (status != 0)
+                       Molby_showError(status);
+       }
+       dialog->Destroy();
+}
+
+wxString
+MyApp::DefaultSettingsPath()
+{
+       wxString name = wxStandardPaths::Get().GetUserConfigDir();
+       wxChar sep = wxFileName::GetPathSeparator();
+       if (name[name.Len() - 1] != sep)
+               name += sep;
+       name += _T("Molby.settings");
+       return name;
+}
+
+void
+MyApp::LoadDefaultSettings()
+{
+       wxString name = DefaultSettingsPath();
+       m_defaultSettings.clear();
+       wxTextFile file(name);
+       if (file.Exists() && file.Open()) {
+               wxString line;
+               int pos;
+               for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
+                       if (line[0] == '#')
+                               continue;
+                       if ((pos = line.Find('=')) != wxNOT_FOUND) {
+                               wxString key = line.Left(pos);
+                               wxString value = line.Right(line.Length() - pos - 1);
+                               SetDefaultSetting(key, value);
+                       }
+                       if (file.Eof())
+                               break;
+               }
+               file.Close();
+       }
+}
+
+void
+MyApp::SaveDefaultSettings()
+{
+       wxString name = DefaultSettingsPath();
+       wxTextFile file(name);
+       if (!file.Exists())
+               file.Create();
+       else
+               file.Open();
+       file.Clear();
+       MyStringHash::iterator it;
+       for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
+               wxString key = it->first;
+               wxString value = it->second;
+               wxString line = key + _T("=") + value;
+               file.AddLine(line);
+       }
+       file.Write();
+       file.Close();
+}
+
+void
+MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
+{
+       //  TODO: The '=' and '#' characters may need to be escaped
+       m_defaultSettings[key] = value;
+}
+
+wxString &
+MyApp::GetDefaultSetting(const wxString& key)
+{
+       return m_defaultSettings[key];
+}
+
+MyListCtrl *
+MyApp::GetGlobalParameterListCtrl()
+{
+       if (parameterFrame != NULL)
+               return parameterFrame->GetListCtrl();
+       else return NULL;
+}
+
+void
+MyApp::OnEndProcess(wxProcessEvent &event)
+{
+       m_processTerminated = true;
+       m_processExitCode = event.GetExitCode();
+}
+
+int
+MyApp::CallSubProcess(const char *cmdline, const char *procname)
+{
+       const int sEndProcessMessageID = 2;
+       int status = 0;
+       char buf[256];
+       FILE *fplog;
+       size_t len, len_total;
+       wxString cmdstr(cmdline, wxConvUTF8);
+
+       //  Show progress panel
+       if (procname == NULL)
+               procname = "subprocess";
+       snprintf(buf, sizeof buf, "Running %s...", procname);
+       ShowProgressPanel(buf);
+       
+       //  Create log file in the current directory
+       snprintf(buf, sizeof buf, "%s.log");
+       fplog = fopen(buf, "w");
+       if (fplog == NULL)
+               return -1;
+
+       //  Create proc object and call subprocess
+       wxProcess *proc = new wxProcess(wxGetApp().GetProgressFrame(), sEndProcessMessageID);
+       proc->Redirect();
+       int flag = wxEXEC_ASYNC;
+#if !__WXMSW__
+       flag |= wxEXEC_MAKE_GROUP_LEADER;
+#endif
+       m_processTerminated = false;
+       m_processExitCode = 0;
+       long pid = ::wxExecute(cmdstr, flag, proc);
+       if (pid == 0) {
+               MyAppCallback_errorMessageBox("Cannot start %s", procname);
+               proc->Detach();
+               HideProgressPanel();
+               fclose(fplog);
+               return -1;
+       }
+       
+       //  Wait until process ends or user interrupts
+       wxInputStream *in = proc->GetInputStream();
+       wxInputStream *err = proc->GetErrorStream();
+       len_total = 0;
+       while (1) {
+               if (m_processTerminated) {
+                       if (m_processExitCode != 0) {
+                               /*  Error from subprocess  */
+                               MyAppCallback_errorMessageBox("%s failed with exit code %d.", procname, m_processExitCode);
+                               status = m_processExitCode;
+                       } else status = 0;
+                       break;
+               }
+               if (wxGetApp().IsInterrupted()) {
+                       /*  User interrupt  */
+#if __WXMSW__
+                       int kflag = wxKILL_NOCHILDREN;
+#else
+                       int kflag = wxKILL_CHILDREN;
+#endif
+                       wxKillError rc;
+                       if (::wxKill(pid, wxSIGTERM, &rc, kflag) != 0) {
+                               const char *emsg;
+                               switch (rc) {
+                                       case wxKILL_BAD_SIGNAL: emsg = "no such signal"; break;
+                                       case wxKILL_ACCESS_DENIED: emsg = "permission denied"; break;
+                                       case wxKILL_NO_PROCESS: emsg = "no such process"; break;
+                                       default: emsg = "unknown error"; break;
+                               }
+                               MyAppCallback_errorMessageBox("Cannot kill subprocess: %s", emsg);
+                       }
+                       proc->Detach();
+                       status = -2;
+                       break;
+               }
+               while (in->CanRead()) {
+                       in->Read(buf, sizeof buf - 1);
+                       if ((len = in->LastRead()) > 0) {
+                               buf[len] = 0;
+                               len_total += len;
+                               fprintf(fplog, "%s", buf);
+                       /*      MyAppCallback_setConsoleColor(0);
+                               MyAppCallback_showScriptMessage("%s", buf); */
+                       }
+               }
+               while (err->CanRead()) {
+                       err->Read(buf, sizeof buf - 1);
+                       if ((len = err->LastRead()) > 0) {
+                               buf[len] = 0;
+                               len_total += len;
+                               fprintf(fplog, "%s", buf);
+                       /*      MyAppCallback_setConsoleColor(1);
+                               MyAppCallback_showScriptMessage("\n%s", buf);
+                               MyAppCallback_setConsoleColor(0);  */
+                       }
+               }
+       }
+       fclose(fplog);
+       HideProgressPanel();
+/*     if (len_total > 0)
+               MyAppCallback_showRubyPrompt(); */
+       return status;
+}
+
+#pragma mark ====== MyFrame (top-level window) ======
+
+/*
+ * This is the top-level window of the application.
+ */
+IMPLEMENT_CLASS(MyFrame, wxDocMDIParentFrame)
+BEGIN_EVENT_TABLE(MyFrame, wxDocMDIParentFrame)
+    EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
+END_EVENT_TABLE()
+
+MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
+    const wxPoint& pos, const wxSize& size, long type):
+  wxDocMDIParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
+{
+       editMenu = (wxMenu *) NULL;
+}
+
+void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
+{
+       extern const char *gVersionString, *gCopyrightString;
+       char *s;
+       asprintf(&s, 
+                        "%s\n%s\n\n"
+                        "Built by use of:\n"
+                        "wxWidgets %d.%d.%d, Copyright (c) 1992-2008 Julian Smart, \n"
+                        "  Robert Roebling, Vadim Zeitlin and other members of the \n"
+                        "  wxWidgets team\n"
+                        "  Portions (c) 1996 Artificial Intelligence Applications Institute\n"
+                        "ruby %s\n%s",
+                        gVersionString, gCopyrightString,
+                        wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER,
+                        gRubyVersion, gRubyCopyright);
+       wxString str(s, wxConvUTF8);
+    (void)wxMessageBox(str, _T("Molby"));
+}
+
+MyFrame *GetMainFrame(void)
+{
+       return frame;
+}
+
+#pragma mark ====== Plain-C interface ======
+
+void
+MyAppCallback_loadGlobalSettings(void)
+{
+       wxGetApp().LoadDefaultSettings();
+}
+
+void
+MyAppCallback_saveGlobalSettings(void)
+{
+       wxGetApp().SaveDefaultSettings();
+}
+
+/*  Note on the global settings  */
+/*  Global settings are stored in a file in the form key="value", where
+    the "value" is the 'inspect'-ed representation of Ruby values.
+    So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
+    not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
+    care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
+    MyAppCallback_setGlobalSettingsWithType().  */
+char *
+MyAppCallback_getGlobalSettings(const char *key)
+{
+       wxString wxkey(key, wxConvUTF8);
+       wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
+       return strdup(wxvalue.mb_str(wxConvUTF8));
+}
+
+void
+MyAppCallback_setGlobalSettings(const char *key, const char *value)
+{
+       wxString wxkey(key, wxConvUTF8);
+       wxString wxvalue(value, wxConvUTF8);
+       wxGetApp().SetDefaultSetting(wxkey, wxvalue);
+}
+
+int
+MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
+{
+       const char *s = MyAppCallback_getGlobalSettings(key);
+       char desc[] = SCRIPT_ACTION("s; ");
+       desc[sizeof(desc) - 2] = type;
+       return MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
+}
+
+int
+MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
+{
+       const char *cmd = "set_global_settings";
+       switch (type) {
+               case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
+               case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
+               case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
+               case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
+               case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
+               default:
+                       MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
+                       return -2;
+       }
+}
+
+int
+MyAppCallback_showScriptMessage(const char *fmt, ...)
+{
+       if (fmt != NULL) {
+               char *p;
+               va_list ap;
+               int retval;
+               va_start(ap, fmt);
+               if (strchr(fmt, '%') == NULL) {
+                       /*  No format characters  */
+                       return wxGetApp().AppendConsoleMessage(fmt);
+               } else if (strcmp(fmt, "%s") == 0) {
+                       /*  Direct output of one string  */
+                       p = va_arg(ap, char *);
+                       return wxGetApp().AppendConsoleMessage(p);
+               }
+#if 1
+               vasprintf(&p, fmt, ap);
+#else
+               /*  Use safe wxString method  */
+               /*  Not necessary any longer; vasprintf() is implemented in Missing.c  */
+               {
+                       wxString str;
+                       str.PrintfV(wxString::FromUTF8(fmt).GetData(), ap);
+                       p = strdup((const char *)str.mb_str(wxConvUTF8));
+               }
+#endif
+               if (p != NULL) {
+                       retval = wxGetApp().AppendConsoleMessage(p);
+                       free(p);
+                       return retval;
+               } else return 0;
+       } else {
+               wxGetApp().FlushConsoleMessage();
+               return 0;
+       }
+  return 0;
+}
+
+void
+MyAppCallback_setConsoleColor(int color)
+{
+       wxGetApp().SetConsoleColor(color);
+}
+
+void
+MyAppCallback_showRubyPrompt(void)
+{
+       MyAppCallback_setConsoleColor(0);
+       MyAppCallback_showScriptMessage("%% ");
+}
+
+int
+MyAppCallback_checkInterrupt(void)
+{
+       return wxGetApp().IsInterrupted();
+}
+
+void
+MyAppCallback_showProgressPanel(const char *msg)
+{
+       wxGetApp().ShowProgressPanel(msg);
+}
+
+void
+MyAppCallback_hideProgressPanel(void)
+{
+       wxGetApp().HideProgressPanel();
+}
+
+void
+MyAppCallback_setProgressValue(double dval)
+{
+       wxGetApp().SetProgressValue(dval);
+}
+
+void
+MyAppCallback_setProgressMessage(const char *msg)
+{
+       wxGetApp().SetProgressMessage(msg);
+}
+
+int
+MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
+{
+       wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
+       wxStaticText *stext;
+       wxTextCtrl *tctrl;
+       int retval;
+       wxString pstr(prompt, wxConvUTF8);
+       {       //  Vertical sizer containing [prompt, textbox, buttons]
+               wxBoxSizer *sizer1;
+               sizer1 = new wxBoxSizer(wxVERTICAL);
+               stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
+               sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
+               tctrl = new wxTextCtrl(dialog, -1, _T(""), wxDefaultPosition, wxSize(200, 22));
+               sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
+               wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
+               sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
+               sizer1->Layout();
+               dialog->SetSizerAndFit(sizer1);
+               dialog->Centre(wxBOTH);
+               tctrl->SetFocus();
+       }
+       if (dialog->ShowModal() == wxID_OK) {
+               strncpy(buf, (const char *)(tctrl->GetValue().mb_str(wxConvUTF8)), bufsize - 1);
+               buf[bufsize - 1] = 0;
+               retval = 1;
+       } else {
+               retval = 0;
+       }
+       dialog->Destroy();
+       return retval;
+}
+
+/*  Generic message box.  Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
+    1 (information), 2 (exclamation), or 3 (stop).  */
+int
+MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
+{
+       int wxflags, wxicon, retval;
+       if (flags == 0)
+               flags = 1;
+       wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
+       switch (icon) {
+               case 3: wxicon = wxICON_ERROR; break;
+               case 2: wxicon = wxICON_EXCLAMATION; break;
+               default: wxicon = wxICON_INFORMATION; break;
+       }
+       wxString wxmessage(message, wxConvUTF8);
+       wxString wxtitle(title, wxConvUTF8);
+       retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
+       return (retval == wxOK ? 1 : 0);
+}
+
+void
+MyAppCallback_errorMessageBox(const char *fmt, ...)
+{
+       char *s;
+       int need_free = 0;
+       va_list ap;
+       va_start(ap, fmt);
+       if (strchr(fmt, '%') == 0) {
+               s = (char *)fmt;
+       } else if (strcmp(fmt, "%s") == 0) {
+               s = va_arg(ap, char *);
+       } else {
+               vasprintf(&s, fmt, ap);
+               need_free = 1;
+       }
+       MyAppCallback_messageBox(s, "Error", 0, 3);
+       if (need_free)
+               free(s);
+}
+       
+char *
+MyAppCallback_getHomeDir(void)
+{
+       char *s;
+#if __WXMSW__
+       /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
+       s = getenv("USERPROFILE");
+#else
+       s = getenv("HOME");
+#endif
+       return (s == NULL ? NULL : strdup(s));
+}
+
+char *
+MyAppCallback_getDocumentHomeDir(void)
+{
+       char *s;
+#if __WXMSW__
+       char *ss;
+       s = getenv("USERPROFILE");
+       asprintf(&ss, "%s\\My Documents", s);
+       return ss;
+#else
+       s = getenv("HOME");
+       return (s == NULL ? NULL : strdup(s));
+#endif
+}
+
+void
+MyAppCallback_registerScriptMenu(const char *cmd, const char *title)
+{
+       wxGetApp().RegisterScriptMenu(cmd, title);
+}
+
+
+RubyValue
+MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
+{
+       RubyValue retval;
+       wxString cwd = wxFileName::GetCwd();
+       wxString path(cpath, wxConvFile);
+       char *p = strdup(cpath);
+       char sep = wxFileName::GetPathSeparator();
+       char *pp, *script = NULL;
+       if ((pp = strrchr(p, sep)) != NULL) {
+               *pp++ = 0;
+               wxString dirname(p, wxConvFile);
+               wxFileName::SetCwd(dirname);
+       } else pp = p;
+       
+       /*  Read the content of the file  */
+       wxFile file;
+       if (file.Open((const wxChar *)path, wxFile::read)) {
+               wxFileOffset len = file.Length();
+               script = (char *)malloc(len + 1);
+               if (script != NULL) {
+                       file.Read(script, len);
+                       script[len] = 0;
+               }
+       }
+       file.Close();
+       
+       retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), status);
+       free(script);
+       free(p);
+       wxFileName::SetCwd(cwd);
+       return retval;
+}
+
+void MyAppCallback_beginUndoGrouping(void)
+{
+       wxList &doclist = wxGetApp().DocManager()->GetDocuments();
+       wxList::iterator iter;
+       for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
+               ((MyDocument *)(*iter))->BeginUndoGrouping();
+       }
+}
+
+void MyAppCallback_endUndoGrouping(void)
+{
+       wxList &doclist = wxGetApp().DocManager()->GetDocuments();
+       wxList::iterator iter;
+       for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
+               ((MyDocument *)(*iter))->EndUndoGrouping();
+       }
+}
+
+int MyAppCallback_callSubProcess(const char *cmdline, const char *procname)
+{
+       return wxGetApp().CallSubProcess(cmdline, procname);
+}
diff --git a/wxSources/MyApp.h b/wxSources/MyApp.h
new file mode 100755 (executable)
index 0000000..e8518d3
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *  MyApp.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MyApp_H__
+#define __MyApp_H__
+
+#include "wx/app.h"
+#include "wx/mdi.h"
+#include "wx/docview.h"
+#include "wx/docmdi.h"
+#include "wx/hashmap.h"
+#include "wx/process.h"
+
+#if __WXMSW__
+#include "wx/snglinst.h"
+#endif
+
+#include "MyDocManager.h"
+
+class MyDocManager;
+class wxMenuBar;
+class wxMenu;
+class wxProgressDialog;
+
+class ConsoleFrame;
+class ProgressFrame;
+class GlobalParameterFrame;
+class GlobalParameterFilesFrame;
+class MyListCtrl;
+
+enum {
+       myMenuID_MyFirstMenuItem = 100,
+       myMenuID_Import = 101,
+       myMenuID_Export = 102,
+       myMenuID_SelectFragment = 103,
+       myMenuID_SelectReverse = 104,
+       myMenuID_AddHydrogen = 105,
+       myMenuID_AddHydrogenSp3 = 106,
+       myMenuID_AddHydrogenSp2 = 107,
+       myMenuID_AddHydrogenLinear = 108,
+       myMenuID_AddHydrogenPyramidal = 109,
+       myMenuID_AddHydrogenBent = 110,
+       myMenuID_FitToScreen = 120,
+       myMenuID_CenterSelection = 121,
+       myMenuID_ShowUnitCell = 122,
+       myMenuID_ShowPeriodicBox = 123,
+       myMenuID_ShowHydrogens = 124,
+       myMenuID_ShowDummyAtoms = 125,
+       myMenuID_ShowExpandedAtoms = 126,
+       myMenuID_ShowEllipsoids = 127,
+       myMenuID_ShowRotationCenter = 128,
+       myMenuID_ShowGraphite = 129,
+       myMenuID_LineMode = 130,
+       myMenuID_ShowAllAtoms = 131,
+       myMenuID_HideSelected = 132,
+       myMenuID_HideUnselected = 133,
+       myMenuID_HideReverse = 134,
+       myMenuID_MolecularDynamics = 140,
+       myMenuID_Minimize = 141,
+       myMenuID_StopMDRun = 142,
+//     myMenuID_ReadParameters = 143,
+       myMenuID_ViewGlobalParameters = 144,
+       myMenuID_ViewParameterFilesList = 145,
+       myMenuID_DefinePeriodicBox = 146,
+       myMenuID_ShowPeriodicImage = 147,
+       myMenuID_PressureControl = 148,
+       myMenuID_DefineSymmetry = 149,
+       myMenuID_ExpandBySymmetry = 150,
+       myMenuID_MDTools = 151,
+       myMenuID_RunAntechamber = 152,
+       myMenuID_RunResp = 153,
+       myMenuID_CreateGamessInput = 160,
+       myMenuID_CreateMOCube = 161,
+       myMenuID_ExecuteScript = 200,
+       myMenuID_OpenConsoleWindow = 201,
+       myMenuID_CustomScript = 202,
+       myMenuID_MyLastMenuItem = 499
+};
+
+enum {
+       myMenuIndex_File = 0,
+       myMenuIndex_Edit = 1,
+       myMenuIndex_Show = 2,
+       myMenuIndex_MMMD = 3,
+       myMenuIndex_QChem = 4,
+       myMenuIndex_Script = 5
+};
+
+WX_DECLARE_STRING_HASH_MAP( wxString, MyStringHash );
+
+// Define a new application
+class MyApp: public wxApp
+{
+  public:
+    MyApp(void);
+    bool OnInit(void);
+    int OnExit(void);
+
+       int AppendConsoleMessage(const char *mes);
+       void FlushConsoleMessage();
+       void SetConsoleColor(int color);
+
+       void ShowProgressPanel(const char *mes);
+       void HideProgressPanel();
+       void SetProgressValue(double dval);
+       void SetProgressMessage(const char *mes);
+       int IsInterrupted();
+       ProgressFrame *GetProgressFrame() { return m_progressFrame; }
+
+    MyDocManager *DocManager() { return m_docManager; }
+
+       wxMenuBar *CreateMenuBar(int kind, wxMenu **out_file_history_menu = NULL, wxMenu **out_edit_menu = NULL);
+
+//     void OnReadParameters(wxCommandEvent& event);
+
+       static wxString DefaultSettingsPath();
+       void LoadDefaultSettings();
+       void SaveDefaultSettings();
+       void SetDefaultSetting(const wxString& key, const wxString& value);
+       wxString& GetDefaultSetting(const wxString& key);
+
+       void RegisterScriptMenu(const char *cmd, const char *title);
+       void UpdateScriptMenu(wxMenuBar *mbar);
+       void OnScriptMenuModified(wxCommandEvent& event);
+       void OnScriptMenuSelected(wxCommandEvent& event);
+       void OnUpdateUI(wxUpdateUIEvent &event);
+       void OnExecuteScript(wxCommandEvent &event);
+       void OnOpenConsoleWindow(wxCommandEvent &event);
+       void OnViewGlobalParameters(wxCommandEvent &event);
+       void OnViewParameterFilesList(wxCommandEvent &event);
+
+       void OnEndProcess(wxProcessEvent &event);
+       int CallSubProcess(const char *cmdline, const char *procname);
+
+       MyListCtrl *GetGlobalParameterListCtrl();
+#if __WXMAC__
+       virtual void MacNewFile();
+#endif
+       
+protected:
+    MyDocManager* m_docManager;
+       ProgressFrame *m_progressFrame;
+       bool m_progressCanceled;
+       int m_progressValue;
+       MyStringHash m_defaultSettings;
+
+       bool m_processTerminated;
+       int m_processExitCode;
+
+       ConsoleFrame *consoleFrame;
+       GlobalParameterFrame *parameterFrame;
+       GlobalParameterFilesFrame *parameterFilesFrame;
+       
+       int countNonCustomScriptMenu;
+       int countScriptMenu;
+       char **scriptMenuCommands;
+       char **scriptMenuTitles;
+       bool scriptMenuModifiedEventPosted;
+       
+#if __WXMSW__
+       wxSingleInstanceChecker *m_checker;
+#endif
+       
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+DECLARE_APP(MyApp)
+
+// Define a new frame
+class MyFrame: public wxDocMDIParentFrame
+{
+       DECLARE_CLASS(MyFrame)
+public:
+       wxMenu *editMenu;
+  
+       MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title, const wxPoint& pos, const wxSize& size, long type);
+
+       void OnAbout(wxCommandEvent& event);
+
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+extern MyFrame *GetMainFrame(void);
+extern bool singleWindowMode;
+
+#endif  /* __MyApp_H__ */
diff --git a/wxSources/MyClipboardData.cpp b/wxSources/MyClipboardData.cpp
new file mode 100644 (file)
index 0000000..e0302dc
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  MyClipboardData.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/11/26.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MyClipboardData.h"
+
+MyClipboardData::MyClipboardData(const char *type):
+       wxDataObjectSimple()
+{
+       wxString ftype(type, wxConvUTF8);
+       wxDataFormat customFormat((const wxChar *)ftype);
+       buffer = NULL;
+       length = 0;
+       SetFormat(customFormat);
+}
+
+MyClipboardData::~MyClipboardData()
+{
+       if (buffer != NULL)
+               free(buffer);
+}
+
+size_t
+MyClipboardData::GetDataSize() const
+{
+       return length;
+}
+
+bool
+MyClipboardData::GetDataHere(void *buf) const
+{
+       if (buffer != NULL) {
+               memmove(buf, buffer, length);
+               return true;
+       } else return false;
+}
+
+bool
+MyClipboardData::SetData(size_t len, const void *buf)
+{
+       char *p;
+       if (buffer == NULL)
+               p = (char *)malloc(len);
+       else
+               p = (char *)realloc(buffer, len);
+       if (p == NULL)
+               return false;
+       memmove(p, buf, len);
+       buffer = p;
+       length = len;
+       return true;
+}
diff --git a/wxSources/MyClipboardData.h b/wxSources/MyClipboardData.h
new file mode 100644 (file)
index 0000000..8a413d6
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  MyClipboardData.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/11/26.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MyClipboardData_h__
+#define __MyClipboardData_h__
+
+#include "wx/dataobj.h"
+
+class MyClipboardData: public wxDataObjectSimple {
+public:
+       char *buffer;
+       int length;
+       
+       MyClipboardData(const char *type);
+       virtual ~MyClipboardData();
+
+       virtual size_t GetDataSize() const;
+       virtual bool GetDataHere(void *buf) const;
+       virtual bool SetData(size_t len, const void *buf);
+};
+
+#endif /* __MyClipboardData_h__ */
diff --git a/wxSources/MyCommand.cpp b/wxSources/MyCommand.cpp
new file mode 100755 (executable)
index 0000000..7a13bc3
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  MyCommand.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/25.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error "You should have DocView architecture enabled in your wxWidgets installation."
+#endif
+
+#include "MoleculeView.h"
+#include "MyCommand.h"
+#include "MyDocument.h"
+
+MyCommand::MyCommand(Molecule *aMolecule, const wxString& name):
+       wxCommand(true, name)
+{
+       mol = MoleculeRetain(aMolecule);
+       undoActions = redoActions = NULL;
+       numUndoActions = numRedoActions = 0;
+}
+
+MyCommand::~MyCommand()
+{
+       MoleculeRelease(mol);
+       SetUndoActions(NULL, 0);
+       SetRedoActions(NULL, 0);
+}
+
+void
+MyCommand::SetUndoActions(MolAction **actions, int count)
+{
+       int i;
+       if (undoActions != NULL) {
+               for (i = 0; i < numUndoActions; i++)
+                       MolActionRelease(undoActions[i]);
+               free(undoActions);
+       }
+       undoActions = actions;
+       numUndoActions = count;
+}
+
+void
+MyCommand::SetRedoActions(MolAction **actions, int count)
+{
+       int i;
+       if (redoActions != NULL) {
+               for (i = 0; i < numRedoActions; i++)
+                       MolActionRelease(redoActions[i]);
+               free(redoActions);
+       }
+       redoActions = actions;
+       numRedoActions = count;
+}
+
+bool
+MyCommand::Do()
+{
+       MyDocument *doc = MyDocumentFromMolecule(mol);
+       int i;
+       if (doc != NULL) {
+               bool retval = true;
+               if (redoActions != NULL) {
+                       doc->SetCurrentCommand(this);
+                       for (i = numRedoActions - 1; i >= 0; i--) {
+                               if (MolActionPerform(mol, redoActions[i]) != 0) {
+                                       retval = false;
+                                       break;
+                               }
+                       }
+                       doc->CleanUndoStack(retval);
+               }
+               return retval;
+       }
+       return false;
+}
+
+bool
+MyCommand::Undo()
+{
+       MyDocument *doc = MyDocumentFromMolecule(mol);
+       int i;
+       if (doc != NULL) {
+               bool retval = true;
+               if (undoActions != NULL) {
+                       doc->SetIsUndoing(true);
+                       doc->SetCurrentCommand(this);
+                       for (i = numUndoActions - 1; i >= 0; i--) {
+                               if (MolActionPerform(mol, undoActions[i]) != 0) {
+                                       retval = false;
+                                       break;
+                               }
+                       }
+                       doc->CleanUndoStack(retval);
+               }
+               return retval;
+       }
+       return false;
+}
diff --git a/wxSources/MyCommand.h b/wxSources/MyCommand.h
new file mode 100755 (executable)
index 0000000..4c05785
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  MyCommand.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/25.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MyCommand_h__
+#define __MyCommand_h__
+
+#include "wx/cmdproc.h"
+
+#include "../MolLib/MolLib.h"
+
+class MyCommand: public wxCommand
+{
+public:
+       Molecule *mol;
+       MolAction **undoActions;
+       int numUndoActions;
+       MolAction **redoActions;
+       int numRedoActions;
+
+    MyCommand(Molecule *aMolecule, const wxString& name = wxEmptyString);
+    virtual ~MyCommand();
+
+       void SetUndoActions(MolAction **actions, int count);
+       void SetRedoActions(MolAction **actions, int count);
+       
+    virtual bool Do();
+       virtual bool Undo();
+};
+
+#endif /* __MyCommand_h__ */
diff --git a/wxSources/MyDocManager.cpp b/wxSources/MyDocManager.cpp
new file mode 100644 (file)
index 0000000..7f89c41
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  MyDocManager.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/11/21.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MyDocManager.h"
+
+BEGIN_EVENT_TABLE(MyDocManager, wxDocManager)
+EVT_MENU(wxID_SAVE, MyDocManager::OnFileSave)
+EVT_MENU(wxID_SAVEAS, MyDocManager::OnFileSaveAs)
+END_EVENT_TABLE()
+
+static const char *sReadOnlyTypes[] = {
+       "out", "fchk", "log", NULL
+};
+
+void
+MyDocManager::SetDocumentTypesEnabled(const char **extensions, bool flag)
+{
+       wxList &tlist = GetTemplates();
+       wxList::iterator iter;
+       for (iter = tlist.begin(); iter != tlist.end(); ++iter) {
+               int i;
+               wxDocTemplate *dt = (wxDocTemplate *)(*iter);
+               const char *p = (const char *)(dt->GetDefaultExtension().mb_str(wxConvUTF8));
+               for (i = 0; extensions[i] != NULL; i++) {
+                       if (strcmp(extensions[i], p) == 0) {
+                               dt->SetFlags(flag ? wxTEMPLATE_VISIBLE : wxTEMPLATE_INVISIBLE);
+                               break;
+                       }
+               }
+       }
+}
+
+void
+MyDocManager::OnFileSave(wxCommandEvent& event)
+{
+       SetDocumentTypesEnabled(sReadOnlyTypes, false);
+       wxDocManager::OnFileSave(event);
+       SetDocumentTypesEnabled(sReadOnlyTypes, true);
+}
+
+void
+MyDocManager::OnFileSaveAs(wxCommandEvent& event)
+{
+       SetDocumentTypesEnabled(sReadOnlyTypes, false);
+       wxDocManager::OnFileSaveAs(event);
+       SetDocumentTypesEnabled(sReadOnlyTypes, true);
+}
+
+bool
+MyDocManager::GetDocumentDescriptionAtIndex(int idx, wxString *outDescription, wxString *outFilter, wxString *outExtension)
+{
+       wxList &tlist = GetTemplates();
+       if (idx >= 0 && idx < tlist.GetCount()) {
+               wxList::iterator iter;
+               wxDocTemplate *dt = (wxDocTemplate *)(tlist.Item(idx)->GetData());
+               if (outDescription != NULL)
+                       *outDescription = dt->GetDescription();
+               if (outFilter != NULL)
+                       *outFilter = dt->GetFileFilter();
+               if (outExtension != NULL)
+                       *outExtension = dt->GetDefaultExtension();
+               return true;
+       } else return false;
+}
+
diff --git a/wxSources/MyDocManager.h b/wxSources/MyDocManager.h
new file mode 100644 (file)
index 0000000..12f446b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  MyDocManager.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/11/21.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MyDocManager_h__
+#define __MyDocManager_h__
+
+#include "wx/docview.h"
+
+class MyDocManager: public wxDocManager {
+public:
+       void OnFileSave(wxCommandEvent& event);
+       void OnFileSaveAs(wxCommandEvent& event);
+       void SetDocumentTypesEnabled(const char **extensions, bool flag);
+       bool GetDocumentDescriptionAtIndex(int idx, wxString *outDescription, wxString *outFilter, wxString *outExtension);
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+#endif  /* __MyDocManager_h__ */
diff --git a/wxSources/MyDocument.cpp b/wxSources/MyDocument.cpp
new file mode 100755 (executable)
index 0000000..5618ba4
--- /dev/null
@@ -0,0 +1,1323 @@
+/*
+ *  MyDocument.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error "You should have DocView architecture enabled in your wxWidgets installation."
+#endif
+
+#if wxUSE_STD_IOSTREAM
+    #include "wx/ioswrap.h"
+#else
+    #include "wx/txtstrm.h"
+#endif
+
+#include "wx/clipbrd.h"
+#include "wx/filename.h"
+#include "wx/dir.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "MyApp.h"
+#include "MyDocManager.h"
+#include "MyDocument.h"
+#include "MoleculeView.h"
+#include "MyCommand.h"
+#include "MyClipboardData.h"
+#include "MyThread.h"
+
+#include "../MolLib/Ruby_bind/Molby_extern.h"
+#include "../MolLib/MD/MDCore.h"
+#include "../MolLib/Missing.h"
+
+IMPLEMENT_DYNAMIC_CLASS(MyDocument, wxDocument)
+
+const wxEventType MyDocumentEvent = wxNewEventType();
+
+BEGIN_EVENT_TABLE(MyDocument, wxDocument)
+       EVT_COMMAND(MyDocumentEvent_willNeedCleanUndoStack, MyDocumentEvent, MyDocument::OnNeedCleanUndoStack)
+       EVT_COMMAND(MyDocumentEvent_documentModified, MyDocumentEvent, MyDocument::OnDocumentModified)
+       EVT_COMMAND(MyDocumentEvent_updateDisplay, MyDocumentEvent, MyDocument::OnUpdateDisplay)
+       EVT_COMMAND(MyDocumentEvent_threadTerminated, MyDocumentEvent, MyDocument::OnSubThreadTerminated)
+       EVT_MENU(myMenuID_Import, MyDocument::OnImport)
+       EVT_MENU(myMenuID_Export, MyDocument::OnExport)
+       EVT_MENU(wxID_COPY, MyDocument::OnCopy)
+       EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
+       EVT_MENU(wxID_CUT, MyDocument::OnCut)
+       EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
+       EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
+       EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
+       EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
+       EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
+       EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
+       EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
+       EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
+       EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
+       EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
+       EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
+       EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
+       EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
+       EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
+       EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
+       EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
+       EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
+       EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
+       EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
+       EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
+       EVT_MENU(myMenuID_DefinePeriodicBox, MyDocument::OnDefinePeriodicBox)
+       EVT_MENU(myMenuID_ShowPeriodicImage, MyDocument::OnShowPeriodicImage)
+       EVT_MENU(myMenuID_PressureControl, MyDocument::OnPressureControl)
+       EVT_MENU(myMenuID_DefineSymmetry, MyDocument::OnDefineSymmetry)
+       EVT_MENU(myMenuID_ExpandBySymmetry, MyDocument::OnExpandBySymmetry)
+       EVT_MENU(myMenuID_RunAntechamber, MyDocument::OnInvokeAntechamber)
+       EVT_MENU(myMenuID_RunResp, MyDocument::OnInvokeResp)
+       EVT_MENU(myMenuID_CreateGamessInput, MyDocument::OnCreateGamessInput)
+       EVT_MENU(myMenuID_CreateMOCube, MyDocument::OnCreateMOCube)
+       EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
+       EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
+       EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
+       EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
+END_EVENT_TABLE()
+
+MyDocument::MyDocument()
+{
+       mol = MoleculeNew();
+       isUndoing = false;
+       isUndoEnabled = true;
+       isModifyNotificationSent = false;
+       currentCommand = NULL;
+       undoStack = NULL;
+       countUndoStack = 0;
+       undoGroupLevel = 0;
+       isCleanUndoStackRequested = false;
+       hasFile = false;
+       subThreadKind = 0;
+}
+
+MyDocument::~MyDocument()
+{
+  int i;
+  if (mol != NULL)
+    MoleculeRelease(mol);
+  if (undoStack != NULL) {
+       for (i = 0; i < countUndoStack; i++)
+         MolActionRelease(undoStack[i]);
+       free(undoStack);
+  }
+}
+
+/*
+MainView *
+MyDocument::GetMainView()
+{
+       MoleculeView *view = (MoleculeView *)GetFirstView();
+       if (view != NULL)
+               return view->mview;
+       else return NULL;
+}
+*/
+
+void
+MyDocument::SetMolecule(Molecule *aMolecule)
+{
+       if (mol == aMolecule)
+               return;
+       if (mol != NULL)
+               MoleculeRelease(mol);
+       mol = aMolecule;
+       if (aMolecule != NULL)
+               MoleculeRetain(aMolecule);
+
+       MoleculeView *view = (MoleculeView *)GetFirstView();
+       if (view != NULL) {
+               MainView_setMolecule(view->mview, aMolecule);
+               if (aMolecule->natoms >= 1000)
+                       view->mview->lineMode = 1;
+       }
+}
+
+bool
+MyDocument::DoSaveDocument(const wxString& file)
+{
+       char buf[128];
+       char *p = strdup((const char *)file.mb_str(wxConvFile));
+       size_t len = strlen(p);
+       int retval;
+       buf[0] = 0;
+       if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
+               free(p);
+               return false;
+       }
+       retval = 0;
+       MoleculeLock(mol);
+       if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
+               /*  Write as a psf and a pdb file  */
+               strcpy(p + len - 4, ".pdb");
+               retval = MoleculeWriteToPdbFile(mol, p, buf, sizeof buf);
+               if (retval != 0) {
+                       free(p);
+                       goto exit;
+               }
+               if (mol->cell != NULL) {
+                       /*  Write an extended info (bounding box)  */
+                       p = (char *)realloc(p, len + 2);
+                       strcpy(p + len - 4, ".info");
+                       retval = MoleculeWriteExtendedInfo(mol, p, buf, sizeof buf);
+                       if (retval != 0) {
+                               free(p);
+                               goto exit;
+                       }
+               }
+       }
+       free(p);
+       GetCommandProcessor()->MarkAsSaved();
+       hasFile = true;
+exit:
+       MoleculeUnlock(mol);
+       return (retval == 0);
+}
+
+bool
+MyDocument::DoOpenDocument(const wxString& file)
+{
+       char *p;
+       int len;
+       Molecule *newmol;
+       p = strdup((const char *)file.mb_str(wxConvFile));
+       newmol = MoleculeNew();
+       SetUndoEnabled(false);
+       if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
+               free(p);
+               SetUndoEnabled(true);
+               return false;
+       }
+       
+       if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
+               //  Look for a ".pdb" file with the same basename 
+               char buf[128];
+               strcpy(p + len - 4, ".pdb");
+               //  The error will be ignored
+               MoleculeReadCoordinatesFromPdbFile(newmol, p, buf, sizeof buf);
+               //  Look for an ".info" file with the same basename
+               p = (char *)realloc(p, len + 2);
+               strcpy(p + len - 4, ".info");
+               MoleculeReadExtendedInfo(newmol, p, buf, sizeof buf);
+       }
+       free(p);
+       SetMolecule(newmol);
+       Modify(false);
+       GetCommandProcessor()->MarkAsSaved();
+       hasFile = true;
+       SetUndoEnabled(true);
+       return true;
+}
+
+void
+MyDocument::OnImport(wxCommandEvent& event)
+{
+       wxString wildcard;
+       {
+               /*  File filter is built from MyDocManager information  */
+               wxString desc, filter, ext;
+               int i;
+               MyDocManager *docm = wxGetApp().DocManager();
+               for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
+                       if (wildcard != _T("")) {
+                               wildcard += (_T("|"));
+                       }
+                       wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
+               }
+       }
+       
+       wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
+       if (dialog->ShowModal() == wxID_OK) {
+               char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
+               MoleculeLock(mol);
+               MolActionCallback_setUndoRegistrationEnabled(mol, 0);
+               MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
+               MolActionCallback_setUndoRegistrationEnabled(mol, 1);
+               MoleculeUnlock(mol);
+               free(p);
+       }
+       dialog->Destroy();
+}
+
+void
+MyDocument::OnExport(wxCommandEvent& event)
+{
+       wxString wildcard;
+       {
+               /*  File filter is built from MyDocManager information  */
+               wxString desc, filter, ext;
+               int i;
+               MyDocManager *docm = wxGetApp().DocManager();
+               for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
+                       if (ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
+                               continue;
+                       if (wildcard != _T("")) {
+                               wildcard += (_T("|"));
+                       }
+                       wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
+               }
+       }
+       
+       wxFileDialog *dialog = new wxFileDialog(NULL, _T(""), _T(""), _T(""), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT |   wxFD_CHANGE_DIR);
+       if (dialog->ShowModal() == wxID_OK) {
+               char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
+               MoleculeLock(mol);
+               MolActionCallback_setUndoRegistrationEnabled(mol, 0);
+               MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
+               MolActionCallback_setUndoRegistrationEnabled(mol, 1);
+               MoleculeUnlock(mol);
+               free(p);
+       }
+       dialog->Destroy();
+}
+
+void
+MyDocument::SetUndoEnabled(bool flag)
+{
+       if (flag) {
+               isUndoEnabled = true;
+       } else {
+               //  Remove all registered actions
+               wxCommandProcessor *cmdProc = GetCommandProcessor();
+               currentCommand = NULL;
+               cmdProc->ClearCommands();
+               CleanUndoStack(false);
+               isUndoEnabled = false;
+               //  TODO: mark the document as "edited"
+       }
+}
+
+void
+MyDocument::PushUndoAction(MolAction *action)
+{
+       if (countUndoStack % 8 == 0) {
+               if (undoStack == NULL)
+                       undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
+               else
+                       undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
+               if (undoStack == NULL)
+                       return;
+       }
+       undoStack[countUndoStack++] = action;
+       MolActionRetain(action);
+       if (countUndoStack == 1) {
+               wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
+               wxPostEvent(this, myEvent);
+       }
+}
+
+/*  Update the modify flag to match the GetCommandProcessor isDirty flag
+    (Is this really necessary? It should be handled by wxDocument automatically.  */
+void
+MyDocument::UpdateModifyFlag()
+{
+//     printf("isDirty = %d\n", (GetCommandProcessor()->IsDirty()));
+       Modify(GetCommandProcessor()->IsDirty());
+}
+
+void
+MyDocument::BeginUndoGrouping()
+{
+       undoGroupLevel++;
+}
+
+void
+MyDocument::EndUndoGrouping()
+{
+       if (undoGroupLevel <= 0)
+               return;  /* This should not happen  */
+       if (--undoGroupLevel == 0) {
+               if (isCleanUndoStackRequested) {
+                       /*  Resend the event so that it can be processed at the next idle time  */
+                       wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
+                       wxPostEvent(this, myEvent);
+                       isCleanUndoStackRequested = false;
+               }
+       }
+}
+
+void
+MyDocument::CleanUndoStack(bool shouldRegister)
+{
+       if (undoStack != NULL) {
+               if (shouldRegister) {
+                       MyCommand *cmd = (MyCommand *)currentCommand;
+                       if (cmd == NULL)
+                               cmd = new MyCommand(mol);
+                       if (isUndoing)
+                               cmd->SetRedoActions(undoStack, countUndoStack);
+                       else
+                               cmd->SetUndoActions(undoStack, countUndoStack);
+                       if (currentCommand == NULL) {
+                               if (!GetCommandProcessor()->Submit(cmd))
+                                       delete cmd;
+                               UpdateModifyFlag();
+                       }
+               } else {
+                       int i;
+                       for (i = 0; i < countUndoStack; i++)
+                               MolActionRelease(undoStack[i]);
+                       free(undoStack);
+               }
+       }
+       isUndoing = false;
+       undoStack = NULL;
+       countUndoStack = 0;
+       currentCommand = NULL;
+}
+
+void
+MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
+{
+       if (undoGroupLevel == 0)
+               CleanUndoStack(true);
+       else {
+               /*  Do not respond to this event immediately; the same event will be
+                   resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
+               isCleanUndoStackRequested = true;
+       }
+}
+
+void
+MyDocument::OnDocumentModified(wxCommandEvent& event)
+{
+//     printf("MyDocument::OnDocumentModified invoked\n");
+       isModifyNotificationSent = false;
+       MoleculeClearModifyCount(GetMainView()->mol);
+       
+       event.Skip();  //  Also pass to other notification handlers
+       UpdateModifyFlag();
+}
+
+void
+MyDocument::OnCopy(wxCommandEvent& event)
+{
+       if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
+               MainView_copyOrCutParameters(GetMainView(), 2);
+       } else {
+               MoleculeLock(mol);
+               MainView_copy(GetMainView());
+               MoleculeUnlock(mol);
+       }
+}
+
+void
+MyDocument::OnCut(wxCommandEvent& event)
+{
+       if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
+               MainView_copyOrCutParameters(GetMainView(), 3);
+       } else {
+               MoleculeLock(mol);
+               MainView_cut(GetMainView());
+               MoleculeUnlock(mol);
+       }
+}
+
+void
+MyDocument::OnPaste(wxCommandEvent& event)
+{
+       if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
+               MainView_pasteParameters(GetMainView());
+       } else {
+               MoleculeLock(mol);
+               MainView_paste(GetMainView());
+               MoleculeUnlock(mol);
+       }
+}
+
+void
+MyDocument::OnDelete(wxCommandEvent& event)
+{
+       if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
+               MainView_copyOrCutParameters(GetMainView(), 1);
+       } else {
+               MoleculeLock(mol);
+               MainView_delete(GetMainView());
+               MoleculeUnlock(mol);
+       }
+}
+
+void
+MyDocument::OnSelectAll(wxCommandEvent& event)
+{
+       if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
+       } else {
+               MoleculeLock(mol);
+               MainView_selectAll(GetMainView());
+               MoleculeUnlock(mol);
+       }
+}
+
+void
+MyDocument::OnSelectFragment(wxCommandEvent& event)
+{
+       MoleculeLock(mol);
+       MainView_selectFragment(GetMainView());
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnSelectReverse(wxCommandEvent& event)
+{
+       MoleculeLock(mol);
+       MainView_selectReverse(GetMainView());
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnAddHydrogen(wxCommandEvent& event)
+{
+       int uid = event.GetId();
+       const char *type;
+       IntGroup *ig;
+       switch (uid) {
+               case myMenuID_AddHydrogenSp3: type = "td"; break;
+               case myMenuID_AddHydrogenSp2: type = "tr"; break;
+               case myMenuID_AddHydrogenLinear: type = "li"; break;
+               case myMenuID_AddHydrogenPyramidal: type = "py"; break;
+               case myMenuID_AddHydrogenBent: type = "be"; break;
+               default: return;
+       }
+       MoleculeLock(mol);
+       ig = MoleculeGetSelection(mol);
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnFitToScreen(wxCommandEvent& event)
+{
+       MoleculeLock(mol);
+       MainView_resizeToFit(GetMainView());
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnCenterSelection(wxCommandEvent& event)
+{
+       MoleculeLock(mol);
+       MainView_centerSelection(GetMainView());
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnShowMenu(wxCommandEvent& event)
+{
+       int uid = event.GetId();
+       if (mol == NULL || mol->mview == NULL)
+               return;
+       switch (uid) {
+               case myMenuID_ShowUnitCell:
+                       mol->mview->showUnitCell = !mol->mview->showUnitCell;
+                       break;
+               case myMenuID_ShowPeriodicBox:
+                       mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
+                       break;
+               case myMenuID_ShowHydrogens:
+                       mol->mview->showHydrogens = !mol->mview->showHydrogens;
+                       break;
+               case myMenuID_ShowDummyAtoms:
+                       mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
+                       break;
+               case myMenuID_ShowExpandedAtoms:
+                       mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
+                       break;
+               case myMenuID_ShowEllipsoids:
+                       mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
+                       break;
+               case myMenuID_ShowRotationCenter:
+                       mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
+                       break;
+       }
+       MainViewCallback_setNeedsDisplay(mol->mview, 1);
+}
+
+void
+MyDocument::OnShowAllAtoms(wxCommandEvent &event)
+{
+       if (mol == NULL || mol->mview == NULL)
+               return;
+       MoleculeShowAllAtoms(mol);
+}
+
+void
+MyDocument::OnHideSelected(wxCommandEvent &event)
+{
+       IntGroup *ig;
+       if (mol == NULL || mol->mview == NULL)
+               return;
+       ig = MoleculeGetSelection(mol);
+       MoleculeHideAtoms(mol, ig);     
+}
+
+void
+MyDocument::OnHideUnselected(wxCommandEvent &event)
+{
+       IntGroup *ig;
+       if (mol == NULL || mol->mview == NULL)
+               return;
+       ig = MoleculeGetSelection(mol);
+       ig = IntGroupNewFromIntGroup(ig);
+       IntGroupReverse(ig, 0, mol->natoms);
+       MoleculeHideAtoms(mol, ig);     
+       IntGroupRelease(ig);
+}
+
+void
+MyDocument::OnHideReverse(wxCommandEvent &event)
+{
+       if (mol == NULL || mol->mview == NULL)
+               return;
+       MoleculeShowReverse(mol);
+}
+
+void
+MyDocument::OnShowGraphite(wxCommandEvent &event)
+{
+       MoleculeLock(mol);
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnToggleLineMode(wxCommandEvent &event)
+{
+       mol->mview->lineMode = !mol->mview->lineMode;
+       MainViewCallback_setNeedsDisplay(mol->mview, 1);
+}
+
+/*  Check whether subthread is running  */
+static int
+sCheckIsSubThreadRunning(Molecule *mol, int n)
+{
+       if (mol->mutex != NULL) {
+               const char *mes;
+               switch (n) {
+                       case 1: mes = "MM/MD is already running."; break;
+                       default: mes = "Some subprocess is already running."; break;
+               }
+               MyAppCallback_errorMessageBox(mes);
+               return 1;
+       }
+       return 0;
+}
+
+/*   Run MD within a subthread  */
+static int
+sDoMolecularDynamics(void *argptr, int argnum)
+{
+       MyDocument *doc = (MyDocument *)argptr;
+       Molecule *mol = doc->GetMolecule();
+       int count, minimize, i, r;
+       if (argnum >= 0) {
+               count = argnum;
+               minimize = 0;
+       } else {
+               count = -argnum;
+               minimize = 1;
+       }
+       if (count == 0) {
+               mol->arena->end_step = mol->arena->start_step;
+               md_main(mol->arena, minimize);
+       } else if (count > 0) {
+               IntGroup *ig;
+               wxCommandEvent displayEvent(MyDocumentEvent, MyDocumentEvent_updateDisplay);
+               for (i = 0; i < count; i++) {
+                       
+                       mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
+                       r = md_main(mol->arena, minimize);
+
+                       if (r == 0) {
+                               if (mol->requestAbortThread)
+                                       r = -1;
+                               else {
+
+                                       /*  Create a new frame and copy the new coordinates  */
+                                       MoleculeLock(mol);
+                                       ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mol), 1, -1);
+                                       MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL);
+                                       IntGroupRelease(ig);                                    
+                                       md_copy_coordinates_from_internal(mol->arena);
+                                       MoleculeUnlock(mol);
+
+                                       if (minimize && mol->arena->minimize_complete)
+                                               break;
+                                       wxPostEvent(doc, displayEvent);
+                               }
+                       }
+                       if (r != 0)
+                               break;
+                       if (wxThread::This()->TestDestroy())
+                               return 0; /* Abnormal termination */
+               }
+       }
+       wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
+       wxPostEvent(doc, myEvent);
+       return 0;
+}
+
+void
+MyDocument::DoMDOrMinimize(int minimize)
+{
+       Int n;
+       if (sCheckIsSubThreadRunning(mol, subThreadKind))
+               return;
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
+       if (n < 0)
+               return;  /*  Canceled  */
+       
+       /*  Check whether any bond/angle/torsion are very distant from the equilibrium values  */
+       {
+       }
+       
+       mol->mutex = new wxMutex;
+       subThreadKind = 1;
+       BeginUndoGrouping();
+       mol->requestAbortThread = 0;
+       MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
+}
+
+void
+MyDocument::OnMolecularDynamics(wxCommandEvent &event)
+{
+       DoMDOrMinimize(0);
+}
+
+void
+MyDocument::OnMinimize(wxCommandEvent &event)
+{
+       DoMDOrMinimize(1);
+}
+
+void
+MyDocument::OnStopMDRun(wxCommandEvent &event)
+{
+       if (mol != NULL && mol->mutex != NULL)
+               mol->requestAbortThread = 1;
+}
+
+void
+MyDocument::OnUpdateDisplay(wxCommandEvent &event)
+{
+       MainView *mview = GetMainView();
+       MainViewCallback_setNeedsDisplay(mview, 1);
+}
+
+void
+MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
+{
+       if (mol != NULL && mol->mutex != NULL) {
+               delete (wxMutex *)mol->mutex;
+               mol->mutex = NULL;
+               mol->requestAbortThread = 0;
+               EndUndoGrouping();
+               subThreadKind = 0;
+               
+               if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
+                       MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
+       }
+}
+
+void
+MyDocument::OnDefinePeriodicBox(wxCommandEvent &event)
+{
+       MoleculeLock(mol);
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_define_unit_cell");
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnShowPeriodicImage(wxCommandEvent &event)
+{
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_periodic_image");
+}
+
+void
+MyDocument::OnPressureControl(wxCommandEvent &event)
+{
+       MoleculeLock(mol);
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_pressure_control");
+       MoleculeUnlock(mol);
+}
+
+void
+MyDocument::OnDefineSymmetry(wxCommandEvent &event)
+{
+}
+
+void
+MyDocument::OnExpandBySymmetry(wxCommandEvent &event)
+{
+}
+
+static bool
+sRemoveDirectoryRecursively(const wxString &dir)
+{
+       wxString name, file;
+       wxArrayString files;
+       int i, n;
+       n = 0;
+       {
+               /*  The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir  */
+               wxDir wdir(dir);
+               if (wdir.GetFirst(&name)) {
+                       do {
+                               file = dir + wxFileName::GetPathSeparator() + name;
+                               files.Add(file);
+                               n++;
+                       } while (wdir.GetNext(&name));
+               }
+       }
+       for (i = 0; i < n; i++) {
+               file = files[i];
+               if (wxDir::Exists(file)) {
+                       if (!sRemoveDirectoryRecursively(file))
+                               return false;
+               } else {
+                       if (!::wxRemoveFile(file))
+                               return false;
+               }
+       }
+       return ::wxRmdir(dir);
+}
+
+void
+MyDocument::OnInvokeAntechamber(wxCommandEvent &event)
+{
+       /*  Ask for antechamber options and log directory  */
+       char *ante_dir, *log_dir, *log_level, buf[256];
+       Int net_charge, i, j, n, log_keep_number;
+       int status;
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(";i"), "cmd_antechamber", &n);
+       if (!n)
+               return;
+       if ((status = MyAppCallback_getGlobalSettingsWithType("antechamber.ante_dir", 's', &ante_dir))
+               || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.nc", 'i', &net_charge))
+               || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level))
+               || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number))
+               || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir))) {
+               Molby_showError(status);
+               return;
+       }
+       
+       /*  Prepare the log directory  */
+       wxString dirname(log_dir, wxConvUTF8);
+       if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
+               MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
+               return;
+       }
+       wxFileName filename(GetFilename());
+       wxString name = filename.GetName();
+       wxString tdir;
+       for (i = 0; i < 1000; i++) {
+               tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
+               if (!wxFileName::DirExists(tdir))
+                       break;
+       }
+       if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
+               MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
+               return;
+       }
+       
+       /*  Move to the temporary directory and export the molecule as a pdb  */
+       wxString cwd = wxFileName::GetCwd();
+       if (!wxFileName::SetCwd(tdir)) {
+               MyAppCallback_errorMessageBox("Cannot move to the temporary directory '%s'", (const char *)tdir.mb_str(wxConvUTF8));
+               return;
+       }
+       if (MoleculeWriteToPdbFile(mol, "mol.pdb", buf, sizeof(buf))) {
+               MyAppCallback_errorMessageBox("PDB export error: %s", buf);
+               wxFileName::SetCwd(cwd);
+               return;
+       }
+       
+       {
+               /*  Run antechamber and parmck  */
+               char *p;
+               asprintf(&p, "%s/antechamber -i mol.pdb -fi pdb -o mol.ac -fo ac -nc %d -c bcc", ante_dir, net_charge);
+       /*      printf("Calling subprocess: %s\n", p);  */
+               status = MyAppCallback_callSubProcess(p, "antechamber");
+               if (status == 0) {
+                       asprintf(&p, "%s/parmchk -i mol.ac -f ac -o frcmod", ante_dir);
+                       status = MyAppCallback_callSubProcess(p, "parmchk");
+               }
+       }
+
+       if (status == 0) {
+               wxString acfile = tdir + wxFileName::GetPathSeparator() + _T("mol.ac");
+               status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_ac", (const char *)acfile.mb_str(wxConvUTF8));
+               if (status != 0) {
+                       MyAppCallback_errorMessageBox("Cannot import antechamber output.");
+               } else {
+                       wxString frcmodfile = tdir + wxFileName::GetPathSeparator() + _T("frcmod");
+                       status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_frcmod", (const char *)frcmodfile.mb_str(wxConvUTF8));
+                       if (status != 0) {
+                               MyAppCallback_errorMessageBox("Cannot import parmchk output.");
+                       }
+               }
+       }
+       
+       /*  Erase log files  */
+       bool success = true;
+       wxString dir2;
+       if (strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
+               /*  Erase the present log  */
+               if (!sRemoveDirectoryRecursively(tdir)) {
+                       success = false;
+                       dir2 = tdir;
+               }
+       } else if (strcmp(log_level, "latest") == 0) {
+               wxDir wdir(dirname);
+               wxString name;
+               wxArrayString files;
+               n = 0;
+               if (wdir.GetFirst(&name)) {
+                       do {
+                               wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
+                               if (wxDir::Exists(fullname)) {
+                                       files.Add(fullname);
+                                       n++;
+                               }
+                       } while (wdir.GetNext(&name));
+               }
+               if (n > log_keep_number) {
+                       /*  Sort directories by creation date  */
+                       struct temp_struct { time_t tm; int idx; } *tp;
+                       tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
+                       for (i = 0; i < n; i++) {
+                               wxFileName fn(files[i], wxEmptyString);
+                               wxDateTime dt;
+                               j = fn.GetTimes(NULL, NULL, &dt);
+                               tp[i].tm = dt.GetTicks();
+                               tp[i].idx = i;
+                       }
+                       for (i = 0; i < n; i++) {
+                               struct temp_struct temp;
+                               int k = i;
+                               for (j = i + 1; j < n; j++) {
+                                       if (tp[j].tm < tp[k].tm)
+                                               k = j;
+                               }
+                               if (k != i) {
+                                       temp = tp[k];
+                                       tp[k] = tp[i];
+                                       tp[i] = temp;
+                               }
+                       }
+                       /*  Keep last log_keep_number and delete the rest  */
+                       for (i = 0; i < n - log_keep_number; i++) {
+                               if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
+                                       success = false;
+                                       dir2 = files[tp[i].idx];
+                                       break;
+                               }
+                       }
+               }
+       }
+       if (!success)
+               MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvUTF8));
+
+       if (status == 0) {
+               ((MoleculeView *)GetFirstView())->GetListCtrl()->Update();
+               MyAppCallback_messageBox("Antechamber succeeded.", "Success", 0, 0);
+       }
+       
+       wxFileName::SetCwd(cwd);
+}
+
+void
+MyDocument::OnInvokeResp(wxCommandEvent &event)
+{
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_gamess_resp");
+}
+
+void
+MyDocument::OnCreateGamessInput(wxCommandEvent &event)
+{
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_gamess_input");
+}
+
+void
+MyDocument::OnCreateMOCube(wxCommandEvent &event)
+{
+       MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_cube");   
+}
+
+void
+MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
+{
+       int uid = event.GetId();
+       IntGroup *ig = MoleculeGetSelection(mol);
+       Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
+//     wxMenuItem *item = (wxMenuItem *)event.GetEventObject();
+       switch (uid) {
+               case wxID_COPY:
+               case wxID_CUT:
+               case wxID_PASTE:
+               case wxID_DELETE:
+                       event.Enable(true);
+                       return;
+               case myMenuID_Import:
+                       event.Enable(true);
+                       return;
+               case wxID_SELECTALL:
+                       event.Enable(true);
+                       return;
+               case myMenuID_SelectFragment:
+                       event.Enable(nselected > 0);
+                       return;
+               case myMenuID_SelectReverse:
+                       event.Enable(true);
+                       return;
+               case myMenuID_AddHydrogenSp3:
+               case myMenuID_AddHydrogenSp2:
+               case myMenuID_AddHydrogenLinear:
+               case myMenuID_AddHydrogenPyramidal:
+               case myMenuID_AddHydrogenBent:
+                       event.Enable(nselected > 0);
+                       return;
+               case myMenuID_FitToScreen:
+                       event.Enable(true);
+                       return;
+               case myMenuID_ShowUnitCell:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
+                       return;
+               case myMenuID_ShowPeriodicBox:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
+                       return;
+               case myMenuID_ShowHydrogens:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
+                       return;
+               case myMenuID_ShowDummyAtoms:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
+                       return;
+               case myMenuID_ShowExpandedAtoms:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
+                       return;
+               case myMenuID_ShowEllipsoids:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
+                       return;
+               case myMenuID_ShowRotationCenter:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
+                       return;
+               case myMenuID_ShowAllAtoms:
+               case myMenuID_HideReverse:
+                       event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
+                       return;
+               case myMenuID_HideSelected:
+               case myMenuID_HideUnselected:
+                       event.Enable(nselected > 0);
+                       return;
+               case myMenuID_LineMode:
+                       event.Enable(true);
+                       event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
+                       return;
+               case myMenuID_MolecularDynamics:
+               case myMenuID_Minimize:
+               case myMenuID_DefinePeriodicBox:
+               case myMenuID_PressureControl:
+                       if (mol != NULL && mol->mutex == NULL)
+                               event.Enable(true);
+                       else event.Enable(false);
+                       return;
+               case myMenuID_StopMDRun:
+                       if (mol != NULL && mol->mutex != NULL)
+                               event.Enable(true);
+                       else event.Enable(false);
+                       return;
+               case myMenuID_ShowPeriodicImage:
+                       event.Enable(true);
+                       return;
+               case myMenuID_CreateGamessInput:
+                       if (mol != NULL && mol->natoms > 0)
+                               event.Enable(true);
+                       else event.Enable(false);
+                       return;
+               case myMenuID_CreateMOCube:
+                       if (mol == NULL || mol->bset == NULL || mol->bset->natoms == 0)
+                               event.Enable(false);
+                       else
+                               event.Enable(true);
+                       return;
+       }
+       event.Skip();
+}
+
+#pragma mark ====== Plain C Interface ======
+
+MyDocument *
+MyDocumentFromMolecule(Molecule *mp)
+{
+  void *ref;
+  if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
+    return ((MoleculeView *)ref)->MolDocument();
+  else return NULL;
+}
+
+Molecule *
+MoleculeCallback_openNewMolecule(const char *fname)
+{
+  MainView *mview = MainViewCallback_newFromFile(fname);
+  if (mview != NULL)
+    return mview->mol;
+  else return NULL;
+}
+
+void
+MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
+{
+       MyDocument *doc = MyDocumentFromMolecule(mp);
+       if (doc && !doc->isModifyNotificationSent) {
+               doc->isModifyNotificationSent = true;
+               wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
+               if (now_flag)
+                       doc->ProcessEvent(myEvent);
+               else
+                       wxPostEvent(doc, myEvent);
+       }
+}
+
+/*
+static NSString *
+sMoleculePasteboardType(const char *type)
+{
+       static NSMutableArray *array;
+       NSString *str;
+       unsigned int idx;
+       if (array == nil)
+               array = [[NSMutableArray array] retain];
+       str = [NSString stringWithUTF8String: type];
+       idx = [array indexOfObject: str];
+       if (idx == NSNotFound) {
+               [array addObject: str];
+               return str;
+       } else {
+               return [array objectAtIndex: idx];
+       }
+}
+*/
+
+static wxDataObject *
+sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
+{
+       if (strcmp(type, "TEXT") == 0) {
+               wxTextDataObject *tp = new wxTextDataObject();
+               if (data != NULL) {
+                       wxString str((const char *)data, wxConvUTF8, length);
+                       tp->SetText(str);
+               }
+               return tp;
+       } else {
+               MyClipboardData *dp = new MyClipboardData(type);
+               if (data != NULL)
+                       dp->SetData(length, data);
+               return dp;
+       }
+}
+
+/*  Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
+    will be taken by the pasteboard. */
+int
+MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
+{
+       int retval = 1;
+       if (wxTheClipboard->Open()) {
+               wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
+       /*      MyClipboardData *myData = new MyClipboardData();
+               if (myData->SetData(length, data)) {
+                       wxTheClipboard->SetData(myData);
+                       retval = 0;
+               } else
+                       delete myData; */
+               wxTheClipboard->Close();
+               retval = 0;
+       }
+       return retval;
+}
+
+int
+MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
+{
+       return 0;
+}
+
+int
+MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
+{
+       int retval = 1;
+       int len;
+       void *p;
+       if (wxTheClipboard->Open()) {
+               wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
+               if (wxTheClipboard->GetData(*dp)) {
+                       if (strcmp(type, "TEXT") == 0) {
+                               wxTextDataObject *tp = (wxTextDataObject *)dp;
+                               wxString str = tp->GetText();
+                               const char *cp = str.mb_str(wxConvUTF8);
+                               len = strlen(cp);
+                               p = malloc(len + 1);
+                               if (p != NULL) {
+                                       strcpy((char *)p, cp);
+                                       *dptr = p;
+                                       *length = len;
+                                       retval = 0;
+                               }
+                               delete tp;
+                       } else {
+                               MyClipboardData *mp = (MyClipboardData *)dp;
+                               len = mp->GetDataSize();
+                               p = malloc(len); 
+                               if (p != NULL) {
+                                       mp->GetDataHere(p);
+                                       *dptr = p;
+                                       *length = len;
+                                       retval = 0;
+                               }
+                               delete mp;
+                       }
+               }
+               wxTheClipboard->Close();
+       }
+       return retval;
+}
+
+int
+MoleculeCallback_isDataInPasteboard(const char *type)
+{
+       if (strcmp(type, "TEXT") == 0)
+               return wxTheClipboard->IsSupported(wxDF_TEXT);
+       else {
+               MyClipboardData myData(type);
+               return wxTheClipboard->IsSupported(myData.GetFormat());
+       }
+}
+
+Molecule *
+MoleculeCallback_currentMolecule(void)
+{
+  MainView *mview = MainViewCallback_activeView();
+  if (mview != NULL)
+    return mview->mol;
+  else return NULL;
+}
+
+Molecule *
+MoleculeCallback_moleculeAtIndex(int idx)
+{
+  MainView *mview = MainViewCallback_viewWithTag(idx);
+  if (mview != NULL)
+    return mview->mol;
+  else return NULL;
+}
+
+Molecule *
+MoleculeCallback_moleculeAtOrderedIndex(int idx)
+{
+  return MoleculeCallback_moleculeAtIndex(idx);
+}
+
+void
+MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
+{
+  MyDocument *doc = MyDocumentFromMolecule(mol);
+  if (doc != NULL) {
+    wxString fname;
+    doc->GetPrintableName(fname);
+    strncpy(buf, (const char*)fname.mb_str(wxConvUTF8), bufsize - 1);  /*  wxString -> UTF8  */
+    buf[bufsize - 1] = 0;
+  } else {
+    buf[0] = 0;
+  }
+}
+
+void
+MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
+{
+       MyDocument *doc = MyDocumentFromMolecule(mol);
+       if (doc != NULL && doc->hasFile)
+               MainViewCallback_getFilename(mol->mview, buf, bufsize);
+       else buf[0] = 0;
+}
+
+void
+MoleculeCallback_lockMutex(void *mutex)
+{
+       ((wxMutex *)mutex)->Lock();
+}
+
+void
+MoleculeCallback_unlockMutex(void *mutex)
+{
+       ((wxMutex *)mutex)->Unlock();
+}
+
+void
+MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
+{
+       MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
+}
+
+void
+MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
+{
+       MyDocument *doc = MyDocumentFromMolecule(mol);
+       if (doc != NULL && doc->IsUndoEnabled())
+               doc->PushUndoAction(action);
+}
+
+int
+MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
+{
+       MyDocument *doc = MyDocumentFromMolecule(mol);
+       if (doc != NULL) {
+               doc->SetUndoEnabled(flag);
+               return (doc->IsUndoEnabled() ? 1 : 0);
+       } else return 0;
+}
+
+int
+MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
+{
+       MyDocument *doc = MyDocumentFromMolecule(mol);
+       if (doc != NULL && doc->IsUndoEnabled())
+               return 1;
+       else return 0;
+}
+
diff --git a/wxSources/MyDocument.h b/wxSources/MyDocument.h
new file mode 100755 (executable)
index 0000000..1517b01
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *  MyDocument.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MyDocument_h__
+#define __MyDocument_h__
+
+#include "wx/docview.h"
+#include "wx/cmdproc.h"
+
+#include "../MolLib/MolLib.h"
+
+/*  Custom Event for Document handling  */
+extern const wxEventType MyDocumentEvent;
+enum {
+       MyDocumentEvent_willNeedCleanUndoStack = 1,
+       MyDocumentEvent_documentModified,
+       MyDocumentEvent_scriptMenuModified,
+       MyDocumentEvent_updateDisplay,
+       MyDocumentEvent_threadTerminated
+};
+
+class MyDocument: public wxDocument
+{
+public:
+       Molecule *mol;
+
+       MyDocument();
+       virtual ~MyDocument();
+
+       void SetMolecule(Molecule *mol);
+       Molecule *GetMolecule() { return mol; }
+
+       //  For "register-undo" based undo/redo mechanism
+       bool isUndoEnabled;
+       bool isUndoing;
+       bool isModifyNotificationSent;
+
+       wxCommand *currentCommand;
+       MolAction **undoStack;
+       int countUndoStack;
+
+       int undoGroupLevel;
+       bool isCleanUndoStackRequested;
+
+       bool hasFile;  /*  wxWidgets does not maintain this info for us  */
+
+       int subThreadKind;  /*  0: none, 1: MM/MD (mutex is in Molecule structure)  */
+       
+       MainView        *GetMainView() { return (mol != NULL ? mol->mview : NULL); }
+       void    SetIsUndoing(bool flag) { isUndoing = flag; }
+       void    SetCurrentCommand(wxCommand *command) { currentCommand = command; }
+       void    PushUndoAction(MolAction *action);
+       void    CleanUndoStack(bool shouldRegister = true);
+       bool    IsUndoEnabled() { return isUndoEnabled; }
+       void    SetUndoEnabled(bool flag);      
+       void    UpdateModifyFlag();
+       void    BeginUndoGrouping();
+       void    EndUndoGrouping();
+       
+       void    OnNeedCleanUndoStack(wxCommandEvent& event);
+
+       void    OnDocumentModified(wxCommandEvent& event);
+       void    OnImport(wxCommandEvent& event);
+       void    OnExport(wxCommandEvent& event);
+
+       void    OnCopy(wxCommandEvent& event);
+       void    OnCut(wxCommandEvent& event);
+       void    OnPaste(wxCommandEvent& event);
+       void    OnDelete(wxCommandEvent& event);
+
+       void    OnSelectAll(wxCommandEvent& event);
+       void    OnSelectFragment(wxCommandEvent& event);
+       void    OnSelectReverse(wxCommandEvent& event);
+
+       void    OnAddHydrogen(wxCommandEvent& event);
+
+       void    OnFitToScreen(wxCommandEvent& event);
+       void    OnCenterSelection(wxCommandEvent& event);
+       void    OnShowMenu(wxCommandEvent& event);
+       void    OnToggleLineMode(wxCommandEvent &event);
+       void    OnShowGraphite(wxCommandEvent &event);
+
+       void    OnShowAllAtoms(wxCommandEvent &event);
+       void    OnHideSelected(wxCommandEvent &event);
+       void    OnHideUnselected(wxCommandEvent &event);
+       void    OnHideReverse(wxCommandEvent &event);
+       
+       void    DoMDOrMinimize(int minimize);
+       void    OnMolecularDynamics(wxCommandEvent &event);
+       void    OnMinimize(wxCommandEvent &event);
+       void    OnStopMDRun(wxCommandEvent &event);
+       void    OnDefinePeriodicBox(wxCommandEvent &event);
+       void    OnShowPeriodicImage(wxCommandEvent &event);
+       void    OnPressureControl(wxCommandEvent &event);
+       void    OnDefineSymmetry(wxCommandEvent &event);
+       void    OnExpandBySymmetry(wxCommandEvent &event);
+
+       void    OnInvokeResp(wxCommandEvent &event);
+       void    OnInvokeAntechamber(wxCommandEvent &event);
+       
+       void    OnCreateGamessInput(wxCommandEvent &event);
+       void    OnCreateMOCube(wxCommandEvent &event);
+       
+       void    OnUpdateDisplay(wxCommandEvent &event);
+       void    OnSubThreadTerminated(wxCommandEvent &event);
+       
+       void    OnUpdateUI(wxUpdateUIEvent &event);
+
+ protected:
+       virtual bool DoSaveDocument(const wxString& file);
+       virtual bool DoOpenDocument(const wxString& file);
+
+ private:
+       DECLARE_DYNAMIC_CLASS(MyDocument)
+       DECLARE_EVENT_TABLE()
+};
+
+MyDocument *MyDocumentFromMolecule(Molecule *mp);
+
+#endif
diff --git a/wxSources/MyGLCanvas.cpp b/wxSources/MyGLCanvas.cpp
new file mode 100755 (executable)
index 0000000..563ab52
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ *  MyGLCanvas.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error "You should have DocView architecture enabled in your wxWidgets installation."
+#endif
+
+#include "wx/docview.h"
+
+#include "MyGLCanvas.h"
+#include "MoleculeView.h"
+#include "MyApp.h"
+
+#include "../MolLib/MolLib.h"
+
+BEGIN_EVENT_TABLE(MyGLCanvas, wxGLCanvas)
+    EVT_SIZE(MyGLCanvas::OnSize)
+    EVT_ERASE_BACKGROUND(MyGLCanvas::OnEraseBackground)
+    EVT_MOUSE_EVENTS(MyGLCanvas::OnMouseEvent)
+    EVT_PAINT(MyGLCanvas::OnPaint)
+       EVT_MOUSE_CAPTURE_LOST(MyGLCanvas::OnCaptureLost)
+END_EVENT_TABLE()
+
+#ifdef __WXMSW__
+int *MyGLAttributes = NULL;
+#else
+int MyGLAttributes[20] = { WX_GL_RGBA, WX_GL_MIN_RED, 1, WX_GL_MIN_GREEN, 1,
+        WX_GL_MIN_BLUE, 1, WX_GL_DEPTH_SIZE, 1,
+        WX_GL_DOUBLEBUFFER,
+#  if defined(__WXMAC__) || defined(__WXCOCOA__)
+        GL_NONE
+#  else
+        None
+#  endif
+       };
+#endif
+
+// Define a constructor for my canvas
+MyGLCanvas::MyGLCanvas(MoleculeView *v, wxWindow *frame, const wxPoint& pos, const wxSize& size, long style):
+  wxGLCanvas(frame, wxID_ANY, pos, size, style, _T("TestGLCanvas"), MyGLAttributes)
+{
+       view = v;
+}
+
+// Define the repainting behaviour
+void
+MyGLCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) )
+{
+    wxPaintDC dc(this);
+
+#ifndef __WXMOTIF__
+    if (!GetContext()) return;
+#endif
+
+    SetCurrent();
+    if (view)
+      view->OnDraw(&dc);
+       else {
+               glClearColor (0, 0, 0, 0);
+               glClear(GL_COLOR_BUFFER_BIT |
+                               GL_DEPTH_BUFFER_BIT);
+       }
+    SwapBuffers();
+}
+
+void
+MyGLCanvas::OnCaptureLost(wxMouseCaptureLostEvent &event)
+{
+       wxPoint pt;
+       int w, h, modifierFlags;
+       float p[2];
+       MainView *mview;
+
+       if (view == NULL)
+               return;
+
+       pt = ScreenToClient(::wxGetMousePosition());
+       GetClientSize(&w, &h);
+       p[0] = pt.x;
+       p[1] = h - pt.y;
+       modifierFlags = MainViewCallback_modifierFlags(NULL);
+       mview = ((MoleculeView *)view)->mview;
+       MoleculeLock(mview->mol);
+       MainView_mouseUp(((MoleculeView *)view)->mview, p, modifierFlags, 0);
+       MoleculeUnlock(mview->mol);
+}
+
+void
+MyGLCanvas::OnMouseEvent(wxMouseEvent &event)
+{
+       if (!view)
+               return;
+
+       wxPoint pt(event.GetPosition());
+       float p[2];
+       int modifierFlags, clickCount;
+       MainView *mview = ((MoleculeView *)view)->mview;
+       int w, h;
+       GetClientSize(&w, &h);
+
+       p[0] = pt.x;
+       p[1] = h - pt.y;  /*  The origin of the internal coordinate should be left-bottom  */
+       modifierFlags = MainViewCallback_modifierFlags(&event);
+       clickCount = MainViewCallback_clickCount(&event);
+
+       if (event.LeftUp() /* || (mview->isDragging && event.Entering() && !event.LeftIsDown()) */) {
+               ReleaseMouse();
+               MoleculeLock(mview->mol);
+               MainView_mouseUp(mview, p, modifierFlags, clickCount);
+               MoleculeUnlock(mview->mol);
+       } else if (event.LeftDClick()) {
+               ReleaseMouse();
+               MoleculeLock(mview->mol);
+               MainView_mouseUp(mview, p, modifierFlags, 2);
+               MoleculeUnlock(mview->mol);
+       } else if (event.Dragging()) {
+               MoleculeLock(mview->mol);
+               MainView_mouseDragged(mview, p, modifierFlags);
+               MoleculeUnlock(mview->mol);
+       } else if (event.LeftDown()) {
+               if (wxWindow::FindFocus() != this)
+                       SetFocus();
+               CaptureMouse();
+               MoleculeLock(mview->mol);
+               MainView_mouseDown(mview, p, modifierFlags);
+               MoleculeUnlock(mview->mol);
+       } else event.Skip();
+}
+
+void
+MyGLCanvas::OnSize(wxSizeEvent &event)
+{
+    // this is also necessary to update the context on some platforms
+    wxGLCanvas::OnSize(event);
+
+    // set GL viewport (not called by wxGLCanvas::OnSize on all platforms...)
+    int w, h;
+    GetClientSize(&w, &h);
+#ifndef __WXMOTIF__
+    if (GetContext())
+#endif
+    {
+        SetCurrent();
+        glViewport(0, 0, (GLint) w, (GLint) h);
+    }
+#if defined(__WXMSW__)
+       //  On MSW, the window is not repainted upon resize event
+       Refresh();
+#endif
+}
+
+void
+MyGLCanvas::OnEraseBackground(wxEraseEvent & WXUNUSED(event))
+{
+    // Do nothing, to avoid flashing.
+}
diff --git a/wxSources/MyGLCanvas.h b/wxSources/MyGLCanvas.h
new file mode 100755 (executable)
index 0000000..a493fd4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  MyGLCanvas.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/10/24.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MyGLCanvas_h__
+#define __MyGLCanvas_h__
+
+#include "wx/glcanvas.h"
+#include "../MolLib/MolLib.h"
+
+class MoleculeView;
+
+class MyGLCanvas: public wxGLCanvas
+{
+public:
+    MoleculeView *view;
+
+    MyGLCanvas(MoleculeView *v, wxWindow *frame, const wxPoint& pos, const wxSize& size, long style = 0);
+    void OnPaint(wxPaintEvent &event);
+    void OnMouseEvent(wxMouseEvent &event);
+    void OnEraseBackground(wxEraseEvent &event);
+    void OnSize(wxSizeEvent &event);
+       void OnChar(wxKeyEvent &event);
+       void OnCaptureLost(wxMouseCaptureLostEvent &event);
+
+private:
+    DECLARE_EVENT_TABLE()
+};
+
+
+#endif  /* __MyGLCanvas_h__ */
+
diff --git a/wxSources/MyListCtrl.cpp b/wxSources/MyListCtrl.cpp
new file mode 100644 (file)
index 0000000..f129942
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ *  MyListCtrl.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/12/09.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MyListCtrl.h"
+
+#include "wx/dcclient.h"
+#include "wx/scrolwin.h"
+
+const wxEventType MyListCtrlEvent = wxNewEventType();
+
+IMPLEMENT_DYNAMIC_CLASS(MyListCtrl, wxGenericListCtrl)
+
+BEGIN_EVENT_TABLE(MyListCtrl, wxGenericListCtrl)
+EVT_LIST_ITEM_SELECTED(-1, MyListCtrl::OnItemSelectionChanged)
+EVT_LIST_ITEM_DESELECTED(-1, MyListCtrl::OnItemSelectionChanged)
+EVT_COMMAND(MyListCtrlEvent_tableSelectionChanged, MyListCtrlEvent, MyListCtrl::OnTableSelectionChanged)
+EVT_COMMAND(MyListCtrlEvent_enableTableSelectionNotification, MyListCtrlEvent, MyListCtrl::OnEnableTableSelectionNotification)
+EVT_LIST_BEGIN_DRAG(-1, MyListCtrl::OnBeginDrag)
+EVT_LEFT_DCLICK(MyListCtrl::OnLeftDClick)
+EVT_KEY_DOWN(MyListCtrl::OnKeyDown)
+EVT_LEFT_DOWN(MyListCtrl::OnMouseDown)
+END_EVENT_TABLE()
+
+MyListCtrl::MyListCtrl()
+{
+}
+
+MyListCtrl::~MyListCtrl()
+{
+}
+
+bool
+MyListCtrl::Create(wxWindow* parent, wxWindowID wid, const wxPoint& pos, const wxSize& size)
+{
+       this->wxGenericListCtrl::Create(parent, wid, pos, size, wxLC_REPORT | wxLC_VIRTUAL | wxBORDER_SIMPLE);
+       dataSource = NULL;
+       editText = NULL;
+       selectionChangeNotificationSent = false;
+       selectionChangeNotificationEnabled = true;
+       subTitleRowAttr = new wxListItemAttr;
+       dragTargetRow = -1;
+       return true;
+}
+
+void
+MyListCtrl::SetDataSource(MyListCtrlDataSource *source)
+{
+       dataSource = source;
+       RefreshTable();
+}
+
+void
+MyListCtrl::RefreshTable()
+{
+       if (dataSource != NULL) {
+               int nrows = dataSource->GetItemCount(this);
+               SetItemCount(nrows);
+               if (nrows > 0) {
+                       RefreshItems(0, nrows - 1);
+               }
+       }
+}
+
+// Define the repainting behaviour
+void
+MyListCtrl::OnPaintCallback(wxDC *dc)
+{
+       if (dragTargetRow >= 0) {
+               wxRect r;
+               wxPen pen = *wxCYAN_PEN;
+               int dx, dy, y;
+               /*  m_mainWin is a protected member in wxGenericListCtrl  */
+               ((wxScrolledWindow *)m_mainWin)->CalcScrolledPosition(0, 0, &dx, &dy);  
+               pen.SetWidth(3);
+               dc->SetPen(pen);
+               if (dragTargetRow == GetItemCount()) {
+                       GetItemRect(dragTargetRow - 1, r);
+                       y = r.y - dy;
+               } else {
+                       GetItemRect(dragTargetRow, r);
+                       y = r.y - dy - r.height;
+               }
+               printf("dragTargetRow = %d, r.y = %d, y = %d\n", dragTargetRow, r.y, y);
+               dc->DrawLine(r.x - dx, y, r.x - dx + r.width, y);
+               
+       /*      r.Inflate(-1);
+               dc->DrawLine(r.x, r.y, r.x + r.width, r.y);
+               dc->DrawLine(r.x + r.width, r.y, r.x + r.width, r.y - r.height);
+               dc->DrawLine(r.x + r.width, r.y - r.height, r.x, r.y - r.height);
+               dc->DrawLine(r.x, r.y - r.height, r.x, r.y); */
+       }
+}
+
+//  Callback function that is called from wxListMainWindow::OnPaint().
+//  This is a very ugly hack, but I can think of no alternative...
+void
+wxListCtrl_onPaintCallback(wxGenericListCtrl *listctrl, wxDC *dc)
+{
+       MyListCtrl *ctrl = wxStaticCast(listctrl, MyListCtrl);
+       if (ctrl != NULL && dc != NULL)
+               ctrl->OnPaintCallback(dc);
+}
+
+wxString
+MyListCtrl::OnGetItemText(long item, long column) const
+{
+       if (dataSource == NULL)
+               return wxEmptyString;
+       return dataSource->GetItemText((MyListCtrl *)this, item, column);
+}
+
+wxListItemAttr *
+MyListCtrl::OnGetItemAttr(long item) const
+{
+       float fg[3], bg[3];
+       long row, col;
+       int ret;
+       row = item % 1000000;
+       col = item / 1000000 - 1;  //  -1 for all rows
+       ret = dataSource->SetItemColor((MyListCtrl *)this, row, col, fg, bg);
+       if (ret == 0)
+               return NULL;
+       if (ret & 1) {
+               wxColour fgcol(fg[0] * 255, fg[1] * 255, fg[2] * 255);
+               subTitleRowAttr->SetTextColour(fgcol);
+       } else subTitleRowAttr->SetTextColour(*wxBLACK);
+       if (ret & 2) {
+               wxColour bgcol(bg[0] * 255, bg[1] * 255, bg[2] * 255);
+               subTitleRowAttr->SetBackgroundColour(bgcol);
+       } else subTitleRowAttr->SetBackgroundColour(*wxWHITE);
+       return subTitleRowAttr;
+}
+
+void
+MyListCtrl::PostSelectionChangeNotification()
+{
+       if (!selectionChangeNotificationSent && selectionChangeNotificationEnabled) {
+               selectionChangeNotificationSent = true;
+               wxCommandEvent myEvent(MyListCtrlEvent, MyListCtrlEvent_tableSelectionChanged);
+               wxPostEvent(this, myEvent);
+       }
+}
+
+void
+MyListCtrl::OnBeginLabelEdit(wxListEvent &event)
+{
+//     printf("OnBeginLabelEdit: item index = %d\n", event.GetIndex());
+}
+
+void
+MyListCtrl::OnEndLabelEdit(wxListEvent &event)
+{
+//     printf("OnEndLabelEdit: item index = %d\n", event.GetIndex());
+}
+
+void
+MyListCtrl::OnItemActivated(wxListEvent &event)
+{
+//     printf("OnItemActivated: item index = %d, col = %d\n", event.GetIndex(), event.GetItem().m_col);
+}
+
+void
+MyListCtrl::OnBeginDrag(wxListEvent &event)
+{
+       int count = GetItemCount();
+       
+       if (dataSource == NULL || !dataSource->IsDragAndDropEnabled(this))
+               return;
+       EndEditText(true);
+       dragTargetRow = -1;
+       while (1) {
+               wxRect r;
+               wxMouseState mstate = wxGetMouseState();
+               wxPoint pt(mstate.GetX(), mstate.GetY());
+               pt = ScreenToClient(pt);
+               long newRow = FindItem(-1, pt, 0);
+               if (newRow != dragTargetRow) {
+                       if (newRow >= 0)
+                               EnsureVisible(newRow);
+                       else {
+                               GetItemRect(0, r);
+                               if (pt.y < r.y)
+                                       EnsureVisible(0);
+                               else {
+                                       GetItemRect(count - 1, r);
+                                       if (pt.y > r.y) {
+                                               EnsureVisible(count - 1);
+                                               if (pt.y < r.y + r.height)
+                                                       newRow = count;
+                                       }
+                               }
+                       }
+                       if (newRow >= 0) {
+                               if (newRow == count) {
+                                       GetItemRect(newRow - 1, r);
+                                       r.y += r.height / 2;
+                               } else {
+                                       GetItemRect(newRow, r);
+                                       r.y -= r.height / 2;
+                               }
+                               RefreshRect(r);
+                       }
+                       if (dragTargetRow >= 0) {
+                               if (dragTargetRow == count) {
+                                       GetItemRect(dragTargetRow - 1, r);
+                                       r.y += r.height / 2;
+                               } else {
+                                       GetItemRect(dragTargetRow, r);
+                                       r.y -= r.height / 2;
+                               }
+                               RefreshRect(r);
+                       }
+                       dragTargetRow = newRow;
+                       Update();
+               }
+               if (!mstate.LeftDown()) {
+                       //  If the mouse cursor is outside the item rect, then dragging should be discarded
+                       if (dragTargetRow >= 0) {
+                               r = GetClientRect();
+                               if (!r.Contains(pt))
+                                       dragTargetRow = -1;
+                       }
+                       break;
+               }
+       }
+       if (dragTargetRow >= 0)
+               dataSource->DragSelectionToRow(this, dragTargetRow);
+       dragTargetRow = -1;
+       Update();
+}
+
+bool
+MyListCtrl::GetItemRectForRowAndColumn(wxRect &rect, int row, int column)
+{
+       int i, xpos, width, xunit, yunit;
+       if (!GetItemRect(row, rect))
+               return false;
+       GetScrollPixelsPerUnit(&xunit, &yunit);
+       xpos = -GetScrollPos(wxHORIZONTAL) * xunit;
+       for (i = 0; i < column; i++) {
+               width = GetColumnWidth(i);
+               xpos += width;
+       }
+       rect.SetX(xpos);
+       rect.SetWidth(GetColumnWidth(column));
+       return true;
+}
+
+void
+MyListCtrl::GetScrollPixelsPerUnit(int *xunit, int *yunit)
+{
+       int x, y;
+       /*  m_mainWin is a protected member in wxGenericListCtrl  */
+       ((wxScrolledWindow *)m_mainWin)->GetScrollPixelsPerUnit(&x, &y);        
+       if (xunit != NULL)
+               *xunit = x;
+       if (yunit != NULL)
+               *yunit = y;
+}
+
+bool
+MyListCtrl::FindItemAtPosition(const wxPoint &pos, int *row, int *column)
+{
+       int i, r, ncols, width, xpos, flags, xunit, yunit;
+       r = (int)HitTest(pos, flags, NULL);
+       if (r == wxNOT_FOUND)
+               return false;
+       ncols = GetColumnCount();
+       GetScrollPixelsPerUnit(&xunit, &yunit);
+       xpos = -GetScrollPos(wxHORIZONTAL) * xunit;
+       for (i = 0; i < ncols; i++) {
+               width = GetColumnWidth(i);
+               xpos += width;
+               if (pos.x < xpos)
+                       break;
+       }
+       if (i >= ncols)
+               return false;
+       *row = r;
+       *column = i;
+       return true;
+}
+
+void
+MyListCtrl::StartEditText(int row, int column)
+{
+       wxRect rect;
+       int x0, x1, dx, size, xpos, ypos, xunit, yunit, yorigin;
+       if (editText != NULL)
+               EndEditText(true);
+       if (dataSource == NULL || !dataSource->IsItemEditable(this, row, column))
+               return;
+
+       /*  Scroll the list so that the editing item is visible  */
+       EnsureVisible(row);
+       GetItemRectForRowAndColumn(rect, row, column);
+       rect.Inflate(1, 2);
+       editRow = row;
+       editColumn = column;
+       GetScrollPixelsPerUnit(&xunit, &yunit);
+       xpos = GetScrollPos(wxHORIZONTAL);
+       ypos = GetScrollPos(wxVERTICAL);
+       x0 = rect.GetX();
+       x1 = x0 + rect.GetWidth();
+       if (x0 < 0) {
+               /*  Scroll right  */
+               dx = x0 / xunit - 1;
+               if (xpos + dx < 0)
+                       dx = -xpos;
+       } else if (x1 > (size = GetSize().GetWidth())) {
+               /*  Scroll left  */
+               dx = ((x1 - size) / xunit) + 1;
+       } else dx = 0;
+       if (dx != 0) {
+               /*  m_mainWin is a protected member in wxGenericListCtrl  */
+               ((wxScrolledWindow *)m_mainWin)->Scroll(xpos + dx, -1);
+               Refresh();
+       }
+
+       /*  Reposition the rect relative to the origin of the scrolling area  */
+       yorigin = ((wxScrolledWindow *)m_mainWin)->GetPosition().y;
+       GetItemRectForRowAndColumn(rect, row, column);
+       rect.Inflate(1, 2);
+       rect.Offset(0, -yorigin);
+
+       wxString str = dataSource->GetItemText(this, editRow, editColumn);
+       editText = new wxTextCtrl(((wxScrolledWindow *)m_mainWin), -1, wxT(""), rect.GetPosition(), rect.GetSize(), wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB);
+       editText->AppendText(str);
+       editText->SetFocus();
+       editText->Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(MyListCtrl::OnKeyDownOnEditText), NULL, this);
+       editText->Connect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(MyListCtrl::OnKillFocusOnEditText), NULL, this);
+       editText->SetSelection(-1, -1);  //  Select all text
+
+       /*  Select only this row  */
+       x1 = GetItemCount();
+       for (x0 = 0; x0 < x1; x0++)
+               SetItemState(x0, (x0 == row ? wxLIST_STATE_SELECTED : 0), wxLIST_STATE_SELECTED);
+
+       //  Call the event handler directly
+       //  (Otherwise, the table selection may be updated from the "current" selection in the molecule
+       //  before the selection is updated from the table)
+       wxCommandEvent dummyEvent;
+       OnTableSelectionChanged(dummyEvent);
+}
+
+void
+MyListCtrl::EndEditText(bool setValueFlag)
+{
+       if (editText != NULL) {
+               if (setValueFlag && dataSource) {
+                       wxString sval = editText->GetValue();
+                       dataSource->SetItemText(this, editRow, editColumn, sval);
+               }
+               wxTextCtrl *t = editText;
+#if __WXMAC__
+               {
+                       /*  Erase the focus ring  */
+                       wxRect rect = t->GetRect();
+                       rect.Inflate(5, 5);
+                       Refresh(true, &rect);
+               }
+#endif
+               editText = NULL;  /*  Clear this field first to avoid recursive call of EndEditText()  */
+               t->Destroy();
+               SetFocus();
+       }
+}
+
+void
+MyListCtrl::OnKillFocusOnEditText(wxFocusEvent &event)
+{
+       if (editText != NULL) {
+               EndEditText(true);
+       }
+}
+
+void
+MyListCtrl::OnKeyDownOnEditText(wxKeyEvent &event)
+{
+       int keyCode, ncols, nrows, ecol, erow;
+       bool shiftDown;
+       keyCode = event.GetKeyCode();
+       ncols = GetColumnCount();
+       nrows = GetItemCount();
+       shiftDown = (event.GetModifiers() == wxMOD_SHIFT);
+       switch (keyCode) {
+               case WXK_TAB:
+                       if (shiftDown) {
+                               if (editColumn == 0) {
+                                       if (editRow == 0)
+                                               return;
+                                       ecol = ncols - 1;
+                                       erow = editRow - 1;
+                               } else {
+                                       erow = editRow;
+                                       ecol = editColumn - 1;
+                               }
+                       } else {
+                               if (editColumn == ncols - 1) {
+                                       if (editRow >= nrows - 1)
+                                               return;
+                                       ecol = 0;
+                                       erow = editRow + 1;
+                               } else {
+                                       ecol = editColumn + 1;
+                                       erow = editRow;
+                               }
+                       }
+                       EndEditText(true);
+                       StartEditText(erow, ecol);
+                       break;
+               case WXK_RETURN:
+                       if (event.GetModifiers() == wxMOD_ALT) {
+                               EndEditText(true);
+                               return;
+                       }
+                       if (shiftDown) {
+                               if (editRow == 0)
+                                       return;
+                               erow = editRow - 1;
+                       } else {
+                               if (editRow == nrows - 1)
+                                       return;
+                               erow = editRow + 1;
+                       }
+                       EndEditText(true);
+                       StartEditText(erow, editColumn);
+                       break;
+               case WXK_ESCAPE:
+                       EndEditText(false);
+                       break;
+               default:
+                       event.Skip();
+                       break;
+       }
+}
+
+bool
+MyListCtrl::DeleteColumn(int col)
+{
+       EndEditText(false);
+       return wxGenericListCtrl::DeleteColumn(col);
+}
+
+bool
+MyListCtrl::InsertColumn(long col, const wxString &heading, int format, int width)
+{
+       EndEditText(false);
+       return wxGenericListCtrl::InsertColumn(col, heading, format, width);
+}
+
+void
+MyListCtrl::OnLeftDClick(wxMouseEvent &event)
+{
+       int row, col;
+       wxPoint pos = event.GetPosition();
+       if (!FindItemAtPosition(pos, &row, &col))
+               return;
+       if (editText != NULL) {
+               if (editRow == row && editColumn == col) {
+                       event.Skip();
+                       return;
+               }
+               EndEditText();
+       }
+       StartEditText(row, col);
+}
+
+void
+MyListCtrl::OnMouseDown(wxMouseEvent &event)
+{
+       if (editText != NULL) {
+               //  During the text edit, mouse down outside the textctrl will terminate the editing
+               EndEditText();
+       }
+
+       //  Intercept mouse down event and post selection change notification
+       //  (a workaround of wxMSW problem where EVT_LIST_ITEM_SELECTED is not sent in some occasions)
+       PostSelectionChangeNotification();
+       event.Skip();
+}
+
+void
+MyListCtrl::OnKeyDown(wxKeyEvent &event)
+{
+       //  See comments on OnMouseUp()
+       PostSelectionChangeNotification();
+       event.Skip();
+}
+
+void
+MyListCtrl::OnItemSelectionChanged(wxListEvent &event)
+{
+       PostSelectionChangeNotification();
+}
+
+void
+MyListCtrl::OnTableSelectionChanged(wxCommandEvent &event)
+{
+       selectionChangeNotificationSent = false;
+       if (dataSource == NULL)
+               return;
+       dataSource->OnSelectionChanged(this);
+}
+
+void
+MyListCtrl::OnEnableTableSelectionNotification(wxCommandEvent &event)
+{
+       selectionChangeNotificationEnabled = true;
+}
diff --git a/wxSources/MyListCtrl.h b/wxSources/MyListCtrl.h
new file mode 100644 (file)
index 0000000..054f256
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *  MyListCtrl.h
+ *
+ *  Created by Toshi Nagata on 08/12/09.
+ *  Copyright 2008-2009 Toshi Nagata. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ */
+
+#ifndef __MyListCtrl_h__
+#define __MyListCtrl_h__
+
+#include "wx/listctrl.h"
+#include "wx/string.h"
+#include "wx/textctrl.h"
+#include "wx/dc.h"
+
+#if __WXMSW__
+#include <wx/generic/listctrl.h>
+#endif
+
+class MyListCtrl;
+extern const wxEventType MyListCtrlEvent;
+enum {
+       MyListCtrlEvent_tableSelectionChanged,
+       MyListCtrlEvent_enableTableSelectionNotification
+};
+
+/*  Data source protocol  */
+class MyListCtrlDataSource {
+public:
+       virtual int GetItemCount(MyListCtrl *ctrl) { return 0; }
+       virtual wxString GetItemText(MyListCtrl *ctrl, long row, long column) const { return _T(""); }
+       virtual int SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value) { return 0; }
+       virtual void DragSelectionToRow(MyListCtrl *ctrl, long row) {}
+       virtual bool IsItemEditable(MyListCtrl *ctrl, long row, long column) { return false; }
+       virtual bool IsDragAndDropEnabled(MyListCtrl *ctrl) { return false; }
+       virtual void OnSelectionChanged(MyListCtrl *ctrl) {}
+       
+       //  Return 1 if foreground color should be modified, 2 if background color should be modified, 3 if both
+       virtual int SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg) { return 0; }
+};
+
+class MyListCtrl: public wxGenericListCtrl {
+
+public:
+       MyListCtrlDataSource *dataSource;
+       wxTextCtrl *editText;
+       int editRow, editColumn;
+       wxListItemAttr *subTitleRowAttr;  /*  Used in SetItemColor()  */
+       int dragTargetRow;
+
+       MyListCtrl();
+       virtual ~MyListCtrl();
+       
+       bool Create(wxWindow* parent, wxWindowID wid, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize);
+
+       void SetDataSource(MyListCtrlDataSource *source);
+
+       void RefreshTable();
+
+       virtual wxString OnGetItemText(long item, long column) const;
+       virtual wxListItemAttr *OnGetItemAttr(long item) const;
+
+       void StartEditing(long item, long column);
+       void EndEditing(long item, long column);
+
+       void GetScrollPixelsPerUnit(int *xunit, int *yunit);
+       bool GetItemRectForRowAndColumn(wxRect &rect, int row, int column);
+       bool FindItemAtPosition(const wxPoint &pos, int *row, int *column);
+       
+       void SetItemTextForColumn(long item, long column, const wxString &text);
+
+       void StartEditText(int row, int column);
+       void EndEditText(bool setValueFlag = true);
+       void OnKeyDownOnEditText(wxKeyEvent &event);
+       void OnKillFocusOnEditText(wxFocusEvent &event);
+       void OnPaintCallback(wxDC *dc);
+
+       /*  Override the wxListCtrl functions to take care of the internal text editor  */
+       bool DeleteColumn(int col);
+       bool InsertColumn(long col, const wxString &heading, int format = wxLIST_FORMAT_LEFT, int width = -1);
+       
+       void OnItemSelectionChanged(wxListEvent &event);
+       void OnTableSelectionChanged(wxCommandEvent &event);
+       void EnableSelectionChangeNotification(bool flag) { selectionChangeNotificationEnabled = flag; }
+       void OnEnableTableSelectionNotification(wxCommandEvent &event);
+
+       void OnBeginLabelEdit(wxListEvent &event);
+       void OnEndLabelEdit(wxListEvent &event);
+       void OnItemActivated(wxListEvent &event);
+       void OnBeginDrag(wxListEvent &event);
+       
+       void OnKeyDown(wxKeyEvent &event);
+       void OnMouseDown(wxMouseEvent &event);
+       void OnLeftDClick(wxMouseEvent &event);
+       
+       void PostSelectionChangeNotification();
+       
+       bool selectionChangeNotificationSent;
+       bool selectionChangeNotificationEnabled;
+       
+private:
+       DECLARE_DYNAMIC_CLASS(MyListCtrl)
+       DECLARE_EVENT_TABLE()
+};
+
+#endif /* __MyListCtrl_h__ */
diff --git a/wxSources/MySlider.cpp b/wxSources/MySlider.cpp
new file mode 100755 (executable)
index 0000000..2420572
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ *  MySlider.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/11/15.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MySlider.h"
+#include "MyApp.h"
+
+BEGIN_EVENT_TABLE(MySlider, wxToggleButton)
+EVT_MOUSE_EVENTS(MySlider::OnMouseEvent)
+// EVT_PAINT(MySlider::OnPaint)
+EVT_MOUSE_CAPTURE_LOST(MySlider::OnCaptureLost)
+END_EVENT_TABLE()
+
+const wxEventType MySliderEvent = wxNewEventType();
+
+MySlider::MySlider(wxWindow* parent, wxWindowID id, int direction, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name):
+       wxToggleButton(parent, id, wxEmptyString, pos, size, style, validator, name)
+{
+       m_direction = direction;
+       SetCursor(wxCursor(wxCURSOR_HAND));
+       mouseStatus = 0;
+}
+
+//void
+//MySlider::OnPaint(wxPaintEvent &event)
+//{
+//     wxToggleButton::OnPaint(event);
+//}
+
+void
+MySlider::OnCaptureLost(wxMouseCaptureLostEvent &event)
+{
+       mouseStatus = 0;
+}
+
+void
+MySlider::OnMouseEvent(wxMouseEvent &event)
+{
+       wxPoint pt(event.GetPosition());
+       bool sendAction = false;
+       
+       if (mouseStatus == 0) {
+               if (event.LeftDown()) {
+                       mouseDownPoint = pt;
+                       mouseDragPoint = pt;
+                       mouseStatus = 1;
+                       sendAction = true;
+                       CaptureMouse();
+               }
+       } else {
+               if (event.Dragging()) {
+                       mouseDragPoint = pt;
+                       mouseStatus = 2;
+                       sendAction = true;
+               } else if (event.LeftUp() /* || (event.Entering() && !event.LeftIsDown()) */) {
+                       mouseStatus = 0;
+                       sendAction = true;
+                       ReleaseMouse();
+       //      } else if (event.Leaving()) {
+       //              wxGetApp().NotifyMouseUpEvent(this);
+               }
+       }
+       if (sendAction) {
+               /*  Send a custom event  */
+               wxCommandEvent event(MySliderEvent, GetId());
+               event.SetEventObject(this);
+               GetEventHandler()->ProcessEvent(event);
+       }
+}
+
+float
+MySlider::GetFloatValue()
+{
+       int width, height;
+       float f;
+       GetSize(&width, &height);
+       if (m_direction == wxHORIZONTAL) {
+               f = (mouseDragPoint.x - mouseDownPoint.x) / (float)width;
+       } else {
+               /*  Vertical  */
+               /*  Note the flipped coordinate! (up is positive internally)  */
+               f = -(mouseDragPoint.y - mouseDownPoint.y) / (float)height;
+       }
+       return f;
+}
diff --git a/wxSources/MySlider.h b/wxSources/MySlider.h
new file mode 100755 (executable)
index 0000000..92db7a6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  MySlider.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/11/15.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MySlider_h__
+#define __MySlider_h__
+
+#include "wx/tglbtn.h"
+#include "wx/string.h"
+#include "wx/slider.h"
+
+extern const wxEventType MySliderEvent;
+
+class MySlider: public wxToggleButton
+{
+public:
+       int m_direction;
+       int mouseStatus;  /*  0: mouseUp, 1: mouseDown, 2: dragging  */
+       wxPoint mouseDownPoint;
+       wxPoint mouseDragPoint;
+       
+    MySlider(wxWindow* parent, wxWindowID id, int direction, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxT("slider"));
+    void OnPaint(wxPaintEvent &event);
+    void OnMouseEvent(wxMouseEvent &event);
+       int GetMouseStatus() { return mouseStatus; }
+       float GetFloatValue();
+       void OnCaptureLost(wxMouseCaptureLostEvent &event);
+
+private:
+    DECLARE_EVENT_TABLE()      
+};
+
+#endif /* __MySlider_h__ */
diff --git a/wxSources/MyThread.cpp b/wxSources/MyThread.cpp
new file mode 100644 (file)
index 0000000..908b6a6
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *  MyThread.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/10/21.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "MyThread.h"
+
+wxThreadError
+MyThread::DetachNewThread(int (*entry_func)(void *, int), int (*exit_func)(void *, int), void *argptr, int argnum)
+{
+       wxThreadError err;
+       MyThread *thread = new MyThread();
+       if (thread == NULL)
+               return wxTHREAD_NO_RESOURCE;
+       thread->m_entry_func = entry_func;
+       thread->m_exit_func = exit_func;
+       thread->m_argptr = argptr;
+       thread->m_argnum = argnum;
+       if ((err = thread->Create()) != wxTHREAD_NO_ERROR)
+               return err;
+       return thread->Run();
+}
+
+MyThread::ExitCode
+MyThread::Entry()
+{
+       ExitCode code = (ExitCode)((*m_entry_func)(m_argptr, m_argnum));
+       if (m_exit_func)
+               (*m_exit_func)(m_argptr, m_argnum);
+       Exit(code);
+}
diff --git a/wxSources/MyThread.h b/wxSources/MyThread.h
new file mode 100644 (file)
index 0000000..7385722
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  MyThread.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/10/21.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __MyThread_h__
+#define __MyThread_h__
+
+#include "wx/thread.h"
+
+class MyThread: public wxThread
+{
+public:
+       static wxThreadError DetachNewThread(int (*entry_func)(void *, int), int (*exit_func)(void *, int), void *argptr, int argnum);
+       virtual ExitCode Entry();
+
+       int (*m_entry_func)(void *, int);
+       int (*m_exit_func)(void *, int);
+       void *m_argptr;
+       int m_argnum;
+};
+
+#endif /* __MyThread_h__ */
diff --git a/wxSources/MyVersion.c b/wxSources/MyVersion.c
new file mode 100644 (file)
index 0000000..dba62c5
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ *  MyVersion.c
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/10/16.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+const char *gVersionString = "v0.5.0 build 20100121";
+const char *gCopyrightString = "Copyright (c) 2008-2009 Toshi Nagata";
diff --git a/wxSources/ProgressFrame.cpp b/wxSources/ProgressFrame.cpp
new file mode 100755 (executable)
index 0000000..be95ca6
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *  ProgressFrame.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/07/15.
+ *  Copyright 2009 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "ProgressFrame.h"
+#include "MyApp.h"
+
+#include "wx/stattext.h"
+#include "wx/gauge.h"
+#include "wx/sizer.h"
+
+#if __WXMAC__
+#include <Carbon/Carbon.h>
+#endif
+
+BEGIN_EVENT_TABLE(ProgressFrame, wxFrame)
+//    EVT_TEXT_ENTER(-1, ConsoleFrame::OnEnterPressed)
+//     EVT_CHAR(ConsoleFrame::OnChar)
+//     EVT_RICHTEXT_RETURN(-1, ConsoleFrame::OnEnterPressed)
+END_EVENT_TABLE()
+
+ProgressFrame::ProgressFrame(const wxString &title, const wxString &mes):
+       wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxCAPTION)
+{
+       //  Vertical sizer containing (1) message text, (2) progress gauge, (3) note text
+       wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+       
+       m_messageText = new wxStaticText(this, -1, wxT("Message"), wxDefaultPosition, wxSize(240, 40), wxST_NO_AUTORESIZE);
+       sizer->Add(m_messageText, 0, wxALL | wxEXPAND, 10);   // Can expand horizontally
+       
+       m_progressGauge = new wxGauge(this, -1, 10000, wxDefaultPosition, wxSize(240, 24), wxGA_HORIZONTAL);
+       sizer->Add(m_progressGauge, 0, wxALL | wxEXPAND, 10);
+       
+       wxStaticText *noteText = new wxStaticText(this, -1, wxT("Press ESC to interrupt"), wxDefaultPosition, wxSize(240, 20), wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
+/*     wxFont smallFont(noteText->GetFont());
+       int size = smallFont.GetPointSize();
+       if (size >= 14)
+               size = 12;
+       else if (size >= 12)
+               size = 10;
+       else if (size >= 10)
+               size = 9;
+       smallFont.SetPointSize(size);
+       noteText->SetFont(smallFont); */
+       noteText->SetFont(*wxSMALL_FONT);
+       sizer->Add(noteText, 0, wxALL | wxEXPAND, 10);
+
+       m_value = -1.0;
+       m_progressGauge->Pulse();
+       m_messageText->SetLabel(mes);
+       
+       m_interruptValue = 0;
+
+       sizer->Layout();
+       this->SetSizerAndFit(sizer);
+       this->Centre();
+       this->Show();
+       
+#if __WXMAC__
+       ::SetWindowModality(((WindowRef)MacGetWindowRef()), kWindowModalityAppModal, NULL);
+#endif
+}
+
+ProgressFrame::~ProgressFrame()
+{
+}
+
+void
+ProgressFrame::SetProgressMessage(const wxString &mes)
+{
+       m_messageText->SetLabel(mes);
+       if (m_value < 0)
+               m_progressGauge->Pulse();
+       Update();
+}
+
+void
+ProgressFrame::SetProgressValue(double value)
+{
+       m_value = value;
+       if (value < 0)
+               m_progressGauge->Pulse();
+       else
+               m_progressGauge->SetValue((int)(value * 10000));
+       Update();
+}
+
+void
+ProgressFrame::SetInterruptValue(int value)
+{
+       m_interruptValue = value;
+}
+
+int
+ProgressFrame::CheckInterrupt()
+{
+       if (this != NULL && m_interruptValue) {
+               int save = m_interruptValue;
+               m_interruptValue = 0;
+               return save;
+       }
+
+#if __WXMAC__
+       ::wxYield();
+#else
+       {
+               wxWindow *activeWin;
+               if (this != NULL)
+                       activeWin = this;
+               else
+                       activeWin = GetMainFrame()->GetActiveChild();
+               ::wxSafeYield(activeWin);
+       }
+#endif
+       if (::wxGetKeyState(WXK_ESCAPE))
+               return 1;
+       else return 0;
+}
diff --git a/wxSources/ProgressFrame.h b/wxSources/ProgressFrame.h
new file mode 100755 (executable)
index 0000000..9c064f9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  ProgressFrame.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 09/07/15.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __ProgressFrame_h__
+#define __ProgressFrame_h__
+
+#include "wx/frame.h"
+#include "wx/gauge.h"
+
+class wxStaticText;
+//class wxGauge;    //  This forward declaration does not work because in wxMSW wxGauge is #define'ed as wxGauge95
+
+class ProgressFrame: public wxFrame
+{
+
+public:
+       ProgressFrame(const wxString& title, const wxString &mes);
+       virtual ~ProgressFrame();
+       
+       void SetProgressMessage(const wxString &mes);
+       void SetProgressValue(double value);
+       void SetInterruptValue(int value);
+       int CheckInterrupt();
+
+       wxStaticText *m_messageText;
+       wxGauge *m_progressGauge;
+       double m_value;
+       int m_interruptValue;
+
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+#endif /* __ProgressFrame_h__ */
diff --git a/wxSources/RubyDialogFrame.cpp b/wxSources/RubyDialogFrame.cpp
new file mode 100644 (file)
index 0000000..4d64509
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ *  RubyDialogFrame.cpp
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/12/05.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#include "wx/gdicmn.h"
+#include "wx/stattext.h"
+#include "wx/textctrl.h"
+#include "wx/button.h"
+#include "wx/filedlg.h"
+#include "wx/dirdlg.h"
+#include "wx/dcclient.h"
+#include "wx/choice.h"
+#include "wx/checkbox.h"
+#include "wx/radiobut.h"
+#include "wx/statline.h"
+
+#include "RubyDialogFrame.h"
+#include "MyApp.h"
+
+BEGIN_EVENT_TABLE(RubyDialogFrame, wxDialog)
+//    EVT_TEXT_ENTER(-1, ConsoleFrame::OnEnterPressed)
+//     EVT_CHAR(ConsoleFrame::OnChar)
+//     EVT_RICHTEXT_RETURN(-1, ConsoleFrame::OnEnterPressed)
+END_EVENT_TABLE()
+
+RubyDialogFrame::RubyDialogFrame(wxWindow* parent, wxWindowID wid, const wxString& title, const wxPoint& pos, const wxSize& size, long style):
+       wxDialog(parent, wid, title, pos, size, style)
+{
+       ditems = NULL;
+       nditems = 0;
+       dval = NULL;
+       
+       //  Create a vertical box sizer that contains a panel containing all controls and a sizer containing
+       //  OK/Cancel buttons
+       contentSizer = new wxBoxSizer(wxVERTICAL);
+       contentPanel = NULL;
+       buttonSizer = NULL;  //  Will be created later
+       boxSizer = new wxBoxSizer(wxVERTICAL);
+       boxSizer->Add(contentSizer, 1, wxALL | wxEXPAND, 14);
+       this->SetSizer(boxSizer);
+       boxSizer->Layout();
+       this->CentreOnScreen();
+}
+
+RubyDialogFrame::~RubyDialogFrame()
+{
+       if (ditems != NULL)
+               free(ditems);
+}
+
+int
+RubyDialogFrame::AddDialogItem(RDItem *item)
+{
+       if (nditems % 8 == 0) {
+               if (nditems == 0)
+                       ditems = (RDItem **)malloc(sizeof(RDItem *) * 8);
+               else
+                       ditems = (RDItem **)realloc(ditems, sizeof(RDItem *) * (nditems + 8));
+       }
+       ditems[nditems++] = item;
+       if (item != NULL && ((wxWindow *)item)->IsKindOf(CLASSINFO(wxPanel))) {
+               wxSize size = ((wxPanel *)item)->GetSize();
+               if (contentPanel == NULL)
+                       contentSizer->Add((wxPanel *)item, 1, wxEXPAND);
+               else
+                       contentSizer->Replace(contentPanel, (wxPanel *)item);
+               contentSizer->SetItemMinSize((wxPanel *)item, size.GetWidth(), size.GetHeight());
+               contentPanel = (wxPanel *)item;
+               boxSizer->Layout();
+               Fit();
+       }
+       return nditems - 1;
+}
+
+RDItem *
+RubyDialogFrame::DialogItemAtIndex(int index)
+{
+       if (index >= 0 && index < nditems)
+               return ditems[index];
+       else return NULL;
+}
+
+int
+RubyDialogFrame::SearchDialogItem(RDItem *item)
+{
+       int i;
+       for (i = 0; i < nditems; i++) {
+               if (item == ditems[i])
+                       return i;
+       }
+       return -1;
+}
+
+void
+RubyDialogFrame::SetRubyObject(RubyValue val)
+{
+       dval = val;
+}
+
+/*  Create standard buttons. If oktitle/canceltitle == NULL, then the button is created but set hidden.
+    If the title is "", then the default titles are used.  */
+void
+RubyDialogFrame::CreateStandardButtons(const char *oktitle, const char *canceltitle)
+{
+       wxSizer *sizer = CreateButtonSizer(wxOK | wxCANCEL);
+       if (oktitle != NULL || canceltitle != NULL) {
+               if (sizer == NULL)
+                       return;  /*  Cannot create  */
+               if (buttonSizer == NULL) {
+                       boxSizer->Add(sizer, 0, wxBOTTOM | wxLEFT | wxRIGHT | wxEXPAND, 14);
+                       buttonSizer = sizer;
+               } else {
+                       boxSizer->Replace(buttonSizer, sizer);
+                       buttonSizer = sizer;
+               }
+       } else {
+               /*  Buttons are created but sizer is left unregistered  */
+               if (buttonSizer != NULL)
+                       boxSizer->Remove(buttonSizer);
+               buttonSizer = NULL;
+       }
+       while (nditems < 2)
+               AddDialogItem(NULL);
+       if (ditems[0] != NULL)
+               ((wxWindow *)ditems[0])->Destroy();
+       if (ditems[1] != NULL)
+               ((wxWindow *)ditems[1])->Destroy();
+       ditems[0] = (RDItem *)wxWindow::FindWindowById(wxID_OK, this);
+       ditems[1] = (RDItem *)wxWindow::FindWindowById(wxID_CANCEL, this);
+       if (oktitle == NULL) {
+               ((wxWindow *)ditems[0])->Show(false);
+       } else {
+               if (oktitle[0] != 0) {
+                       wxString label1(oktitle, wxConvUTF8);
+                       ((wxButton *)ditems[0])->SetLabel(label1);
+               }
+               ((wxWindow *)ditems[0])->Connect(-1, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, this);
+       }
+       if (canceltitle == NULL) {
+               ((wxWindow *)ditems[1])->Show(false);
+       } else {
+               if (canceltitle[0] != 0) {
+                       wxString label2(canceltitle, wxConvUTF8);
+                       ((wxButton *)ditems[1])->SetLabel(label2);
+               }
+               ((wxWindow *)ditems[1])->Connect(-1, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, this);
+       }
+}
+
+void
+RubyDialogFrame::OnDialogItemAction(wxCommandEvent &event)
+{
+       RubyDialog_doItemAction((RubyValue)dval, (RDItem *)(event.GetEventObject()));
+}
+
+#pragma mark ====== Plain C interface ======
+
+RubyDialog *
+RubyDialogCallback_new(void)
+{
+       RubyDialogFrame *dref = new RubyDialogFrame(GetMainFrame(), -1, _T("Ruby Dialog"));
+       return (RubyDialog *)dref;
+}
+
+void
+RubyDialogCallback_release(RubyDialog *dref)
+{
+       ((RubyDialogFrame *)dref)->Destroy();
+}
+
+void
+RubyDialogCallback_setRubyObject(RubyDialog *dref, RubyValue val)
+{
+       ((RubyDialogFrame *)dref)->SetRubyObject(val);
+}
+
+void
+RubyDialogCallback_setWindowTitle(RubyDialog *dref, const char *title)
+{
+       wxString str(title, wxConvUTF8);
+       ((RubyDialogFrame *)dref)->SetLabel(str);
+}
+
+int
+RubyDialogCallback_runModal(RubyDialog *dref)
+{
+       int retval = ((RubyDialogFrame *)dref)->ShowModal();
+       if (retval == wxID_OK)
+               return 0;  /*  OK  */
+       else return 1;  /* Cancel */
+}
+
+void
+RubyDialogCallback_endModal(RubyDialog *dref, int status)
+{
+       ((RubyDialogFrame *)dref)->EndModal(status == 0 ? wxID_OK : wxID_CANCEL);
+}
+
+void
+RubyDialogCallback_close(RubyDialog *dref)
+{
+       ((RubyDialogFrame *)dref)->Close();
+}
+
+static inline RDRect
+RDRectFromwxRect(const wxRect &frame)
+{
+       RDRect rframe;
+       rframe.origin.x = frame.x;
+       rframe.origin.y = frame.y;
+       rframe.size.width = frame.width;
+       rframe.size.height = frame.height;
+       return rframe;
+}
+
+static inline wxRect
+wxRectFromRDRect(RDRect rframe)
+{
+       wxRect frame(rframe.origin.x, rframe.origin.y, rframe.size.width, rframe.size.height);
+       return frame;
+}
+
+RDSize
+RubyDialogCallback_windowMinSize(RubyDialog *dref)
+{
+       wxSize minSize = ((RubyDialogFrame *)dref)->GetMinSize();
+       RDSize rminSize;
+       rminSize.width = minSize.GetWidth();
+       rminSize.height = minSize.GetHeight();
+       return rminSize;
+}
+
+void
+RubyDialogCallback_setWindowSize(RubyDialog *dref, RDSize size)
+{
+       wxSize wsize(size.width, size.height);
+       ((RubyDialogFrame *)dref)->SetSize(wsize);
+       ((RubyDialogFrame *)dref)->CentreOnScreen();
+}
+
+void
+RubyDialogCallback_createStandardButtons(RubyDialog *dref, const char *oktitle, const char *canceltitle)
+{
+       ((RubyDialogFrame *)dref)->CreateStandardButtons(oktitle, canceltitle);
+}
+
+static wxRect
+OffsetForItemRect(const char *type)
+{
+       wxRect offset(0, 0, 0, 0);
+       if (strcmp(type, "textfield") == 0)
+               offset.height = 5;
+       else if (strcmp(type, "button") == 0) {
+               offset.width = 24;
+               offset.height = 14;
+       } else if (strcmp(type, "checkbox") == 0) {
+               offset.width = 10;
+       }
+       return offset;
+}
+
+RDItem *
+RubyDialogCallback_createItem(RubyDialog *dref, const char *type, const char *title, RDRect frame)
+{
+       wxWindow *control = NULL;
+       wxRect rect, offset;
+       RubyDialogFrame *parent = ((RubyDialogFrame *)dref);
+       wxString tstr((title ? title : ""), wxConvUTF8);
+       bool no_action = false;
+       
+       rect = wxRectFromRDRect(frame);
+       offset = OffsetForItemRect(type);
+       if (rect.width == 0 && rect.height == 0) {
+               rect.SetPosition(wxDefaultPosition);
+               rect.SetSize(wxDefaultSize);
+       } else {        
+               rect.SetX(rect.x + offset.x);
+               rect.SetY(rect.y + offset.y);
+               rect.SetWidth(rect.width + offset.width);
+               rect.SetHeight(rect.height + offset.height);
+       }
+
+       if (strcmp(type, "text") == 0) {
+               /*  Static text */
+               wxStaticText *st = new wxStaticText(parent, -1, tstr, rect.GetPosition(), rect.GetSize(), wxST_NO_AUTORESIZE);
+               control = st;
+               no_action = true;
+       } else if (strcmp(type, "textfield") == 0) {
+               /*  Editable text  */
+               wxTextCtrl *tc = new wxTextCtrl(parent, -1, tstr, rect.GetPosition(), rect.GetSize());
+               control = tc;
+               tc->Connect(-1, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, parent);
+       } else if (strcmp(type, "textview") == 0) {
+               /*  Text view  */
+               wxTextCtrl *tc = new wxTextCtrl(parent, -1, tstr, rect.GetPosition(), rect.GetSize(), wxTE_MULTILINE);
+               control = tc;
+               tc->Connect(-1, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, parent);
+       } else if (strcmp(type, "view") == 0) {
+               /*  Panel  */
+               wxPanel *pn = new wxPanel(parent, -1, rect.GetPosition(), rect.GetSize());
+               control = pn;
+       } else if (strcmp(type, "line") == 0) {
+               /*  Separator line  */
+               int direction = (frame.size.width > frame.size.height ? wxLI_HORIZONTAL : wxLI_VERTICAL);
+               wxStaticLine *ln = new wxStaticLine(parent, -1, rect.GetPosition(), rect.GetSize(), direction);
+               control = ln;
+       /*      printf("is_vertical = %d\n", (int)ln->IsVertical()); */
+       } else if (strcmp(type, "button") == 0) {
+               /*  Button  */
+               wxButton *bn = new wxButton(parent, -1, tstr, rect.GetPosition(), rect.GetSize());
+               control = bn;
+               bn->Connect(-1, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, parent);
+       } else if (strcmp(type, "popup") == 0) {
+               /*  Popup button (wxChoice)  */
+               wxString du[1] = { _T(" ") };
+               wxChoice *ch = new wxChoice(parent, -1, rect.GetPosition(), rect.GetSize(), 1, du);
+               control = ch;
+               ch->Connect(-1, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, parent);
+       } else if (strcmp(type, "checkbox") == 0) {
+               /*  Checkbox (wxCheckBox)  */
+               wxCheckBox *cb = new wxCheckBox(parent, -1, tstr, rect.GetPosition(), rect.GetSize());
+               control = cb;
+               cb->Connect(-1, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, parent);
+       } else if (strcmp(type, "radio") == 0) {
+               /*  Radio button (not grouped)  */
+               wxRadioButton *rb = new wxRadioButton(parent, -2, tstr, rect.GetPosition(), rect.GetSize(), wxRB_SINGLE);
+               control = rb;
+               rb->Connect(-1, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler(RubyDialogFrame::OnDialogItemAction), NULL, parent);
+       } else return NULL;
+       
+       if (title[0] != 0 || strcmp(type, "textfield") == 0) {
+               /*  Resize the frame rect as necessary  */
+               RDSize minSize = RubyDialogCallback_sizeOfString((RDItem *)control, title);
+               wxSize size = control->GetSize();
+               if (size.GetHeight() < minSize.height)
+                       size.SetHeight(minSize.height);
+               if (size.GetWidth() < minSize.width)
+                       size.SetWidth(minSize.width);
+               size.SetWidth(size.GetWidth() + offset.width);
+               size.SetHeight(size.GetHeight() + offset.height);
+               control->SetSize(size);
+       }
+
+       ((RubyDialogFrame *)dref)->AddDialogItem((RDItem *)control);
+       
+       return (RDItem *)control;
+}
+
+RDItem *
+RubyDialogCallback_dialogItemAtIndex(RubyDialog *dref, int idx)
+{
+       if (idx == -1)
+               return (RDItem *)dref;
+       else return ((RubyDialogFrame *)dref)->DialogItemAtIndex(idx);
+}
+
+int
+RubyDialogCallback_indexOfItem(RubyDialog *dref, RDItem *item)
+{
+       return ((RubyDialogFrame *)dref)->SearchDialogItem(item);
+}
+
+void
+RubyDialogCallback_moveItemUnderView(RDItem *item, RDItem *superView, RDPoint origin)
+{
+       if (item == NULL || superView == NULL || item == superView)
+               return;
+       if (((wxWindow *)item)->Reparent((wxWindow *)superView)) {
+               ((wxWindow *)item)->Move(origin.x, origin.y);
+       }
+}
+
+RDItem *
+RubyDialogCallback_superview(RDItem *item)
+{
+       return (RDItem *)(((wxWindow *)item)->GetParent());
+}
+
+RDRect
+RubyDialogCallback_frameOfItem(RDItem *item)
+{
+       wxRect rect = ((wxWindow *)item)->GetRect();
+       if (gRubyDialogIsFlipped) {
+               wxWindow *parent = ((wxWindow *)item)->GetParent();
+               if (parent != NULL) {
+                       wxRect superRect = parent->GetRect();
+                       rect.SetY(superRect.GetHeight() - rect.GetHeight() - rect.GetY());
+               }
+       }
+       return RDRectFromwxRect(rect);
+}
+
+void
+RubyDialogCallback_setFrameOfItem(RDItem *item, RDRect rect)
+{
+       wxRect wrect = wxRectFromRDRect(rect);
+       if (gRubyDialogIsFlipped) {
+               wxWindow *parent = ((wxWindow *)item)->GetParent();
+               if (parent != NULL) {
+                       wxRect srect = parent->GetRect();
+                       wrect.SetY(srect.GetHeight() - wrect.GetHeight() - wrect.GetY());
+               }
+       }
+       ((wxWindow *)item)->SetSize(wrect);
+}
+
+void
+RubyDialogCallback_setStringToItem(RDItem *item, const char *s)
+{
+       wxString str(s, wxConvUTF8);
+       if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL) {
+               ((wxTextCtrl *)item)->SetValue(str);
+       }
+}
+
+void
+RubyDialogCallback_getStringFromItem(RDItem *item, char *buf, int bufsize)
+{
+       wxString str;
+       if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL) {
+               str = ((wxTextCtrl *)item)->GetValue();
+       } else {
+               buf[0] = 0;
+               return;
+       }
+       strncpy(buf, str.mb_str(wxConvUTF8), bufsize - 1);
+       buf[bufsize - 1] = 0;
+}
+
+char *
+RubyDialogCallback_getStringPtrFromItem(RDItem *item)
+{
+       wxString str;
+       if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL) {
+               str = ((wxTextCtrl *)item)->GetValue();
+       } else {
+               return NULL;
+       }
+       return strdup(str.mb_str(wxConvUTF8));
+}
+
+char *
+RubyDialogCallback_titleOfItem(RDItem *item)
+{
+       wxString str;
+       if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL) {
+               str = ((wxTextCtrl *)item)->GetValue();
+       } else {
+               str = ((wxWindow *)item)->GetLabel();
+       }
+       return strdup(str.mb_str(wxConvUTF8));
+}
+
+void
+RubyDialogCallback_setTitleToItem(RDItem *item, const char *s)
+{
+       wxString str(s, wxConvUTF8);
+       if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL) {
+               ((wxTextCtrl *)item)->SetValue(str);
+       } else {
+               ((wxWindow *)item)->SetLabel(str);
+       }
+}
+
+void
+RubyDialogCallback_setEnabledForItem(RDItem *item, int flag)
+{
+       ((wxWindow *)item)->Enable(flag);
+}
+
+int
+RubyDialogCallback_isItemEnabled(RDItem *item)
+{
+/*     if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL)
+               return ((wxTextCtrl *)item)->IsEditable();
+       else */
+       return ((wxWindow *)item)->IsEnabled();
+}
+
+void
+RubyDialogCallback_setEditableForItem(RDItem *item, int flag)
+{
+       if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL)
+               return ((wxTextCtrl *)item)->SetEditable(flag);
+}
+
+int
+RubyDialogCallback_isItemEditable(RDItem *item)
+{
+       if (wxDynamicCast((wxWindow *)item, wxTextCtrl) != NULL)
+               return ((wxTextCtrl *)item)->IsEditable();
+       else return 0;
+}
+
+void
+RubyDialogCallback_setStateForItem(RDItem *item, int state)
+{
+       if (wxDynamicCast((wxWindow *)item, wxRadioButton) != NULL) {
+               ((wxRadioButton *)item)->SetValue(state);
+       } else if (wxDynamicCast((wxWindow *)item, wxCheckBox) != NULL) {
+               ((wxCheckBox *)item)->SetValue(state);
+       }
+}
+
+int
+RubyDialogCallback_getStateForItem(RDItem *item)
+{
+       if (wxDynamicCast((wxWindow *)item, wxRadioButton) != NULL) {
+               return ((wxRadioButton *)item)->GetValue();
+       } else if (wxDynamicCast((wxWindow *)item, wxCheckBox) != NULL) {
+               return ((wxCheckBox *)item)->GetValue();
+       } else return -1;
+}
+
+void
+RubyDialogCallback_setHiddenForItem(RDItem *item, int flag)
+{
+       ((wxWindow *)item)->Show(flag == 0);
+}
+
+int
+RubyDialogCallback_isItemHidden(RDItem *item)
+{
+       return !(((wxWindow *)item)->IsShown());
+}
+
+int
+RubyDialogCallback_countSubItems(RDItem *item)
+{
+       if (wxDynamicCast((wxWindow *)item, wxChoice) != NULL) {
+               return ((wxChoice *)item)->GetCount();
+       } else return 0;
+}
+
+int
+RubyDialogCallback_appendSubItem(RDItem *item, const char *s)
+{
+       wxString str(s, wxConvUTF8);
+       if (wxDynamicCast((wxWindow *)item, wxChoice) != NULL) {
+               return ((wxChoice *)item)->Append(str);
+       } else return -1;
+}
+
+int
+RubyDialogCallback_insertSubItem(RDItem *item, const char *s, int pos)
+{
+       wxString str(s, wxConvUTF8);
+       if (wxDynamicCast((wxWindow *)item, wxChoice) != NULL && pos >= 0 && pos < ((wxChoice *)item)->GetCount()) {
+               return ((wxChoice *)item)->Insert(str, pos);
+       } else return -1;
+}
+
+int
+RubyDialogCallback_deleteSubItem(RDItem *item, int pos)
+{      
+       if (wxDynamicCast((wxWindow *)item, wxChoice) != NULL && pos >= 0 && pos < ((wxChoice *)item)->GetCount()) {
+               ((wxChoice *)item)->Delete(pos);
+               return pos;
+       } else return -1;
+}
+
+char *
+RubyDialogCallback_titleOfSubItem(RDItem *item, int pos)
+{
+       if (wxDynamicCast((wxWindow *)item, wxChoice) != NULL && pos >= 0 && pos < ((wxChoice *)item)->GetCount()) {
+               wxString str = ((wxChoice *)item)->GetString(pos);
+               return strdup(str.mb_str(wxConvUTF8));
+       } else return NULL;
+}
+
+void
+RubyDialogCallback_setSelectedSubItem(RDItem *item, int pos)
+{
+       if (wxDynamicCast((wxWindow *)item, wxChoice) != NULL && pos >= 0 && pos < ((wxChoice *)item)->GetCount()) {
+               ((wxChoice *)item)->SetSelection(pos);
+       }
+}
+
+int
+RubyDialogCallback_selectedSubItem(RDItem *item)
+{
+       if (wxDynamicCast((wxWindow *)item, wxChoice) != NULL) {
+               return ((wxChoice *)item)->GetSelection();
+       } else return -1;
+}
+
+RDSize
+RubyDialogCallback_sizeOfString(RDItem *item, const char *s)
+{
+       RDSize size;
+       wxCoord w, h, descent, leading;
+       wxPaintDC dc((wxWindow *)item);
+       int len = strlen(s);
+       const char *s1, *s2;
+       size.width = size.height = 0;
+       s1 = (s == NULL || s[0] == 0 ? " " : s);
+       while (1) {
+               s2 = strchr(s1, '\n');
+               if (s2 == NULL)
+                       s2 = s + len;
+               wxString str(s1, wxConvUTF8, s2 - s1);
+               dc.GetTextExtent(str, &w, &h, &descent, &leading);
+               if (size.width < w)
+                       size.width = w;
+               size.height += h + descent + leading;
+               if (s2 == s + len)
+                       break;
+               s1 = s2 + 1;
+       }
+       return size;
+}
+
+RDSize
+RubyDialogCallback_resizeToBest(RDItem *item)
+{
+       wxSize size;
+       RDSize rsize;
+       size = ((wxWindow *)item)->GetBestSize();
+       ((wxWindow *)item)->SetSize(size);
+       rsize.width = size.GetWidth();
+       rsize.height = size.GetHeight();
+       return rsize;
+}
+
+int
+RubyDialogCallback_savePanel(const char *title, const char *dirname, const char *wildcard, char *buf, int bufsize)
+{
+       int result;
+       wxString pstr((dirname ? dirname : ""), wxConvUTF8);
+       wxString tstr((title ? title : "Choose a file"), wxConvUTF8);
+       wxString fstr(buf, wxConvUTF8);
+       wxString wstr((wildcard ? wildcard : "All files (*.*)|*.*"), wxConvUTF8);
+       wxFileDialog *dialog = new wxFileDialog(NULL, tstr, pstr, fstr, wstr, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+       if (dialog->ShowModal() == wxID_OK) {
+               strncpy(buf, dialog->GetPath().mb_str(wxConvFile), bufsize - 1);
+               buf[bufsize - 1] = 0;
+               result = 1;
+       } else {
+               buf[0] = 0;
+               result = 0;
+       }
+       dialog->Destroy();
+       return result;
+}
+
+int
+RubyDialogCallback_openPanel(const char *title, const char *dirname, const char *wildcard, char ***array, int for_directories, int multiple_selection)
+{
+       int result = 0;
+       wxString pstr((dirname ? dirname : ""), wxConvUTF8);
+       wxString wstr((wildcard ? wildcard : "All files (*.*)|*.*"), wxConvUTF8);
+       int style = wxFD_OPEN | (multiple_selection ? wxFD_MULTIPLE : 0);
+       if (for_directories) {
+               wxString tstr((title ? title : "Choose a directory"), wxConvUTF8);
+               wxDirDialog *dialog = new wxDirDialog(NULL, tstr, pstr);
+               if (dialog->ShowModal() == wxID_OK) {
+                       *array = (char **)malloc(sizeof(char *));
+                       (*array)[0] = strdup(dialog->GetPath().mb_str(wxConvFile));
+                       result = 1;
+               }
+               dialog->Destroy();
+       } else {
+               wxString tstr((title ? title : "Choose a file"), wxConvUTF8);
+               wxFileDialog *dialog = new wxFileDialog(NULL, tstr, pstr, _T(""), wstr, style);
+               if (dialog->ShowModal() == wxID_OK) {
+                       *array = (char **)malloc(sizeof(char *));
+                       (*array)[0] = strdup(dialog->GetPath().mb_str(wxConvFile));
+                       result = 1;                     
+               }
+               dialog->Destroy();
+       }
+       return result;
+}
diff --git a/wxSources/RubyDialogFrame.h b/wxSources/RubyDialogFrame.h
new file mode 100644 (file)
index 0000000..faf70a7
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  RubyDialogFrame.h
+ *  Molby
+ *
+ *  Created by Toshi Nagata on 08/12/05.
+ *  Copyright 2008 Toshi Nagata. All rights reserved.
+ *
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ */
+
+#ifndef __RubyDialogFrame_h__
+#define __RubyDialogFrame_h__
+
+#include "wx/dialog.h"
+#include "wx/sizer.h"
+#include "wx/panel.h"
+#include "../Mollib/Ruby_bind/ruby_dialog.h"
+
+class RubyDialogFrame: public wxDialog {       
+public:
+       RDItem **ditems;
+       int nditems;
+       RubyValue dval;  /*  The Ruby value representing this object  */
+
+       wxPanel *contentPanel;
+       wxSizer *contentSizer;
+       wxSizer *buttonSizer;
+       wxBoxSizer *boxSizer;
+       
+       RubyDialogFrame(wxWindow* parent, wxWindowID wid, const wxString& title, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE);
+       virtual ~RubyDialogFrame();
+
+       int AddDialogItem(RDItem *item);
+       RDItem *DialogItemAtIndex(int index);
+       int SearchDialogItem(RDItem *item);
+       void SetRubyObject(RubyValue val);
+       void CreateStandardButtons(const char *oktitle, const char *canceltitle);
+
+       void OnDialogItemAction(wxCommandEvent &event);
+       
+private:
+       DECLARE_EVENT_TABLE()
+};
+
+#endif /* __RubyDialogFrame_h__ */
diff --git a/wxSources/listctrl.cpp b/wxSources/listctrl.cpp
new file mode 100644 (file)
index 0000000..2d9ffd1
--- /dev/null
@@ -0,0 +1,6009 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        src/generic/listctrl.cpp
+// Purpose:     generic implementation of wxListCtrl
+// Author:      Robert Roebling
+//              Vadim Zeitlin (virtual list control support)
+// Id:          $Id: listctrl.cpp,v 1.3 2009/12/08 11:59:39 tngrp Exp $
+// Copyright:   (c) 1998 Robert Roebling
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// TODO
+//
+//   1. we need to implement searching/sorting for virtual controls somehow
+// 2. when changing selection the lines are refreshed twice
+
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#if wxUSE_LISTCTRL
+
+#include "wx/listctrl.h"
+#include <wx/generic/listctrl.h>  //  2009.07.20. TN
+
+
+#if (!defined(__WXMSW__) || defined(__WXUNIVERSAL__)) && (!defined(__WXMAC__)|| defined(__WXUNIVERSAL__))
+    // if we have a native version, its implementation file does all this
+    IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
+    IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
+    IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
+
+    IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxGenericListCtrl)
+#endif
+
+#ifndef WX_PRECOMP
+    #include "wx/scrolwin.h"
+    #include "wx/timer.h"
+    #include "wx/settings.h"
+    #include "wx/dynarray.h"
+    #include "wx/dcclient.h"
+    #include "wx/dcscreen.h"
+    #include "wx/math.h"
+#endif
+
+#include "wx/imaglist.h"
+#include "wx/selstore.h"
+#include "wx/renderer.h"
+
+#ifdef __WXMAC__
+    #include "wx/mac/private.h"
+#endif
+
+
+// NOTE: If using the wxListBox visual attributes works everywhere then this can
+// be removed, as well as the #else case below.
+#define _USE_VISATTR 0
+
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+// // the height of the header window (FIXME: should depend on its font!)
+// static const int HEADER_HEIGHT = 23;
+
+static const int SCROLL_UNIT_X = 15;
+
+// the spacing between the lines (in report mode)
+static const int LINE_SPACING = 0;
+
+// extra margins around the text label
+#ifdef __WXGTK__
+static const int EXTRA_WIDTH = 6;
+#else
+static const int EXTRA_WIDTH = 4;
+#endif
+static const int EXTRA_HEIGHT = 4;
+
+// margin between the window and the items
+static const int EXTRA_BORDER_X = 2;
+static const int EXTRA_BORDER_Y = 2;
+
+// offset for the header window
+static const int HEADER_OFFSET_X = 0;
+static const int HEADER_OFFSET_Y = 0;
+
+// margin between rows of icons in [small] icon view
+static const int MARGIN_BETWEEN_ROWS = 6;
+
+// when autosizing the columns, add some slack
+static const int AUTOSIZE_COL_MARGIN = 10;
+
+// default width for the header columns
+static const int WIDTH_COL_DEFAULT = 80;
+
+// the space between the image and the text in the report mode
+static const int IMAGE_MARGIN_IN_REPORT_MODE = 5;
+
+// the space between the image and the text in the report mode in header
+static const int HEADER_IMAGE_MARGIN_IN_REPORT_MODE = 2;
+
+// ============================================================================
+// private classes
+// ============================================================================
+
+//-----------------------------------------------------------------------------
+//  wxColWidthInfo (internal)
+//-----------------------------------------------------------------------------
+
+struct wxColWidthInfo
+{
+    int     nMaxWidth;
+    bool    bNeedsUpdate;   //  only set to true when an item whose
+                            //  width == nMaxWidth is removed
+
+    wxColWidthInfo(int w = 0, bool needsUpdate = false)
+    {
+        nMaxWidth = w;
+        bNeedsUpdate = needsUpdate;
+    }
+};
+
+WX_DEFINE_ARRAY_PTR(wxColWidthInfo *, ColWidthArray);
+
+//-----------------------------------------------------------------------------
+//  wxListItemData (internal)
+//-----------------------------------------------------------------------------
+
+class wxListItemData
+{
+public:
+    wxListItemData(wxListMainWindow *owner);
+    ~wxListItemData();
+
+    void SetItem( const wxListItem &info );
+    void SetImage( int image ) { m_image = image; }
+    void SetData( wxUIntPtr data ) { m_data = data; }
+    void SetPosition( int x, int y );
+    void SetSize( int width, int height );
+
+    bool HasText() const { return !m_text.empty(); }
+    const wxString& GetText() const { return m_text; }
+    void SetText(const wxString& text) { m_text = text; }
+
+    // we can't use empty string for measuring the string width/height, so
+    // always return something
+    wxString GetTextForMeasuring() const
+    {
+        wxString s = GetText();
+        if ( s.empty() )
+            s = _T('H');
+
+        return s;
+    }
+
+    bool IsHit( int x, int y ) const;
+
+    int GetX() const;
+    int GetY() const;
+    int GetWidth() const;
+    int GetHeight() const;
+
+    int GetImage() const { return m_image; }
+    bool HasImage() const { return GetImage() != -1; }
+
+    void GetItem( wxListItem &info ) const;
+
+    void SetAttr(wxListItemAttr *attr) { m_attr = attr; }
+    wxListItemAttr *GetAttr() const { return m_attr; }
+
+public:
+    // the item image or -1
+    int m_image;
+
+    // user data associated with the item
+    wxUIntPtr m_data;
+
+    // the item coordinates are not used in report mode; instead this pointer is
+    // NULL and the owner window is used to retrieve the item position and size
+    wxRect *m_rect;
+
+    // the list ctrl we are in
+    wxListMainWindow *m_owner;
+
+    // custom attributes or NULL
+    wxListItemAttr *m_attr;
+
+protected:
+    // common part of all ctors
+    void Init();
+
+    wxString m_text;
+};
+
+//-----------------------------------------------------------------------------
+//  wxListHeaderData (internal)
+//-----------------------------------------------------------------------------
+
+class wxListHeaderData : public wxObject
+{
+public:
+    wxListHeaderData();
+    wxListHeaderData( const wxListItem &info );
+    void SetItem( const wxListItem &item );
+    void SetPosition( int x, int y );
+    void SetWidth( int w );
+    void SetState( int state );
+    void SetFormat( int format );
+    void SetHeight( int h );
+    bool HasImage() const;
+
+    bool HasText() const { return !m_text.empty(); }
+    const wxString& GetText() const { return m_text; }
+    void SetText(const wxString& text) { m_text = text; }
+
+    void GetItem( wxListItem &item );
+
+    bool IsHit( int x, int y ) const;
+    int GetImage() const;
+    int GetWidth() const;
+    int GetFormat() const;
+    int GetState() const;
+
+protected:
+    long      m_mask;
+    int       m_image;
+    wxString  m_text;
+    int       m_format;
+    int       m_width;
+    int       m_xpos,
+              m_ypos;
+    int       m_height;
+    int       m_state;
+
+private:
+    void Init();
+};
+
+//-----------------------------------------------------------------------------
+//  wxListLineData (internal)
+//-----------------------------------------------------------------------------
+
+WX_DECLARE_EXPORTED_LIST(wxListItemData, wxListItemDataList);
+#include "wx/listimpl.cpp"
+WX_DEFINE_LIST(wxListItemDataList)
+
+class wxListLineData
+{
+public:
+    // the list of subitems: only may have more than one item in report mode
+    wxListItemDataList m_items;
+
+    // this is not used in report view
+    struct GeometryInfo
+    {
+        // total item rect
+        wxRect m_rectAll;
+
+        // label only
+        wxRect m_rectLabel;
+
+        // icon only
+        wxRect m_rectIcon;
+
+        // the part to be highlighted
+        wxRect m_rectHighlight;
+
+        // extend all our rects to be centered inside the one of given width
+        void ExtendWidth(wxCoord w)
+        {
+            wxASSERT_MSG( m_rectAll.width <= w,
+                            _T("width can only be increased") );
+
+            m_rectAll.width = w;
+            m_rectLabel.x = m_rectAll.x + (w - m_rectLabel.width) / 2;
+            m_rectIcon.x = m_rectAll.x + (w - m_rectIcon.width) / 2;
+            m_rectHighlight.x = m_rectAll.x + (w - m_rectHighlight.width) / 2;
+        }
+    }
+    *m_gi;
+
+    // is this item selected? [NB: not used in virtual mode]
+    bool m_highlighted;
+
+    // back pointer to the list ctrl
+    wxListMainWindow *m_owner;
+
+public:
+    wxListLineData(wxListMainWindow *owner);
+
+    ~wxListLineData()
+    {
+        WX_CLEAR_LIST(wxListItemDataList, m_items);
+        delete m_gi;
+    }
+
+    // are we in report mode?
+    inline bool InReportView() const;
+
+    // are we in virtual report mode?
+    inline bool IsVirtual() const;
+
+    // these 2 methods shouldn't be called for report view controls, in that
+    // case we determine our position/size ourselves
+
+    // calculate the size of the line
+    void CalculateSize( wxDC *dc, int spacing );
+
+    // remember the position this line appears at
+    void SetPosition( int x, int y, int spacing );
+
+    // wxListCtrl API
+
+    void SetImage( int image ) { SetImage(0, image); }
+    int GetImage() const { return GetImage(0); }
+    void SetImage( int index, int image );
+    int GetImage( int index ) const;
+
+    bool HasImage() const { return GetImage() != -1; }
+    bool HasText() const { return !GetText(0).empty(); }
+
+    void SetItem( int index, const wxListItem &info );
+    void GetItem( int index, wxListItem &info );
+
+    wxString GetText(int index) const;
+    void SetText( int index, const wxString& s );
+
+    wxListItemAttr *GetAttr() const;
+    void SetAttr(wxListItemAttr *attr);
+
+    // return true if the highlighting really changed
+    bool Highlight( bool on );
+
+    void ReverseHighlight();
+
+    bool IsHighlighted() const
+    {
+        wxASSERT_MSG( !IsVirtual(), _T("unexpected call to IsHighlighted") );
+
+        return m_highlighted;
+    }
+
+    // draw the line on the given DC in icon/list mode
+    void Draw( wxDC *dc );
+
+    // the same in report mode
+    void DrawInReportMode( wxDC *dc,
+                           const wxRect& rect,
+                           const wxRect& rectHL,
+                           bool highlighted, long line );  //  2009.11.28. TN; add "line" argument
+
+private:
+    // set the line to contain num items (only can be > 1 in report mode)
+    void InitItems( int num );
+
+    // get the mode (i.e. style)  of the list control
+    inline int GetMode() const;
+
+    // prepare the DC for drawing with these item's attributes, return true if
+    // we need to draw the items background to highlight it, false otherwise
+    bool SetAttributes(wxDC *dc,
+                       const wxListItemAttr *attr,
+                       bool highlight);
+
+    // draw the text on the DC with the correct justification; also add an
+    // ellipsis if the text is too large to fit in the current width
+    void DrawTextFormatted(wxDC *dc,
+                           const wxString &text,
+                           int col,
+                           int x,
+                           int yMid,    // this is middle, not top, of the text
+                           int width);
+};
+
+WX_DECLARE_EXPORTED_OBJARRAY(wxListLineData, wxListLineDataArray);
+#include "wx/arrimpl.cpp"
+WX_DEFINE_OBJARRAY(wxListLineDataArray)
+
+//-----------------------------------------------------------------------------
+//  wxListHeaderWindow (internal)
+//-----------------------------------------------------------------------------
+
+class wxListHeaderWindow : public wxWindow
+{
+protected:
+    wxListMainWindow  *m_owner;
+    const wxCursor    *m_currentCursor;
+    wxCursor          *m_resizeCursor;
+    bool               m_isDragging;
+
+    // column being resized or -1
+    int m_column;
+
+    // divider line position in logical (unscrolled) coords
+    int m_currentX;
+
+    // minimal position beyond which the divider line
+    // can't be dragged in logical coords
+    int m_minX;
+
+public:
+    wxListHeaderWindow();
+
+    wxListHeaderWindow( wxWindow *win,
+                        wxWindowID id,
+                        wxListMainWindow *owner,
+                        const wxPoint &pos = wxDefaultPosition,
+                        const wxSize &size = wxDefaultSize,
+                        long style = 0,
+                        const wxString &name = wxT("wxlistctrlcolumntitles") );
+
+    virtual ~wxListHeaderWindow();
+
+    void DrawCurrent();
+    void AdjustDC( wxDC& dc );
+
+    void OnPaint( wxPaintEvent &event );
+    void OnMouse( wxMouseEvent &event );
+    void OnSetFocus( wxFocusEvent &event );
+
+    // needs refresh
+    bool m_dirty;
+
+private:
+    // common part of all ctors
+    void Init();
+
+    // generate and process the list event of the given type, return true if
+    // it wasn't vetoed, i.e. if we should proceed
+    bool SendListEvent(wxEventType type, const wxPoint& pos);
+
+    DECLARE_DYNAMIC_CLASS(wxListHeaderWindow)
+    DECLARE_EVENT_TABLE()
+};
+
+//-----------------------------------------------------------------------------
+// wxListRenameTimer (internal)
+//-----------------------------------------------------------------------------
+
+class wxListRenameTimer: public wxTimer
+{
+private:
+    wxListMainWindow *m_owner;
+
+public:
+    wxListRenameTimer( wxListMainWindow *owner );
+    void Notify();
+};
+
+//-----------------------------------------------------------------------------
+// wxListTextCtrlWrapper: wraps a wxTextCtrl to make it work for inline editing
+//-----------------------------------------------------------------------------
+
+class wxListTextCtrlWrapper : public wxEvtHandler
+{
+public:
+    // NB: text must be a valid object but not Create()d yet
+    wxListTextCtrlWrapper(wxListMainWindow *owner,
+                          wxTextCtrl *text,
+                          size_t itemEdit);
+
+    wxTextCtrl *GetText() const { return m_text; }
+
+    void AcceptChangesAndFinish();
+
+protected:
+    void OnChar( wxKeyEvent &event );
+    void OnKeyUp( wxKeyEvent &event );
+    void OnKillFocus( wxFocusEvent &event );
+
+    bool AcceptChanges();
+    void Finish();
+
+private:
+    wxListMainWindow   *m_owner;
+    wxTextCtrl         *m_text;
+    wxString            m_startValue;
+    size_t              m_itemEdited;
+    bool                m_finished;
+    bool                m_aboutToFinish;
+
+    DECLARE_EVENT_TABLE()
+};
+
+//-----------------------------------------------------------------------------
+//  wxListMainWindow (internal)
+//-----------------------------------------------------------------------------
+
+WX_DECLARE_EXPORTED_LIST(wxListHeaderData, wxListHeaderDataList);
+#include "wx/listimpl.cpp"
+WX_DEFINE_LIST(wxListHeaderDataList)
+
+class wxListMainWindow : public wxScrolledWindow
+{
+public:
+    wxListMainWindow();
+    wxListMainWindow( wxWindow *parent,
+                      wxWindowID id,
+                      const wxPoint& pos = wxDefaultPosition,
+                      const wxSize& size = wxDefaultSize,
+                      long style = 0,
+                      const wxString &name = _T("listctrlmainwindow") );
+
+    virtual ~wxListMainWindow();
+
+    bool HasFlag(int flag) const { return m_parent->HasFlag(flag); }
+
+    // return true if this is a virtual list control
+    bool IsVirtual() const { return HasFlag(wxLC_VIRTUAL); }
+
+    // return true if the control is in report mode
+    bool InReportView() const { return HasFlag(wxLC_REPORT); }
+
+    // return true if we are in single selection mode, false if multi sel
+    bool IsSingleSel() const { return HasFlag(wxLC_SINGLE_SEL); }
+
+    // do we have a header window?
+    bool HasHeader() const
+        { return InReportView() && !HasFlag(wxLC_NO_HEADER); }
+
+    void HighlightAll( bool on );
+
+    // all these functions only do something if the line is currently visible
+
+    // change the line "selected" state, return true if it really changed
+    bool HighlightLine( size_t line, bool highlight = true);
+
+    // as HighlightLine() but do it for the range of lines: this is incredibly
+    // more efficient for virtual list controls!
+    //
+    // NB: unlike HighlightLine() this one does refresh the lines on screen
+    void HighlightLines( size_t lineFrom, size_t lineTo, bool on = true );
+
+    // toggle the line state and refresh it
+    void ReverseHighlight( size_t line )
+        { HighlightLine(line, !IsHighlighted(line)); RefreshLine(line); }
+
+    // return true if the line is highlighted
+    bool IsHighlighted(size_t line) const;
+
+    // refresh one or several lines at once
+    void RefreshLine( size_t line );
+    void RefreshLines( size_t lineFrom, size_t lineTo );
+
+    // refresh all selected items
+    void RefreshSelected();
+
+    // refresh all lines below the given one: the difference with
+    // RefreshLines() is that the index here might not be a valid one (happens
+    // when the last line is deleted)
+    void RefreshAfter( size_t lineFrom );
+
+    // the methods which are forwarded to wxListLineData itself in list/icon
+    // modes but are here because the lines don't store their positions in the
+    // report mode
+
+    // get the bound rect for the entire line
+    wxRect GetLineRect(size_t line) const;
+
+    // get the bound rect of the label
+    wxRect GetLineLabelRect(size_t line) const;
+
+    // get the bound rect of the items icon (only may be called if we do have
+    // an icon!)
+    wxRect GetLineIconRect(size_t line) const;
+
+    // get the rect to be highlighted when the item has focus
+    wxRect GetLineHighlightRect(size_t line) const;
+
+    // get the size of the total line rect
+    wxSize GetLineSize(size_t line) const
+        { return GetLineRect(line).GetSize(); }
+
+    // return the hit code for the corresponding position (in this line)
+    long HitTestLine(size_t line, int x, int y) const;
+
+    // bring the selected item into view, scrolling to it if necessary
+    void MoveToItem(size_t item);
+
+    bool ScrollList( int WXUNUSED(dx), int dy );
+
+    // bring the current item into view
+    void MoveToFocus() { MoveToItem(m_current); }
+
+    // start editing the label of the given item
+    wxTextCtrl *EditLabel(long item,
+                          wxClassInfo* textControlClass = CLASSINFO(wxTextCtrl));
+    wxTextCtrl *GetEditControl() const
+    {
+        return m_textctrlWrapper ? m_textctrlWrapper->GetText() : NULL;
+    }
+
+    void FinishEditing(wxTextCtrl *text)
+    {
+        delete text;
+        m_textctrlWrapper = NULL;
+        SetFocusIgnoringChildren();
+    }
+
+    // suspend/resume redrawing the control
+    void Freeze();
+    void Thaw();
+
+    void OnRenameTimer();
+    bool OnRenameAccept(size_t itemEdit, const wxString& value);
+    void OnRenameCancelled(size_t itemEdit);
+
+    void OnMouse( wxMouseEvent &event );
+
+    // called to switch the selection from the current item to newCurrent,
+    void OnArrowChar( size_t newCurrent, const wxKeyEvent& event );
+
+    void OnChar( wxKeyEvent &event );
+    void OnKeyDown( wxKeyEvent &event );
+    void OnKeyUp( wxKeyEvent &event );
+    void OnSetFocus( wxFocusEvent &event );
+    void OnKillFocus( wxFocusEvent &event );
+    void OnScroll( wxScrollWinEvent& event );
+
+    void OnPaint( wxPaintEvent &event );
+
+    void OnChildFocus(wxChildFocusEvent& event);
+    
+    void DrawImage( int index, wxDC *dc, int x, int y );
+    void GetImageSize( int index, int &width, int &height ) const;
+    int GetTextLength( const wxString &s ) const;
+
+    void SetImageList( wxImageList *imageList, int which );
+    void SetItemSpacing( int spacing, bool isSmall = false );
+    int GetItemSpacing( bool isSmall = false );
+
+    void SetColumn( int col, wxListItem &item );
+    void SetColumnWidth( int col, int width );
+    void GetColumn( int col, wxListItem &item ) const;
+    int GetColumnWidth( int col ) const;
+    int GetColumnCount() const { return m_columns.GetCount(); }
+
+    // returns the sum of the heights of all columns
+    int GetHeaderWidth() const;
+
+    int GetCountPerPage() const;
+
+    void SetItem( wxListItem &item );
+    void GetItem( wxListItem &item ) const;
+    void SetItemState( long item, long state, long stateMask );
+    void SetItemStateAll( long state, long stateMask );
+    int GetItemState( long item, long stateMask ) const;
+    void GetItemRect( long index, wxRect &rect ) const;
+    wxRect GetViewRect() const;
+    bool GetItemPosition( long item, wxPoint& pos ) const;
+    int GetSelectedItemCount() const;
+
+    wxString GetItemText(long item) const
+    {
+        wxListItem info;
+        info.m_mask = wxLIST_MASK_TEXT;
+        info.m_itemId = item;
+        GetItem( info );
+        return info.m_text;
+    }
+
+    void SetItemText(long item, const wxString& value)
+    {
+        wxListItem info;
+        info.m_mask = wxLIST_MASK_TEXT;
+        info.m_itemId = item;
+        info.m_text = value;
+        SetItem( info );
+    }
+
+    // set the scrollbars and update the positions of the items
+    void RecalculatePositions(bool noRefresh = false);
+
+    // refresh the window and the header
+    void RefreshAll();
+
+    long GetNextItem( long item, int geometry, int state ) const;
+    void DeleteItem( long index );
+    void DeleteAllItems();
+    void DeleteColumn( int col );
+    void DeleteEverything();
+    void EnsureVisible( long index );
+    long FindItem( long start, const wxString& str, bool partial = false );
+    long FindItem( long start, wxUIntPtr data);
+    long FindItem( const wxPoint& pt );
+    long HitTest( int x, int y, int &flags ) const;
+    void InsertItem( wxListItem &item );
+    void InsertColumn( long col, wxListItem &item );
+    int GetItemWidthWithImage(wxListItem * item);
+    void SortItems( wxListCtrlCompare fn, long data );
+
+    size_t GetItemCount() const;
+    bool IsEmpty() const { return GetItemCount() == 0; }
+    void SetItemCount(long count);
+
+    // change the current (== focused) item, send a notification event
+    void ChangeCurrent(size_t current);
+    void ResetCurrent() { ChangeCurrent((size_t)-1); }
+    bool HasCurrent() const { return m_current != (size_t)-1; }
+
+    // send out a wxListEvent
+    void SendNotify( size_t line,
+                     wxEventType command,
+                     const wxPoint& point = wxDefaultPosition );
+
+    // override base class virtual to reset m_lineHeight when the font changes
+    virtual bool SetFont(const wxFont& font)
+    {
+        if ( !wxScrolledWindow::SetFont(font) )
+            return false;
+
+        m_lineHeight = 0;
+
+        return true;
+    }
+
+    // these are for wxListLineData usage only
+
+    // get the backpointer to the list ctrl
+    wxGenericListCtrl *GetListCtrl() const
+    {
+        return wxStaticCast(GetParent(), wxGenericListCtrl);
+    }
+
+    // get the height of all lines (assuming they all do have the same height)
+    wxCoord GetLineHeight() const;
+
+    // get the y position of the given line (only for report view)
+    wxCoord GetLineY(size_t line) const;
+
+    // get the brush to use for the item highlighting
+    wxBrush *GetHighlightBrush() const
+    {
+        return m_hasFocus ? m_highlightBrush : m_highlightUnfocusedBrush;
+    }
+
+    bool HasFocus() const
+    {
+        return m_hasFocus;
+    }
+
+       //  2009.11.28. TN
+       wxListItemAttr *GetItemAttrForColumn(long line, long col)
+       {
+               return GetListCtrl()->OnGetItemAttr(line + 1000000 * (col + 1));
+       }
+       //  End TN
+       
+//protected:
+    // the array of all line objects for a non virtual list control (for the
+    // virtual list control we only ever use m_lines[0])
+    wxListLineDataArray  m_lines;
+
+    // the list of column objects
+    wxListHeaderDataList m_columns;
+
+    // currently focused item or -1
+    size_t               m_current;
+
+    // the number of lines per page
+    int                  m_linesPerPage;
+
+    // this flag is set when something which should result in the window
+    // redrawing happens (i.e. an item was added or deleted, or its appearance
+    // changed) and OnPaint() doesn't redraw the window while it is set which
+    // allows to minimize the number of repaintings when a lot of items are
+    // being added. The real repainting occurs only after the next OnIdle()
+    // call
+    bool                 m_dirty;
+
+    wxColour            *m_highlightColour;
+    wxImageList         *m_small_image_list;
+    wxImageList         *m_normal_image_list;
+    int                  m_small_spacing;
+    int                  m_normal_spacing;
+    bool                 m_hasFocus;
+
+    bool                 m_lastOnSame;
+    wxTimer             *m_renameTimer;
+    bool                 m_isCreated;
+    int                  m_dragCount;
+    wxPoint              m_dragStart;
+    ColWidthArray        m_aColWidths;
+
+    // for double click logic
+    size_t m_lineLastClicked,
+           m_lineBeforeLastClicked,
+           m_lineSelectSingleOnUp;
+
+protected:
+    wxWindow *GetMainWindowOfCompositeControl() { return GetParent(); }
+
+    // the total count of items in a virtual list control
+    size_t m_countVirt;
+
+    // the object maintaining the items selection state, only used in virtual
+    // controls
+    wxSelectionStore m_selStore;
+
+    // common part of all ctors
+    void Init();
+
+    // get the line data for the given index
+    wxListLineData *GetLine(size_t n) const
+    {
+        wxASSERT_MSG( n != (size_t)-1, _T("invalid line index") );
+
+        if ( IsVirtual() )
+        {
+            wxConstCast(this, wxListMainWindow)->CacheLineData(n);
+            n = 0;
+        }
+
+        return &m_lines[n];
+    }
+
+    // get a dummy line which can be used for geometry calculations and such:
+    // you must use GetLine() if you want to really draw the line
+    wxListLineData *GetDummyLine() const;
+
+    // cache the line data of the n-th line in m_lines[0]
+    void CacheLineData(size_t line);
+
+    // get the range of visible lines
+    void GetVisibleLinesRange(size_t *from, size_t *to);
+
+    // force us to recalculate the range of visible lines
+    void ResetVisibleLinesRange() { m_lineFrom = (size_t)-1; }
+
+    // get the colour to be used for drawing the rules
+    wxColour GetRuleColour() const
+    {
+        return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
+    }
+
+private:
+    // initialize the current item if needed
+    void UpdateCurrent();
+
+    // delete all items but don't refresh: called from dtor
+    void DoDeleteAllItems();
+
+    // the height of one line using the current font
+    wxCoord m_lineHeight;
+
+    // the total header width or 0 if not calculated yet
+    wxCoord m_headerWidth;
+
+    // the first and last lines being shown on screen right now (inclusive),
+    // both may be -1 if they must be calculated so never access them directly:
+    // use GetVisibleLinesRange() above instead
+    size_t m_lineFrom,
+           m_lineTo;
+
+    // the brushes to use for item highlighting when we do/don't have focus
+    wxBrush *m_highlightBrush,
+            *m_highlightUnfocusedBrush;
+
+    // if this is > 0, the control is frozen and doesn't redraw itself
+    size_t m_freezeCount;
+
+    // wrapper around the text control currently used for in place editing or
+    // NULL if no item is being edited
+    wxListTextCtrlWrapper *m_textctrlWrapper;
+
+
+    DECLARE_DYNAMIC_CLASS(wxListMainWindow)
+    DECLARE_EVENT_TABLE()
+
+    friend class wxGenericListCtrl;
+};
+
+
+wxListItemData::~wxListItemData()
+{
+    // in the virtual list control the attributes are managed by the main
+    // program, so don't delete them
+    if ( !m_owner->IsVirtual() )
+        delete m_attr;
+
+    delete m_rect;
+}
+
+void wxListItemData::Init()
+{
+    m_image = -1;
+    m_data = 0;
+
+    m_attr = NULL;
+}
+
+wxListItemData::wxListItemData(wxListMainWindow *owner)
+{
+    Init();
+
+    m_owner = owner;
+
+    if ( owner->InReportView() )
+        m_rect = NULL;
+    else
+        m_rect = new wxRect;
+}
+
+void wxListItemData::SetItem( const wxListItem &info )
+{
+    if ( info.m_mask & wxLIST_MASK_TEXT )
+        SetText(info.m_text);
+    if ( info.m_mask & wxLIST_MASK_IMAGE )
+        m_image = info.m_image;
+    if ( info.m_mask & wxLIST_MASK_DATA )
+        m_data = info.m_data;
+
+    if ( info.HasAttributes() )
+    {
+        if ( m_attr )
+            m_attr->AssignFrom(*info.GetAttributes());
+        else
+            m_attr = new wxListItemAttr(*info.GetAttributes());
+    }
+
+    if ( m_rect )
+    {
+        m_rect->x =
+        m_rect->y =
+        m_rect->height = 0;
+        m_rect->width = info.m_width;
+    }
+}
+
+void wxListItemData::SetPosition( int x, int y )
+{
+    wxCHECK_RET( m_rect, _T("unexpected SetPosition() call") );
+
+    m_rect->x = x;
+    m_rect->y = y;
+}
+
+void wxListItemData::SetSize( int width, int height )
+{
+    wxCHECK_RET( m_rect, _T("unexpected SetSize() call") );
+
+    if ( width != -1 )
+        m_rect->width = width;
+    if ( height != -1 )
+        m_rect->height = height;
+}
+
+bool wxListItemData::IsHit( int x, int y ) const
+{
+    wxCHECK_MSG( m_rect, false, _T("can't be called in this mode") );
+
+    return wxRect(GetX(), GetY(), GetWidth(), GetHeight()).Contains(x, y);
+}
+
+int wxListItemData::GetX() const
+{
+    wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+    return m_rect->x;
+}
+
+int wxListItemData::GetY() const
+{
+    wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+    return m_rect->y;
+}
+
+int wxListItemData::GetWidth() const
+{
+    wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+    return m_rect->width;
+}
+
+int wxListItemData::GetHeight() const
+{
+    wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+    return m_rect->height;
+}
+
+void wxListItemData::GetItem( wxListItem &info ) const
+{
+    long mask = info.m_mask;
+    if ( !mask )
+        // by default, get everything for backwards compatibility
+        mask = -1;
+
+    if ( mask & wxLIST_MASK_TEXT )
+        info.m_text = m_text;
+    if ( mask & wxLIST_MASK_IMAGE )
+        info.m_image = m_image;
+    if ( mask & wxLIST_MASK_DATA )
+        info.m_data = m_data;
+
+    if ( m_attr )
+    {
+        if ( m_attr->HasTextColour() )
+            info.SetTextColour(m_attr->GetTextColour());
+        if ( m_attr->HasBackgroundColour() )
+            info.SetBackgroundColour(m_attr->GetBackgroundColour());
+        if ( m_attr->HasFont() )
+            info.SetFont(m_attr->GetFont());
+    }
+}
+
+//-----------------------------------------------------------------------------
+//  wxListHeaderData
+//-----------------------------------------------------------------------------
+
+void wxListHeaderData::Init()
+{
+    m_mask = 0;
+    m_image = -1;
+    m_format = 0;
+    m_width = 0;
+    m_xpos = 0;
+    m_ypos = 0;
+    m_height = 0;
+    m_state = 0;
+}
+
+wxListHeaderData::wxListHeaderData()
+{
+    Init();
+}
+
+wxListHeaderData::wxListHeaderData( const wxListItem &item )
+{
+    Init();
+
+    SetItem( item );
+}
+
+void wxListHeaderData::SetItem( const wxListItem &item )
+{
+    m_mask = item.m_mask;
+
+    if ( m_mask & wxLIST_MASK_TEXT )
+        m_text = item.m_text;
+
+    if ( m_mask & wxLIST_MASK_IMAGE )
+        m_image = item.m_image;
+
+    if ( m_mask & wxLIST_MASK_FORMAT )
+        m_format = item.m_format;
+
+    if ( m_mask & wxLIST_MASK_WIDTH )
+        SetWidth(item.m_width);
+
+    if ( m_mask & wxLIST_MASK_STATE )
+        SetState(item.m_state);
+}
+
+void wxListHeaderData::SetPosition( int x, int y )
+{
+    m_xpos = x;
+    m_ypos = y;
+}
+
+void wxListHeaderData::SetHeight( int h )
+{
+    m_height = h;
+}
+
+void wxListHeaderData::SetWidth( int w )
+{
+    m_width = w < 0 ? WIDTH_COL_DEFAULT : w;
+}
+
+void wxListHeaderData::SetState( int flag )
+{
+    m_state = flag;
+}
+
+void wxListHeaderData::SetFormat( int format )
+{
+    m_format = format;
+}
+
+bool wxListHeaderData::HasImage() const
+{
+    return m_image != -1;
+}
+
+bool wxListHeaderData::IsHit( int x, int y ) const
+{
+    return ((x >= m_xpos) && (x <= m_xpos+m_width) && (y >= m_ypos) && (y <= m_ypos+m_height));
+}
+
+void wxListHeaderData::GetItem( wxListItem& item )
+{
+    item.m_mask = m_mask;
+    item.m_text = m_text;
+    item.m_image = m_image;
+    item.m_format = m_format;
+    item.m_width = m_width;
+    item.m_state = m_state;
+}
+
+int wxListHeaderData::GetImage() const
+{
+    return m_image;
+}
+
+int wxListHeaderData::GetWidth() const
+{
+    return m_width;
+}
+
+int wxListHeaderData::GetFormat() const
+{
+    return m_format;
+}
+
+int wxListHeaderData::GetState() const
+{
+    return m_state;
+}
+
+//-----------------------------------------------------------------------------
+//  wxListLineData
+//-----------------------------------------------------------------------------
+
+inline int wxListLineData::GetMode() const
+{
+    return m_owner->GetListCtrl()->GetWindowStyleFlag() & wxLC_MASK_TYPE;
+}
+
+inline bool wxListLineData::InReportView() const
+{
+    return m_owner->HasFlag(wxLC_REPORT);
+}
+
+inline bool wxListLineData::IsVirtual() const
+{
+    return m_owner->IsVirtual();
+}
+
+wxListLineData::wxListLineData( wxListMainWindow *owner )
+{
+    m_owner = owner;
+
+    if ( InReportView() )
+        m_gi = NULL;
+    else // !report
+        m_gi = new GeometryInfo;
+
+    m_highlighted = false;
+
+    InitItems( GetMode() == wxLC_REPORT ? m_owner->GetColumnCount() : 1 );
+}
+
+void wxListLineData::CalculateSize( wxDC *dc, int spacing )
+{
+    wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+    wxCHECK_RET( node, _T("no subitems at all??") );
+
+    wxListItemData *item = node->GetData();
+
+    wxString s;
+    wxCoord lw, lh;
+
+    switch ( GetMode() )
+    {
+        case wxLC_ICON:
+        case wxLC_SMALL_ICON:
+            m_gi->m_rectAll.width = spacing;
+
+            s = item->GetText();
+
+            if ( s.empty() )
+            {
+                lh =
+                m_gi->m_rectLabel.width =
+                m_gi->m_rectLabel.height = 0;
+            }
+            else // has label
+            {
+                dc->GetTextExtent( s, &lw, &lh );
+                lw += EXTRA_WIDTH;
+                lh += EXTRA_HEIGHT;
+
+                m_gi->m_rectAll.height = spacing + lh;
+                if (lw > spacing)
+                    m_gi->m_rectAll.width = lw;
+
+                m_gi->m_rectLabel.width = lw;
+                m_gi->m_rectLabel.height = lh;
+            }
+
+            if (item->HasImage())
+            {
+                int w, h;
+                m_owner->GetImageSize( item->GetImage(), w, h );
+                m_gi->m_rectIcon.width = w + 8;
+                m_gi->m_rectIcon.height = h + 8;
+
+                if ( m_gi->m_rectIcon.width > m_gi->m_rectAll.width )
+                    m_gi->m_rectAll.width = m_gi->m_rectIcon.width;
+                if ( m_gi->m_rectIcon.height + lh > m_gi->m_rectAll.height - 4 )
+                    m_gi->m_rectAll.height = m_gi->m_rectIcon.height + lh + 4;
+            }
+
+            if ( item->HasText() )
+            {
+                m_gi->m_rectHighlight.width = m_gi->m_rectLabel.width;
+                m_gi->m_rectHighlight.height = m_gi->m_rectLabel.height;
+            }
+            else // no text, highlight the icon
+            {
+                m_gi->m_rectHighlight.width = m_gi->m_rectIcon.width;
+                m_gi->m_rectHighlight.height = m_gi->m_rectIcon.height;
+            }
+            break;
+
+        case wxLC_LIST:
+            s = item->GetTextForMeasuring();
+
+            dc->GetTextExtent( s, &lw, &lh );
+            lw += EXTRA_WIDTH;
+            lh += EXTRA_HEIGHT;
+
+            m_gi->m_rectLabel.width = lw;
+            m_gi->m_rectLabel.height = lh;
+
+            m_gi->m_rectAll.width = lw;
+            m_gi->m_rectAll.height = lh;
+
+            if (item->HasImage())
+            {
+                int w, h;
+                m_owner->GetImageSize( item->GetImage(), w, h );
+                m_gi->m_rectIcon.width = w;
+                m_gi->m_rectIcon.height = h;
+
+                m_gi->m_rectAll.width += 4 + w;
+                if (h > m_gi->m_rectAll.height)
+                    m_gi->m_rectAll.height = h;
+            }
+
+            m_gi->m_rectHighlight.width = m_gi->m_rectAll.width;
+            m_gi->m_rectHighlight.height = m_gi->m_rectAll.height;
+            break;
+
+        case wxLC_REPORT:
+            wxFAIL_MSG( _T("unexpected call to SetSize") );
+            break;
+
+        default:
+            wxFAIL_MSG( _T("unknown mode") );
+            break;
+    }
+}
+
+void wxListLineData::SetPosition( int x, int y, int spacing )
+{
+    wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+    wxCHECK_RET( node, _T("no subitems at all??") );
+
+    wxListItemData *item = node->GetData();
+
+    switch ( GetMode() )
+    {
+        case wxLC_ICON:
+        case wxLC_SMALL_ICON:
+            m_gi->m_rectAll.x = x;
+            m_gi->m_rectAll.y = y;
+
+            if ( item->HasImage() )
+            {
+                m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 4 +
+                    (m_gi->m_rectAll.width - m_gi->m_rectIcon.width) / 2;
+                m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 4;
+            }
+
+            if ( item->HasText() )
+            {
+                if (m_gi->m_rectAll.width > spacing)
+                    m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2);
+                else
+                    m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2) + (spacing / 2) - (m_gi->m_rectLabel.width / 2);
+                m_gi->m_rectLabel.y = m_gi->m_rectAll.y + m_gi->m_rectAll.height + 2 - m_gi->m_rectLabel.height;
+                m_gi->m_rectHighlight.x = m_gi->m_rectLabel.x - 2;
+                m_gi->m_rectHighlight.y = m_gi->m_rectLabel.y - 2;
+            }
+            else // no text, highlight the icon
+            {
+                m_gi->m_rectHighlight.x = m_gi->m_rectIcon.x - 4;
+                m_gi->m_rectHighlight.y = m_gi->m_rectIcon.y - 4;
+            }
+            break;
+
+        case wxLC_LIST:
+            m_gi->m_rectAll.x = x;
+            m_gi->m_rectAll.y = y;
+
+            m_gi->m_rectHighlight.x = m_gi->m_rectAll.x;
+            m_gi->m_rectHighlight.y = m_gi->m_rectAll.y;
+            m_gi->m_rectLabel.y = m_gi->m_rectAll.y + 2;
+
+            if (item->HasImage())
+            {
+                m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 2;
+                m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 2;
+                m_gi->m_rectLabel.x = m_gi->m_rectAll.x + 4 + (EXTRA_WIDTH/2) + m_gi->m_rectIcon.width;
+            }
+            else
+            {
+                m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2);
+            }
+            break;
+
+        case wxLC_REPORT:
+            wxFAIL_MSG( _T("unexpected call to SetPosition") );
+            break;
+
+        default:
+            wxFAIL_MSG( _T("unknown mode") );
+            break;
+    }
+}
+
+void wxListLineData::InitItems( int num )
+{
+    for (int i = 0; i < num; i++)
+        m_items.Append( new wxListItemData(m_owner) );
+}
+
+void wxListLineData::SetItem( int index, const wxListItem &info )
+{
+    wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+    wxCHECK_RET( node, _T("invalid column index in SetItem") );
+
+    wxListItemData *item = node->GetData();
+    item->SetItem( info );
+}
+
+void wxListLineData::GetItem( int index, wxListItem &info )
+{
+    wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+    if (node)
+    {
+        wxListItemData *item = node->GetData();
+        item->GetItem( info );
+    }
+}
+
+wxString wxListLineData::GetText(int index) const
+{
+    wxString s;
+
+    wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+    if (node)
+    {
+        wxListItemData *item = node->GetData();
+        s = item->GetText();
+    }
+
+    return s;
+}
+
+void wxListLineData::SetText( int index, const wxString& s )
+{
+    wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+    if (node)
+    {
+        wxListItemData *item = node->GetData();
+        item->SetText( s );
+    }
+}
+
+void wxListLineData::SetImage( int index, int image )
+{
+    wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+    wxCHECK_RET( node, _T("invalid column index in SetImage()") );
+
+    wxListItemData *item = node->GetData();
+    item->SetImage(image);
+}
+
+int wxListLineData::GetImage( int index ) const
+{
+    wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+    wxCHECK_MSG( node, -1, _T("invalid column index in GetImage()") );
+
+    wxListItemData *item = node->GetData();
+    return item->GetImage();
+}
+
+wxListItemAttr *wxListLineData::GetAttr() const
+{
+    wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+    wxCHECK_MSG( node, NULL, _T("invalid column index in GetAttr()") );
+
+    wxListItemData *item = node->GetData();
+    return item->GetAttr();
+}
+
+void wxListLineData::SetAttr(wxListItemAttr *attr)
+{
+    wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+    wxCHECK_RET( node, _T("invalid column index in SetAttr()") );
+
+    wxListItemData *item = node->GetData();
+    item->SetAttr(attr);
+}
+
+bool wxListLineData::SetAttributes(wxDC *dc,
+                                   const wxListItemAttr *attr,
+                                   bool highlighted)
+{
+    wxWindow *listctrl = m_owner->GetParent();
+
+    // fg colour
+
+    // don't use foreground colour for drawing highlighted items - this might
+    // make them completely invisible (and there is no way to do bit
+    // arithmetics on wxColour, unfortunately)
+    wxColour colText;
+    if ( highlighted )
+#ifdef __WXMAC__
+    {
+        if (m_owner->HasFocus()
+#ifdef __WXMAC__
+                && IsControlActive( (ControlRef)m_owner->GetHandle() )
+#endif
+        )
+            colText = *wxWHITE;
+        else
+            colText = *wxBLACK;
+    }
+#else
+        colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
+#endif
+    else if ( attr && attr->HasTextColour() )
+        colText = attr->GetTextColour();
+    else
+        colText = listctrl->GetForegroundColour();
+
+    dc->SetTextForeground(colText);
+
+    // font
+    wxFont font;
+    if ( attr && attr->HasFont() )
+        font = attr->GetFont();
+    else
+        font = listctrl->GetFont();
+
+    dc->SetFont(font);
+
+    // bg colour
+    bool hasBgCol = attr && attr->HasBackgroundColour();
+    if ( highlighted || hasBgCol )
+    {
+        if ( highlighted )
+            dc->SetBrush( *m_owner->GetHighlightBrush() );
+        else
+            dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID));
+
+        dc->SetPen( *wxTRANSPARENT_PEN );
+
+        return true;
+    }
+
+    return false;
+}
+
+void wxListLineData::Draw( wxDC *dc )
+{
+    wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+    wxCHECK_RET( node, _T("no subitems at all??") );
+
+    bool highlighted = IsHighlighted();
+
+    wxListItemAttr *attr = GetAttr();
+
+    if ( SetAttributes(dc, attr, highlighted) )
+#if ( !defined(__WXGTK20__) && !defined(__WXMAC__) )
+    {
+        dc->DrawRectangle( m_gi->m_rectHighlight );
+    }
+#else
+    {
+        if (highlighted)
+        {
+            int flags = wxCONTROL_SELECTED;
+            if (m_owner->HasFocus()
+#ifdef __WXMAC__
+                && IsControlActive( (ControlRef)m_owner->GetHandle() )
+#endif
+            )
+                flags |= wxCONTROL_FOCUSED;
+            wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, m_gi->m_rectHighlight, flags );
+
+        }
+        else
+        {
+            dc->DrawRectangle( m_gi->m_rectHighlight );
+        }
+    }
+#endif
+
+    // just for debugging to better see where the items are
+#if 0
+    dc->SetPen(*wxRED_PEN);
+    dc->SetBrush(*wxTRANSPARENT_BRUSH);
+    dc->DrawRectangle( m_gi->m_rectAll );
+    dc->SetPen(*wxGREEN_PEN);
+    dc->DrawRectangle( m_gi->m_rectIcon );
+#endif
+
+    wxListItemData *item = node->GetData();
+    if (item->HasImage())
+    {
+        // centre the image inside our rectangle, this looks nicer when items
+        // ae aligned in a row
+        const wxRect& rectIcon = m_gi->m_rectIcon;
+
+        m_owner->DrawImage(item->GetImage(), dc, rectIcon.x, rectIcon.y);
+    }
+
+    if (item->HasText())
+    {
+        const wxRect& rectLabel = m_gi->m_rectLabel;
+
+        wxDCClipper clipper(*dc, rectLabel);
+        dc->DrawText(item->GetText(), rectLabel.x, rectLabel.y);
+    }
+}
+
+void wxListLineData::DrawInReportMode( wxDC *dc,
+                                       const wxRect& rect,
+                                       const wxRect& rectHL,
+                                       bool highlighted, long line )  //  2009.11.28. TN; add "line" argument
+{
+    // TODO: later we should support setting different attributes for
+    //       different columns - to do it, just add "col" argument to
+    //       GetAttr() and move these lines into the loop below
+    wxListItemAttr *attr = GetAttr();
+
+       //  2009.11.28. TN
+       int flags = 0;
+       if (highlighted) {
+               flags = wxCONTROL_SELECTED;
+               if (m_owner->HasFocus()
+               #ifdef __WXMAC__
+                        && IsControlActive( (ControlRef)m_owner->GetHandle() )
+               #endif
+               )
+                       flags |= wxCONTROL_FOCUSED;
+       }
+       //  End TN
+
+    if ( SetAttributes(dc, attr, highlighted) )
+#if ( !defined(__WXGTK20__) && !defined(__WXMAC__) )
+    {
+        dc->DrawRectangle( rectHL );
+    }
+#else
+    {
+        if (highlighted)
+        {
+                       /*  2009.11.28. TN; this is done before SetAttributes()
+            int flags = wxCONTROL_SELECTED;
+            if (m_owner->HasFocus()
+#ifdef __WXMAC__
+                && IsControlActive( (ControlRef)m_owner->GetHandle() )
+#endif
+            )
+                flags |= wxCONTROL_FOCUSED;
+                       */ //  End TN
+            wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, rectHL, flags );
+        }
+        else
+        {
+            dc->DrawRectangle( rectHL );
+        }
+    }
+#endif
+
+    wxCoord x = rect.x + HEADER_OFFSET_X,
+            yMid = rect.y + rect.height/2;
+#ifdef __WXGTK__
+    // This probably needs to be done
+    // on all platforms as the icons
+    // otherwise nearly touch the border
+    x += 2;
+#endif
+
+    size_t col = 0;
+    for ( wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+          node;
+          node = node->GetNext(), col++ )
+    {
+        wxListItemData *item = node->GetData();
+
+        int width = m_owner->GetColumnWidth(col);
+        int xOld = x;
+        x += width;
+
+               //  2009.11.28. TN
+               {
+                       wxListItemAttr *cattr = m_owner->GetItemAttrForColumn(line, col);
+                       wxRect itemRect(xOld, rect.y, width, rect.height);
+                       if ( cattr != NULL && SetAttributes(dc, cattr, highlighted) )
+#if ( !defined(__WXGTK20__) && !defined(__WXMAC__) )
+                       {
+                               dc->DrawRectangle( itemRect );
+                       }
+#else
+                       {
+                               if (highlighted)
+                                       wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, itemRect, flags );
+                               else
+                                       dc->DrawRectangle( itemRect );
+                       }
+#endif
+               }
+               //  End TN
+               
+        if ( item->HasImage() )
+        {
+            int ix, iy;
+            m_owner->GetImageSize( item->GetImage(), ix, iy );
+            m_owner->DrawImage( item->GetImage(), dc, xOld, yMid - iy/2 );
+
+            ix += IMAGE_MARGIN_IN_REPORT_MODE;
+
+            xOld += ix;
+            width -= ix;
+        }
+
+        if ( item->HasText() )
+       //     DrawTextFormatted(dc, item->GetText(), col, xOld, yMid, width - 8);
+                       DrawTextFormatted(dc, item->GetText(), col, xOld + 4, yMid, width - 8);  // 2009.11.28. TN
+    }
+}
+
+void wxListLineData::DrawTextFormatted(wxDC *dc,
+                                       const wxString& textOrig,
+                                       int col,
+                                       int x,
+                                       int yMid,
+                                       int width)
+{
+    // we don't support displaying multiple lines currently (and neither does
+    // wxMSW FWIW) so just merge all the lines
+    wxString text(textOrig);
+    text.Replace(_T("\n"), _T(" "));
+
+    wxCoord w, h;
+    dc->GetTextExtent(text, &w, &h);
+
+    const wxCoord y = yMid - (h + 1)/2;
+
+    wxDCClipper clipper(*dc, x, y, width, h);
+
+    // determine if the string can fit inside the current width
+    if (w <= width)
+    {
+        // it can, draw it using the items alignment
+        wxListItem item;
+        m_owner->GetColumn(col, item);
+        switch ( item.GetAlign() )
+        {
+            case wxLIST_FORMAT_LEFT:
+                // nothing to do
+                break;
+
+            case wxLIST_FORMAT_RIGHT:
+                x += width - w;
+                break;
+
+            case wxLIST_FORMAT_CENTER:
+                x += (width - w) / 2;
+                break;
+
+            default:
+                wxFAIL_MSG( _T("unknown list item format") );
+                break;
+        }
+
+        dc->DrawText(text, x, y);
+    }
+    else // otherwise, truncate and add an ellipsis if possible
+    {
+        // determine the base width
+        wxString ellipsis(wxT("..."));
+        wxCoord base_w;
+        dc->GetTextExtent(ellipsis, &base_w, &h);
+
+        // continue until we have enough space or only one character left
+        wxCoord w_c, h_c;
+        size_t len = text.length();
+        wxString drawntext = text.Left(len);
+        while (len > 1)
+        {
+            dc->GetTextExtent(drawntext.Last(), &w_c, &h_c);
+            drawntext.RemoveLast();
+            len--;
+            w -= w_c;
+            if (w + base_w <= width)
+                break;
+        }
+
+        // if still not enough space, remove ellipsis characters
+        while (ellipsis.length() > 0 && w + base_w > width)
+        {
+            ellipsis = ellipsis.Left(ellipsis.length() - 1);
+            dc->GetTextExtent(ellipsis, &base_w, &h);
+        }
+
+        // now draw the text
+        dc->DrawText(drawntext, x, y);
+        dc->DrawText(ellipsis, x + w, y);
+    }
+}
+
+bool wxListLineData::Highlight( bool on )
+{
+    wxCHECK_MSG( !IsVirtual(), false, _T("unexpected call to Highlight") );
+
+    if ( on == m_highlighted )
+        return false;
+
+    m_highlighted = on;
+
+    return true;
+}
+
+void wxListLineData::ReverseHighlight( void )
+{
+    Highlight(!IsHighlighted());
+}
+
+//-----------------------------------------------------------------------------
+//  wxListHeaderWindow
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxListHeaderWindow,wxWindow)
+
+BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow)
+    EVT_PAINT         (wxListHeaderWindow::OnPaint)
+    EVT_MOUSE_EVENTS  (wxListHeaderWindow::OnMouse)
+    EVT_SET_FOCUS     (wxListHeaderWindow::OnSetFocus)
+END_EVENT_TABLE()
+
+void wxListHeaderWindow::Init()
+{
+    m_currentCursor = (wxCursor *) NULL;
+    m_isDragging = false;
+    m_dirty = false;
+}
+
+wxListHeaderWindow::wxListHeaderWindow()
+{
+    Init();
+
+    m_owner = (wxListMainWindow *) NULL;
+    m_resizeCursor = (wxCursor *) NULL;
+}
+
+wxListHeaderWindow::wxListHeaderWindow( wxWindow *win,
+                                        wxWindowID id,
+                                        wxListMainWindow *owner,
+                                        const wxPoint& pos,
+                                        const wxSize& size,
+                                        long style,
+                                        const wxString &name )
+                  : wxWindow( win, id, pos, size, style, name )
+{
+    Init();
+
+    m_owner = owner;
+    m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
+
+#if _USE_VISATTR
+    wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
+    SetOwnForegroundColour( attr.colFg );
+    SetOwnBackgroundColour( attr.colBg );
+    if (!m_hasFont)
+        SetOwnFont( attr.font );
+#else
+    SetOwnForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+    SetOwnBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
+    if (!m_hasFont)
+        SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT ));
+#endif
+}
+
+wxListHeaderWindow::~wxListHeaderWindow()
+{
+    delete m_resizeCursor;
+}
+
+#ifdef __WXUNIVERSAL__
+#include "wx/univ/renderer.h"
+#include "wx/univ/theme.h"
+#endif
+
+// shift the DC origin to match the position of the main window horz
+// scrollbar: this allows us to always use logical coords
+void wxListHeaderWindow::AdjustDC(wxDC& dc)
+{
+    int xpix;
+    m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
+
+    int view_start;
+    m_owner->GetViewStart( &view_start, NULL );
+
+
+    int org_x = 0;
+    int org_y = 0;
+    dc.GetDeviceOrigin( &org_x, &org_y );
+
+    // account for the horz scrollbar offset
+#ifdef __WXGTK__
+    if (GetLayoutDirection() == wxLayout_RightToLeft)
+    {
+        // Maybe we just have to check for m_signX
+        // in the DC, but I leave the #ifdef __WXGTK__
+        // for now
+        dc.SetDeviceOrigin( org_x + (view_start * xpix), org_y );
+    }
+    else
+#endif
+        dc.SetDeviceOrigin( org_x - (view_start * xpix), org_y );
+}
+
+void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
+{
+    wxPaintDC dc( this );
+
+    PrepareDC( dc );
+    AdjustDC( dc );
+
+    dc.SetFont( GetFont() );
+
+    // width and height of the entire header window
+    int w, h;
+    GetClientSize( &w, &h );
+    m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
+
+    dc.SetBackgroundMode(wxTRANSPARENT);
+    dc.SetTextForeground(GetForegroundColour());
+
+    int x = HEADER_OFFSET_X;
+    int numColumns = m_owner->GetColumnCount();
+    wxListItem item;
+    for ( int i = 0; i < numColumns && x < w; i++ )
+    {
+        m_owner->GetColumn( i, item );
+        int wCol = item.m_width;
+
+        int cw = wCol;
+        int ch = h;
+
+        int flags = 0;
+        if (!m_parent->IsEnabled())
+            flags |= wxCONTROL_DISABLED;
+
+// NB: The code below is not really Mac-specific, but since we are close
+// to 2.8 release and I don't have time to test on other platforms, I
+// defined this only for wxMac. If this behavior is desired on
+// other platforms, please go ahead and revise or remove the #ifdef.
+#ifdef __WXMAC__
+        if ( !m_owner->IsVirtual() && (item.m_mask & wxLIST_MASK_STATE) &&
+                (item.m_state & wxLIST_STATE_SELECTED) )
+            flags |= wxCONTROL_SELECTED;
+#endif
+
+        wxRendererNative::Get().DrawHeaderButton
+                                (
+                                    this,
+                                    dc,
+                                    wxRect(x, HEADER_OFFSET_Y, cw, ch),
+                                    flags
+                                );
+
+        // see if we have enough space for the column label
+
+        // for this we need the width of the text
+        wxCoord wLabel;
+        wxCoord hLabel;
+        dc.GetTextExtent(item.GetText(), &wLabel, &hLabel);
+        wLabel += 2 * EXTRA_WIDTH;
+
+        // and the width of the icon, if any
+        int ix = 0, iy = 0;    // init them just to suppress the compiler warnings
+        const int image = item.m_image;
+        wxImageList *imageList;
+        if ( image != -1 )
+        {
+            imageList = m_owner->m_small_image_list;
+            if ( imageList )
+            {
+                imageList->GetSize(image, ix, iy);
+                wLabel += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
+            }
+        }
+        else
+        {
+            imageList = NULL;
+        }
+
+        // ignore alignment if there is not enough space anyhow
+        int xAligned;
+        switch ( wLabel < cw ? item.GetAlign() : wxLIST_FORMAT_LEFT )
+        {
+            default:
+                wxFAIL_MSG( _T("unknown list item format") );
+                // fall through
+
+            case wxLIST_FORMAT_LEFT:
+                xAligned = x;
+                break;
+
+            case wxLIST_FORMAT_RIGHT:
+                xAligned = x + cw - wLabel;
+                break;
+
+            case wxLIST_FORMAT_CENTER:
+                xAligned = x + (cw - wLabel) / 2;
+                break;
+        }
+
+        // draw the text and image clipping them so that they
+        // don't overwrite the column boundary
+        wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h  );
+
+        // if we have an image, draw it on the right of the label
+        if ( imageList )
+        {
+            imageList->Draw
+                       (
+                        image,
+                        dc,
+                        xAligned + wLabel - ix - HEADER_IMAGE_MARGIN_IN_REPORT_MODE,
+                        HEADER_OFFSET_Y + (h - 4 - iy)/2,
+                        wxIMAGELIST_DRAW_TRANSPARENT
+                       );
+        }
+
+        dc.DrawText( item.GetText(),
+                     xAligned + EXTRA_WIDTH, h / 2 - hLabel / 2 ); //HEADER_OFFSET_Y + EXTRA_HEIGHT );
+
+        x += wCol;
+    }
+
+    // Fill in what's missing to the right of the columns, otherwise we will
+    // leave an unpainted area when columns are removed (and it looks better)
+    if ( x < w )
+    {
+        wxRendererNative::Get().DrawHeaderButton
+                                (
+                                    this,
+                                    dc,
+                                    wxRect(x, HEADER_OFFSET_Y, w - x, h),
+                                    0
+                                );
+    }
+}
+
+void wxListHeaderWindow::DrawCurrent()
+{
+#if 1
+    m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
+#else
+    int x1 = m_currentX;
+    int y1 = 0;
+    m_owner->ClientToScreen( &x1, &y1 );
+
+    int x2 = m_currentX;
+    int y2 = 0;
+    m_owner->GetClientSize( NULL, &y2 );
+    m_owner->ClientToScreen( &x2, &y2 );
+
+    wxScreenDC dc;
+    dc.SetLogicalFunction( wxINVERT );
+    dc.SetPen( wxPen( *wxBLACK, 2, wxSOLID ) );
+    dc.SetBrush( *wxTRANSPARENT_BRUSH );
+
+    AdjustDC(dc);
+
+    dc.DrawLine( x1, y1, x2, y2 );
+
+    dc.SetLogicalFunction( wxCOPY );
+
+    dc.SetPen( wxNullPen );
+    dc.SetBrush( wxNullBrush );
+#endif
+}
+
+void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
+{
+    // we want to work with logical coords
+    int x;
+    m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
+    int y = event.GetY();
+
+    if (m_isDragging)
+    {
+        SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition());
+
+        // we don't draw the line beyond our window, but we allow dragging it
+        // there
+        int w = 0;
+        GetClientSize( &w, NULL );
+        m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
+        w -= 6;
+
+        // erase the line if it was drawn
+        if ( m_currentX < w )
+            DrawCurrent();
+
+        if (event.ButtonUp())
+        {
+            ReleaseMouse();
+            m_isDragging = false;
+            m_dirty = true;
+            m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
+            SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition());
+        }
+        else
+        {
+            if (x > m_minX + 7)
+                m_currentX = x;
+            else
+                m_currentX = m_minX + 7;
+
+            // draw in the new location
+            if ( m_currentX < w )
+                DrawCurrent();
+        }
+    }
+    else // not dragging
+    {
+        m_minX = 0;
+        bool hit_border = false;
+
+        // end of the current column
+        int xpos = 0;
+
+        // find the column where this event occurred
+        int col,
+            countCol = m_owner->GetColumnCount();
+        for (col = 0; col < countCol; col++)
+        {
+            xpos += m_owner->GetColumnWidth( col );
+            m_column = col;
+
+            if ( (abs(x-xpos) < 3) && (y < 22) )
+            {
+                // near the column border
+                hit_border = true;
+                break;
+            }
+
+            if ( x < xpos )
+            {
+                // inside the column
+                break;
+            }
+
+            m_minX = xpos;
+        }
+
+        if ( col == countCol )
+            m_column = -1;
+
+        if (event.LeftDown() || event.RightUp())
+        {
+            if (hit_border && event.LeftDown())
+            {
+                if ( SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
+                                   event.GetPosition()) )
+                {
+                    m_isDragging = true;
+                    m_currentX = x;
+                    CaptureMouse();
+                    DrawCurrent();
+                }
+                //else: column resizing was vetoed by the user code
+            }
+            else // click on a column
+            {
+                // record the selected state of the columns
+                if (event.LeftDown())
+                {
+                    for (int i=0; i < m_owner->GetColumnCount(); i++)
+                    {
+                        wxListItem colItem;
+                        m_owner->GetColumn(i, colItem);
+                        long state = colItem.GetState();
+                        if (i == m_column)
+                            colItem.SetState(state | wxLIST_STATE_SELECTED);
+                        else
+                            colItem.SetState(state & ~wxLIST_STATE_SELECTED);
+                        m_owner->SetColumn(i, colItem);
+                    }
+                }
+
+                SendListEvent( event.LeftDown()
+                                    ? wxEVT_COMMAND_LIST_COL_CLICK
+                                    : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK,
+                                event.GetPosition());
+            }
+        }
+        else if (event.Moving())
+        {
+            bool setCursor;
+            if (hit_border)
+            {
+                setCursor = m_currentCursor == wxSTANDARD_CURSOR;
+                m_currentCursor = m_resizeCursor;
+            }
+            else
+            {
+                setCursor = m_currentCursor != wxSTANDARD_CURSOR;
+                m_currentCursor = wxSTANDARD_CURSOR;
+            }
+
+            if ( setCursor )
+                SetCursor(*m_currentCursor);
+        }
+    }
+}
+
+void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
+{
+    m_owner->SetFocus();
+    m_owner->Update();
+}
+
+bool wxListHeaderWindow::SendListEvent(wxEventType type, const wxPoint& pos)
+{
+    wxWindow *parent = GetParent();
+    wxListEvent le( type, parent->GetId() );
+    le.SetEventObject( parent );
+    le.m_pointDrag = pos;
+
+    // the position should be relative to the parent window, not
+    // this one for compatibility with MSW and common sense: the
+    // user code doesn't know anything at all about this header
+    // window, so why should it get positions relative to it?
+    le.m_pointDrag.y -= GetSize().y;
+
+    le.m_col = m_column;
+    return !parent->GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
+}
+
+//-----------------------------------------------------------------------------
+// wxListRenameTimer (internal)
+//-----------------------------------------------------------------------------
+
+wxListRenameTimer::wxListRenameTimer( wxListMainWindow *owner )
+{
+    m_owner = owner;
+}
+
+void wxListRenameTimer::Notify()
+{
+    m_owner->OnRenameTimer();
+}
+
+//-----------------------------------------------------------------------------
+// wxListTextCtrlWrapper (internal)
+//-----------------------------------------------------------------------------
+
+BEGIN_EVENT_TABLE(wxListTextCtrlWrapper, wxEvtHandler)
+    EVT_CHAR           (wxListTextCtrlWrapper::OnChar)
+    EVT_KEY_UP         (wxListTextCtrlWrapper::OnKeyUp)
+    EVT_KILL_FOCUS     (wxListTextCtrlWrapper::OnKillFocus)
+END_EVENT_TABLE()
+
+wxListTextCtrlWrapper::wxListTextCtrlWrapper(wxListMainWindow *owner,
+                                             wxTextCtrl *text,
+                                             size_t itemEdit)
+              : m_startValue(owner->GetItemText(itemEdit)),
+                m_itemEdited(itemEdit)
+{
+    m_owner = owner;
+    m_text = text;
+    m_finished = false;
+    m_aboutToFinish = false;
+
+    wxRect rectLabel = owner->GetLineLabelRect(itemEdit);
+
+    m_owner->CalcScrolledPosition(rectLabel.x, rectLabel.y,
+                                  &rectLabel.x, &rectLabel.y);
+
+    m_text->Create(owner, wxID_ANY, m_startValue,
+                   wxPoint(rectLabel.x-4,rectLabel.y-4),
+                   wxSize(rectLabel.width+11,rectLabel.height+8));
+    m_text->SetFocus();
+
+    m_text->PushEventHandler(this);
+}
+
+void wxListTextCtrlWrapper::Finish()
+{
+    if ( !m_finished )
+    {
+        m_finished = true;
+
+        m_text->RemoveEventHandler(this);
+        m_owner->FinishEditing(m_text);
+
+        wxPendingDelete.Append( this );
+    }
+}
+
+bool wxListTextCtrlWrapper::AcceptChanges()
+{
+    const wxString value = m_text->GetValue();
+
+    // notice that we should always call OnRenameAccept() to generate the "end
+    // label editing" event, even if the user hasn't really changed anything
+    if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
+    {
+        // vetoed by the user
+        return false;
+    }
+
+    // accepted, do rename the item (unless nothing changed)
+    if ( value != m_startValue )
+        m_owner->SetItemText(m_itemEdited, value);
+
+    return true;
+}
+
+void wxListTextCtrlWrapper::AcceptChangesAndFinish()
+{
+    m_aboutToFinish = true;
+
+    // Notify the owner about the changes
+    AcceptChanges();
+
+    // Even if vetoed, close the control (consistent with MSW)
+    Finish();
+}
+
+void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event )
+{
+    switch ( event.m_keyCode )
+    {
+        case WXK_RETURN:
+            AcceptChangesAndFinish();
+            break;
+
+        case WXK_ESCAPE:
+            m_owner->OnRenameCancelled( m_itemEdited );
+            Finish();
+            break;
+
+        default:
+            event.Skip();
+    }
+}
+
+void wxListTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
+{
+    if (m_finished)
+    {
+        event.Skip();
+        return;
+    }
+
+    // auto-grow the textctrl:
+    wxSize parentSize = m_owner->GetSize();
+    wxPoint myPos = m_text->GetPosition();
+    wxSize mySize = m_text->GetSize();
+    int sx, sy;
+    m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
+    if (myPos.x + sx > parentSize.x)
+        sx = parentSize.x - myPos.x;
+    if (mySize.x > sx)
+        sx = mySize.x;
+    m_text->SetSize(sx, wxDefaultCoord);
+
+    event.Skip();
+}
+
+void wxListTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
+{
+    if ( !m_finished && !m_aboutToFinish )
+    {
+        if ( !AcceptChanges() )
+            m_owner->OnRenameCancelled( m_itemEdited );
+
+        Finish();
+    }
+
+    // We must let the native text control handle focus
+    event.Skip();
+}
+
+//-----------------------------------------------------------------------------
+//  wxListMainWindow
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxListMainWindow,wxScrolledWindow)
+
+BEGIN_EVENT_TABLE(wxListMainWindow,wxScrolledWindow)
+  EVT_PAINT          (wxListMainWindow::OnPaint)
+  EVT_MOUSE_EVENTS   (wxListMainWindow::OnMouse)
+  EVT_CHAR           (wxListMainWindow::OnChar)
+  EVT_KEY_DOWN       (wxListMainWindow::OnKeyDown)
+  EVT_KEY_UP         (wxListMainWindow::OnKeyUp)
+  EVT_SET_FOCUS      (wxListMainWindow::OnSetFocus)
+  EVT_KILL_FOCUS     (wxListMainWindow::OnKillFocus)
+  EVT_SCROLLWIN      (wxListMainWindow::OnScroll)
+  EVT_CHILD_FOCUS    (wxListMainWindow::OnChildFocus)
+END_EVENT_TABLE()
+
+void wxListMainWindow::Init()
+{
+    m_dirty = true;
+    m_countVirt = 0;
+    m_lineFrom =
+    m_lineTo = (size_t)-1;
+    m_linesPerPage = 0;
+
+    m_headerWidth =
+    m_lineHeight = 0;
+
+    m_small_image_list = (wxImageList *) NULL;
+    m_normal_image_list = (wxImageList *) NULL;
+
+    m_small_spacing = 30;
+    m_normal_spacing = 40;
+
+    m_hasFocus = false;
+    m_dragCount = 0;
+    m_isCreated = false;
+
+    m_lastOnSame = false;
+    m_renameTimer = new wxListRenameTimer( this );
+    m_textctrlWrapper = NULL;
+
+    m_current =
+    m_lineLastClicked =
+    m_lineSelectSingleOnUp =
+    m_lineBeforeLastClicked = (size_t)-1;
+
+    m_freezeCount = 0;
+}
+
+wxListMainWindow::wxListMainWindow()
+{
+    Init();
+
+    m_highlightBrush =
+    m_highlightUnfocusedBrush = (wxBrush *) NULL;
+}
+
+wxListMainWindow::wxListMainWindow( wxWindow *parent,
+                                    wxWindowID id,
+                                    const wxPoint& pos,
+                                    const wxSize& size,
+                                    long style,
+                                    const wxString &name )
+                : wxScrolledWindow( parent, id, pos, size,
+                                    style | wxHSCROLL | wxVSCROLL, name )
+{
+    Init();
+
+    m_highlightBrush = new wxBrush
+                         (
+                            wxSystemSettings::GetColour
+                            (
+                                wxSYS_COLOUR_HIGHLIGHT
+                            ),
+                            wxSOLID
+                         );
+
+    m_highlightUnfocusedBrush = new wxBrush
+                              (
+                                 wxSystemSettings::GetColour
+                                 (
+                                     wxSYS_COLOUR_BTNSHADOW
+                                 ),
+                                 wxSOLID
+                              );
+
+    SetScrollbars( 0, 0, 0, 0, 0, 0 );
+
+    wxVisualAttributes attr = wxGenericListCtrl::GetClassDefaultAttributes();
+    SetOwnForegroundColour( attr.colFg );
+    SetOwnBackgroundColour( attr.colBg );
+    if (!m_hasFont)
+        SetOwnFont( attr.font );
+}
+
+wxListMainWindow::~wxListMainWindow()
+{
+    DoDeleteAllItems();
+    WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
+    WX_CLEAR_ARRAY(m_aColWidths);
+
+    delete m_highlightBrush;
+    delete m_highlightUnfocusedBrush;
+    delete m_renameTimer;
+}
+
+void wxListMainWindow::CacheLineData(size_t line)
+{
+    wxGenericListCtrl *listctrl = GetListCtrl();
+
+    wxListLineData *ld = GetDummyLine();
+
+    size_t countCol = GetColumnCount();
+    for ( size_t col = 0; col < countCol; col++ )
+    {
+        ld->SetText(col, listctrl->OnGetItemText(line, col));
+        ld->SetImage(col, listctrl->OnGetItemColumnImage(line, col));
+    }
+
+    ld->SetAttr(listctrl->OnGetItemAttr(line));
+}
+
+wxListLineData *wxListMainWindow::GetDummyLine() const
+{
+    wxASSERT_MSG( !IsEmpty(), _T("invalid line index") );
+    wxASSERT_MSG( IsVirtual(), _T("GetDummyLine() shouldn't be called") );
+
+    wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
+
+    // we need to recreate the dummy line if the number of columns in the
+    // control changed as it would have the incorrect number of fields
+    // otherwise
+    if ( !m_lines.IsEmpty() &&
+            m_lines[0].m_items.GetCount() != (size_t)GetColumnCount() )
+    {
+        self->m_lines.Clear();
+    }
+
+    if ( m_lines.IsEmpty() )
+    {
+        wxListLineData *line = new wxListLineData(self);
+        self->m_lines.Add(line);
+
+        // don't waste extra memory -- there never going to be anything
+        // else/more in this array
+        self->m_lines.Shrink();
+    }
+
+    return &m_lines[0];
+}
+
+// ----------------------------------------------------------------------------
+// line geometry (report mode only)
+// ----------------------------------------------------------------------------
+
+wxCoord wxListMainWindow::GetLineHeight() const
+{
+    // we cache the line height as calling GetTextExtent() is slow
+    if ( !m_lineHeight )
+    {
+        wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
+
+        wxClientDC dc( self );
+        dc.SetFont( GetFont() );
+
+        wxCoord y;
+        dc.GetTextExtent(_T("H"), NULL, &y);
+
+        if ( m_small_image_list && m_small_image_list->GetImageCount() )
+        {
+            int iw = 0, ih = 0;
+            m_small_image_list->GetSize(0, iw, ih);
+            y = wxMax(y, ih);
+        }
+
+        y += EXTRA_HEIGHT;
+        self->m_lineHeight = y + LINE_SPACING;
+    }
+
+    return m_lineHeight;
+}
+
+wxCoord wxListMainWindow::GetLineY(size_t line) const
+{
+    wxASSERT_MSG( InReportView(), _T("only works in report mode") );
+
+    return LINE_SPACING + line * GetLineHeight();
+}
+
+wxRect wxListMainWindow::GetLineRect(size_t line) const
+{
+    if ( !InReportView() )
+        return GetLine(line)->m_gi->m_rectAll;
+
+    wxRect rect;
+    rect.x = HEADER_OFFSET_X;
+    rect.y = GetLineY(line);
+    rect.width = GetHeaderWidth();
+    rect.height = GetLineHeight();
+
+    return rect;
+}
+
+wxRect wxListMainWindow::GetLineLabelRect(size_t line) const
+{
+    if ( !InReportView() )
+        return GetLine(line)->m_gi->m_rectLabel;
+
+    int image_x = 0;
+    wxListLineData *data = GetLine(line);
+    wxListItemDataList::compatibility_iterator node = data->m_items.GetFirst();
+    if (node)
+    {
+        wxListItemData *item = node->GetData();
+        if ( item->HasImage() )
+        {
+            int ix, iy;
+            GetImageSize( item->GetImage(), ix, iy );
+            image_x = 3 + ix + IMAGE_MARGIN_IN_REPORT_MODE;
+        }
+    }
+
+    wxRect rect;
+    rect.x = image_x + HEADER_OFFSET_X;
+    rect.y = GetLineY(line);
+    rect.width = GetColumnWidth(0) - image_x;
+    rect.height = GetLineHeight();
+
+    return rect;
+}
+
+wxRect wxListMainWindow::GetLineIconRect(size_t line) const
+{
+    if ( !InReportView() )
+        return GetLine(line)->m_gi->m_rectIcon;
+
+    wxListLineData *ld = GetLine(line);
+    wxASSERT_MSG( ld->HasImage(), _T("should have an image") );
+
+    wxRect rect;
+    rect.x = HEADER_OFFSET_X;
+    rect.y = GetLineY(line);
+    GetImageSize(ld->GetImage(), rect.width, rect.height);
+
+    return rect;
+}
+
+wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const
+{
+    return InReportView() ? GetLineRect(line)
+                          : GetLine(line)->m_gi->m_rectHighlight;
+}
+
+long wxListMainWindow::HitTestLine(size_t line, int x, int y) const
+{
+    wxASSERT_MSG( line < GetItemCount(), _T("invalid line in HitTestLine") );
+
+    wxListLineData *ld = GetLine(line);
+
+    if ( ld->HasImage() && GetLineIconRect(line).Contains(x, y) )
+        return wxLIST_HITTEST_ONITEMICON;
+
+    // VS: Testing for "ld->HasText() || InReportView()" instead of
+    //     "ld->HasText()" is needed to make empty lines in report view
+    //     possible
+    if ( ld->HasText() || InReportView() )
+    {
+        wxRect rect = InReportView() ? GetLineRect(line)
+                                     : GetLineLabelRect(line);
+
+        if ( rect.Contains(x, y) )
+            return wxLIST_HITTEST_ONITEMLABEL;
+    }
+
+    return 0;
+}
+
+// ----------------------------------------------------------------------------
+// highlight (selection) handling
+// ----------------------------------------------------------------------------
+
+bool wxListMainWindow::IsHighlighted(size_t line) const
+{
+    if ( IsVirtual() )
+    {
+        return m_selStore.IsSelected(line);
+    }
+    else // !virtual
+    {
+        wxListLineData *ld = GetLine(line);
+        wxCHECK_MSG( ld, false, _T("invalid index in IsHighlighted") );
+
+        return ld->IsHighlighted();
+    }
+}
+
+void wxListMainWindow::HighlightLines( size_t lineFrom,
+                                       size_t lineTo,
+                                       bool highlight )
+{
+    if ( IsVirtual() )
+    {
+        wxArrayInt linesChanged;
+        if ( !m_selStore.SelectRange(lineFrom, lineTo, highlight,
+                                     &linesChanged) )
+        {
+            // meny items changed state, refresh everything
+            RefreshLines(lineFrom, lineTo);
+        }
+        else // only a few items changed state, refresh only them
+        {
+            size_t count = linesChanged.GetCount();
+            for ( size_t n = 0; n < count; n++ )
+            {
+                RefreshLine(linesChanged[n]);
+            }
+        }
+    }
+    else // iterate over all items in non report view
+    {
+        for ( size_t line = lineFrom; line <= lineTo; line++ )
+        {
+            if ( HighlightLine(line, highlight) )
+                RefreshLine(line);
+        }
+    }
+}
+
+bool wxListMainWindow::HighlightLine( size_t line, bool highlight )
+{
+    bool changed;
+
+    if ( IsVirtual() )
+    {
+        changed = m_selStore.SelectItem(line, highlight);
+    }
+    else // !virtual
+    {
+        wxListLineData *ld = GetLine(line);
+        wxCHECK_MSG( ld, false, _T("invalid index in HighlightLine") );
+
+        changed = ld->Highlight(highlight);
+    }
+
+    if ( changed )
+    {
+        SendNotify( line, highlight ? wxEVT_COMMAND_LIST_ITEM_SELECTED
+                                    : wxEVT_COMMAND_LIST_ITEM_DESELECTED );
+    }
+
+    return changed;
+}
+
+void wxListMainWindow::RefreshLine( size_t line )
+{
+    if ( InReportView() )
+    {
+        size_t visibleFrom, visibleTo;
+        GetVisibleLinesRange(&visibleFrom, &visibleTo);
+
+        if ( line < visibleFrom || line > visibleTo )
+            return;
+    }
+
+    wxRect rect = GetLineRect(line);
+
+    CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+    RefreshRect( rect );
+}
+
+void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo )
+{
+    // we suppose that they are ordered by caller
+    wxASSERT_MSG( lineFrom <= lineTo, _T("indices in disorder") );
+
+    wxASSERT_MSG( lineTo < GetItemCount(), _T("invalid line range") );
+
+    if ( InReportView() )
+    {
+        size_t visibleFrom, visibleTo;
+        GetVisibleLinesRange(&visibleFrom, &visibleTo);
+
+        if ( lineFrom < visibleFrom )
+            lineFrom = visibleFrom;
+        if ( lineTo > visibleTo )
+            lineTo = visibleTo;
+
+        wxRect rect;
+        rect.x = 0;
+        rect.y = GetLineY(lineFrom);
+        rect.width = GetClientSize().x;
+        rect.height = GetLineY(lineTo) - rect.y + GetLineHeight();
+
+        CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+        RefreshRect( rect );
+    }
+    else // !report
+    {
+        // TODO: this should be optimized...
+        for ( size_t line = lineFrom; line <= lineTo; line++ )
+        {
+            RefreshLine(line);
+        }
+    }
+}
+
+void wxListMainWindow::RefreshAfter( size_t lineFrom )
+{
+    if ( InReportView() )
+    {
+        size_t visibleFrom, visibleTo;
+        GetVisibleLinesRange(&visibleFrom, &visibleTo);
+
+        if ( lineFrom < visibleFrom )
+            lineFrom = visibleFrom;
+        else if ( lineFrom > visibleTo )
+            return;
+
+        wxRect rect;
+        rect.x = 0;
+        rect.y = GetLineY(lineFrom);
+        CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+        wxSize size = GetClientSize();
+        rect.width = size.x;
+
+        // refresh till the bottom of the window
+        rect.height = size.y - rect.y;
+
+        RefreshRect( rect );
+    }
+    else // !report
+    {
+        // TODO: how to do it more efficiently?
+        m_dirty = true;
+    }
+}
+
+void wxListMainWindow::RefreshSelected()
+{
+    if ( IsEmpty() )
+        return;
+
+    size_t from, to;
+    if ( InReportView() )
+    {
+        GetVisibleLinesRange(&from, &to);
+    }
+    else // !virtual
+    {
+        from = 0;
+        to = GetItemCount() - 1;
+    }
+
+    if ( HasCurrent() && m_current >= from && m_current <= to )
+        RefreshLine(m_current);
+
+    for ( size_t line = from; line <= to; line++ )
+    {
+        // NB: the test works as expected even if m_current == -1
+        if ( line != m_current && IsHighlighted(line) )
+            RefreshLine(line);
+    }
+}
+
+void wxListMainWindow::Freeze()
+{
+    m_freezeCount++;
+}
+
+void wxListMainWindow::Thaw()
+{
+    wxCHECK_RET( m_freezeCount > 0, _T("thawing unfrozen list control?") );
+
+    if ( --m_freezeCount == 0 )
+        Refresh();
+}
+
+extern void wxListCtrl_onPaintCallback(wxGenericListCtrl *listctrl, wxDC *dc); //  2009.12.8. TN
+
+void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
+{
+    // Note: a wxPaintDC must be constructed even if no drawing is
+    // done (a Windows requirement).
+    wxPaintDC dc( this );
+
+    if ( IsEmpty() || m_freezeCount )
+        // nothing to draw or not the moment to draw it
+        return;
+
+    if ( m_dirty )
+        // delay the repainting until we calculate all the items positions
+        return;
+
+    PrepareDC( dc );
+
+    int dev_x, dev_y;
+    CalcScrolledPosition( 0, 0, &dev_x, &dev_y );
+
+    dc.SetFont( GetFont() );
+
+    if ( InReportView() )
+    {
+        int lineHeight = GetLineHeight();
+
+        size_t visibleFrom, visibleTo;
+        GetVisibleLinesRange(&visibleFrom, &visibleTo);
+
+        wxRect rectLine;
+        int xOrig = dc.LogicalToDeviceX( 0 );
+        int yOrig = dc.LogicalToDeviceY( 0 );
+
+        // tell the caller cache to cache the data
+        if ( IsVirtual() )
+        {
+            wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT,
+                                GetParent()->GetId());
+            evCache.SetEventObject( GetParent() );
+            evCache.m_oldItemIndex = visibleFrom;
+            evCache.m_itemIndex = visibleTo;
+            GetParent()->GetEventHandler()->ProcessEvent( evCache );
+        }
+
+        for ( size_t line = visibleFrom; line <= visibleTo; line++ )
+        {
+            rectLine = GetLineRect(line);
+
+
+            if ( !IsExposed(rectLine.x + xOrig, rectLine.y + yOrig,
+                            rectLine.width, rectLine.height) )
+            {
+                // don't redraw unaffected lines to avoid flicker
+                continue;
+            }
+
+            GetLine(line)->DrawInReportMode( &dc,
+                                             rectLine,
+                                             GetLineHighlightRect(line),
+                                             IsHighlighted(line), line);  //  2009.11.28. TN; add "line" argument
+        }
+
+        if ( HasFlag(wxLC_HRULES) )
+        {
+            wxPen pen(GetRuleColour(), 1, wxSOLID);
+            wxSize clientSize = GetClientSize();
+
+            size_t i = visibleFrom;
+            if (i == 0) i = 1; // Don't draw the first one
+            for ( ; i <= visibleTo; i++ )
+            {
+                dc.SetPen(pen);
+                dc.SetBrush( *wxTRANSPARENT_BRUSH );
+                dc.DrawLine(0 - dev_x, i * lineHeight,
+                            clientSize.x - dev_x, i * lineHeight);
+            }
+
+            // Draw last horizontal rule
+            if ( visibleTo == GetItemCount() - 1 )
+            {
+                dc.SetPen( pen );
+                dc.SetBrush( *wxTRANSPARENT_BRUSH );
+                dc.DrawLine(0 - dev_x, (m_lineTo + 1) * lineHeight,
+                            clientSize.x - dev_x , (m_lineTo + 1) * lineHeight );
+            }
+        }
+
+        // Draw vertical rules if required
+        if ( HasFlag(wxLC_VRULES) && !IsEmpty() )
+        {
+            wxPen pen(GetRuleColour(), 1, wxSOLID);
+            wxRect firstItemRect, lastItemRect;
+
+            GetItemRect(visibleFrom, firstItemRect);
+            GetItemRect(visibleTo, lastItemRect);
+            int x = firstItemRect.GetX();
+            dc.SetPen(pen);
+            dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+            for (int col = 0; col < GetColumnCount(); col++)
+            {
+                int colWidth = GetColumnWidth(col);
+                x += colWidth;
+                int x_pos = x - dev_x;
+                if (col < GetColumnCount()-1) x_pos -= 2;
+                dc.DrawLine(x_pos, firstItemRect.GetY() - 1 - dev_y,
+                            x_pos, lastItemRect.GetBottom() + 1 - dev_y);
+            }
+        }
+    }
+    else // !report
+    {
+        size_t count = GetItemCount();
+        for ( size_t i = 0; i < count; i++ )
+        {
+            GetLine(i)->Draw( &dc );
+        }
+    }
+
+       ::wxListCtrl_onPaintCallback(GetListCtrl(), &dc);  // 2009.12.8. TN
+
+#ifndef __WXMAC__
+    // Don't draw rect outline under Mac at all.
+    if ( HasCurrent() )
+    {
+        if ( m_hasFocus )
+        {
+            wxRect rect( GetLineHighlightRect( m_current ) );
+#ifndef __WXGTK20__
+            dc.SetPen( *wxBLACK_PEN );
+            dc.SetBrush( *wxTRANSPARENT_BRUSH );
+            dc.DrawRectangle( rect );
+#else
+            wxRendererNative::Get().DrawItemSelectionRect( this, dc, rect, wxCONTROL_CURRENT|wxCONTROL_FOCUSED );
+
+#endif
+        }
+    }
+#endif
+}
+
+void wxListMainWindow::HighlightAll( bool on )
+{
+    if ( IsSingleSel() )
+    {
+        wxASSERT_MSG( !on, _T("can't do this in a single selection control") );
+
+        // we just have one item to turn off
+        if ( HasCurrent() && IsHighlighted(m_current) )
+        {
+            HighlightLine(m_current, false);
+            RefreshLine(m_current);
+        }
+    }
+    else // multi selection
+    {
+        if ( !IsEmpty() )
+            HighlightLines(0, GetItemCount() - 1, on);
+    }
+}
+
+void wxListMainWindow::OnChildFocus(wxChildFocusEvent& WXUNUSED(event))
+{
+    // Do nothing here.  This prevents the default handler in wxScrolledWindow
+    // from needlessly scrolling the window when the edit control is
+    // dismissed.  See ticket #9563.
+}
+
+void wxListMainWindow::SendNotify( size_t line,
+                                   wxEventType command,
+                                   const wxPoint& point )
+{
+    wxListEvent le( command, GetParent()->GetId() );
+    le.SetEventObject( GetParent() );
+
+    le.m_itemIndex = line;
+
+    // set only for events which have position
+    if ( point != wxDefaultPosition )
+        le.m_pointDrag = point;
+
+    // don't try to get the line info for virtual list controls: the main
+    // program has it anyhow and if we did it would result in accessing all
+    // the lines, even those which are not visible now and this is precisely
+    // what we're trying to avoid
+    if ( !IsVirtual() )
+    {
+        if ( line != (size_t)-1 )
+        {
+            GetLine(line)->GetItem( 0, le.m_item );
+        }
+        //else: this happens for wxEVT_COMMAND_LIST_ITEM_FOCUSED event
+    }
+    //else: there may be no more such item
+
+    GetParent()->GetEventHandler()->ProcessEvent( le );
+}
+
+void wxListMainWindow::ChangeCurrent(size_t current)
+{
+    m_current = current;
+
+    // as the current item changed, we shouldn't start editing it when the
+    // "slow click" timer expires as the click happened on another item
+    if ( m_renameTimer->IsRunning() )
+        m_renameTimer->Stop();
+
+    SendNotify(current, wxEVT_COMMAND_LIST_ITEM_FOCUSED);
+}
+
+wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass)
+{
+    wxCHECK_MSG( (item >= 0) && ((size_t)item < GetItemCount()), NULL,
+                 wxT("wrong index in wxGenericListCtrl::EditLabel()") );
+
+    wxASSERT_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)),
+                 wxT("EditLabel() needs a text control") );
+
+    size_t itemEdit = (size_t)item;
+
+    wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() );
+    le.SetEventObject( GetParent() );
+    le.m_itemIndex = item;
+    wxListLineData *data = GetLine(itemEdit);
+    wxCHECK_MSG( data, NULL, _T("invalid index in EditLabel()") );
+    data->GetItem( 0, le.m_item );
+
+    if ( GetParent()->GetEventHandler()->ProcessEvent( le ) && !le.IsAllowed() )
+    {
+        // vetoed by user code
+        return NULL;
+    }
+
+    // We have to call this here because the label in question might just have
+    // been added and no screen update taken place.
+    if ( m_dirty )
+    {
+        wxSafeYield();
+
+        // Pending events dispatched by wxSafeYield might have changed the item
+        // count
+        if ( (size_t)item >= GetItemCount() )
+            return NULL;
+    }
+
+    wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
+    m_textctrlWrapper = new wxListTextCtrlWrapper(this, text, item);
+    return m_textctrlWrapper->GetText();
+}
+
+void wxListMainWindow::OnRenameTimer()
+{
+    wxCHECK_RET( HasCurrent(), wxT("unexpected rename timer") );
+
+    EditLabel( m_current );
+}
+
+bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value)
+{
+    wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
+    le.SetEventObject( GetParent() );
+    le.m_itemIndex = itemEdit;
+
+    wxListLineData *data = GetLine(itemEdit);
+
+    wxCHECK_MSG( data, false, _T("invalid index in OnRenameAccept()") );
+
+    data->GetItem( 0, le.m_item );
+    le.m_item.m_text = value;
+    return !GetParent()->GetEventHandler()->ProcessEvent( le ) ||
+                le.IsAllowed();
+}
+
+void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
+{
+    // let owner know that the edit was cancelled
+    wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
+
+    le.SetEditCanceled(true);
+
+    le.SetEventObject( GetParent() );
+    le.m_itemIndex = itemEdit;
+
+    wxListLineData *data = GetLine(itemEdit);
+    wxCHECK_RET( data, _T("invalid index in OnRenameCancelled()") );
+
+    data->GetItem( 0, le.m_item );
+    GetEventHandler()->ProcessEvent( le );
+}
+
+void wxListMainWindow::OnMouse( wxMouseEvent &event )
+{
+
+#ifdef __WXMAC__
+    // On wxMac we can't depend on the EVT_KILL_FOCUS event to properly
+    // shutdown the edit control when the mouse is clicked elsewhere on the
+    // listctrl because the order of events is different (or something like
+    // that), so explicitly end the edit if it is active.
+    if ( event.LeftDown() && m_textctrlWrapper )
+        m_textctrlWrapper->AcceptChangesAndFinish();
+#endif // __WXMAC__
+
+    if ( event.LeftDown() )
+        SetFocusIgnoringChildren();
+
+    event.SetEventObject( GetParent() );
+    if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
+        return;
+
+    if (event.GetEventType() == wxEVT_MOUSEWHEEL)
+    {
+        // let the base handle mouse wheel events.
+        event.Skip();
+        return;
+    }
+
+    if ( !HasCurrent() || IsEmpty() )
+    {
+        if (event.RightDown())
+        {
+            SendNotify( (size_t)-1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
+            // Allow generation of context menu event
+            event.Skip();
+        }
+        return;
+    }
+
+    if (m_dirty)
+        return;
+
+    if ( !(event.Dragging() || event.ButtonDown() || event.LeftUp() ||
+        event.ButtonDClick()) )
+        return;
+
+    int x = event.GetX();
+    int y = event.GetY();
+    CalcUnscrolledPosition( x, y, &x, &y );
+
+    // where did we hit it (if we did)?
+    long hitResult = 0;
+
+    size_t count = GetItemCount(),
+           current;
+
+    if ( InReportView() )
+    {
+        current = y / GetLineHeight();
+        if ( current < count )
+            hitResult = HitTestLine(current, x, y);
+    }
+    else // !report
+    {
+        // TODO: optimize it too! this is less simple than for report view but
+        //       enumerating all items is still not a way to do it!!
+        for ( current = 0; current < count; current++ )
+        {
+            hitResult = HitTestLine(current, x, y);
+            if ( hitResult )
+                break;
+        }
+    }
+
+    if (event.Dragging())
+    {
+        if (m_dragCount == 0)
+        {
+            // we have to report the raw, physical coords as we want to be
+            // able to call HitTest(event.m_pointDrag) from the user code to
+            // get the item being dragged
+            m_dragStart = event.GetPosition();
+        }
+
+        m_dragCount++;
+
+        if (m_dragCount != 3)
+            return;
+
+        int command = event.RightIsDown() ? wxEVT_COMMAND_LIST_BEGIN_RDRAG
+                                          : wxEVT_COMMAND_LIST_BEGIN_DRAG;
+
+        wxListEvent le( command, GetParent()->GetId() );
+        le.SetEventObject( GetParent() );
+        le.m_itemIndex = m_lineLastClicked;
+        le.m_pointDrag = m_dragStart;
+        GetParent()->GetEventHandler()->ProcessEvent( le );
+
+        return;
+    }
+    else
+    {
+        m_dragCount = 0;
+    }
+
+    if ( !hitResult )
+    {
+        // outside of any item
+        if (event.RightDown())
+        {
+            SendNotify( (size_t) -1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
+
+            wxContextMenuEvent evtCtx(
+                wxEVT_CONTEXT_MENU,
+                GetParent()->GetId(),
+                ClientToScreen(event.GetPosition()));
+            evtCtx.SetEventObject(GetParent());
+            GetParent()->GetEventHandler()->ProcessEvent(evtCtx);
+        }
+        else
+        {
+            // reset the selection and bail out
+            HighlightAll(false);
+        }
+
+        return;
+    }
+
+    bool forceClick = false;
+    if (event.ButtonDClick())
+    {
+        if ( m_renameTimer->IsRunning() )
+            m_renameTimer->Stop();
+
+        m_lastOnSame = false;
+
+        if ( current == m_lineLastClicked )
+        {
+            SendNotify( current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
+
+            return;
+        }
+        else
+        {
+            // The first click was on another item, so don't interpret this as
+            // a double click, but as a simple click instead
+            forceClick = true;
+        }
+    }
+
+    if (event.LeftUp())
+    {
+        if (m_lineSelectSingleOnUp != (size_t)-1)
+        {
+            // select single line
+            HighlightAll( false );
+            ReverseHighlight(m_lineSelectSingleOnUp);
+        }
+
+        if (m_lastOnSame)
+        {
+            if ((current == m_current) &&
+                (hitResult == wxLIST_HITTEST_ONITEMLABEL) &&
+                HasFlag(wxLC_EDIT_LABELS) )
+            {
+                if (InReportView())
+                {
+                    wxRect label = GetLineLabelRect( current );
+                    if (label.Contains( x, y ))
+                        m_renameTimer->Start( 250, true );
+
+                }
+                else
+                    m_renameTimer->Start( 250, true );
+            }
+        }
+
+        m_lastOnSame = false;
+        m_lineSelectSingleOnUp = (size_t)-1;
+    }
+    else
+    {
+        // This is necessary, because after a DnD operation in
+        // from and to ourself, the up event is swallowed by the
+        // DnD code. So on next non-up event (which means here and
+        // now) m_lineSelectSingleOnUp should be reset.
+        m_lineSelectSingleOnUp = (size_t)-1;
+    }
+    if (event.RightDown())
+    {
+        m_lineBeforeLastClicked = m_lineLastClicked;
+        m_lineLastClicked = current;
+
+        // If the item is already selected, do not update the selection.
+        // Multi-selections should not be cleared if a selected item is clicked.
+        if (!IsHighlighted(current))
+        {
+            HighlightAll(false);
+            ChangeCurrent(current);
+            ReverseHighlight(m_current);
+        }
+
+        SendNotify( current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
+
+        wxContextMenuEvent evtCtx(
+                wxEVT_CONTEXT_MENU,
+                GetParent()->GetId(),
+                ClientToScreen(event.GetPosition()));
+        evtCtx.SetEventObject(GetParent());
+        GetParent()->GetEventHandler()->ProcessEvent(evtCtx);
+    }
+    else if (event.MiddleDown())
+    {
+        SendNotify( current, wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK );
+    }
+    else if ( event.LeftDown() || forceClick )
+    {
+        m_lineBeforeLastClicked = m_lineLastClicked;
+        m_lineLastClicked = current;
+
+        size_t oldCurrent = m_current;
+        bool oldWasSelected = IsHighlighted(m_current);
+
+        bool cmdModifierDown = event.CmdDown();
+        if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
+        {
+            if ( IsSingleSel() || !IsHighlighted(current) )
+            {
+                HighlightAll( false );
+
+                ChangeCurrent(current);
+
+                ReverseHighlight(m_current);
+            }
+            else // multi sel & current is highlighted & no mod keys
+            {
+                m_lineSelectSingleOnUp = current;
+                ChangeCurrent(current); // change focus
+            }
+        }
+        else // multi sel & either ctrl or shift is down
+        {
+            if (cmdModifierDown)
+            {
+                ChangeCurrent(current);
+
+                ReverseHighlight(m_current);
+            }
+            else if (event.ShiftDown())
+            {
+                ChangeCurrent(current);
+
+                size_t lineFrom = oldCurrent,
+                       lineTo = current;
+
+                if ( lineTo < lineFrom )
+                {
+                    lineTo = lineFrom;
+                    lineFrom = m_current;
+                }
+
+                HighlightLines(lineFrom, lineTo);
+            }
+            else // !ctrl, !shift
+            {
+                // test in the enclosing if should make it impossible
+                wxFAIL_MSG( _T("how did we get here?") );
+            }
+        }
+
+        if (m_current != oldCurrent)
+            RefreshLine( oldCurrent );
+
+        // forceClick is only set if the previous click was on another item
+        m_lastOnSame = !forceClick && (m_current == oldCurrent) && oldWasSelected;
+    }
+}
+
+void wxListMainWindow::MoveToItem(size_t item)
+{
+    if ( item == (size_t)-1 )
+        return;
+
+    wxRect rect = GetLineRect(item);
+
+    int client_w, client_h;
+    GetClientSize( &client_w, &client_h );
+
+    const int hLine = GetLineHeight();
+
+    int view_x = SCROLL_UNIT_X * GetScrollPos( wxHORIZONTAL );
+    int view_y = hLine * GetScrollPos( wxVERTICAL );
+
+    if ( InReportView() )
+    {
+        // the next we need the range of lines shown it might be different,
+        // so recalculate it
+        ResetVisibleLinesRange();
+
+        if (rect.y < view_y)
+            Scroll( -1, rect.y / hLine );
+        if (rect.y + rect.height + 5 > view_y + client_h)
+            Scroll( -1, (rect.y + rect.height - client_h + hLine) / hLine );
+
+#ifdef __WXMAC__
+        // At least on Mac the visible lines value will get reset inside of
+        // Scroll *before* it actually scrolls the window because of the
+        // Update() that happens there, so it will still have the wrong value.
+        // So let's reset it again and wait for it to be recalculated in the
+        // next paint event.  I would expect this problem to show up in wxGTK
+        // too but couldn't duplicate it there.  Perhaps the order of events
+        // is different...  --Robin
+        ResetVisibleLinesRange();
+#endif
+    }
+    else // !report
+    {
+        int sx = -1,
+            sy = -1;
+
+        if (rect.x-view_x < 5)
+            sx = (rect.x - 5) / SCROLL_UNIT_X;
+        if (rect.x + rect.width - 5 > view_x + client_w)
+            sx = (rect.x + rect.width - client_w + SCROLL_UNIT_X) / SCROLL_UNIT_X;
+
+        if (rect.y-view_y < 5)
+            sy = (rect.y - 5) / hLine;
+        if (rect.y + rect.height - 5 > view_y + client_h)
+            sy = (rect.y + rect.height - client_h + hLine) / hLine;
+
+        Scroll(sx, sy);
+    }
+}
+
+bool wxListMainWindow::ScrollList(int WXUNUSED(dx), int dy)
+{
+    if ( !InReportView() )
+    {
+        // TODO: this should work in all views but is not implemented now
+        return false;
+    }
+
+    size_t top, bottom;
+    GetVisibleLinesRange(&top, &bottom);
+
+    if ( bottom == (size_t)-1 )
+        return 0;
+
+    ResetVisibleLinesRange();
+
+    int hLine = GetLineHeight();
+
+    Scroll(-1, top + dy / hLine);
+
+#ifdef __WXMAC__
+    // see comment in MoveToItem() for why we do this
+    ResetVisibleLinesRange();
+#endif
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+// keyboard handling
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event)
+{
+    wxCHECK_RET( newCurrent < (size_t)GetItemCount(),
+                 _T("invalid item index in OnArrowChar()") );
+
+    size_t oldCurrent = m_current;
+
+    // in single selection we just ignore Shift as we can't select several
+    // items anyhow
+    if ( event.ShiftDown() && !IsSingleSel() )
+    {
+        ChangeCurrent(newCurrent);
+
+        // refresh the old focus to remove it
+        RefreshLine( oldCurrent );
+
+        // select all the items between the old and the new one
+        if ( oldCurrent > newCurrent )
+        {
+            newCurrent = oldCurrent;
+            oldCurrent = m_current;
+        }
+
+        HighlightLines(oldCurrent, newCurrent);
+    }
+    else // !shift
+    {
+        // all previously selected items are unselected unless ctrl is held
+        // in a multiselection control
+        if ( !event.ControlDown() || IsSingleSel() )
+            HighlightAll(false);
+
+        ChangeCurrent(newCurrent);
+
+        // refresh the old focus to remove it
+        RefreshLine( oldCurrent );
+
+        // in single selection mode we must always have a selected item
+        if ( !event.ControlDown() || IsSingleSel() )
+            HighlightLine( m_current, true );
+    }
+
+    RefreshLine( m_current );
+
+    MoveToFocus();
+}
+
+void wxListMainWindow::OnKeyDown( wxKeyEvent &event )
+{
+    wxWindow *parent = GetParent();
+
+    // propagate the key event upwards
+    wxKeyEvent ke(event);
+    if (parent->GetEventHandler()->ProcessEvent( ke ))
+        return;
+
+    event.Skip();
+}
+
+void wxListMainWindow::OnKeyUp( wxKeyEvent &event )
+{
+    wxWindow *parent = GetParent();
+
+    // propagate the key event upwards
+    wxKeyEvent ke(event);
+    ke.SetEventObject( parent );
+    if (parent->GetEventHandler()->ProcessEvent( ke ))
+        return;
+
+    event.Skip();
+}
+
+void wxListMainWindow::OnChar( wxKeyEvent &event )
+{
+    wxWindow *parent = GetParent();
+
+    // send a list_key event up
+    if ( HasCurrent() )
+    {
+        wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, GetParent()->GetId() );
+        le.m_itemIndex = m_current;
+        GetLine(m_current)->GetItem( 0, le.m_item );
+        le.m_code = event.GetKeyCode();
+        le.SetEventObject( parent );
+        parent->GetEventHandler()->ProcessEvent( le );
+    }
+
+    // propagate the char event upwards
+    wxKeyEvent ke(event);
+    if (parent->GetEventHandler()->ProcessEvent( ke ))
+        return;
+
+    if (event.GetKeyCode() == WXK_TAB)
+    {
+        wxNavigationKeyEvent nevent;
+        nevent.SetWindowChange( event.ControlDown() );
+        nevent.SetDirection( !event.ShiftDown() );
+        nevent.SetEventObject( GetParent()->GetParent() );
+        nevent.SetCurrentFocus( m_parent );
+        if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
+            return;
+    }
+
+    // no item -> nothing to do
+    if (!HasCurrent())
+    {
+        event.Skip();
+        return;
+    }
+
+    // don't use m_linesPerPage directly as it might not be computed yet
+    const int pageSize = GetCountPerPage();
+    wxCHECK_RET( pageSize, _T("should have non zero page size") );
+
+    if (GetLayoutDirection() == wxLayout_RightToLeft)
+    {
+        if (event.GetKeyCode() == WXK_RIGHT)
+            event.m_keyCode = WXK_LEFT;
+        else if (event.GetKeyCode() == WXK_LEFT)
+            event.m_keyCode = WXK_RIGHT;
+    }
+
+    switch ( event.GetKeyCode() )
+    {
+        case WXK_UP:
+            if ( m_current > 0 )
+                OnArrowChar( m_current - 1, event );
+            break;
+
+        case WXK_DOWN:
+            if ( m_current < (size_t)GetItemCount() - 1 )
+                OnArrowChar( m_current + 1, event );
+            break;
+
+        case WXK_END:
+            if (!IsEmpty())
+                OnArrowChar( GetItemCount() - 1, event );
+            break;
+
+        case WXK_HOME:
+            if (!IsEmpty())
+                OnArrowChar( 0, event );
+            break;
+
+        case WXK_PAGEUP:
+            {
+                int steps = InReportView() ? pageSize - 1
+                                           : m_current % pageSize;
+
+                int index = m_current - steps;
+                if (index < 0)
+                    index = 0;
+
+                OnArrowChar( index, event );
+            }
+            break;
+
+        case WXK_PAGEDOWN:
+            {
+                int steps = InReportView()
+                                ? pageSize - 1
+                                : pageSize - (m_current % pageSize) - 1;
+
+                size_t index = m_current + steps;
+                size_t count = GetItemCount();
+                if ( index >= count )
+                    index = count - 1;
+
+                OnArrowChar( index, event );
+            }
+            break;
+
+        case WXK_LEFT:
+            if ( !InReportView() )
+            {
+                int index = m_current - pageSize;
+                if (index < 0)
+                    index = 0;
+
+                OnArrowChar( index, event );
+            }
+            break;
+
+        case WXK_RIGHT:
+            if ( !InReportView() )
+            {
+                size_t index = m_current + pageSize;
+
+                size_t count = GetItemCount();
+                if ( index >= count )
+                    index = count - 1;
+
+                OnArrowChar( index, event );
+            }
+            break;
+
+        case WXK_SPACE:
+            if ( IsSingleSel() )
+            {
+                if ( event.ControlDown() )
+                {
+                    ReverseHighlight(m_current);
+                }
+                else // normal space press
+                {
+                    SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
+                }
+            }
+            else // multiple selection
+            {
+                ReverseHighlight(m_current);
+            }
+            break;
+
+        case WXK_RETURN:
+        case WXK_EXECUTE:
+            SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
+            break;
+
+        default:
+            event.Skip();
+    }
+}
+
+// ----------------------------------------------------------------------------
+// focus handling
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
+{
+    if ( GetParent() )
+    {
+        wxFocusEvent event( wxEVT_SET_FOCUS, GetParent()->GetId() );
+        event.SetEventObject( GetParent() );
+        if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
+            return;
+    }
+
+    // wxGTK sends us EVT_SET_FOCUS events even if we had never got
+    // EVT_KILL_FOCUS before which means that we finish by redrawing the items
+    // which are already drawn correctly resulting in horrible flicker - avoid
+    // it
+    if ( !m_hasFocus )
+    {
+        m_hasFocus = true;
+
+        RefreshSelected();
+    }
+}
+
+void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
+{
+    if ( GetParent() )
+    {
+        wxFocusEvent event( wxEVT_KILL_FOCUS, GetParent()->GetId() );
+        event.SetEventObject( GetParent() );
+        if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
+            return;
+    }
+
+    m_hasFocus = false;
+    RefreshSelected();
+}
+
+void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y )
+{
+    if ( HasFlag(wxLC_ICON) && (m_normal_image_list))
+    {
+        m_normal_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
+    }
+    else if ( HasFlag(wxLC_SMALL_ICON) && (m_small_image_list))
+    {
+        m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
+    }
+    else if ( HasFlag(wxLC_LIST) && (m_small_image_list))
+    {
+        m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
+    }
+    else if ( InReportView() && (m_small_image_list))
+    {
+        m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
+    }
+}
+
+void wxListMainWindow::GetImageSize( int index, int &width, int &height ) const
+{
+    if ( HasFlag(wxLC_ICON) && m_normal_image_list )
+    {
+        m_normal_image_list->GetSize( index, width, height );
+    }
+    else if ( HasFlag(wxLC_SMALL_ICON) && m_small_image_list )
+    {
+        m_small_image_list->GetSize( index, width, height );
+    }
+    else if ( HasFlag(wxLC_LIST) && m_small_image_list )
+    {
+        m_small_image_list->GetSize( index, width, height );
+    }
+    else if ( InReportView() && m_small_image_list )
+    {
+        m_small_image_list->GetSize( index, width, height );
+    }
+    else
+    {
+        width =
+        height = 0;
+    }
+}
+
+int wxListMainWindow::GetTextLength( const wxString &s ) const
+{
+    wxClientDC dc( wxConstCast(this, wxListMainWindow) );
+    dc.SetFont( GetFont() );
+
+    wxCoord lw;
+    dc.GetTextExtent( s, &lw, NULL );
+
+    return lw + AUTOSIZE_COL_MARGIN;
+}
+
+void wxListMainWindow::SetImageList( wxImageList *imageList, int which )
+{
+    m_dirty = true;
+
+    // calc the spacing from the icon size
+    int width = 0, height = 0;
+
+    if ((imageList) && (imageList->GetImageCount()) )
+        imageList->GetSize(0, width, height);
+
+    if (which == wxIMAGE_LIST_NORMAL)
+    {
+        m_normal_image_list = imageList;
+        m_normal_spacing = width + 8;
+    }
+
+    if (which == wxIMAGE_LIST_SMALL)
+    {
+        m_small_image_list = imageList;
+        m_small_spacing = width + 14;
+        m_lineHeight = 0;  // ensure that the line height will be recalc'd
+    }
+}
+
+void wxListMainWindow::SetItemSpacing( int spacing, bool isSmall )
+{
+    m_dirty = true;
+    if (isSmall)
+        m_small_spacing = spacing;
+    else
+        m_normal_spacing = spacing;
+}
+
+int wxListMainWindow::GetItemSpacing( bool isSmall )
+{
+    return isSmall ? m_small_spacing : m_normal_spacing;
+}
+
+// ----------------------------------------------------------------------------
+// columns
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::SetColumn( int col, wxListItem &item )
+{
+    wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
+
+    wxCHECK_RET( node, _T("invalid column index in SetColumn") );
+
+    if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER )
+        item.m_width = GetTextLength( item.m_text );
+
+    wxListHeaderData *column = node->GetData();
+    column->SetItem( item );
+
+    wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
+    if ( headerWin )
+        headerWin->m_dirty = true;
+
+    m_dirty = true;
+
+    // invalidate it as it has to be recalculated
+    m_headerWidth = 0;
+}
+
+void wxListMainWindow::SetColumnWidth( int col, int width )
+{
+    wxCHECK_RET( col >= 0 && col < GetColumnCount(),
+                 _T("invalid column index") );
+
+    wxCHECK_RET( InReportView(),
+                 _T("SetColumnWidth() can only be called in report mode.") );
+
+    m_dirty = true;
+    wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
+    if ( headerWin )
+        headerWin->m_dirty = true;
+
+    wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
+    wxCHECK_RET( node, _T("no column?") );
+
+    wxListHeaderData *column = node->GetData();
+
+    size_t count = GetItemCount();
+
+    if (width == wxLIST_AUTOSIZE_USEHEADER)
+    {
+        width = GetTextLength(column->GetText());
+        width += 2*EXTRA_WIDTH;
+
+        // check for column header's image availability
+        const int image = column->GetImage();
+        if ( image != -1 )
+        {
+            if ( m_small_image_list )
+            {
+                int ix = 0, iy = 0;
+                m_small_image_list->GetSize(image, ix, iy);
+                width += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
+            }
+        }
+    }
+    else if ( width == wxLIST_AUTOSIZE )
+    {
+        if ( IsVirtual() )
+        {
+            // TODO: determine the max width somehow...
+            width = WIDTH_COL_DEFAULT;
+        }
+        else // !virtual
+        {
+            wxClientDC dc(this);
+            dc.SetFont( GetFont() );
+
+            int max = AUTOSIZE_COL_MARGIN;
+
+            //  if the cached column width isn't valid then recalculate it
+            if (m_aColWidths.Item(col)->bNeedsUpdate)
+            {
+                for (size_t i = 0; i < count; i++)
+                {
+                    wxListLineData *line = GetLine( i );
+                    wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
+
+                    wxCHECK_RET( n, _T("no subitem?") );
+
+                    wxListItemData *itemData = n->GetData();
+                    wxListItem      item;
+
+                    itemData->GetItem(item);
+                    int itemWidth = GetItemWidthWithImage(&item);
+                    if (itemWidth > max)
+                        max = itemWidth;
+                }
+
+                m_aColWidths.Item(col)->bNeedsUpdate = false;
+                m_aColWidths.Item(col)->nMaxWidth = max;
+            }
+
+            max = m_aColWidths.Item(col)->nMaxWidth;
+            width = max + AUTOSIZE_COL_MARGIN;
+        }
+    }
+
+    column->SetWidth( width );
+
+    // invalidate it as it has to be recalculated
+    m_headerWidth = 0;
+}
+
+int wxListMainWindow::GetHeaderWidth() const
+{
+    if ( !m_headerWidth )
+    {
+        wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
+
+        size_t count = GetColumnCount();
+        for ( size_t col = 0; col < count; col++ )
+        {
+            self->m_headerWidth += GetColumnWidth(col);
+        }
+    }
+
+    return m_headerWidth;
+}
+
+void wxListMainWindow::GetColumn( int col, wxListItem &item ) const
+{
+    wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
+    wxCHECK_RET( node, _T("invalid column index in GetColumn") );
+
+    wxListHeaderData *column = node->GetData();
+    column->GetItem( item );
+}
+
+int wxListMainWindow::GetColumnWidth( int col ) const
+{
+    wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
+    wxCHECK_MSG( node, 0, _T("invalid column index") );
+
+    wxListHeaderData *column = node->GetData();
+    return column->GetWidth();
+}
+
+// ----------------------------------------------------------------------------
+// item state
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::SetItem( wxListItem &item )
+{
+    long id = item.m_itemId;
+    wxCHECK_RET( id >= 0 && (size_t)id < GetItemCount(),
+                 _T("invalid item index in SetItem") );
+
+    if ( !IsVirtual() )
+    {
+        wxListLineData *line = GetLine((size_t)id);
+        line->SetItem( item.m_col, item );
+
+        // Set item state if user wants
+        if ( item.m_mask & wxLIST_MASK_STATE )
+            SetItemState( item.m_itemId, item.m_state, item.m_state );
+
+        if (InReportView())
+        {
+            //  update the Max Width Cache if needed
+            int width = GetItemWidthWithImage(&item);
+
+            if (width > m_aColWidths.Item(item.m_col)->nMaxWidth)
+                m_aColWidths.Item(item.m_col)->nMaxWidth = width;
+        }
+    }
+
+    // update the item on screen
+    wxRect rectItem;
+    GetItemRect(id, rectItem);
+    RefreshRect(rectItem);
+}
+
+void wxListMainWindow::SetItemStateAll(long state, long stateMask)
+{
+    if ( IsEmpty() )
+        return;
+
+    // first deal with selection
+    if ( stateMask & wxLIST_STATE_SELECTED )
+    {
+        // set/clear select state
+        if ( IsVirtual() )
+        {
+            // optimized version for virtual listctrl.
+            m_selStore.SelectRange(0, GetItemCount() - 1, state == wxLIST_STATE_SELECTED);
+            Refresh();
+        }
+        else if ( state & wxLIST_STATE_SELECTED )
+        {
+            const long count = GetItemCount();
+            for( long i = 0; i <  count; i++ )
+            {
+                SetItemState( i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
+            }
+
+        }
+        else
+        {
+            // clear for non virtual (somewhat optimized by using GetNextItem())
+            long i = -1;
+            while ( (i = GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1 )
+            {
+                SetItemState( i, 0, wxLIST_STATE_SELECTED );
+            }
+        }
+    }
+
+    if ( HasCurrent() && (state == 0) && (stateMask & wxLIST_STATE_FOCUSED) )
+    {
+        // unfocus all: only one item can be focussed, so clearing focus for
+        // all items is simply clearing focus of the focussed item.
+        SetItemState(m_current, state, stateMask);
+    }
+    //(setting focus to all items makes no sense, so it is not handled here.)
+}
+
+void wxListMainWindow::SetItemState( long litem, long state, long stateMask )
+{
+    if ( litem == -1 )
+    {
+        SetItemStateAll(state, stateMask);
+        return;
+    }
+
+    wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(),
+                 _T("invalid list ctrl item index in SetItem") );
+
+    size_t oldCurrent = m_current;
+    size_t item = (size_t)litem;    // safe because of the check above
+
+    // do we need to change the focus?
+    if ( stateMask & wxLIST_STATE_FOCUSED )
+    {
+        if ( state & wxLIST_STATE_FOCUSED )
+        {
+            // don't do anything if this item is already focused
+            if ( item != m_current )
+            {
+                ChangeCurrent(item);
+
+                if ( oldCurrent != (size_t)-1 )
+                {
+                    if ( IsSingleSel() )
+                    {
+                        HighlightLine(oldCurrent, false);
+                    }
+
+                    RefreshLine(oldCurrent);
+                }
+
+                RefreshLine( m_current );
+            }
+        }
+        else // unfocus
+        {
+            // don't do anything if this item is not focused
+            if ( item == m_current )
+            {
+                ResetCurrent();
+
+                if ( IsSingleSel() )
+                {
+                    // we must unselect the old current item as well or we
+                    // might end up with more than one selected item in a
+                    // single selection control
+                    HighlightLine(oldCurrent, false);
+                }
+
+                RefreshLine( oldCurrent );
+            }
+        }
+    }
+
+    // do we need to change the selection state?
+    if ( stateMask & wxLIST_STATE_SELECTED )
+    {
+        bool on = (state & wxLIST_STATE_SELECTED) != 0;
+
+        if ( IsSingleSel() )
+        {
+            if ( on )
+            {
+                // selecting the item also makes it the focused one in the
+                // single sel mode
+                if ( m_current != item )
+                {
+                    ChangeCurrent(item);
+
+                    if ( oldCurrent != (size_t)-1 )
+                    {
+                        HighlightLine( oldCurrent, false );
+                        RefreshLine( oldCurrent );
+                    }
+                }
+            }
+            else // off
+            {
+                // only the current item may be selected anyhow
+                if ( item != m_current )
+                    return;
+            }
+        }
+
+        if ( HighlightLine(item, on) )
+        {
+            RefreshLine(item);
+        }
+    }
+}
+
+int wxListMainWindow::GetItemState( long item, long stateMask ) const
+{
+    wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), 0,
+                 _T("invalid list ctrl item index in GetItemState()") );
+
+    int ret = wxLIST_STATE_DONTCARE;
+
+    if ( stateMask & wxLIST_STATE_FOCUSED )
+    {
+        if ( (size_t)item == m_current )
+            ret |= wxLIST_STATE_FOCUSED;
+    }
+
+    if ( stateMask & wxLIST_STATE_SELECTED )
+    {
+        if ( IsHighlighted(item) )
+            ret |= wxLIST_STATE_SELECTED;
+    }
+
+    return ret;
+}
+
+void wxListMainWindow::GetItem( wxListItem &item ) const
+{
+    wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId < GetItemCount(),
+                 _T("invalid item index in GetItem") );
+
+    wxListLineData *line = GetLine((size_t)item.m_itemId);
+    line->GetItem( item.m_col, item );
+
+    // Get item state if user wants it
+    if ( item.m_mask & wxLIST_MASK_STATE )
+        item.m_state = GetItemState( item.m_itemId, wxLIST_STATE_SELECTED |
+                                                 wxLIST_STATE_FOCUSED );
+}
+
+// ----------------------------------------------------------------------------
+// item count
+// ----------------------------------------------------------------------------
+
+size_t wxListMainWindow::GetItemCount() const
+{
+    return IsVirtual() ? m_countVirt : m_lines.GetCount();
+}
+
+void wxListMainWindow::SetItemCount(long count)
+{
+    m_selStore.SetItemCount(count);
+    m_countVirt = count;
+
+    ResetVisibleLinesRange();
+
+    // scrollbars must be reset
+    m_dirty = true;
+}
+
+int wxListMainWindow::GetSelectedItemCount() const
+{
+    // deal with the quick case first
+    if ( IsSingleSel() )
+        return HasCurrent() ? IsHighlighted(m_current) : false;
+
+    // virtual controls remmebers all its selections itself
+    if ( IsVirtual() )
+        return m_selStore.GetSelectedCount();
+
+    // TODO: we probably should maintain the number of items selected even for
+    //       non virtual controls as enumerating all lines is really slow...
+    size_t countSel = 0;
+    size_t count = GetItemCount();
+    for ( size_t line = 0; line < count; line++ )
+    {
+        if ( GetLine(line)->IsHighlighted() )
+            countSel++;
+    }
+
+    return countSel;
+}
+
+// ----------------------------------------------------------------------------
+// item position/size
+// ----------------------------------------------------------------------------
+
+wxRect wxListMainWindow::GetViewRect() const
+{
+    wxASSERT_MSG( !HasFlag(wxLC_REPORT | wxLC_LIST),
+                    _T("wxListCtrl::GetViewRect() only works in icon mode") );
+
+    // we need to find the longest/tallest label
+    wxCoord xMax = 0, yMax = 0;
+    const int count = GetItemCount();
+    if ( count )
+    {
+        for ( int i = 0; i < count; i++ )
+        {
+            // we need logical, not physical, coordinates here, so use
+            // GetLineRect() instead of GetItemRect()
+            wxRect r = GetLineRect(i);
+
+            wxCoord x = r.GetRight(),
+                    y = r.GetBottom();
+
+            if ( x > xMax )
+                xMax = x;
+            if ( y > yMax )
+                yMax = y;
+        }
+    }
+
+    // some fudge needed to make it look prettier
+    xMax += 2 * EXTRA_BORDER_X;
+    yMax += 2 * EXTRA_BORDER_Y;
+
+    // account for the scrollbars if necessary
+    const wxSize sizeAll = GetClientSize();
+    if ( xMax > sizeAll.x )
+        yMax += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
+    if ( yMax > sizeAll.y )
+        xMax += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
+
+    return wxRect(0, 0, xMax, yMax);
+}
+
+void wxListMainWindow::GetItemRect( long index, wxRect &rect ) const
+{
+    wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
+                 _T("invalid index in GetItemRect") );
+
+    // ensure that we're laid out, otherwise we could return nonsense
+    if ( m_dirty )
+    {
+        wxConstCast(this, wxListMainWindow)->
+            RecalculatePositions(true /* no refresh */);
+    }
+
+    rect = GetLineRect((size_t)index);
+
+    CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
+}
+
+bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) const
+{
+    wxRect rect;
+    GetItemRect(item, rect);
+
+    pos.x = rect.x;
+    pos.y = rect.y;
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+// geometry calculation
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::RecalculatePositions(bool noRefresh)
+{
+    const int lineHeight = GetLineHeight();
+
+    wxClientDC dc( this );
+    dc.SetFont( GetFont() );
+
+    const size_t count = GetItemCount();
+
+    int iconSpacing;
+    if ( HasFlag(wxLC_ICON) && m_normal_image_list )
+        iconSpacing = m_normal_spacing;
+    else if ( HasFlag(wxLC_SMALL_ICON) && m_small_image_list )
+        iconSpacing = m_small_spacing;
+    else
+        iconSpacing = 0;
+
+    // Note that we do not call GetClientSize() here but
+    // GetSize() and subtract the border size for sunken
+    // borders manually. This is technically incorrect,
+    // but we need to know the client area's size WITHOUT
+    // scrollbars here. Since we don't know if there are
+    // any scrollbars, we use GetSize() instead. Another
+    // solution would be to call SetScrollbars() here to
+    // remove the scrollbars and call GetClientSize() then,
+    // but this might result in flicker and - worse - will
+    // reset the scrollbars to 0 which is not good at all
+    // if you resize a dialog/window, but don't want to
+    // reset the window scrolling. RR.
+    // Furthermore, we actually do NOT subtract the border
+    // width as 2 pixels is just the extra space which we
+    // need around the actual content in the window. Other-
+    // wise the text would e.g. touch the upper border. RR.
+    int clientWidth,
+        clientHeight;
+    GetSize( &clientWidth, &clientHeight );
+
+    if ( InReportView() )
+    {
+        // all lines have the same height and we scroll one line per step
+        int entireHeight = count * lineHeight + LINE_SPACING;
+
+        m_linesPerPage = clientHeight / lineHeight;
+
+        ResetVisibleLinesRange();
+
+        SetScrollbars( SCROLL_UNIT_X, lineHeight,
+                       GetHeaderWidth() / SCROLL_UNIT_X,
+                       (entireHeight + lineHeight - 1) / lineHeight,
+                       GetScrollPos(wxHORIZONTAL),
+                       GetScrollPos(wxVERTICAL),
+                       true );
+    }
+    else // !report
+    {
+        // we have 3 different layout strategies: either layout all items
+        // horizontally/vertically (wxLC_ALIGN_XXX styles explicitly given) or
+        // to arrange them in top to bottom, left to right (don't ask me why
+        // not the other way round...) order
+        if ( HasFlag(wxLC_ALIGN_LEFT | wxLC_ALIGN_TOP) )
+        {
+            int x = EXTRA_BORDER_X;
+            int y = EXTRA_BORDER_Y;
+
+            wxCoord widthMax = 0;
+
+            size_t i;
+            for ( i = 0; i < count; i++ )
+            {
+                wxListLineData *line = GetLine(i);
+                line->CalculateSize( &dc, iconSpacing );
+                line->SetPosition( x, y, iconSpacing );
+
+                wxSize sizeLine = GetLineSize(i);
+
+                if ( HasFlag(wxLC_ALIGN_TOP) )
+                {
+                    if ( sizeLine.x > widthMax )
+                        widthMax = sizeLine.x;
+
+                    y += sizeLine.y;
+                }
+                else // wxLC_ALIGN_LEFT
+                {
+                    x += sizeLine.x + MARGIN_BETWEEN_ROWS;
+                }
+            }
+
+            if ( HasFlag(wxLC_ALIGN_TOP) )
+            {
+                // traverse the items again and tweak their sizes so that they are
+                // all the same in a row
+                for ( i = 0; i < count; i++ )
+                {
+                    wxListLineData *line = GetLine(i);
+                    line->m_gi->ExtendWidth(widthMax);
+                }
+            }
+
+            SetScrollbars
+            (
+                SCROLL_UNIT_X,
+                lineHeight,
+                (x + SCROLL_UNIT_X) / SCROLL_UNIT_X,
+                (y + lineHeight) / lineHeight,
+                GetScrollPos( wxHORIZONTAL ),
+                GetScrollPos( wxVERTICAL ),
+                true
+            );
+        }
+        else // "flowed" arrangement, the most complicated case
+        {
+            // at first we try without any scrollbars, if the items don't fit into
+            // the window, we recalculate after subtracting the space taken by the
+            // scrollbar
+
+            int entireWidth = 0;
+
+            for (int tries = 0; tries < 2; tries++)
+            {
+                entireWidth = 2 * EXTRA_BORDER_X;
+
+                if (tries == 1)
+                {
+                    // Now we have decided that the items do not fit into the
+                    // client area, so we need a scrollbar
+                    entireWidth += SCROLL_UNIT_X;
+                }
+
+                int x = EXTRA_BORDER_X;
+                int y = EXTRA_BORDER_Y;
+                int maxWidthInThisRow = 0;
+
+                m_linesPerPage = 0;
+                int currentlyVisibleLines = 0;
+
+                for (size_t i = 0; i < count; i++)
+                {
+                    currentlyVisibleLines++;
+                    wxListLineData *line = GetLine( i );
+                    line->CalculateSize( &dc, iconSpacing );
+                    line->SetPosition( x, y, iconSpacing );
+
+                    wxSize sizeLine = GetLineSize( i );
+
+                    if ( maxWidthInThisRow < sizeLine.x )
+                        maxWidthInThisRow = sizeLine.x;
+
+                    y += sizeLine.y;
+                    if (currentlyVisibleLines > m_linesPerPage)
+                        m_linesPerPage = currentlyVisibleLines;
+
+                    if ( y + sizeLine.y >= clientHeight )
+                    {
+                        currentlyVisibleLines = 0;
+                        y = EXTRA_BORDER_Y;
+                        maxWidthInThisRow += MARGIN_BETWEEN_ROWS;
+                        x += maxWidthInThisRow;
+                        entireWidth += maxWidthInThisRow;
+                        maxWidthInThisRow = 0;
+                    }
+
+                    // We have reached the last item.
+                    if ( i == count - 1 )
+                        entireWidth += maxWidthInThisRow;
+
+                    if ( (tries == 0) &&
+                            (entireWidth + SCROLL_UNIT_X > clientWidth) )
+                    {
+                        clientHeight -= wxSystemSettings::
+                                            GetMetric(wxSYS_HSCROLL_Y);
+                        m_linesPerPage = 0;
+                        break;
+                    }
+
+                    if ( i == count - 1 )
+                        tries = 1;  // Everything fits, no second try required.
+                }
+            }
+
+            SetScrollbars
+            (
+                SCROLL_UNIT_X,
+                lineHeight,
+                (entireWidth + SCROLL_UNIT_X) / SCROLL_UNIT_X,
+                0,
+                GetScrollPos( wxHORIZONTAL ),
+                0,
+                true
+            );
+        }
+    }
+
+    if ( !noRefresh )
+    {
+        // FIXME: why should we call it from here?
+        UpdateCurrent();
+
+        RefreshAll();
+    }
+}
+
+void wxListMainWindow::RefreshAll()
+{
+    m_dirty = false;
+    Refresh();
+
+    wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
+    if ( headerWin && headerWin->m_dirty )
+    {
+        headerWin->m_dirty = false;
+        headerWin->Refresh();
+    }
+}
+
+void wxListMainWindow::UpdateCurrent()
+{
+    if ( !HasCurrent() && !IsEmpty() )
+        ChangeCurrent(0);
+}
+
+long wxListMainWindow::GetNextItem( long item,
+                                    int WXUNUSED(geometry),
+                                    int state ) const
+{
+    long ret = item,
+         max = GetItemCount();
+    wxCHECK_MSG( (ret == -1) || (ret < max), -1,
+                 _T("invalid listctrl index in GetNextItem()") );
+
+    // notice that we start with the next item (or the first one if item == -1)
+    // and this is intentional to allow writing a simple loop to iterate over
+    // all selected items
+    ret++;
+    if ( ret == max )
+        // this is not an error because the index was OK initially,
+        // just no such item
+        return -1;
+
+    if ( !state )
+        // any will do
+        return (size_t)ret;
+
+    size_t count = GetItemCount();
+    for ( size_t line = (size_t)ret; line < count; line++ )
+    {
+        if ( (state & wxLIST_STATE_FOCUSED) && (line == m_current) )
+            return line;
+
+        if ( (state & wxLIST_STATE_SELECTED) && IsHighlighted(line) )
+            return line;
+    }
+
+    return -1;
+}
+
+// ----------------------------------------------------------------------------
+// deleting stuff
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::DeleteItem( long lindex )
+{
+    size_t count = GetItemCount();
+
+    wxCHECK_RET( (lindex >= 0) && ((size_t)lindex < count),
+                 _T("invalid item index in DeleteItem") );
+
+    size_t index = (size_t)lindex;
+
+    // we don't need to adjust the index for the previous items
+    if ( HasCurrent() && m_current >= index )
+    {
+        // if the current item is being deleted, we want the next one to
+        // become selected - unless there is no next one - so don't adjust
+        // m_current in this case
+        if ( m_current != index || m_current == count - 1 )
+            m_current--;
+    }
+
+    if ( InReportView() )
+    {
+        //  mark the Column Max Width cache as dirty if the items in the line
+        //  we're deleting contain the Max Column Width
+        wxListLineData * const line = GetLine(index);
+        wxListItemDataList::compatibility_iterator n;
+        wxListItemData *itemData;
+        wxListItem      item;
+        int             itemWidth;
+
+        for (size_t i = 0; i < m_columns.GetCount(); i++)
+        {
+            n = line->m_items.Item( i );
+            itemData = n->GetData();
+            itemData->GetItem(item);
+
+            itemWidth = GetItemWidthWithImage(&item);
+
+            if (itemWidth >= m_aColWidths.Item(i)->nMaxWidth)
+                m_aColWidths.Item(i)->bNeedsUpdate = true;
+        }
+
+        ResetVisibleLinesRange();
+    }
+
+    SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM, wxDefaultPosition );
+
+    if ( IsVirtual() )
+    {
+        m_countVirt--;
+        m_selStore.OnItemDelete(index);
+    }
+    else
+    {
+        m_lines.RemoveAt( index );
+    }
+
+    // we need to refresh the (vert) scrollbar as the number of items changed
+    m_dirty = true;
+
+    RefreshAfter(index);
+}
+
+void wxListMainWindow::DeleteColumn( int col )
+{
+    wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
+
+    wxCHECK_RET( node, wxT("invalid column index in DeleteColumn()") );
+
+    m_dirty = true;
+    delete node->GetData();
+    m_columns.Erase( node );
+
+    if ( !IsVirtual() )
+    {
+        // update all the items
+        for ( size_t i = 0; i < m_lines.GetCount(); i++ )
+        {
+            wxListLineData * const line = GetLine(i);
+            wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
+            delete n->GetData();
+            line->m_items.Erase(n);
+        }
+    }
+
+    if ( InReportView() )   //  we only cache max widths when in Report View
+    {
+        delete m_aColWidths.Item(col);
+        m_aColWidths.RemoveAt(col);
+    }
+
+    // invalidate it as it has to be recalculated
+    m_headerWidth = 0;
+}
+
+void wxListMainWindow::DoDeleteAllItems()
+{
+    if ( IsEmpty() )
+        // nothing to do - in particular, don't send the event
+        return;
+
+    ResetCurrent();
+
+    // to make the deletion of all items faster, we don't send the
+    // notifications for each item deletion in this case but only one event
+    // for all of them: this is compatible with wxMSW and documented in
+    // DeleteAllItems() description
+
+    wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() );
+    event.SetEventObject( GetParent() );
+    GetParent()->GetEventHandler()->ProcessEvent( event );
+
+    if ( IsVirtual() )
+    {
+        m_countVirt = 0;
+        m_selStore.Clear();
+    }
+
+    if ( InReportView() )
+    {
+        ResetVisibleLinesRange();
+        for (size_t i = 0; i < m_aColWidths.GetCount(); i++)
+        {
+            m_aColWidths.Item(i)->bNeedsUpdate = true;
+        }
+    }
+
+    m_lines.Clear();
+}
+
+void wxListMainWindow::DeleteAllItems()
+{
+    DoDeleteAllItems();
+
+    RecalculatePositions();
+}
+
+void wxListMainWindow::DeleteEverything()
+{
+    WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
+    WX_CLEAR_ARRAY(m_aColWidths);
+
+    DeleteAllItems();
+}
+
+// ----------------------------------------------------------------------------
+// scanning for an item
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::EnsureVisible( long index )
+{
+    wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
+                 _T("invalid index in EnsureVisible") );
+
+    // We have to call this here because the label in question might just have
+    // been added and its position is not known yet
+    if ( m_dirty )
+        RecalculatePositions(true /* no refresh */);
+
+    MoveToItem((size_t)index);
+}
+
+long wxListMainWindow::FindItem(long start, const wxString& str, bool partial )
+{
+    if (str.empty())
+        return wxNOT_FOUND;
+
+    long pos = start;
+    wxString str_upper = str.Upper();
+    if (pos < 0)
+        pos = 0;
+
+    size_t count = GetItemCount();
+    for ( size_t i = (size_t)pos; i < count; i++ )
+    {
+        wxListLineData *line = GetLine(i);
+        wxString line_upper = line->GetText(0).Upper();
+        if (!partial)
+        {
+            if (line_upper == str_upper )
+                return i;
+        }
+        else
+        {
+            if (line_upper.find(str_upper) == 0)
+                return i;
+        }
+    }
+
+    return wxNOT_FOUND;
+}
+
+long wxListMainWindow::FindItem(long start, wxUIntPtr data)
+{
+    long pos = start;
+    if (pos < 0)
+        pos = 0;
+
+    size_t count = GetItemCount();
+    for (size_t i = (size_t)pos; i < count; i++)
+    {
+        wxListLineData *line = GetLine(i);
+        wxListItem item;
+        line->GetItem( 0, item );
+        if (item.m_data == data)
+            return i;
+    }
+
+    return wxNOT_FOUND;
+}
+
+long wxListMainWindow::FindItem( const wxPoint& pt )
+{
+    size_t topItem;
+    GetVisibleLinesRange( &topItem, NULL );
+
+    wxPoint p;
+    GetItemPosition( GetItemCount() - 1, p );
+    if ( p.y == 0 )
+        return topItem;
+
+    long id = (long)floor( pt.y * double(GetItemCount() - topItem - 1) / p.y + topItem );
+    if ( id >= 0 && id < (long)GetItemCount() )
+        return id;
+
+    return wxNOT_FOUND;
+}
+
+long wxListMainWindow::HitTest( int x, int y, int &flags ) const
+{
+    CalcUnscrolledPosition( x, y, &x, &y );
+
+    size_t count = GetItemCount();
+
+    if ( InReportView() )
+    {
+        size_t current = y / GetLineHeight();
+        if ( current < count )
+        {
+            flags = HitTestLine(current, x, y);
+            if ( flags )
+                return current;
+        }
+    }
+    else // !report
+    {
+        // TODO: optimize it too! this is less simple than for report view but
+        //       enumerating all items is still not a way to do it!!
+        for ( size_t current = 0; current < count; current++ )
+        {
+            flags = HitTestLine(current, x, y);
+            if ( flags )
+                return current;
+        }
+    }
+
+    return wxNOT_FOUND;
+}
+
+// ----------------------------------------------------------------------------
+// adding stuff
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::InsertItem( wxListItem &item )
+{
+    wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual control") );
+
+    int count = GetItemCount();
+    wxCHECK_RET( item.m_itemId >= 0, _T("invalid item index") );
+
+    if (item.m_itemId > count)
+        item.m_itemId = count;
+
+    size_t id = item.m_itemId;
+
+    m_dirty = true;
+
+    if ( InReportView() )
+    {
+        ResetVisibleLinesRange();
+
+        // calculate the width of the item and adjust the max column width
+        wxColWidthInfo *pWidthInfo = m_aColWidths.Item(item.GetColumn());
+        int width = GetItemWidthWithImage(&item);
+        item.SetWidth(width);
+        if (width > pWidthInfo->nMaxWidth)
+            pWidthInfo->nMaxWidth = width;
+    }
+
+    wxListLineData *line = new wxListLineData(this);
+
+    line->SetItem( item.m_col, item );
+
+    m_lines.Insert( line, id );
+
+    m_dirty = true;
+
+    // If an item is selected at or below the point of insertion, we need to
+    // increment the member variables because the current row's index has gone
+    // up by one
+    if ( HasCurrent() && m_current >= id )
+        m_current++;
+
+    SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM);
+
+    RefreshLines(id, GetItemCount() - 1);
+}
+
+void wxListMainWindow::InsertColumn( long col, wxListItem &item )
+{
+    m_dirty = true;
+    if ( InReportView() )
+    {
+        if (item.m_width == wxLIST_AUTOSIZE_USEHEADER)
+            item.m_width = GetTextLength( item.m_text );
+
+        wxListHeaderData *column = new wxListHeaderData( item );
+        wxColWidthInfo *colWidthInfo = new wxColWidthInfo();
+
+        bool insert = (col >= 0) && ((size_t)col < m_columns.GetCount());
+        if ( insert )
+        {
+            wxListHeaderDataList::compatibility_iterator
+                node = m_columns.Item( col );
+            m_columns.Insert( node, column );
+            m_aColWidths.Insert( colWidthInfo, col );
+        }
+        else
+        {
+            m_columns.Append( column );
+            m_aColWidths.Add( colWidthInfo );
+        }
+
+        if ( !IsVirtual() )
+        {
+            // update all the items
+            for ( size_t i = 0; i < m_lines.GetCount(); i++ )
+            {
+                wxListLineData * const line = GetLine(i);
+                wxListItemData * const data = new wxListItemData(this);
+                if ( insert )
+                    line->m_items.Insert(col, data);
+                else
+                    line->m_items.Append(data);
+            }
+        }
+
+        // invalidate it as it has to be recalculated
+        m_headerWidth = 0;
+    }
+}
+
+int wxListMainWindow::GetItemWidthWithImage(wxListItem * item)
+{
+    int width = 0;
+    wxClientDC dc(this);
+
+    dc.SetFont( GetFont() );
+
+    if (item->GetImage() != -1)
+    {
+        int ix, iy;
+        GetImageSize( item->GetImage(), ix, iy );
+        width += ix + 5;
+    }
+
+    if (!item->GetText().empty())
+    {
+        wxCoord w;
+        dc.GetTextExtent( item->GetText(), &w, NULL );
+        width += w;
+    }
+
+    return width;
+}
+
+// ----------------------------------------------------------------------------
+// sorting
+// ----------------------------------------------------------------------------
+
+wxListCtrlCompare list_ctrl_compare_func_2;
+long              list_ctrl_compare_data;
+
+int LINKAGEMODE list_ctrl_compare_func_1( wxListLineData **arg1, wxListLineData **arg2 )
+{
+    wxListLineData *line1 = *arg1;
+    wxListLineData *line2 = *arg2;
+    wxListItem item;
+    line1->GetItem( 0, item );
+    wxUIntPtr data1 = item.m_data;
+    line2->GetItem( 0, item );
+    wxUIntPtr data2 = item.m_data;
+    return list_ctrl_compare_func_2( data1, data2, list_ctrl_compare_data );
+}
+
+void wxListMainWindow::SortItems( wxListCtrlCompare fn, long data )
+{
+    // selections won't make sense any more after sorting the items so reset
+    // them
+    HighlightAll(false);
+    ResetCurrent();
+
+    list_ctrl_compare_func_2 = fn;
+    list_ctrl_compare_data = data;
+    m_lines.Sort( list_ctrl_compare_func_1 );
+    m_dirty = true;
+}
+
+// ----------------------------------------------------------------------------
+// scrolling
+// ----------------------------------------------------------------------------
+
+void wxListMainWindow::OnScroll(wxScrollWinEvent& event)
+{
+    // FIXME
+#if ( defined(__WXGTK__) || defined(__WXMAC__) ) && !defined(__WXUNIVERSAL__)
+    wxScrolledWindow::OnScroll(event);
+#else
+    HandleOnScroll( event );
+#endif
+
+    // update our idea of which lines are shown when we redraw the window the
+    // next time
+    ResetVisibleLinesRange();
+
+    if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
+    {
+        wxGenericListCtrl* lc = GetListCtrl();
+        wxCHECK_RET( lc, _T("no listctrl window?") );
+
+        lc->m_headerWin->Refresh();
+        lc->m_headerWin->Update();
+    }
+}
+
+int wxListMainWindow::GetCountPerPage() const
+{
+    if ( !m_linesPerPage )
+    {
+        wxConstCast(this, wxListMainWindow)->
+            m_linesPerPage = GetClientSize().y / GetLineHeight();
+    }
+
+    return m_linesPerPage;
+}
+
+void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
+{
+    wxASSERT_MSG( InReportView(), _T("this is for report mode only") );
+
+    if ( m_lineFrom == (size_t)-1 )
+    {
+        size_t count = GetItemCount();
+        if ( count )
+        {
+            m_lineFrom = GetScrollPos(wxVERTICAL);
+
+            // this may happen if SetScrollbars() hadn't been called yet
+            if ( m_lineFrom >= count )
+                m_lineFrom = count - 1;
+
+            // we redraw one extra line but this is needed to make the redrawing
+            // logic work when there is a fractional number of lines on screen
+            m_lineTo = m_lineFrom + m_linesPerPage;
+            if ( m_lineTo >= count )
+                m_lineTo = count - 1;
+        }
+        else // empty control
+        {
+            m_lineFrom = 0;
+            m_lineTo = (size_t)-1;
+        }
+    }
+
+    wxASSERT_MSG( IsEmpty() ||
+                  (m_lineFrom <= m_lineTo && m_lineTo < GetItemCount()),
+                  _T("GetVisibleLinesRange() returns incorrect result") );
+
+    if ( from )
+        *from = m_lineFrom;
+    if ( to )
+        *to = m_lineTo;
+}
+
+// -------------------------------------------------------------------------------------
+// wxGenericListCtrl
+// -------------------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl)
+
+BEGIN_EVENT_TABLE(wxGenericListCtrl,wxControl)
+  EVT_SIZE(wxGenericListCtrl::OnSize)
+END_EVENT_TABLE()
+
+wxGenericListCtrl::wxGenericListCtrl()
+{
+    m_imageListNormal = (wxImageList *) NULL;
+    m_imageListSmall = (wxImageList *) NULL;
+    m_imageListState = (wxImageList *) NULL;
+
+    m_ownsImageListNormal =
+    m_ownsImageListSmall =
+    m_ownsImageListState = false;
+
+    m_mainWin = (wxListMainWindow*) NULL;
+    m_headerWin = (wxListHeaderWindow*) NULL;
+    m_headerHeight = 0;
+}
+
+wxGenericListCtrl::~wxGenericListCtrl()
+{
+    if (m_ownsImageListNormal)
+        delete m_imageListNormal;
+    if (m_ownsImageListSmall)
+        delete m_imageListSmall;
+    if (m_ownsImageListState)
+        delete m_imageListState;
+}
+
+void wxGenericListCtrl::CalculateAndSetHeaderHeight()
+{
+    if ( m_headerWin )
+    {
+#ifdef __WXMAC__
+        SInt32 h;
+        GetThemeMetric( kThemeMetricListHeaderHeight, &h );
+#else
+        // we use 'g' to get the descent, too
+        int w, h, d;
+        m_headerWin->GetTextExtent(wxT("Hg"), &w, &h, &d);
+        h += d + 2 * HEADER_OFFSET_Y + EXTRA_HEIGHT;
+#endif
+
+        // only update if changed
+        if ( h != m_headerHeight )
+        {
+            m_headerHeight = h;
+
+            if ( HasHeader() )
+                ResizeReportView(true);
+            else    //why is this needed if it doesn't have a header?
+                m_headerWin->SetSize(m_headerWin->GetSize().x, m_headerHeight);
+        }
+    }
+}
+
+void wxGenericListCtrl::CreateHeaderWindow()
+{
+    m_headerWin = new wxListHeaderWindow
+                      (
+                        this, wxID_ANY, m_mainWin,
+                        wxPoint(0,0),
+                        wxSize(GetClientSize().x, m_headerHeight),
+                        wxTAB_TRAVERSAL
+                      );
+    CalculateAndSetHeaderHeight();
+}
+
+bool wxGenericListCtrl::Create(wxWindow *parent,
+                        wxWindowID id,
+                        const wxPoint &pos,
+                        const wxSize &size,
+                        long style,
+                        const wxValidator &validator,
+                        const wxString &name)
+{
+    m_imageListNormal =
+    m_imageListSmall =
+    m_imageListState = (wxImageList *) NULL;
+    m_ownsImageListNormal =
+    m_ownsImageListSmall =
+    m_ownsImageListState = false;
+
+    m_mainWin = (wxListMainWindow*) NULL;
+    m_headerWin = (wxListHeaderWindow*) NULL;
+
+    m_headerHeight = 0;
+
+    if ( !(style & wxLC_MASK_TYPE) )
+    {
+        style = style | wxLC_LIST;
+    }
+
+    // add more styles here that should only appear
+    // in the main window
+    unsigned long only_main_window_style = wxALWAYS_SHOW_SB;
+
+    if ( !wxControl::Create( parent, id, pos, size, style & ~only_main_window_style, validator, name ) )
+        return false;
+
+    // don't create the inner window with the border
+    style &= ~wxBORDER_MASK;
+
+    m_mainWin = new wxListMainWindow( this, wxID_ANY, wxPoint(0, 0), size, style );
+
+#ifdef  __WXMAC_CARBON__
+    // Human Interface Guidelines ask us for a special font in this case
+    if ( GetWindowVariant() == wxWINDOW_VARIANT_NORMAL )
+    {
+        wxFont font;
+        font.MacCreateThemeFont( kThemeViewsFont );
+        SetFont( font );
+    }
+#endif
+
+    if ( InReportView() )
+    {
+        CreateHeaderWindow();
+
+#ifdef  __WXMAC_CARBON__
+        if (m_headerWin)
+        {
+            wxFont font;
+            font.MacCreateThemeFont( kThemeSmallSystemFont );
+            m_headerWin->SetFont( font );
+            CalculateAndSetHeaderHeight();
+        }
+#endif
+
+        if ( HasFlag(wxLC_NO_HEADER) )
+            // VZ: why do we create it at all then?
+            m_headerWin->Show( false );
+    }
+
+    SetInitialSize(size);
+
+    return true;
+}
+
+void wxGenericListCtrl::SetSingleStyle( long style, bool add )
+{
+    wxASSERT_MSG( !(style & wxLC_VIRTUAL),
+                  _T("wxLC_VIRTUAL can't be [un]set") );
+
+    long flag = GetWindowStyle();
+
+    if (add)
+    {
+        if (style & wxLC_MASK_TYPE)
+            flag &= ~(wxLC_MASK_TYPE | wxLC_VIRTUAL);
+        if (style & wxLC_MASK_ALIGN)
+            flag &= ~wxLC_MASK_ALIGN;
+        if (style & wxLC_MASK_SORT)
+            flag &= ~wxLC_MASK_SORT;
+    }
+
+    if (add)
+        flag |= style;
+    else
+        flag &= ~style;
+
+    // some styles can be set without recreating everything (as happens in
+    // SetWindowStyleFlag() which calls wxListMainWindow::DeleteEverything())
+    if ( !(style & ~(wxLC_HRULES | wxLC_VRULES)) )
+    {
+        Refresh();
+        wxWindow::SetWindowStyleFlag(flag);
+    }
+    else
+    {
+        SetWindowStyleFlag( flag );
+    }
+}
+
+void wxGenericListCtrl::SetWindowStyleFlag( long flag )
+{
+    if (m_mainWin)
+    {
+        m_mainWin->DeleteEverything();
+
+        // has the header visibility changed?
+        bool hasHeader = HasHeader();
+        bool willHaveHeader = (flag & wxLC_REPORT) && !(flag & wxLC_NO_HEADER);
+
+        if ( hasHeader != willHaveHeader )
+        {
+            // toggle it
+            if ( hasHeader )
+            {
+                if ( m_headerWin )
+                {
+                    // don't delete, just hide, as we can reuse it later
+                    m_headerWin->Show(false);
+                }
+                //else: nothing to do
+            }
+            else // must show header
+            {
+                if (!m_headerWin)
+                {
+                    CreateHeaderWindow();
+                }
+                else // already have it, just show
+                {
+                    m_headerWin->Show( true );
+                }
+            }
+
+            ResizeReportView(willHaveHeader);
+        }
+    }
+
+    wxWindow::SetWindowStyleFlag( flag );
+}
+
+bool wxGenericListCtrl::GetColumn(int col, wxListItem &item) const
+{
+    m_mainWin->GetColumn( col, item );
+    return true;
+}
+
+bool wxGenericListCtrl::SetColumn( int col, wxListItem& item )
+{
+    m_mainWin->SetColumn( col, item );
+    return true;
+}
+
+int wxGenericListCtrl::GetColumnWidth( int col ) const
+{
+    return m_mainWin->GetColumnWidth( col );
+}
+
+bool wxGenericListCtrl::SetColumnWidth( int col, int width )
+{
+    m_mainWin->SetColumnWidth( col, width );
+    return true;
+}
+
+int wxGenericListCtrl::GetCountPerPage() const
+{
+  return m_mainWin->GetCountPerPage();  // different from Windows ?
+}
+
+bool wxGenericListCtrl::GetItem( wxListItem &info ) const
+{
+    m_mainWin->GetItem( info );
+    return true;
+}
+
+bool wxGenericListCtrl::SetItem( wxListItem &info )
+{
+    m_mainWin->SetItem( info );
+    return true;
+}
+
+long wxGenericListCtrl::SetItem( long index, int col, const wxString& label, int imageId )
+{
+    wxListItem info;
+    info.m_text = label;
+    info.m_mask = wxLIST_MASK_TEXT;
+    info.m_itemId = index;
+    info.m_col = col;
+    if ( imageId > -1 )
+    {
+        info.m_image = imageId;
+        info.m_mask |= wxLIST_MASK_IMAGE;
+    }
+
+    m_mainWin->SetItem(info);
+    return true;
+}
+
+int wxGenericListCtrl::GetItemState( long item, long stateMask ) const
+{
+    return m_mainWin->GetItemState( item, stateMask );
+}
+
+bool wxGenericListCtrl::SetItemState( long item, long state, long stateMask )
+{
+    m_mainWin->SetItemState( item, state, stateMask );
+    return true;
+}
+
+bool
+wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) )
+{
+    return SetItemColumnImage(item, 0, image);
+}
+
+bool
+wxGenericListCtrl::SetItemColumnImage( long item, long column, int image )
+{
+    wxListItem info;
+    info.m_image = image;
+    info.m_mask = wxLIST_MASK_IMAGE;
+    info.m_itemId = item;
+    info.m_col = column;
+    m_mainWin->SetItem( info );
+    return true;
+}
+
+wxString wxGenericListCtrl::GetItemText( long item ) const
+{
+    return m_mainWin->GetItemText(item);
+}
+
+void wxGenericListCtrl::SetItemText( long item, const wxString& str )
+{
+    m_mainWin->SetItemText(item, str);
+}
+
+wxUIntPtr wxGenericListCtrl::GetItemData( long item ) const
+{
+    wxListItem info;
+    info.m_mask = wxLIST_MASK_DATA;
+    info.m_itemId = item;
+    m_mainWin->GetItem( info );
+    return info.m_data;
+}
+
+bool wxGenericListCtrl::SetItemPtrData( long item, wxUIntPtr data )
+{
+    wxListItem info;
+    info.m_mask = wxLIST_MASK_DATA;
+    info.m_itemId = item;
+    info.m_data = data;
+    m_mainWin->SetItem( info );
+    return true;
+}
+
+bool wxGenericListCtrl::SetItemData(long item, long data)
+{
+    return SetItemPtrData(item, data);
+}
+
+wxRect wxGenericListCtrl::GetViewRect() const
+{
+    return m_mainWin->GetViewRect();
+}
+
+bool wxGenericListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const
+{
+    m_mainWin->GetItemRect( item, rect );
+    if ( m_mainWin->HasHeader() )
+        rect.y += m_headerHeight + 1;
+    return true;
+}
+
+bool wxGenericListCtrl::GetItemPosition( long item, wxPoint& pos ) const
+{
+    m_mainWin->GetItemPosition( item, pos );
+    return true;
+}
+
+bool wxGenericListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) )
+{
+    return 0;
+}
+
+int wxGenericListCtrl::GetItemCount() const
+{
+    return m_mainWin->GetItemCount();
+}
+
+int wxGenericListCtrl::GetColumnCount() const
+{
+    return m_mainWin->GetColumnCount();
+}
+
+void wxGenericListCtrl::SetItemSpacing( int spacing, bool isSmall )
+{
+    m_mainWin->SetItemSpacing( spacing, isSmall );
+}
+
+wxSize wxGenericListCtrl::GetItemSpacing() const
+{
+    const int spacing = m_mainWin->GetItemSpacing(HasFlag(wxLC_SMALL_ICON));
+
+    return wxSize(spacing, spacing);
+}
+
+#if WXWIN_COMPATIBILITY_2_6
+int wxGenericListCtrl::GetItemSpacing( bool isSmall ) const
+{
+    return m_mainWin->GetItemSpacing( isSmall );
+}
+#endif // WXWIN_COMPATIBILITY_2_6
+
+void wxGenericListCtrl::SetItemTextColour( long item, const wxColour &col )
+{
+    wxListItem info;
+    info.m_itemId = item;
+    info.SetTextColour( col );
+    m_mainWin->SetItem( info );
+}
+
+wxColour wxGenericListCtrl::GetItemTextColour( long item ) const
+{
+    wxListItem info;
+    info.m_itemId = item;
+    m_mainWin->GetItem( info );
+    return info.GetTextColour();
+}
+
+void wxGenericListCtrl::SetItemBackgroundColour( long item, const wxColour &col )
+{
+    wxListItem info;
+    info.m_itemId = item;
+    info.SetBackgroundColour( col );
+    m_mainWin->SetItem( info );
+}
+
+wxColour wxGenericListCtrl::GetItemBackgroundColour( long item ) const
+{
+    wxListItem info;
+    info.m_itemId = item;
+    m_mainWin->GetItem( info );
+    return info.GetBackgroundColour();
+}
+
+int wxGenericListCtrl::GetScrollPos( int orient ) const
+{
+    return m_mainWin->GetScrollPos( orient );
+}
+
+void wxGenericListCtrl::SetScrollPos( int orient, int pos, bool refresh )
+{
+    m_mainWin->SetScrollPos( orient, pos, refresh );
+}
+
+void wxGenericListCtrl::SetItemFont( long item, const wxFont &f )
+{
+    wxListItem info;
+    info.m_itemId = item;
+    info.SetFont( f );
+    m_mainWin->SetItem( info );
+}
+
+wxFont wxGenericListCtrl::GetItemFont( long item ) const
+{
+    wxListItem info;
+    info.m_itemId = item;
+    m_mainWin->GetItem( info );
+    return info.GetFont();
+}
+
+int wxGenericListCtrl::GetSelectedItemCount() const
+{
+    return m_mainWin->GetSelectedItemCount();
+}
+
+wxColour wxGenericListCtrl::GetTextColour() const
+{
+    return GetForegroundColour();
+}
+
+void wxGenericListCtrl::SetTextColour(const wxColour& col)
+{
+    SetForegroundColour(col);
+}
+
+long wxGenericListCtrl::GetTopItem() const
+{
+    size_t top;
+    m_mainWin->GetVisibleLinesRange(&top, NULL);
+    return (long)top;
+}
+
+long wxGenericListCtrl::GetNextItem( long item, int geom, int state ) const
+{
+    return m_mainWin->GetNextItem( item, geom, state );
+}
+
+wxImageList *wxGenericListCtrl::GetImageList(int which) const
+{
+    if (which == wxIMAGE_LIST_NORMAL)
+        return m_imageListNormal;
+    else if (which == wxIMAGE_LIST_SMALL)
+        return m_imageListSmall;
+    else if (which == wxIMAGE_LIST_STATE)
+        return m_imageListState;
+
+    return (wxImageList *) NULL;
+}
+
+void wxGenericListCtrl::SetImageList( wxImageList *imageList, int which )
+{
+    if ( which == wxIMAGE_LIST_NORMAL )
+    {
+        if (m_ownsImageListNormal)
+            delete m_imageListNormal;
+        m_imageListNormal = imageList;
+        m_ownsImageListNormal = false;
+    }
+    else if ( which == wxIMAGE_LIST_SMALL )
+    {
+        if (m_ownsImageListSmall)
+            delete m_imageListSmall;
+        m_imageListSmall = imageList;
+        m_ownsImageListSmall = false;
+    }
+    else if ( which == wxIMAGE_LIST_STATE )
+    {
+        if (m_ownsImageListState)
+            delete m_imageListState;
+        m_imageListState = imageList;
+        m_ownsImageListState = false;
+    }
+
+    m_mainWin->SetImageList( imageList, which );
+}
+
+void wxGenericListCtrl::AssignImageList(wxImageList *imageList, int which)
+{
+    SetImageList(imageList, which);
+    if ( which == wxIMAGE_LIST_NORMAL )
+        m_ownsImageListNormal = true;
+    else if ( which == wxIMAGE_LIST_SMALL )
+        m_ownsImageListSmall = true;
+    else if ( which == wxIMAGE_LIST_STATE )
+        m_ownsImageListState = true;
+}
+
+bool wxGenericListCtrl::Arrange( int WXUNUSED(flag) )
+{
+    return 0;
+}
+
+bool wxGenericListCtrl::DeleteItem( long item )
+{
+    m_mainWin->DeleteItem( item );
+    return true;
+}
+
+bool wxGenericListCtrl::DeleteAllItems()
+{
+    m_mainWin->DeleteAllItems();
+    return true;
+}
+
+bool wxGenericListCtrl::DeleteAllColumns()
+{
+    size_t count = m_mainWin->m_columns.GetCount();
+    for ( size_t n = 0; n < count; n++ )
+        DeleteColumn( 0 );
+    return true;
+}
+
+void wxGenericListCtrl::ClearAll()
+{
+    m_mainWin->DeleteEverything();
+}
+
+bool wxGenericListCtrl::DeleteColumn( int col )
+{
+    m_mainWin->DeleteColumn( col );
+
+    // if we don't have the header any longer, we need to relayout the window
+    if ( !GetColumnCount() )
+        ResizeReportView(false /* no header */);
+    return true;
+}
+
+wxTextCtrl *wxGenericListCtrl::EditLabel(long item,
+                                         wxClassInfo* textControlClass)
+{
+    return m_mainWin->EditLabel( item, textControlClass );
+}
+
+wxTextCtrl *wxGenericListCtrl::GetEditControl() const
+{
+    return m_mainWin->GetEditControl();
+}
+
+bool wxGenericListCtrl::EnsureVisible( long item )
+{
+    m_mainWin->EnsureVisible( item );
+    return true;
+}
+
+long wxGenericListCtrl::FindItem( long start, const wxString& str, bool partial )
+{
+    return m_mainWin->FindItem( start, str, partial );
+}
+
+long wxGenericListCtrl::FindItem( long start, wxUIntPtr data )
+{
+    return m_mainWin->FindItem( start, data );
+}
+
+long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& pt,
+                           int WXUNUSED(direction))
+{
+    return m_mainWin->FindItem( pt );
+}
+
+// TODO: sub item hit testing
+long wxGenericListCtrl::HitTest(const wxPoint& point, int& flags, long *) const
+{
+    return m_mainWin->HitTest( (int)point.x, (int)point.y, flags );
+}
+
+long wxGenericListCtrl::InsertItem( wxListItem& info )
+{
+    m_mainWin->InsertItem( info );
+    return info.m_itemId;
+}
+
+long wxGenericListCtrl::InsertItem( long index, const wxString &label )
+{
+    wxListItem info;
+    info.m_text = label;
+    info.m_mask = wxLIST_MASK_TEXT;
+    info.m_itemId = index;
+    return InsertItem( info );
+}
+
+long wxGenericListCtrl::InsertItem( long index, int imageIndex )
+{
+    wxListItem info;
+    info.m_mask = wxLIST_MASK_IMAGE;
+    info.m_image = imageIndex;
+    info.m_itemId = index;
+    return InsertItem( info );
+}
+
+long wxGenericListCtrl::InsertItem( long index, const wxString &label, int imageIndex )
+{
+    wxListItem info;
+    info.m_text = label;
+    info.m_image = imageIndex;
+    info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE;
+    info.m_itemId = index;
+    return InsertItem( info );
+}
+
+long wxGenericListCtrl::InsertColumn( long col, wxListItem &item )
+{
+    wxCHECK_MSG( m_headerWin, -1, _T("can't add column in non report mode") );
+
+    m_mainWin->InsertColumn( col, item );
+
+    // if we hadn't had a header before but have one now
+    // then we need to relayout the window
+    if ( GetColumnCount() == 1 && m_mainWin->HasHeader() )
+        ResizeReportView(true /* have header */);
+
+    m_headerWin->Refresh();
+
+    return 0;
+}
+
+long wxGenericListCtrl::InsertColumn( long col, const wxString &heading,
+                               int format, int width )
+{
+    wxListItem item;
+    item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
+    item.m_text = heading;
+    if (width >= -2)
+    {
+        item.m_mask |= wxLIST_MASK_WIDTH;
+        item.m_width = width;
+    }
+
+    item.m_format = format;
+
+    return InsertColumn( col, item );
+}
+
+bool wxGenericListCtrl::ScrollList( int dx, int dy )
+{
+    return m_mainWin->ScrollList(dx, dy);
+}
+
+// Sort items.
+// fn is a function which takes 3 long arguments: item1, item2, data.
+// item1 is the long data associated with a first item (NOT the index).
+// item2 is the long data associated with a second item (NOT the index).
+// data is the same value as passed to SortItems.
+// The return value is a negative number if the first item should precede the second
+// item, a positive number of the second item should precede the first,
+// or zero if the two items are equivalent.
+// data is arbitrary data to be passed to the sort function.
+
+bool wxGenericListCtrl::SortItems( wxListCtrlCompare fn, long data )
+{
+    m_mainWin->SortItems( fn, data );
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+// event handlers
+// ----------------------------------------------------------------------------
+
+void wxGenericListCtrl::OnSize(wxSizeEvent& WXUNUSED(event))
+{
+    if ( !m_mainWin )
+        return;
+
+    ResizeReportView(m_mainWin->HasHeader());
+    m_mainWin->RecalculatePositions();
+}
+
+void wxGenericListCtrl::ResizeReportView(bool showHeader)
+{
+    int cw, ch;
+    GetClientSize( &cw, &ch );
+
+    if ( showHeader )
+    {
+        m_headerWin->SetSize( 0, 0, cw, m_headerHeight );
+        if(ch > m_headerHeight)
+            m_mainWin->SetSize( 0, m_headerHeight + 1,
+                                   cw, ch - m_headerHeight - 1 );
+        else
+            m_mainWin->SetSize( 0, m_headerHeight + 1,
+                                   cw, 0);
+    }
+    else // no header window
+    {
+        m_mainWin->SetSize( 0, 0, cw, ch );
+    }
+}
+
+void wxGenericListCtrl::OnInternalIdle()
+{
+    wxWindow::OnInternalIdle();
+
+    // do it only if needed
+    if ( !m_mainWin->m_dirty )
+        return;
+
+    m_mainWin->RecalculatePositions();
+}
+
+// ----------------------------------------------------------------------------
+// font/colours
+// ----------------------------------------------------------------------------
+
+bool wxGenericListCtrl::SetBackgroundColour( const wxColour &colour )
+{
+    if (m_mainWin)
+    {
+        m_mainWin->SetBackgroundColour( colour );
+        m_mainWin->m_dirty = true;
+    }
+
+    return true;
+}
+
+bool wxGenericListCtrl::SetForegroundColour( const wxColour &colour )
+{
+    if ( !wxWindow::SetForegroundColour( colour ) )
+        return false;
+
+    if (m_mainWin)
+    {
+        m_mainWin->SetForegroundColour( colour );
+        m_mainWin->m_dirty = true;
+    }
+
+    if (m_headerWin)
+        m_headerWin->SetForegroundColour( colour );
+
+    return true;
+}
+
+bool wxGenericListCtrl::SetFont( const wxFont &font )
+{
+    if ( !wxWindow::SetFont( font ) )
+        return false;
+
+    if (m_mainWin)
+    {
+        m_mainWin->SetFont( font );
+        m_mainWin->m_dirty = true;
+    }
+
+    if (m_headerWin)
+    {
+        m_headerWin->SetFont( font );
+        CalculateAndSetHeaderHeight();
+    }
+
+    Refresh();
+
+    return true;
+}
+
+// static
+wxVisualAttributes
+wxGenericListCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
+{
+#if _USE_VISATTR
+    // Use the same color scheme as wxListBox
+    return wxListBox::GetClassDefaultAttributes(variant);
+#else
+    wxUnusedVar(variant);
+    wxVisualAttributes attr;
+    attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+    attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX);
+    attr.font  = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
+    return attr;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// methods forwarded to m_mainWin
+// ----------------------------------------------------------------------------
+
+#if wxUSE_DRAG_AND_DROP
+
+void wxGenericListCtrl::SetDropTarget( wxDropTarget *dropTarget )
+{
+    m_mainWin->SetDropTarget( dropTarget );
+}
+
+wxDropTarget *wxGenericListCtrl::GetDropTarget() const
+{
+    return m_mainWin->GetDropTarget();
+}
+
+#endif
+
+bool wxGenericListCtrl::SetCursor( const wxCursor &cursor )
+{
+    return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : false;
+}
+
+wxColour wxGenericListCtrl::GetBackgroundColour() const
+{
+    return m_mainWin ? m_mainWin->GetBackgroundColour() : wxColour();
+}
+
+wxColour wxGenericListCtrl::GetForegroundColour() const
+{
+    return m_mainWin ? m_mainWin->GetForegroundColour() : wxColour();
+}
+
+bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y )
+{
+#if wxUSE_MENUS
+    return m_mainWin->PopupMenu( menu, x, y );
+#else
+    return false;
+#endif
+}
+
+void wxGenericListCtrl::DoClientToScreen( int *x, int *y ) const
+{
+    m_mainWin->DoClientToScreen(x, y);
+}
+
+void wxGenericListCtrl::DoScreenToClient( int *x, int *y ) const
+{
+    m_mainWin->DoScreenToClient(x, y);
+}
+
+void wxGenericListCtrl::SetFocus()
+{
+    // The test in window.cpp fails as we are a composite
+    // window, so it checks against "this", but not m_mainWin.
+    if ( DoFindFocus() != this )
+        m_mainWin->SetFocus();
+}
+
+wxSize wxGenericListCtrl::DoGetBestSize() const
+{
+    // Something is better than nothing...
+    // 100x80 is what the MSW version will get from the default
+    // wxControl::DoGetBestSize
+    return wxSize(100, 80);
+}
+
+// ----------------------------------------------------------------------------
+// virtual list control support
+// ----------------------------------------------------------------------------
+
+wxString wxGenericListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) const
+{
+    // this is a pure virtual function, in fact - which is not really pure
+    // because the controls which are not virtual don't need to implement it
+    wxFAIL_MSG( _T("wxGenericListCtrl::OnGetItemText not supposed to be called") );
+
+    return wxEmptyString;
+}
+
+int wxGenericListCtrl::OnGetItemImage(long WXUNUSED(item)) const
+{
+    wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL),
+                -1,
+                wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden."));
+    return -1;
+}
+
+int wxGenericListCtrl::OnGetItemColumnImage(long item, long column) const
+{
+    if (!column)
+        return OnGetItemImage(item);
+
+   return -1;
+}
+
+wxListItemAttr *
+wxGenericListCtrl::OnGetItemAttr(long item) const // 2009.11.28. TN; remove WXUNUSED_UNLESS_DEBUG
+{
+       item = item % 1000000;  // 2009.11.28. TN; if it is >= 1000000, then item/1000000 - 1 is the column number
+    wxASSERT_MSG( item >= 0 && item < GetItemCount(),
+                  _T("invalid item index in OnGetItemAttr()") );
+
+    // no attributes by default
+    return NULL;
+}
+
+void wxGenericListCtrl::SetItemCount(long count)
+{
+    wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
+
+    m_mainWin->SetItemCount(count);
+}
+
+void wxGenericListCtrl::RefreshItem(long item)
+{
+    m_mainWin->RefreshLine(item);
+}
+
+void wxGenericListCtrl::RefreshItems(long itemFrom, long itemTo)
+{
+    m_mainWin->RefreshLines(itemFrom, itemTo);
+}
+
+// Generic wxListCtrl is more or less a container for two other
+// windows which drawings are done upon. These are namely
+// 'm_headerWin' and 'm_mainWin'.
+// Here we override 'virtual wxWindow::Refresh()' to mimic the
+// behaviour wxListCtrl has under wxMSW.
+//
+void wxGenericListCtrl::Refresh(bool eraseBackground, const wxRect *rect)
+{
+    if (!rect)
+    {
+        // The easy case, no rectangle specified.
+        if (m_headerWin)
+            m_headerWin->Refresh(eraseBackground);
+
+        if (m_mainWin)
+            m_mainWin->Refresh(eraseBackground);
+    }
+    else
+    {
+        // Refresh the header window
+        if (m_headerWin)
+        {
+            wxRect rectHeader = m_headerWin->GetRect();
+            rectHeader.Intersect(*rect);
+            if (rectHeader.GetWidth() && rectHeader.GetHeight())
+            {
+                int x, y;
+                m_headerWin->GetPosition(&x, &y);
+                rectHeader.Offset(-x, -y);
+                m_headerWin->Refresh(eraseBackground, &rectHeader);
+            }
+        }
+
+        // Refresh the main window
+        if (m_mainWin)
+        {
+            wxRect rectMain = m_mainWin->GetRect();
+            rectMain.Intersect(*rect);
+            if (rectMain.GetWidth() && rectMain.GetHeight())
+            {
+                int x, y;
+                m_mainWin->GetPosition(&x, &y);
+                rectMain.Offset(-x, -y);
+                m_mainWin->Refresh(eraseBackground, &rectMain);
+            }
+        }
+    }
+}
+
+void wxGenericListCtrl::Freeze()
+{
+    m_mainWin->Freeze();
+}
+
+void wxGenericListCtrl::Thaw()
+{
+    m_mainWin->Thaw();
+}
+
+#endif // wxUSE_LISTCTRL
diff --git a/xcode-build/English.lproj/InfoPlist.strings b/xcode-build/English.lproj/InfoPlist.strings
new file mode 100755 (executable)
index 0000000..d2cf83e
Binary files /dev/null and b/xcode-build/English.lproj/InfoPlist.strings differ
diff --git a/xcode-build/Info.plist b/xcode-build/Info.plist
new file mode 100755 (executable)
index 0000000..815c94e
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleDocumentTypes</key>
+       <array>
+               <dict>
+                       <key>CFBundleTypeExtensions</key>
+                       <array>
+                               <string>*</string>
+                       </array>
+                       <key>CFBundleTypeName</key>
+                       <string>MyDocumentType</string>
+                       <key>CFBundleTypeRole</key>
+                       <string>Editor</string>
+                       <key>LSTypeIsPackage</key>
+                       <false/>
+                       <key>NSPersistentStoreTypeKey</key>
+                       <string>XML</string>
+               </dict>
+       </array>
+       <key>CFBundleExecutable</key>
+       <string>${EXECUTABLE_NAME}</string>
+       <key>CFBundleIconFile</key>
+       <string>molby_icon.icns</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.yourcompany.Molby</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>${PRODUCT_NAME}</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>v0.5.0 build 20100121</string>
+</dict>
+</plist>
diff --git a/xcode-build/Molby.xcodeproj/project.pbxproj b/xcode-build/Molby.xcodeproj/project.pbxproj
new file mode 100755 (executable)
index 0000000..87ce327
--- /dev/null
@@ -0,0 +1,628 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 42;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               E4299DA70F2A191600780B44 /* ruby_types.c in Sources */ = {isa = PBXBuildFile; fileRef = E4299DA60F2A191600780B44 /* ruby_types.c */; };
+               E42D8AF81030193F00C20247 /* MDGraphite.c in Sources */ = {isa = PBXBuildFile; fileRef = E42D8AEF1030193F00C20247 /* MDGraphite.c */; };
+               E42D8AF91030193F00C20247 /* MDCore.c in Sources */ = {isa = PBXBuildFile; fileRef = E42D8AF11030193F00C20247 /* MDCore.c */; };
+               E42D8AFA1030193F00C20247 /* MDForce.c in Sources */ = {isa = PBXBuildFile; fileRef = E42D8AF31030193F00C20247 /* MDForce.c */; };
+               E42D8AFB1030193F00C20247 /* MDPressure.c in Sources */ = {isa = PBXBuildFile; fileRef = E42D8AF61030193F00C20247 /* MDPressure.c */; };
+               E42D8BB31031BB2900C20247 /* ruby_md.c in Sources */ = {isa = PBXBuildFile; fileRef = E42D8BB21031BB2900C20247 /* ruby_md.c */; };
+               E43226E30ECE8681004963D7 /* MySlider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E43226E20ECE8681004963D7 /* MySlider.cpp */; };
+               E433CE790EC7099B00675985 /* ruby_bind.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE760EC7099B00675985 /* ruby_bind.c */; };
+               E433CE900EC709BD00675985 /* IntGroup.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE7C0EC709BD00675985 /* IntGroup.c */; };
+               E433CE910EC709BD00675985 /* MainView.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE7E0EC709BD00675985 /* MainView.c */; };
+               E433CE920EC709BD00675985 /* Missing.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE800EC709BD00675985 /* Missing.c */; };
+               E433CE930EC709BD00675985 /* MolAction.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE820EC709BD00675985 /* MolAction.c */; };
+               E433CE940EC709BD00675985 /* Molecule.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE840EC709BD00675985 /* Molecule.c */; };
+               E433CE950EC709BD00675985 /* Object.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE870EC709BD00675985 /* Object.c */; };
+               E433CE960EC709BD00675985 /* Parameter.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE890EC709BD00675985 /* Parameter.c */; };
+               E433CE970EC709BD00675985 /* Trackball.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE8B0EC709BD00675985 /* Trackball.c */; };
+               E433CE980EC709BD00675985 /* Types.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE8D0EC709BD00675985 /* Types.c */; };
+               E433CEA50EC709D300675985 /* ConsoleFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E433CE990EC709D300675985 /* ConsoleFrame.cpp */; };
+               E433CEA60EC709D300675985 /* MoleculeView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E433CE9B0EC709D300675985 /* MoleculeView.cpp */; };
+               E433CEA70EC709D300675985 /* MyApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E433CE9D0EC709D300675985 /* MyApp.cpp */; };
+               E433CEA80EC709D300675985 /* MyCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E433CE9F0EC709D300675985 /* MyCommand.cpp */; };
+               E433CEA90EC709D300675985 /* MyDocument.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E433CEA10EC709D300675985 /* MyDocument.cpp */; };
+               E433CEAA0EC709D300675985 /* MyGLCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E433CEA30EC709D300675985 /* MyGLCanvas.cpp */; };
+               E43CCDDA0EB20F0F00108D2D /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD10EB20F0F00108D2D /* Accelerate.framework */; };
+               E43CCDDB0EB20F0F00108D2D /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD20EB20F0F00108D2D /* AGL.framework */; };
+               E43CCDDC0EB20F0F00108D2D /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD30EB20F0F00108D2D /* Carbon.framework */; };
+               E43CCDDD0EB20F0F00108D2D /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD40EB20F0F00108D2D /* Cocoa.framework */; };
+               E43CCDDE0EB20F0F00108D2D /* GLUT.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD50EB20F0F00108D2D /* GLUT.framework */; };
+               E43CCDDF0EB20F0F00108D2D /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD60EB20F0F00108D2D /* IOKit.framework */; };
+               E43CCDE00EB20F0F00108D2D /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD70EB20F0F00108D2D /* OpenGL.framework */; };
+               E43CCDE10EB20F0F00108D2D /* QuickTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD80EB20F0F00108D2D /* QuickTime.framework */; };
+               E43CCDE20EB20F0F00108D2D /* System.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E43CCDD90EB20F0F00108D2D /* System.framework */; };
+               E44CEED6100E1E9C0040BD51 /* ProgressFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E44CEED4100E1E9C0040BD51 /* ProgressFrame.cpp */; };
+               E4513F550EED72C0009DF1F7 /* MyListCtrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4513F540EED72C0009DF1F7 /* MyListCtrl.cpp */; };
+               E46C904A108F491E000E984D /* MyThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E46C9049108F491E000E984D /* MyThread.cpp */; };
+               E472B7C51087F63200F931F3 /* MyVersion.c in Sources */ = {isa = PBXBuildFile; fileRef = E472B7C41087F63200F931F3 /* MyVersion.c */; };
+               E496A76F0EDD392F00499646 /* MyClipboardData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E496A76E0EDD392F00499646 /* MyClipboardData.cpp */; };
+               E49D17F3107753DF00C4841E /* MDSurface.c in Sources */ = {isa = PBXBuildFile; fileRef = E49D17F1107753DF00C4841E /* MDSurface.c */; };
+               E4A02341106B652F0094DBF8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E4A0233F106B652F0094DBF8 /* InfoPlist.strings */; };
+               E4A02347106B65640094DBF8 /* molby_icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = E4A02346106B65640094DBF8 /* molby_icon.icns */; };
+               E4A02357106B657D0094DBF8 /* Scripts in Resources */ = {isa = PBXBuildFile; fileRef = E4A0234A106B657D0094DBF8 /* Scripts */; };
+               E4A4667610C15B4300E40A9D /* listctrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A4667410C15B4300E40A9D /* listctrl.cpp */; };
+               E4C0C4960F25734B00161EC2 /* Dcd.c in Sources */ = {isa = PBXBuildFile; fileRef = E4C0C4950F25734B00161EC2 /* Dcd.c */; };
+               E4C8CBDF10B83060006C4692 /* MyDocManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CBDE10B83060006C4692 /* MyDocManager.cpp */; };
+               E4E2D5C510A249820007644A /* GlobalParameterFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4E2D5C410A249820007644A /* GlobalParameterFrame.cpp */; };
+               E4E470C910AE6F8600F72B68 /* GlobalParameterFilesFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4E470C810AE6F8600F72B68 /* GlobalParameterFilesFrame.cpp */; };
+               E4FFA9050EE960650045EDF9 /* RubyDialogFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4FFA9040EE960650045EDF9 /* RubyDialogFrame.cpp */; };
+               E4FFA95E0EEAADCA0045EDF9 /* ruby_dialog.c in Sources */ = {isa = PBXBuildFile; fileRef = E433CE770EC7099B00675985 /* ruby_dialog.c */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+               13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
+               29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+               29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+               8D1107320486CEB800E47090 /* Molby.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Molby.app; sourceTree = BUILT_PRODUCTS_DIR; };
+               E4299DA60F2A191600780B44 /* ruby_types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ruby_types.c; sourceTree = "<group>"; };
+               E42D8AEF1030193F00C20247 /* MDGraphite.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MDGraphite.c; sourceTree = "<group>"; };
+               E42D8AF01030193F00C20247 /* MDGraphite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDGraphite.h; sourceTree = "<group>"; };
+               E42D8AF11030193F00C20247 /* MDCore.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MDCore.c; sourceTree = "<group>"; };
+               E42D8AF21030193F00C20247 /* MDCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDCore.h; sourceTree = "<group>"; };
+               E42D8AF31030193F00C20247 /* MDForce.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MDForce.c; sourceTree = "<group>"; };
+               E42D8AF41030193F00C20247 /* MDForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDForce.h; sourceTree = "<group>"; };
+               E42D8AF61030193F00C20247 /* MDPressure.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MDPressure.c; sourceTree = "<group>"; };
+               E42D8AF71030193F00C20247 /* MDPressure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDPressure.h; sourceTree = "<group>"; };
+               E42D8BB21031BB2900C20247 /* ruby_md.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ruby_md.c; sourceTree = "<group>"; };
+               E43226E10ECE8681004963D7 /* MySlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MySlider.h; path = ../wxSources/MySlider.h; sourceTree = SOURCE_ROOT; };
+               E43226E20ECE8681004963D7 /* MySlider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MySlider.cpp; path = ../wxSources/MySlider.cpp; sourceTree = SOURCE_ROOT; };
+               E433CE740EC7099B00675985 /* Molby_extern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Molby_extern.h; path = ../MolLib/Ruby_bind/Molby_extern.h; sourceTree = SOURCE_ROOT; };
+               E433CE750EC7099B00675985 /* Molby.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Molby.h; path = ../MolLib/Ruby_bind/Molby.h; sourceTree = SOURCE_ROOT; };
+               E433CE760EC7099B00675985 /* ruby_bind.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ruby_bind.c; path = ../MolLib/Ruby_bind/ruby_bind.c; sourceTree = SOURCE_ROOT; };
+               E433CE770EC7099B00675985 /* ruby_dialog.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ruby_dialog.c; path = ../MolLib/Ruby_bind/ruby_dialog.c; sourceTree = SOURCE_ROOT; };
+               E433CE780EC7099B00675985 /* ruby_dialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ruby_dialog.h; path = ../MolLib/Ruby_bind/ruby_dialog.h; sourceTree = SOURCE_ROOT; };
+               E433CE7C0EC709BD00675985 /* IntGroup.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = IntGroup.c; path = ../MolLib/IntGroup.c; sourceTree = SOURCE_ROOT; };
+               E433CE7D0EC709BD00675985 /* IntGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IntGroup.h; path = ../MolLib/IntGroup.h; sourceTree = SOURCE_ROOT; };
+               E433CE7E0EC709BD00675985 /* MainView.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = MainView.c; path = ../MolLib/MainView.c; sourceTree = SOURCE_ROOT; };
+               E433CE7F0EC709BD00675985 /* MainView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MainView.h; path = ../MolLib/MainView.h; sourceTree = SOURCE_ROOT; };
+               E433CE800EC709BD00675985 /* Missing.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Missing.c; path = ../MolLib/Missing.c; sourceTree = SOURCE_ROOT; };
+               E433CE810EC709BD00675985 /* Missing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Missing.h; path = ../MolLib/Missing.h; sourceTree = SOURCE_ROOT; };
+               E433CE820EC709BD00675985 /* MolAction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = MolAction.c; path = ../MolLib/MolAction.c; sourceTree = SOURCE_ROOT; wrapsLines = 1; };
+               E433CE830EC709BD00675985 /* MolAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MolAction.h; path = ../MolLib/MolAction.h; sourceTree = SOURCE_ROOT; };
+               E433CE840EC709BD00675985 /* Molecule.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Molecule.c; path = ../MolLib/Molecule.c; sourceTree = SOURCE_ROOT; };
+               E433CE850EC709BD00675985 /* Molecule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Molecule.h; path = ../MolLib/Molecule.h; sourceTree = SOURCE_ROOT; };
+               E433CE860EC709BD00675985 /* MolLib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MolLib.h; path = ../MolLib/MolLib.h; sourceTree = SOURCE_ROOT; };
+               E433CE870EC709BD00675985 /* Object.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Object.c; path = ../MolLib/Object.c; sourceTree = SOURCE_ROOT; };
+               E433CE880EC709BD00675985 /* Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Object.h; path = ../MolLib/Object.h; sourceTree = SOURCE_ROOT; };
+               E433CE890EC709BD00675985 /* Parameter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Parameter.c; path = ../MolLib/Parameter.c; sourceTree = SOURCE_ROOT; };
+               E433CE8A0EC709BD00675985 /* Parameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Parameter.h; path = ../MolLib/Parameter.h; sourceTree = SOURCE_ROOT; };
+               E433CE8B0EC709BD00675985 /* Trackball.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Trackball.c; path = ../MolLib/Trackball.c; sourceTree = SOURCE_ROOT; };
+               E433CE8C0EC709BD00675985 /* Trackball.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Trackball.h; path = ../MolLib/Trackball.h; sourceTree = SOURCE_ROOT; };
+               E433CE8D0EC709BD00675985 /* Types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Types.c; path = ../MolLib/Types.c; sourceTree = SOURCE_ROOT; };
+               E433CE8E0EC709BD00675985 /* Types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Types.h; path = ../MolLib/Types.h; sourceTree = SOURCE_ROOT; };
+               E433CE990EC709D300675985 /* ConsoleFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConsoleFrame.cpp; path = ../wxSources/ConsoleFrame.cpp; sourceTree = SOURCE_ROOT; };
+               E433CE9A0EC709D300675985 /* ConsoleFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConsoleFrame.h; path = ../wxSources/ConsoleFrame.h; sourceTree = SOURCE_ROOT; };
+               E433CE9B0EC709D300675985 /* MoleculeView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MoleculeView.cpp; path = ../wxSources/MoleculeView.cpp; sourceTree = SOURCE_ROOT; };
+               E433CE9C0EC709D300675985 /* MoleculeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MoleculeView.h; path = ../wxSources/MoleculeView.h; sourceTree = SOURCE_ROOT; };
+               E433CE9D0EC709D300675985 /* MyApp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyApp.cpp; path = ../wxSources/MyApp.cpp; sourceTree = SOURCE_ROOT; };
+               E433CE9E0EC709D300675985 /* MyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyApp.h; path = ../wxSources/MyApp.h; sourceTree = SOURCE_ROOT; };
+               E433CE9F0EC709D300675985 /* MyCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyCommand.cpp; path = ../wxSources/MyCommand.cpp; sourceTree = SOURCE_ROOT; };
+               E433CEA00EC709D300675985 /* MyCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyCommand.h; path = ../wxSources/MyCommand.h; sourceTree = SOURCE_ROOT; };
+               E433CEA10EC709D300675985 /* MyDocument.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyDocument.cpp; path = ../wxSources/MyDocument.cpp; sourceTree = SOURCE_ROOT; };
+               E433CEA20EC709D300675985 /* MyDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyDocument.h; path = ../wxSources/MyDocument.h; sourceTree = SOURCE_ROOT; };
+               E433CEA30EC709D300675985 /* MyGLCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyGLCanvas.cpp; path = ../wxSources/MyGLCanvas.cpp; sourceTree = SOURCE_ROOT; };
+               E433CEA40EC709D300675985 /* MyGLCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyGLCanvas.h; path = ../wxSources/MyGLCanvas.h; sourceTree = SOURCE_ROOT; };
+               E43CCDD10EB20F0F00108D2D /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = /System/Library/Frameworks/Accelerate.framework; sourceTree = "<absolute>"; };
+               E43CCDD20EB20F0F00108D2D /* AGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AGL.framework; path = /System/Library/Frameworks/AGL.framework; sourceTree = "<absolute>"; };
+               E43CCDD30EB20F0F00108D2D /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
+               E43CCDD40EB20F0F00108D2D /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+               E43CCDD50EB20F0F00108D2D /* GLUT.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLUT.framework; path = /System/Library/Frameworks/GLUT.framework; sourceTree = "<absolute>"; };
+               E43CCDD60EB20F0F00108D2D /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
+               E43CCDD70EB20F0F00108D2D /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
+               E43CCDD80EB20F0F00108D2D /* QuickTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickTime.framework; path = /System/Library/Frameworks/QuickTime.framework; sourceTree = "<absolute>"; };
+               E43CCDD90EB20F0F00108D2D /* System.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = System.framework; path = /System/Library/Frameworks/System.framework; sourceTree = "<absolute>"; };
+               E44CEED4100E1E9C0040BD51 /* ProgressFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProgressFrame.cpp; path = ../wxSources/ProgressFrame.cpp; sourceTree = SOURCE_ROOT; };
+               E44CEED5100E1E9C0040BD51 /* ProgressFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressFrame.h; path = ../wxSources/ProgressFrame.h; sourceTree = SOURCE_ROOT; };
+               E4513F530EED72C0009DF1F7 /* MyListCtrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyListCtrl.h; path = ../wxSources/MyListCtrl.h; sourceTree = SOURCE_ROOT; };
+               E4513F540EED72C0009DF1F7 /* MyListCtrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyListCtrl.cpp; path = ../wxSources/MyListCtrl.cpp; sourceTree = SOURCE_ROOT; };
+               E46C9048108F491E000E984D /* MyThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyThread.h; path = ../wxSources/MyThread.h; sourceTree = SOURCE_ROOT; };
+               E46C9049108F491E000E984D /* MyThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyThread.cpp; path = ../wxSources/MyThread.cpp; sourceTree = SOURCE_ROOT; };
+               E472B7C41087F63200F931F3 /* MyVersion.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = MyVersion.c; path = ../wxSources/MyVersion.c; sourceTree = SOURCE_ROOT; };
+               E496A76D0EDD392F00499646 /* MyClipboardData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyClipboardData.h; path = ../wxSources/MyClipboardData.h; sourceTree = SOURCE_ROOT; };
+               E496A76E0EDD392F00499646 /* MyClipboardData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyClipboardData.cpp; path = ../wxSources/MyClipboardData.cpp; sourceTree = SOURCE_ROOT; };
+               E49D17F1107753DF00C4841E /* MDSurface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MDSurface.c; sourceTree = "<group>"; };
+               E49D17F2107753DF00C4841E /* MDSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDSurface.h; sourceTree = "<group>"; };
+               E4A0233B106B651C0094DBF8 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               E4A02340106B652F0094DBF8 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+               E4A02346106B65640094DBF8 /* molby_icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = molby_icon.icns; path = ../bitmaps/molby_icon.icns; sourceTree = SOURCE_ROOT; };
+               E4A0234A106B657D0094DBF8 /* Scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Scripts; path = ../Scripts; sourceTree = SOURCE_ROOT; };
+               E4A4667410C15B4300E40A9D /* listctrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = listctrl.cpp; path = ../wxSources/listctrl.cpp; sourceTree = SOURCE_ROOT; };
+               E4C0C4940F25734B00161EC2 /* Dcd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dcd.h; sourceTree = "<group>"; };
+               E4C0C4950F25734B00161EC2 /* Dcd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Dcd.c; sourceTree = "<group>"; };
+               E4C8CBDD10B83060006C4692 /* MyDocManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MyDocManager.h; path = ../wxSources/MyDocManager.h; sourceTree = SOURCE_ROOT; };
+               E4C8CBDE10B83060006C4692 /* MyDocManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MyDocManager.cpp; path = ../wxSources/MyDocManager.cpp; sourceTree = SOURCE_ROOT; };
+               E4CBF38C10A91EAC00C7C74A /* doc_source.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = doc_source.html; path = ../Documents/src/doc_source.html; sourceTree = SOURCE_ROOT; };
+               E4CBF39510A91ED500C7C74A /* style.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = style.css; path = ../Documents/etc/style.css; sourceTree = SOURCE_ROOT; };
+               E4CBF39710A91EDA00C7C74A /* makedoc.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = makedoc.rb; path = ../Documents/makedoc.rb; sourceTree = SOURCE_ROOT; };
+               E4E2D5C310A249820007644A /* GlobalParameterFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GlobalParameterFrame.h; path = ../wxSources/GlobalParameterFrame.h; sourceTree = SOURCE_ROOT; };
+               E4E2D5C410A249820007644A /* GlobalParameterFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GlobalParameterFrame.cpp; path = ../wxSources/GlobalParameterFrame.cpp; sourceTree = SOURCE_ROOT; };
+               E4E470C710AE6F8600F72B68 /* GlobalParameterFilesFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GlobalParameterFilesFrame.h; path = ../wxSources/GlobalParameterFilesFrame.h; sourceTree = SOURCE_ROOT; };
+               E4E470C810AE6F8600F72B68 /* GlobalParameterFilesFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GlobalParameterFilesFrame.cpp; path = ../wxSources/GlobalParameterFilesFrame.cpp; sourceTree = SOURCE_ROOT; };
+               E4FFA9030EE960650045EDF9 /* RubyDialogFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RubyDialogFrame.h; path = ../wxSources/RubyDialogFrame.h; sourceTree = SOURCE_ROOT; };
+               E4FFA9040EE960650045EDF9 /* RubyDialogFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RubyDialogFrame.cpp; path = ../wxSources/RubyDialogFrame.cpp; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               8D11072E0486CEB800E47090 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E43CCDDA0EB20F0F00108D2D /* Accelerate.framework in Frameworks */,
+                               E43CCDDB0EB20F0F00108D2D /* AGL.framework in Frameworks */,
+                               E43CCDDC0EB20F0F00108D2D /* Carbon.framework in Frameworks */,
+                               E43CCDDD0EB20F0F00108D2D /* Cocoa.framework in Frameworks */,
+                               E43CCDDE0EB20F0F00108D2D /* GLUT.framework in Frameworks */,
+                               E43CCDDF0EB20F0F00108D2D /* IOKit.framework in Frameworks */,
+                               E43CCDE00EB20F0F00108D2D /* OpenGL.framework in Frameworks */,
+                               E43CCDE10EB20F0F00108D2D /* QuickTime.framework in Frameworks */,
+                               E43CCDE20EB20F0F00108D2D /* System.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+                               13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
+                               29B97325FDCFA39411CA2CEA /* Foundation.framework */,
+                       );
+                       name = "Other Frameworks";
+                       sourceTree = "<group>";
+               };
+               19C28FACFE9D520D11CA2CBB /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               8D1107320486CEB800E47090 /* Molby.app */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               29B97314FDCFA39411CA2CEA /* MyWxOpenGLDoc2 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               29B97315FDCFA39411CA2CEA /* Sources */,
+                               E4CBF39010A91EBA00C7C74A /* doc */,
+                               E4A02338106B64F80094DBF8 /* Resources */,
+                               29B97323FDCFA39411CA2CEA /* Frameworks */,
+                               19C28FACFE9D520D11CA2CBB /* Products */,
+                       );
+                       name = MyWxOpenGLDoc2;
+                       sourceTree = "<group>";
+               };
+               29B97315FDCFA39411CA2CEA /* Sources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E43CCF150EB3229800108D2D /* wxSources */,
+                       );
+                       name = Sources;
+                       sourceTree = "<group>";
+               };
+               29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E43CCDCA0EB20E9E00108D2D /* Linked Frameworks */,
+                               1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               E42D8AEC1030192900C20247 /* MD */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E42D8AEF1030193F00C20247 /* MDGraphite.c */,
+                               E42D8AF01030193F00C20247 /* MDGraphite.h */,
+                               E42D8AF11030193F00C20247 /* MDCore.c */,
+                               E42D8AF21030193F00C20247 /* MDCore.h */,
+                               E42D8AF31030193F00C20247 /* MDForce.c */,
+                               E42D8AF41030193F00C20247 /* MDForce.h */,
+                               E42D8AF61030193F00C20247 /* MDPressure.c */,
+                               E42D8AF71030193F00C20247 /* MDPressure.h */,
+                               E49D17F1107753DF00C4841E /* MDSurface.c */,
+                               E49D17F2107753DF00C4841E /* MDSurface.h */,
+                       );
+                       path = MD;
+                       sourceTree = "<group>";
+               };
+               E43CCD8E0EB20BC800108D2D /* MolLib */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E42D8AEC1030192900C20247 /* MD */,
+                               E433CE7D0EC709BD00675985 /* IntGroup.h */,
+                               E433CE7C0EC709BD00675985 /* IntGroup.c */,
+                               E433CE7F0EC709BD00675985 /* MainView.h */,
+                               E433CE7E0EC709BD00675985 /* MainView.c */,
+                               E433CE810EC709BD00675985 /* Missing.h */,
+                               E433CE800EC709BD00675985 /* Missing.c */,
+                               E433CE830EC709BD00675985 /* MolAction.h */,
+                               E433CE820EC709BD00675985 /* MolAction.c */,
+                               E433CE850EC709BD00675985 /* Molecule.h */,
+                               E433CE840EC709BD00675985 /* Molecule.c */,
+                               E433CE860EC709BD00675985 /* MolLib.h */,
+                               E433CE880EC709BD00675985 /* Object.h */,
+                               E433CE870EC709BD00675985 /* Object.c */,
+                               E4C0C4940F25734B00161EC2 /* Dcd.h */,
+                               E4C0C4950F25734B00161EC2 /* Dcd.c */,
+                               E433CE8A0EC709BD00675985 /* Parameter.h */,
+                               E433CE890EC709BD00675985 /* Parameter.c */,
+                               E433CE8C0EC709BD00675985 /* Trackball.h */,
+                               E433CE8B0EC709BD00675985 /* Trackball.c */,
+                               E433CE8E0EC709BD00675985 /* Types.h */,
+                               E433CE8D0EC709BD00675985 /* Types.c */,
+                               E43CCE0F0EB210DD00108D2D /* Ruby_bind */,
+                       );
+                       name = MolLib;
+                       path = ../MolLib;
+                       sourceTree = SOURCE_ROOT;
+               };
+               E43CCDCA0EB20E9E00108D2D /* Linked Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E43CCDD10EB20F0F00108D2D /* Accelerate.framework */,
+                               E43CCDD20EB20F0F00108D2D /* AGL.framework */,
+                               E43CCDD30EB20F0F00108D2D /* Carbon.framework */,
+                               E43CCDD40EB20F0F00108D2D /* Cocoa.framework */,
+                               E43CCDD50EB20F0F00108D2D /* GLUT.framework */,
+                               E43CCDD60EB20F0F00108D2D /* IOKit.framework */,
+                               E43CCDD70EB20F0F00108D2D /* OpenGL.framework */,
+                               E43CCDD80EB20F0F00108D2D /* QuickTime.framework */,
+                               E43CCDD90EB20F0F00108D2D /* System.framework */,
+                       );
+                       name = "Linked Frameworks";
+                       sourceTree = "<group>";
+               };
+               E43CCE0F0EB210DD00108D2D /* Ruby_bind */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E433CE740EC7099B00675985 /* Molby_extern.h */,
+                               E433CE750EC7099B00675985 /* Molby.h */,
+                               E433CE760EC7099B00675985 /* ruby_bind.c */,
+                               E42D8BB21031BB2900C20247 /* ruby_md.c */,
+                               E4299DA60F2A191600780B44 /* ruby_types.c */,
+                               E433CE780EC7099B00675985 /* ruby_dialog.h */,
+                               E433CE770EC7099B00675985 /* ruby_dialog.c */,
+                       );
+                       path = Ruby_bind;
+                       sourceTree = "<group>";
+               };
+               E43CCF150EB3229800108D2D /* wxSources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E4A4667410C15B4300E40A9D /* listctrl.cpp */,
+                               E4513F530EED72C0009DF1F7 /* MyListCtrl.h */,
+                               E4513F540EED72C0009DF1F7 /* MyListCtrl.cpp */,
+                               E4C8CBDD10B83060006C4692 /* MyDocManager.h */,
+                               E4C8CBDE10B83060006C4692 /* MyDocManager.cpp */,
+                               E4FFA9030EE960650045EDF9 /* RubyDialogFrame.h */,
+                               E4FFA9040EE960650045EDF9 /* RubyDialogFrame.cpp */,
+                               E433CE9A0EC709D300675985 /* ConsoleFrame.h */,
+                               E433CE990EC709D300675985 /* ConsoleFrame.cpp */,
+                               E4E2D5C310A249820007644A /* GlobalParameterFrame.h */,
+                               E4E2D5C410A249820007644A /* GlobalParameterFrame.cpp */,
+                               E4E470C710AE6F8600F72B68 /* GlobalParameterFilesFrame.h */,
+                               E4E470C810AE6F8600F72B68 /* GlobalParameterFilesFrame.cpp */,
+                               E433CE9C0EC709D300675985 /* MoleculeView.h */,
+                               E433CE9B0EC709D300675985 /* MoleculeView.cpp */,
+                               E433CE9E0EC709D300675985 /* MyApp.h */,
+                               E433CE9D0EC709D300675985 /* MyApp.cpp */,
+                               E433CEA00EC709D300675985 /* MyCommand.h */,
+                               E433CE9F0EC709D300675985 /* MyCommand.cpp */,
+                               E433CEA20EC709D300675985 /* MyDocument.h */,
+                               E433CEA10EC709D300675985 /* MyDocument.cpp */,
+                               E46C9048108F491E000E984D /* MyThread.h */,
+                               E46C9049108F491E000E984D /* MyThread.cpp */,
+                               E433CEA40EC709D300675985 /* MyGLCanvas.h */,
+                               E433CEA30EC709D300675985 /* MyGLCanvas.cpp */,
+                               E43226E10ECE8681004963D7 /* MySlider.h */,
+                               E43226E20ECE8681004963D7 /* MySlider.cpp */,
+                               E496A76D0EDD392F00499646 /* MyClipboardData.h */,
+                               E496A76E0EDD392F00499646 /* MyClipboardData.cpp */,
+                               E44CEED5100E1E9C0040BD51 /* ProgressFrame.h */,
+                               E44CEED4100E1E9C0040BD51 /* ProgressFrame.cpp */,
+                               E472B7C41087F63200F931F3 /* MyVersion.c */,
+                               E43CCD8E0EB20BC800108D2D /* MolLib */,
+                       );
+                       name = wxSources;
+                       sourceTree = "<group>";
+               };
+               E4A02338106B64F80094DBF8 /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E4A02346106B65640094DBF8 /* molby_icon.icns */,
+                               E4A0234A106B657D0094DBF8 /* Scripts */,
+                               E4A0233F106B652F0094DBF8 /* InfoPlist.strings */,
+                               E4A0233B106B651C0094DBF8 /* Info.plist */,
+                       );
+                       name = Resources;
+                       sourceTree = "<group>";
+               };
+               E4CBF39010A91EBA00C7C74A /* doc */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E4CBF38C10A91EAC00C7C74A /* doc_source.html */,
+                               E4CBF39510A91ED500C7C74A /* style.css */,
+                               E4CBF39710A91EDA00C7C74A /* makedoc.rb */,
+                       );
+                       name = doc;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               8D1107260486CEB800E47090 /* Molby */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Molby" */;
+                       buildPhases = (
+                               E472B79C1087EFAC00F931F3 /* ShellScript */,
+                               8D1107290486CEB800E47090 /* Resources */,
+                               8D11072C0486CEB800E47090 /* Sources */,
+                               8D11072E0486CEB800E47090 /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = Molby;
+                       productInstallPath = "$(HOME)/Applications";
+                       productName = MyWxOpenGLDoc2;
+                       productReference = 8D1107320486CEB800E47090 /* Molby.app */;
+                       productType = "com.apple.product-type.application";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               29B97313FDCFA39411CA2CEA /* Project object */ = {
+                       isa = PBXProject;
+                       buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Molby" */;
+                       compatibilityVersion = "Xcode 2.4";
+                       hasScannedForEncodings = 1;
+                       mainGroup = 29B97314FDCFA39411CA2CEA /* MyWxOpenGLDoc2 */;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               8D1107260486CEB800E47090 /* Molby */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               8D1107290486CEB800E47090 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E4A02341106B652F0094DBF8 /* InfoPlist.strings in Resources */,
+                               E4A02347106B65640094DBF8 /* molby_icon.icns in Resources */,
+                               E4A02357106B657D0094DBF8 /* Scripts in Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+               E472B79C1087EFAC00F931F3 /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "if [ \"$BUILD_STYLE\" = \"Release\" ]; then\n  cd $PROJECT_DIR/..\n  /usr/bin/ruby \"update_version.rb\"\n  cd Documents\n  /usr/bin/ruby \"makedoc.rb\"\nfi\n\n";
+               };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               8D11072C0486CEB800E47090 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E433CE790EC7099B00675985 /* ruby_bind.c in Sources */,
+                               E433CE900EC709BD00675985 /* IntGroup.c in Sources */,
+                               E433CE910EC709BD00675985 /* MainView.c in Sources */,
+                               E433CE920EC709BD00675985 /* Missing.c in Sources */,
+                               E433CE930EC709BD00675985 /* MolAction.c in Sources */,
+                               E433CE940EC709BD00675985 /* Molecule.c in Sources */,
+                               E433CE950EC709BD00675985 /* Object.c in Sources */,
+                               E433CE960EC709BD00675985 /* Parameter.c in Sources */,
+                               E433CE970EC709BD00675985 /* Trackball.c in Sources */,
+                               E433CE980EC709BD00675985 /* Types.c in Sources */,
+                               E433CEA50EC709D300675985 /* ConsoleFrame.cpp in Sources */,
+                               E433CEA60EC709D300675985 /* MoleculeView.cpp in Sources */,
+                               E433CEA70EC709D300675985 /* MyApp.cpp in Sources */,
+                               E433CEA80EC709D300675985 /* MyCommand.cpp in Sources */,
+                               E433CEA90EC709D300675985 /* MyDocument.cpp in Sources */,
+                               E433CEAA0EC709D300675985 /* MyGLCanvas.cpp in Sources */,
+                               E43226E30ECE8681004963D7 /* MySlider.cpp in Sources */,
+                               E496A76F0EDD392F00499646 /* MyClipboardData.cpp in Sources */,
+                               E4FFA9050EE960650045EDF9 /* RubyDialogFrame.cpp in Sources */,
+                               E4FFA95E0EEAADCA0045EDF9 /* ruby_dialog.c in Sources */,
+                               E4513F550EED72C0009DF1F7 /* MyListCtrl.cpp in Sources */,
+                               E4C0C4960F25734B00161EC2 /* Dcd.c in Sources */,
+                               E4299DA70F2A191600780B44 /* ruby_types.c in Sources */,
+                               E44CEED6100E1E9C0040BD51 /* ProgressFrame.cpp in Sources */,
+                               E42D8AF81030193F00C20247 /* MDGraphite.c in Sources */,
+                               E42D8AF91030193F00C20247 /* MDCore.c in Sources */,
+                               E42D8AFA1030193F00C20247 /* MDForce.c in Sources */,
+                               E42D8AFB1030193F00C20247 /* MDPressure.c in Sources */,
+                               E42D8BB31031BB2900C20247 /* ruby_md.c in Sources */,
+                               E49D17F3107753DF00C4841E /* MDSurface.c in Sources */,
+                               E472B7C51087F63200F931F3 /* MyVersion.c in Sources */,
+                               E46C904A108F491E000E984D /* MyThread.cpp in Sources */,
+                               E4E2D5C510A249820007644A /* GlobalParameterFrame.cpp in Sources */,
+                               E4E470C910AE6F8600F72B68 /* GlobalParameterFilesFrame.cpp in Sources */,
+                               E4C8CBDF10B83060006C4692 /* MyDocManager.cpp in Sources */,
+                               E4A4667610C15B4300E40A9D /* listctrl.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+               E4A0233F106B652F0094DBF8 /* InfoPlist.strings */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               E4A02340106B652F0094DBF8 /* English */,
+                       );
+                       name = InfoPlist.strings;
+                       sourceTree = "<group>";
+               };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+               C01FCF4B08A954540054247B /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DEBUGGING_SYMBOLS = full;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_FIX_AND_CONTINUE = YES;
+                               GCC_MODEL_TUNING = G5;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_VERSION = 4.0;
+                               HEADER_SEARCH_PATHS = (
+                                       "$(HOME)/Development/wxMac/include",
+                                       "$(HOME)/Development/wxMac/osx-build/lib/wx/include/mac-unicode-release-static-2.8",
+                                       "$(HOME)/Development/ruby-1.8.7-static",
+                               );
+                               INFOPLIST_FILE = Info.plist;
+                               INSTALL_PATH = "$(HOME)/Applications";
+                               LIBRARY_SEARCH_PATHS = (
+                                       "$(HOME)/Development/wxMac/osx-build/lib",
+                                       "$(HOME)/Development/ruby-1.8.7-static",
+                               );
+                               OTHER_CFLAGS = (
+                                       "-D_FILE_OFFSET_BITS=64",
+                                       "-D_LARGE_FILES",
+                                       "-D__WXMAC__",
+                               );
+                               OTHER_LDFLAGS = (
+                                       "-lwx_macu_html-2.8",
+                                       "-lwx_macu_richtext-2.8",
+                                       "-lwx_macu_core-2.8",
+                                       "-lwx_base_carbonu-2.8",
+                                       "-lwx_macu_gl-2.8",
+                                       "-lwx_macu_adv-2.8",
+                                       "-lwxregexu-2.8",
+                                       "-lwxexpat-2.8",
+                                       "-lwxtiff-2.8",
+                                       "-lwxjpeg-2.8",
+                                       "-lwxpng-2.8",
+                                       "-lz",
+                                       "-lpthread",
+                                       "-liconv",
+                                       "-lruby-static",
+                               );
+                               PRODUCT_NAME = Molby;
+                               SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+                               WRAPPER_EXTENSION = app;
+                               ZERO_LINK = NO;
+                       };
+                       name = Debug;
+               };
+               C01FCF4C08A954540054247B /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = (
+                                       ppc,
+                                       i386,
+                               );
+                               COPY_PHASE_STRIP = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_MODEL_TUNING = G5;
+                               HEADER_SEARCH_PATHS = (
+                                       "$(HOME)/Development/wxMac/include",
+                                       "$(HOME)/Development/wxMac/osx-build/lib/wx/include/mac-unicode-release-static-2.8",
+                                       "$(HOME)/Development/ruby-1.8.7-static",
+                               );
+                               INFOPLIST_FILE = Info.plist;
+                               INSTALL_PATH = "$(HOME)/Applications";
+                               LIBRARY_SEARCH_PATHS = (
+                                       "$(HOME)/Development/wxMac/osx-build/lib",
+                                       "$(HOME)/Development/ruby-1.8.7-static",
+                               );
+                               OTHER_CFLAGS = (
+                                       "-D_FILE_OFFSET_BITS=64",
+                                       "-D_LARGE_FILES",
+                                       "-D__WXMAC__",
+                               );
+                               OTHER_LDFLAGS = (
+                                       "-lwx_macu_html-2.8",
+                                       "-lwx_macu_richtext-2.8",
+                                       "-lwx_macu_core-2.8",
+                                       "-lwx_base_carbonu-2.8",
+                                       "-lwx_macu_gl-2.8",
+                                       "-lwx_macu_adv-2.8",
+                                       "-lwxregexu-2.8",
+                                       "-lwxexpat-2.8",
+                                       "-lwxtiff-2.8",
+                                       "-lwxjpeg-2.8",
+                                       "-lwxpng-2.8",
+                                       "-lz",
+                                       "-lpthread",
+                                       "-liconv",
+                                       "-lruby-static",
+                               );
+                               PRODUCT_NAME = Molby;
+                               WRAPPER_EXTENSION = app;
+                               ZERO_LINK = NO;
+                       };
+                       name = Release;
+               };
+               C01FCF4F08A954540054247B /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               PREBINDING = NO;
+                               SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+                       };
+                       name = Debug;
+               };
+               C01FCF5008A954540054247B /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               PREBINDING = NO;
+                               SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Molby" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               C01FCF4B08A954540054247B /* Debug */,
+                               C01FCF4C08A954540054247B /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Molby" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               C01FCF4F08A954540054247B /* Debug */,
+                               C01FCF5008A954540054247B /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/xcode-build/Molby.xcodeproj/toshi_n.mode1v3 b/xcode-build/Molby.xcodeproj/toshi_n.mode1v3
new file mode 100644 (file)
index 0000000..18efd6f
--- /dev/null
@@ -0,0 +1,1382 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>ActivePerspectiveName</key>
+       <string>Project</string>
+       <key>AllowedModules</key>
+       <array>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXSmartGroupTreeModule</string>
+                       <key>Name</key>
+                       <string>Groups and Files Outline View</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXNavigatorGroup</string>
+                       <key>Name</key>
+                       <string>Editor</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCTaskListModule</string>
+                       <key>Name</key>
+                       <string>Task List</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCDetailModule</string>
+                       <key>Name</key>
+                       <string>File and Smart Group Detail Viewer</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXBuildResultsModule</string>
+                       <key>Name</key>
+                       <string>Detailed Build Results Viewer</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXProjectFindModule</string>
+                       <key>Name</key>
+                       <string>Project Batch Find Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCProjectFormatConflictsModule</string>
+                       <key>Name</key>
+                       <string>Project Format Conflicts List</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXBookmarksModule</string>
+                       <key>Name</key>
+                       <string>Bookmarks Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXClassBrowserModule</string>
+                       <key>Name</key>
+                       <string>Class Browser</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXCVSModule</string>
+                       <key>Name</key>
+                       <string>Source Code Control Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXDebugBreakpointsModule</string>
+                       <key>Name</key>
+                       <string>Debug Breakpoints Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCDockableInspector</string>
+                       <key>Name</key>
+                       <string>Inspector</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>PBXOpenQuicklyModule</string>
+                       <key>Name</key>
+                       <string>Open Quickly Tool</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXDebugSessionModule</string>
+                       <key>Name</key>
+                       <string>Debugger</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>1</string>
+                       <key>Module</key>
+                       <string>PBXDebugCLIModule</string>
+                       <key>Name</key>
+                       <string>Debug Console</string>
+               </dict>
+               <dict>
+                       <key>BundleLoadPath</key>
+                       <string></string>
+                       <key>MaxInstances</key>
+                       <string>n</string>
+                       <key>Module</key>
+                       <string>XCSnapshotModule</string>
+                       <key>Name</key>
+                       <string>Snapshots Tool</string>
+               </dict>
+       </array>
+       <key>BundlePath</key>
+       <string>/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources</string>
+       <key>Description</key>
+       <string>DefaultDescriptionKey</string>
+       <key>DockingSystemVisible</key>
+       <false/>
+       <key>Extension</key>
+       <string>mode1v3</string>
+       <key>FavBarConfig</key>
+       <dict>
+               <key>PBXProjectModuleGUID</key>
+               <string>E4157E011108B47A00ECF4D9</string>
+               <key>XCBarModuleItemNames</key>
+               <dict/>
+               <key>XCBarModuleItems</key>
+               <array/>
+       </dict>
+       <key>FirstTimeWindowDisplayed</key>
+       <false/>
+       <key>Identifier</key>
+       <string>com.apple.perspectives.project.mode1v3</string>
+       <key>MajorVersion</key>
+       <integer>33</integer>
+       <key>MinorVersion</key>
+       <integer>0</integer>
+       <key>Name</key>
+       <string>Default</string>
+       <key>Notifications</key>
+       <array/>
+       <key>OpenEditors</key>
+       <array/>
+       <key>PerspectiveWidths</key>
+       <array>
+               <integer>-1</integer>
+               <integer>-1</integer>
+       </array>
+       <key>Perspectives</key>
+       <array>
+               <dict>
+                       <key>ChosenToolbarItems</key>
+                       <array>
+                               <string>active-combo-popup</string>
+                               <string>action</string>
+                               <string>NSToolbarFlexibleSpaceItem</string>
+                               <string>build</string>
+                               <string>build-and-go</string>
+                               <string>com.apple.ide.PBXToolbarStopButton</string>
+                               <string>get-info</string>
+                               <string>NSToolbarFlexibleSpaceItem</string>
+                               <string>com.apple.pbx.toolbar.searchfield</string>
+                       </array>
+                       <key>ControllerClassBaseName</key>
+                       <string></string>
+                       <key>IconName</key>
+                       <string>WindowOfProjectWithEditor</string>
+                       <key>Identifier</key>
+                       <string>perspective.project</string>
+                       <key>IsVertical</key>
+                       <false/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>BecomeActive</key>
+                                       <true/>
+                                       <key>ContentConfiguration</key>
+                                       <dict>
+                                               <key>PBXBottomSmartGroupGIDs</key>
+                                               <array>
+                                                       <string>1C37FBAC04509CD000000102</string>
+                                                       <string>1C37FAAC04509CD000000102</string>
+                                                       <string>1C08E77C0454961000C914BD</string>
+                                                       <string>1C37FABC05509CD000000102</string>
+                                                       <string>1C37FABC05539CD112110102</string>
+                                                       <string>E2644B35053B69B200211256</string>
+                                                       <string>1C37FABC04509CD000100104</string>
+                                                       <string>1CC0EA4004350EF90044410B</string>
+                                                       <string>1CC0EA4004350EF90041110B</string>
+                                               </array>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>1CE0B1FE06471DED0097A5F4</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>Files</string>
+                                               <key>PBXProjectStructureProvided</key>
+                                               <string>yes</string>
+                                               <key>PBXSmartGroupTreeModuleColumnData</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+                                                       <array>
+                                                               <real>186</real>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+                                                       <array>
+                                                               <string>MainColumn</string>
+                                                       </array>
+                                               </dict>
+                                               <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+                                                       <array>
+                                                               <string>29B97314FDCFA39411CA2CEA</string>
+                                                               <string>1C37FABC05509CD000000102</string>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+                                                       <array>
+                                                               <array>
+                                                                       <integer>0</integer>
+                                                               </array>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+                                                       <string>{{0, 0}, {186, 445}}</string>
+                                               </dict>
+                                               <key>PBXTopSmartGroupGIDs</key>
+                                               <array/>
+                                               <key>XCIncludePerspectivesSwitch</key>
+                                               <true/>
+                                               <key>XCSharingToken</key>
+                                               <string>com.apple.Xcode.GFSharingToken</string>
+                                       </dict>
+                                       <key>GeometryConfiguration</key>
+                                       <dict>
+                                               <key>Frame</key>
+                                               <string>{{0, 0}, {203, 463}}</string>
+                                               <key>GroupTreeTableConfiguration</key>
+                                               <array>
+                                                       <string>MainColumn</string>
+                                                       <real>186</real>
+                                               </array>
+                                               <key>RubberWindowFrame</key>
+                                               <string>31 251 788 504 0 0 1280 778 </string>
+                                       </dict>
+                                       <key>Module</key>
+                                       <string>PBXSmartGroupTreeModule</string>
+                                       <key>Proportion</key>
+                                       <string>203pt</string>
+                               </dict>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CE0B20306471E060097A5F4</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>MyNewFile14.java</string>
+                                                               <key>PBXSplitModuleInNavigatorKey</key>
+                                                               <dict>
+                                                                       <key>Split0</key>
+                                                                       <dict>
+                                                                               <key>PBXProjectModuleGUID</key>
+                                                                               <string>1CE0B20406471E060097A5F4</string>
+                                                                               <key>PBXProjectModuleLabel</key>
+                                                                               <string>MyNewFile14.java</string>
+                                                                       </dict>
+                                                                       <key>SplitCount</key>
+                                                                       <string>1</string>
+                                                               </dict>
+                                                               <key>StatusBarVisibility</key>
+                                                               <true/>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {580, 285}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>31 251 788 504 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>285pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CE0B20506471E060097A5F4</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>詳細</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 290}, {580, 173}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>31 251 788 504 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>XCDetailModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>173pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>580pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Project</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCModuleDock</string>
+                               <string>PBXSmartGroupTreeModule</string>
+                               <string>XCModuleDock</string>
+                               <string>PBXNavigatorGroup</string>
+                               <string>XCDetailModule</string>
+                       </array>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>E4157DFF1108B47A00ECF4D9</string>
+                               <string>1CE0B1FE06471DED0097A5F4</string>
+                               <string>E4157E001108B47A00ECF4D9</string>
+                               <string>1CE0B20306471E060097A5F4</string>
+                               <string>1CE0B20506471E060097A5F4</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.defaultV3</string>
+               </dict>
+               <dict>
+                       <key>ControllerClassBaseName</key>
+                       <string></string>
+                       <key>IconName</key>
+                       <string>WindowOfProject</string>
+                       <key>Identifier</key>
+                       <string>perspective.morph</string>
+                       <key>IsVertical</key>
+                       <integer>0</integer>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>BecomeActive</key>
+                                       <integer>1</integer>
+                                       <key>ContentConfiguration</key>
+                                       <dict>
+                                               <key>PBXBottomSmartGroupGIDs</key>
+                                               <array>
+                                                       <string>1C37FBAC04509CD000000102</string>
+                                                       <string>1C37FAAC04509CD000000102</string>
+                                                       <string>1C08E77C0454961000C914BD</string>
+                                                       <string>1C37FABC05509CD000000102</string>
+                                                       <string>1C37FABC05539CD112110102</string>
+                                                       <string>E2644B35053B69B200211256</string>
+                                                       <string>1C37FABC04509CD000100104</string>
+                                                       <string>1CC0EA4004350EF90044410B</string>
+                                                       <string>1CC0EA4004350EF90041110B</string>
+                                               </array>
+                                               <key>PBXProjectModuleGUID</key>
+                                               <string>11E0B1FE06471DED0097A5F4</string>
+                                               <key>PBXProjectModuleLabel</key>
+                                               <string>Files</string>
+                                               <key>PBXProjectStructureProvided</key>
+                                               <string>yes</string>
+                                               <key>PBXSmartGroupTreeModuleColumnData</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+                                                       <array>
+                                                               <real>186</real>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+                                                       <array>
+                                                               <string>MainColumn</string>
+                                                       </array>
+                                               </dict>
+                                               <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+                                               <dict>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+                                                       <array>
+                                                               <string>29B97314FDCFA39411CA2CEA</string>
+                                                               <string>1C37FABC05509CD000000102</string>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+                                                       <array>
+                                                               <array>
+                                                                       <integer>0</integer>
+                                                               </array>
+                                                       </array>
+                                                       <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+                                                       <string>{{0, 0}, {186, 337}}</string>
+                                               </dict>
+                                               <key>PBXTopSmartGroupGIDs</key>
+                                               <array/>
+                                               <key>XCIncludePerspectivesSwitch</key>
+                                               <integer>1</integer>
+                                               <key>XCSharingToken</key>
+                                               <string>com.apple.Xcode.GFSharingToken</string>
+                                       </dict>
+                                       <key>GeometryConfiguration</key>
+                                       <dict>
+                                               <key>Frame</key>
+                                               <string>{{0, 0}, {203, 355}}</string>
+                                               <key>GroupTreeTableConfiguration</key>
+                                               <array>
+                                                       <string>MainColumn</string>
+                                                       <real>186</real>
+                                               </array>
+                                               <key>RubberWindowFrame</key>
+                                               <string>373 269 690 397 0 0 1440 878 </string>
+                                       </dict>
+                                       <key>Module</key>
+                                       <string>PBXSmartGroupTreeModule</string>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Morph</string>
+                       <key>PreferredWidth</key>
+                       <integer>300</integer>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCModuleDock</string>
+                               <string>PBXSmartGroupTreeModule</string>
+                       </array>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>11E0B1FE06471DED0097A5F4</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.default.shortV3</string>
+               </dict>
+       </array>
+       <key>PerspectivesBarVisible</key>
+       <false/>
+       <key>ShelfIsVisible</key>
+       <false/>
+       <key>SourceDescription</key>
+       <string>file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec'</string>
+       <key>StatusbarIsVisible</key>
+       <true/>
+       <key>TimeStamp</key>
+       <real>0.0</real>
+       <key>ToolbarDisplayMode</key>
+       <integer>1</integer>
+       <key>ToolbarIsVisible</key>
+       <true/>
+       <key>ToolbarSizeMode</key>
+       <integer>1</integer>
+       <key>Type</key>
+       <string>Perspectives</string>
+       <key>UpdateMessage</key>
+       <string>The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature).  You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature.  Do you wish to update to the latest Workspace defaults for project '%@'?</string>
+       <key>WindowJustification</key>
+       <integer>5</integer>
+       <key>WindowOrderList</key>
+       <array>
+               <string>E4157E021108B47A00ECF4D9</string>
+               <string>E4157E031108B47A00ECF4D9</string>
+               <string>1C78EAAD065D492600B07095</string>
+               <string>1CD10A99069EF8BA00B06720</string>
+               <string>E4157DF51108B47700ECF4D9</string>
+               <string>/Users/toshi_n/Development/Molby/xcode-build/Molby.xcodeproj</string>
+       </array>
+       <key>WindowString</key>
+       <string>31 251 788 504 0 0 1280 778 </string>
+       <key>WindowToolsV3</key>
+       <array>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.build</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CD0528F0623707200166675</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string></string>
+                                                               <key>StatusBarVisibility</key>
+                                                               <true/>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {500, 218}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>52 232 500 500 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>218pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <true/>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>XCMainBuildResultsModuleGUID</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>ビルド</string>
+                                                               <key>XCBuildResultsTrigger_Collapse</key>
+                                                               <integer>1021</integer>
+                                                               <key>XCBuildResultsTrigger_Open</key>
+                                                               <integer>1011</integer>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 223}, {500, 236}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>52 232 500 500 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXBuildResultsModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>236pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>459pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Build Results</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXBuildResultsModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>E4157DF51108B47700ECF4D9</string>
+                               <string>E4157DF61108B47700ECF4D9</string>
+                               <string>1CD0528F0623707200166675</string>
+                               <string>XCMainBuildResultsModuleGUID</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.buildV3</string>
+                       <key>WindowString</key>
+                       <string>52 232 500 500 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>E4157DF51108B47700ECF4D9</string>
+                       <key>WindowToolIsVisible</key>
+                       <false/>
+               </dict>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.debugger</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>Debugger</key>
+                                                               <dict>
+                                                                       <key>HorizontalSplitView</key>
+                                                                       <dict>
+                                                                               <key>_collapsingFrameDimension</key>
+                                                                               <real>0.0</real>
+                                                                               <key>_indexOfCollapsedView</key>
+                                                                               <integer>0</integer>
+                                                                               <key>_percentageOfCollapsedView</key>
+                                                                               <real>0.0</real>
+                                                                               <key>isCollapsed</key>
+                                                                               <string>yes</string>
+                                                                               <key>sizes</key>
+                                                                               <array>
+                                                                                       <string>{{0, 0}, {316, 185}}</string>
+                                                                                       <string>{{316, 0}, {378, 185}}</string>
+                                                                               </array>
+                                                                       </dict>
+                                                                       <key>VerticalSplitView</key>
+                                                                       <dict>
+                                                                               <key>_collapsingFrameDimension</key>
+                                                                               <real>0.0</real>
+                                                                               <key>_indexOfCollapsedView</key>
+                                                                               <integer>0</integer>
+                                                                               <key>_percentageOfCollapsedView</key>
+                                                                               <real>0.0</real>
+                                                                               <key>isCollapsed</key>
+                                                                               <string>yes</string>
+                                                                               <key>sizes</key>
+                                                                               <array>
+                                                                                       <string>{{0, 0}, {694, 185}}</string>
+                                                                                       <string>{{0, 185}, {694, 196}}</string>
+                                                                               </array>
+                                                                       </dict>
+                                                               </dict>
+                                                               <key>LauncherConfigVersion</key>
+                                                               <string>8</string>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1C162984064C10D400B95A72</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Debug - GLUTExamples (Underwater)</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>DebugConsoleVisible</key>
+                                                               <string>None</string>
+                                                               <key>DebugConsoleWindowFrame</key>
+                                                               <string>{{200, 200}, {500, 300}}</string>
+                                                               <key>DebugSTDIOWindowFrame</key>
+                                                               <string>{{200, 200}, {500, 300}}</string>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {694, 381}}</string>
+                                                               <key>PBXDebugSessionStackFrameViewKey</key>
+                                                               <dict>
+                                                                       <key>DebugVariablesTableConfiguration</key>
+                                                                       <array>
+                                                                               <string>Name</string>
+                                                                               <real>120</real>
+                                                                               <string>Value</string>
+                                                                               <real>85</real>
+                                                                               <string>Summary</string>
+                                                                               <real>148</real>
+                                                                       </array>
+                                                                       <key>Frame</key>
+                                                                       <string>{{316, 0}, {378, 185}}</string>
+                                                                       <key>RubberWindowFrame</key>
+                                                                       <string>73 287 694 422 0 0 1280 778 </string>
+                                                               </dict>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>73 287 694 422 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXDebugSessionModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>381pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>381pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Debugger</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXDebugSessionModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1CD10A99069EF8BA00B06720</string>
+                               <string>E4157DF71108B47700ECF4D9</string>
+                               <string>1C162984064C10D400B95A72</string>
+                               <string>E4157DF81108B47700ECF4D9</string>
+                               <string>E4157DF91108B47700ECF4D9</string>
+                               <string>E4157DFA1108B47700ECF4D9</string>
+                               <string>E4157DFB1108B47700ECF4D9</string>
+                               <string>E4157DFC1108B47700ECF4D9</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.debugV3</string>
+                       <key>WindowString</key>
+                       <string>73 287 694 422 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1CD10A99069EF8BA00B06720</string>
+                       <key>WindowToolIsVisible</key>
+                       <false/>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.find</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Dock</key>
+                                                       <array>
+                                                               <dict>
+                                                                       <key>ContentConfiguration</key>
+                                                                       <dict>
+                                                                               <key>PBXProjectModuleGUID</key>
+                                                                               <string>1CDD528C0622207200134675</string>
+                                                                               <key>PBXProjectModuleLabel</key>
+                                                                               <string>&lt;No Editor&gt;</string>
+                                                                               <key>PBXSplitModuleInNavigatorKey</key>
+                                                                               <dict>
+                                                                                       <key>Split0</key>
+                                                                                       <dict>
+                                                                                               <key>PBXProjectModuleGUID</key>
+                                                                                               <string>1CD0528D0623707200166675</string>
+                                                                                       </dict>
+                                                                                       <key>SplitCount</key>
+                                                                                       <string>1</string>
+                                                                               </dict>
+                                                                               <key>StatusBarVisibility</key>
+                                                                               <integer>1</integer>
+                                                                       </dict>
+                                                                       <key>GeometryConfiguration</key>
+                                                                       <dict>
+                                                                               <key>Frame</key>
+                                                                               <string>{{0, 0}, {781, 167}}</string>
+                                                                               <key>RubberWindowFrame</key>
+                                                                               <string>62 385 781 470 0 0 1440 878 </string>
+                                                                       </dict>
+                                                                       <key>Module</key>
+                                                                       <string>PBXNavigatorGroup</string>
+                                                                       <key>Proportion</key>
+                                                                       <string>781pt</string>
+                                                               </dict>
+                                                       </array>
+                                                       <key>Proportion</key>
+                                                       <string>50%</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CD0528E0623707200166675</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Project Find</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{8, 0}, {773, 254}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>62 385 781 470 0 0 1440 878 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXProjectFindModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>50%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>428pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Project Find</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXProjectFindModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>1</integer>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C530D57069F1CE1000CFCEE</string>
+                               <string>1C530D58069F1CE1000CFCEE</string>
+                               <string>1C530D59069F1CE1000CFCEE</string>
+                               <string>1CDD528C0622207200134675</string>
+                               <string>1C530D5A069F1CE1000CFCEE</string>
+                               <string>1CE0B1FE06471DED0097A5F4</string>
+                               <string>1CD0528E0623707200166675</string>
+                       </array>
+                       <key>WindowString</key>
+                       <string>62 385 781 470 0 0 1440 878 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1C530D57069F1CE1000CFCEE</string>
+                       <key>WindowToolIsVisible</key>
+                       <integer>0</integer>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>MENUSEPARATOR</string>
+               </dict>
+               <dict>
+                       <key>FirstTimeWindowDisplayed</key>
+                       <false/>
+                       <key>Identifier</key>
+                       <string>windowTool.debuggerConsole</string>
+                       <key>IsVertical</key>
+                       <true/>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1C78EAAC065D492600B07095</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Debugger Console</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {650, 209}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>73 459 650 250 0 0 1280 778 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXDebugCLIModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>209pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>209pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Debugger Console</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXDebugCLIModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <true/>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C78EAAD065D492600B07095</string>
+                               <string>E4157DFD1108B47700ECF4D9</string>
+                               <string>1C78EAAC065D492600B07095</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.consoleV3</string>
+                       <key>WindowString</key>
+                       <string>73 459 650 250 0 0 1280 778 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1C78EAAD065D492600B07095</string>
+                       <key>WindowToolIsVisible</key>
+                       <false/>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.snapshots</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Module</key>
+                                                       <string>XCSnapshotModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Snapshots</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCSnapshotModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <string>Yes</string>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.snapshots</string>
+                       <key>WindowString</key>
+                       <string>315 824 300 550 0 0 1440 878 </string>
+                       <key>WindowToolIsVisible</key>
+                       <string>Yes</string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.scm</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1C78EAB2065D492600B07095</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>&lt;No Editor&gt;</string>
+                                                               <key>PBXSplitModuleInNavigatorKey</key>
+                                                               <dict>
+                                                                       <key>Split0</key>
+                                                                       <dict>
+                                                                               <key>PBXProjectModuleGUID</key>
+                                                                               <string>1C78EAB3065D492600B07095</string>
+                                                                       </dict>
+                                                                       <key>SplitCount</key>
+                                                                       <string>1</string>
+                                                               </dict>
+                                                               <key>StatusBarVisibility</key>
+                                                               <integer>1</integer>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {452, 0}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>743 379 452 308 0 0 1280 1002 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>0pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CD052920623707200166675</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>SCM</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>ConsoleFrame</key>
+                                                               <string>{{0, 259}, {452, 0}}</string>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 7}, {452, 259}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>743 379 452 308 0 0 1280 1002 </string>
+                                                               <key>TableConfiguration</key>
+                                                               <array>
+                                                                       <string>Status</string>
+                                                                       <real>30</real>
+                                                                       <string>FileName</string>
+                                                                       <real>199</real>
+                                                                       <string>Path</string>
+                                                                       <real>197.0950012207031</real>
+                                                               </array>
+                                                               <key>TableFrame</key>
+                                                               <string>{{0, 0}, {452, 250}}</string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXCVSModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>262pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>266pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>SCM</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXCVSModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>1</integer>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C78EAB4065D492600B07095</string>
+                               <string>1C78EAB5065D492600B07095</string>
+                               <string>1C78EAB2065D492600B07095</string>
+                               <string>1CD052920623707200166675</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.scm</string>
+                       <key>WindowString</key>
+                       <string>743 379 452 308 0 0 1280 1002 </string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.breakpoints</string>
+                       <key>IsVertical</key>
+                       <integer>0</integer>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXBottomSmartGroupGIDs</key>
+                                                               <array>
+                                                                       <string>1C77FABC04509CD000000102</string>
+                                                               </array>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CE0B1FE06471DED0097A5F4</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Files</string>
+                                                               <key>PBXProjectStructureProvided</key>
+                                                               <string>no</string>
+                                                               <key>PBXSmartGroupTreeModuleColumnData</key>
+                                                               <dict>
+                                                                       <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+                                                                       <array>
+                                                                               <real>168</real>
+                                                                       </array>
+                                                                       <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+                                                                       <array>
+                                                                               <string>MainColumn</string>
+                                                                       </array>
+                                                               </dict>
+                                                               <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+                                                               <dict>
+                                                                       <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+                                                                       <array>
+                                                                               <string>1C77FABC04509CD000000102</string>
+                                                                       </array>
+                                                                       <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+                                                                       <array>
+                                                                               <array>
+                                                                                       <integer>0</integer>
+                                                                               </array>
+                                                                       </array>
+                                                                       <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+                                                                       <string>{{0, 0}, {168, 350}}</string>
+                                                               </dict>
+                                                               <key>PBXTopSmartGroupGIDs</key>
+                                                               <array/>
+                                                               <key>XCIncludePerspectivesSwitch</key>
+                                                               <integer>0</integer>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {185, 368}}</string>
+                                                               <key>GroupTreeTableConfiguration</key>
+                                                               <array>
+                                                                       <string>MainColumn</string>
+                                                                       <real>168</real>
+                                                               </array>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>315 424 744 409 0 0 1440 878 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXSmartGroupTreeModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>185pt</string>
+                                               </dict>
+                                               <dict>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CA1AED706398EBD00589147</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Detail</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{{190, 0}, {554, 368}}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>315 424 744 409 0 0 1440 878 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>XCDetailModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>554pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>368pt</string>
+                               </dict>
+                       </array>
+                       <key>MajorVersion</key>
+                       <integer>3</integer>
+                       <key>MinorVersion</key>
+                       <integer>0</integer>
+                       <key>Name</key>
+                       <string>Breakpoints</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXSmartGroupTreeModule</string>
+                               <string>XCDetailModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>1</integer>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1CDDB66807F98D9800BB5817</string>
+                               <string>1CDDB66907F98D9800BB5817</string>
+                               <string>1CE0B1FE06471DED0097A5F4</string>
+                               <string>1CA1AED706398EBD00589147</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.breakpointsV3</string>
+                       <key>WindowString</key>
+                       <string>315 424 744 409 0 0 1440 878 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1CDDB66807F98D9800BB5817</string>
+                       <key>WindowToolIsVisible</key>
+                       <integer>1</integer>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.debugAnimator</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Module</key>
+                                                       <string>PBXNavigatorGroup</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Debug Visualizer</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXNavigatorGroup</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>1</integer>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.debugAnimatorV3</string>
+                       <key>WindowString</key>
+                       <string>100 100 700 500 0 0 1280 1002 </string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.bookmarks</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Module</key>
+                                                       <string>PBXBookmarksModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Bookmarks</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXBookmarksModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>0</integer>
+                       <key>WindowString</key>
+                       <string>538 42 401 187 0 0 1280 1002 </string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.projectFormatConflicts</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>Module</key>
+                                                       <string>XCProjectFormatConflictsModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Project Format Conflicts</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCProjectFormatConflictsModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>0</integer>
+                       <key>WindowContentMinSize</key>
+                       <string>450 300</string>
+                       <key>WindowString</key>
+                       <string>50 850 472 307 0 0 1440 877</string>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.classBrowser</string>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>ContentConfiguration</key>
+                                                       <dict>
+                                                               <key>OptionsSetName</key>
+                                                               <string>Hierarchy, all classes</string>
+                                                               <key>PBXProjectModuleGUID</key>
+                                                               <string>1CA6456E063B45B4001379D8</string>
+                                                               <key>PBXProjectModuleLabel</key>
+                                                               <string>Class Browser - NSObject</string>
+                                                       </dict>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>ClassesFrame</key>
+                                                               <string>{{0, 0}, {374, 96}}</string>
+                                                               <key>ClassesTreeTableConfiguration</key>
+                                                               <array>
+                                                                       <string>PBXClassNameColumnIdentifier</string>
+                                                                       <real>208</real>
+                                                                       <string>PBXClassBookColumnIdentifier</string>
+                                                                       <real>22</real>
+                                                               </array>
+                                                               <key>Frame</key>
+                                                               <string>{{0, 0}, {630, 331}}</string>
+                                                               <key>MembersFrame</key>
+                                                               <string>{{0, 105}, {374, 395}}</string>
+                                                               <key>MembersTreeTableConfiguration</key>
+                                                               <array>
+                                                                       <string>PBXMemberTypeIconColumnIdentifier</string>
+                                                                       <real>22</real>
+                                                                       <string>PBXMemberNameColumnIdentifier</string>
+                                                                       <real>216</real>
+                                                                       <string>PBXMemberTypeColumnIdentifier</string>
+                                                                       <real>97</real>
+                                                                       <string>PBXMemberBookColumnIdentifier</string>
+                                                                       <real>22</real>
+                                                               </array>
+                                                               <key>PBXModuleWindowStatusBarHidden2</key>
+                                                               <integer>1</integer>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>385 179 630 352 0 0 1440 878 </string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>PBXClassBrowserModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>332pt</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>332pt</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Class Browser</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>PBXClassBrowserModule</string>
+                       </array>
+                       <key>StatusbarIsVisible</key>
+                       <integer>0</integer>
+                       <key>TableOfContents</key>
+                       <array>
+                               <string>1C0AD2AF069F1E9B00FABCE6</string>
+                               <string>1C0AD2B0069F1E9B00FABCE6</string>
+                               <string>1CA6456E063B45B4001379D8</string>
+                       </array>
+                       <key>ToolbarConfiguration</key>
+                       <string>xcode.toolbar.config.classbrowser</string>
+                       <key>WindowString</key>
+                       <string>385 179 630 352 0 0 1440 878 </string>
+                       <key>WindowToolGUID</key>
+                       <string>1C0AD2AF069F1E9B00FABCE6</string>
+                       <key>WindowToolIsVisible</key>
+                       <integer>0</integer>
+               </dict>
+               <dict>
+                       <key>Identifier</key>
+                       <string>windowTool.refactoring</string>
+                       <key>IncludeInToolsMenu</key>
+                       <integer>0</integer>
+                       <key>Layout</key>
+                       <array>
+                               <dict>
+                                       <key>Dock</key>
+                                       <array>
+                                               <dict>
+                                                       <key>BecomeActive</key>
+                                                       <integer>1</integer>
+                                                       <key>GeometryConfiguration</key>
+                                                       <dict>
+                                                               <key>Frame</key>
+                                                               <string>{0, 0}, {500, 335}</string>
+                                                               <key>RubberWindowFrame</key>
+                                                               <string>{0, 0}, {500, 335}</string>
+                                                       </dict>
+                                                       <key>Module</key>
+                                                       <string>XCRefactoringModule</string>
+                                                       <key>Proportion</key>
+                                                       <string>100%</string>
+                                               </dict>
+                                       </array>
+                                       <key>Proportion</key>
+                                       <string>100%</string>
+                               </dict>
+                       </array>
+                       <key>Name</key>
+                       <string>Refactoring</string>
+                       <key>ServiceClasses</key>
+                       <array>
+                               <string>XCRefactoringModule</string>
+                       </array>
+                       <key>WindowString</key>
+                       <string>200 200 500 356 0 0 1920 1200 </string>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/xcode-build/Molby.xcodeproj/toshi_n.pbxuser b/xcode-build/Molby.xcodeproj/toshi_n.pbxuser
new file mode 100644 (file)
index 0000000..00d1cb6
--- /dev/null
@@ -0,0 +1,84 @@
+// !$*UTF8*$!
+{
+       29B97313FDCFA39411CA2CEA /* Project object */ = {
+               activeBuildConfigurationName = Debug;
+               activeExecutable = E4157DE61108B44900ECF4D9 /* Molby */;
+               activeTarget = 8D1107260486CEB800E47090 /* Molby */;
+               codeSenseManager = E4157DEE1108B46800ECF4D9 /* Code sense */;
+               executables = (
+                       E4157DE61108B44900ECF4D9 /* Molby */,
+               );
+               perUserDictionary = {
+                       PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
+                               PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+                               PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
+                               PBXFileTableDataSourceColumnWidthsKey = (
+                                       20,
+                                       341,
+                                       20,
+                                       48.16259765625,
+                                       43,
+                                       43,
+                                       20,
+                               );
+                               PBXFileTableDataSourceColumnsKey = (
+                                       PBXFileDataSource_FiletypeID,
+                                       PBXFileDataSource_Filename_ColumnID,
+                                       PBXFileDataSource_Built_ColumnID,
+                                       PBXFileDataSource_ObjectSize_ColumnID,
+                                       PBXFileDataSource_Errors_ColumnID,
+                                       PBXFileDataSource_Warnings_ColumnID,
+                                       PBXFileDataSource_Target_ColumnID,
+                               );
+                       };
+                       PBXPerProjectTemplateStateSaveDate = 285783113;
+                       PBXWorkspaceStateSaveDate = 285783113;
+               };
+               sourceControlManager = E4157DED1108B46800ECF4D9 /* Source Control */;
+               userBuildSettings = {
+               };
+       };
+       8D1107260486CEB800E47090 /* Molby */ = {
+               activeExec = 0;
+               executables = (
+                       E4157DE61108B44900ECF4D9 /* Molby */,
+               );
+       };
+       E4157DE61108B44900ECF4D9 /* Molby */ = {
+               isa = PBXExecutable;
+               activeArgIndices = (
+               );
+               argumentStrings = (
+               );
+               autoAttachOnCrash = 1;
+               breakpointsEnabled = 1;
+               configStateDict = {
+               };
+               customDataFormattersEnabled = 1;
+               debuggerPlugin = GDBDebugging;
+               disassemblyDisplayState = 0;
+               dylibVariantSuffix = "";
+               enableDebugStr = 1;
+               environmentEntries = (
+               );
+               executableSystemSymbolLevel = 0;
+               executableUserSymbolLevel = 0;
+               libgmallocEnabled = 0;
+               name = Molby;
+               savedGlobals = {
+               };
+               sourceDirectories = (
+               );
+       };
+       E4157DED1108B46800ECF4D9 /* Source Control */ = {
+               isa = PBXSourceControlManager;
+               fallbackIsa = XCSourceControlManager;
+               isSCMEnabled = 0;
+               scmConfiguration = {
+               };
+       };
+       E4157DEE1108B46800ECF4D9 /* Code sense */ = {
+               isa = PBXCodeSenseManager;
+               indexTemplatePath = "";
+       };
+}