OSDN Git Service

Batch mode is implemented (still testing)
[molby/Molby.git] / MolLib / Ruby_bind / ruby_bind.c
1 /*
2  *  ruby_bind.c
3  *  Ruby binding
4  *
5  *  Created by Toshi Nagata on 07/11/09.
6  *  Copyright 2007-2008 Toshi Nagata. All rights reserved.
7  *
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation version 2 of the License.
11  
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16 */
17
18 #include "Molby.h"
19 #include "ruby_dialog.h"
20 #include "../MD/MDCore.h"
21
22 #include <stdio.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <limits.h>
27
28 #include "version.h"       /*  for Ruby version  */
29 #include "ruby/version.h"  /*  for RUBY_BIRTH_YEAR etc.  */
30 #include "ruby/encoding.h" /*  for rb_str_encode() etc. */
31 /*#include <node.h>     *//*  for rb_add_event_hook()  */
32
33 #if defined(__WXMAC__) || defined(__CMDMAC__)
34 #define USE_PTHREAD_FOR_TIMER 1
35 #endif
36
37 #if !__WXMSW__
38 #if USE_PTHREAD_FOR_TIMER
39 #include <unistd.h>   /*  for usleep()  */
40 #include <pthread.h>  /*  for pthread  */
41 #else
42 #include <signal.h>   /*  for sigaction()  */
43 #endif
44 #endif
45
46 #include "../Missing.h"
47
48 #pragma mark ====== Global Values ======
49
50 VALUE rb_eMolbyError;
51 VALUE rb_mMolby;
52 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
53 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
54
55 VALUE gMolbyBacktrace;
56 VALUE gMolbyErrorHistory;
57 VALUE gScriptMenuCommands;
58 VALUE gScriptMenuEnablers;
59
60 int gMolbyRunLevel = 0;
61 int gMolbyIsCheckingInterrupt = 0;
62
63 char *gRubyVersion, *gRubyCopyright;
64
65 /*  For convenience  */
66 static ID s_ID_equal;  /*  rb_intern("==")  */
67
68 int g_RubyID_call;
69
70 /*  Symbols for atom attributes  */
71 static VALUE
72         s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
73         s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
74         s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
75         s_RSym, s_XSym, s_YSym, s_ZSym,
76         s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
77         s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
78         s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
79         s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
80         s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
81         s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
82
83 /*  Symbols for parameter attributes  */
84 static VALUE
85         s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
86         s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
87         /* s_ASym, s_BSym, */
88         s_ReqSym, s_EpsSym,
89         /* s_A14Sym, s_B14Sym, */
90         s_Req14Sym, s_Eps14Sym,
91         s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym, s_VdwRadiusSym,
92         s_CommentSym, s_SourceSym;
93
94 /*  Symbols for graphics  */
95 static VALUE
96         s_LineSym, s_PolySym, s_CylinderSym, s_ConeSym, s_EllipsoidSym;
97
98 /*
99  *  Utility function
100  *  Get ary[i] by calling "[]" method
101  */
102 VALUE
103 Ruby_ObjectAtIndex(VALUE ary, int idx)
104 {
105         static ID index_method = 0;
106         if (TYPE(ary) == T_ARRAY) {
107                 int len = RARRAY_LEN(ary);
108                 if (idx >= 0 && idx < len)
109                         return (RARRAY_PTR(ary))[idx];
110                 else return Qnil;
111         }
112         if (index_method == 0)
113                 index_method = rb_intern("[]");
114         return rb_funcall(ary, index_method, 1, INT2NUM(idx));
115 }
116
117 char *
118 Ruby_FileStringValuePtr(VALUE *valp)
119 {
120 #if __WXMSW__
121         char *p = strdup(StringValuePtr(*valp));
122         translate_char(p, '/', '\\');
123         *valp = rb_str_new2(p);
124         free(p);
125         return StringValuePtr(*valp);
126 #else
127         return StringValuePtr(*valp);
128 #endif
129 }
130
131 VALUE
132 Ruby_NewFileStringValue(const char *fstr)
133 {
134 #if __WXMSW__
135         VALUE retval;
136         char *p = strdup(fstr);
137         translate_char(p, '\\', '/');
138         retval = rb_enc_str_new(p, strlen(p), rb_default_external_encoding());
139         free(p);
140         return retval;
141 #else
142         return rb_str_new2(fstr);
143 #endif
144 }
145
146 char *
147 Ruby_EncodedStringValuePtr(VALUE *valp)
148 {
149         rb_string_value(valp);
150         *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
151         return RSTRING_PTR(*valp);
152 }
153
154 VALUE
155 Ruby_NewEncodedStringValue(const char *str, int len)
156 {
157         if (len <= 0)
158                 len = strlen(str);
159         return rb_enc_str_new(str, len, rb_default_external_encoding());
160 }
161
162 VALUE
163 Ruby_ObjToStringObj(VALUE val)
164 {
165         switch (TYPE(val)) {
166                 case T_STRING:
167                         return val;
168                 case T_SYMBOL:
169                         return rb_str_new2(rb_id2name(SYM2ID(val)));
170                 default:
171                         return rb_str_to_str(val);
172         }
173 }
174
175 #pragma mark ====== Message input/output ======
176
177 /*
178  *  call-seq:
179  *     message_box(str, title, button = nil, icon = :info)
180  *
181  *  Show a message box.
182  *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
183  *  Icon: :info, :warning, :error
184  */
185 static VALUE
186 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
187 {
188         char *str, *title, *s;
189         int buttons, icon;
190         VALUE sval, tval, bval, ival;
191         rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
192         str = StringValuePtr(sval);
193         title = StringValuePtr(tval);
194         if (bval != Qnil) {
195                 bval = Ruby_ObjToStringObj(bval);
196                 s = RSTRING_PTR(bval);
197                 if (strncmp(s, "ok", 2) == 0)
198                         buttons = 1;
199                 else if (strncmp(s, "cancel", 6) == 0)
200                         buttons = 2;
201                 else
202                         rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
203         } else buttons = 3;
204         if (ival != Qnil) {
205                 ival = Ruby_ObjToStringObj(ival);
206                 s = RSTRING_PTR(ival);
207                 if (strncmp(s, "info", 4) == 0)
208                         icon = 1;
209                 else if (strncmp(s, "warn", 4) == 0)
210                         icon = 2;
211                 else if (strncmp(s, "err", 3) == 0)
212                         icon = 3;
213                 else
214                         rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
215         } else icon = 1;
216         MyAppCallback_messageBox(str, title, buttons, icon);
217         return Qnil;
218 }
219
220 /*
221  *  call-seq:
222  *     error_message_box(str)
223  *
224  *  Show an error message box.
225  */
226 static VALUE
227 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
228 {
229         char *str = StringValuePtr(sval);
230         MyAppCallback_errorMessageBox("%s", str);
231         return Qnil;
232 }
233
234 /*
235  *  call-seq:
236  *     ask(prompt, default = nil) -> string
237  *
238  *  Open a modal dialog and get a line of text.
239  */
240 static VALUE
241 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
242 {
243         volatile VALUE prompt, message;
244         char buf[1024];
245         int retval;
246         rb_scan_args(argc, argv, "11", &prompt, &message);
247         if (message != Qnil) {
248                 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
249                 buf[sizeof buf - 1] = 0;
250         } else buf[0] = 0;
251         retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
252         if (retval)
253                 return rb_str_new2(buf);
254         else
255                 return Qnil;    
256 }
257
258 /*
259  *  call-seq:
260  *     show_console_window
261  *
262  *  Show the console window and bring to the front.
263  */
264 static VALUE
265 s_Kernel_ShowConsoleWindow(VALUE self)
266 {
267         MyAppCallback_showConsoleWindow();
268         return Qnil;
269 }
270
271 /*
272  *  call-seq:
273  *     hide_console_window
274  *
275  *  Hide the console window.
276  */
277 static VALUE
278 s_Kernel_HideConsoleWindow(VALUE self)
279 {
280         MyAppCallback_hideConsoleWindow();
281         return Qnil;
282 }
283
284 /*
285  *  call-seq:
286  *     bell
287  *
288  *  Ring the system bell.
289  */
290 static VALUE
291 s_Kernel_Bell(VALUE self)
292 {
293         MyAppCallback_bell();
294         return Qnil;
295 }
296
297 /*
298  *  call-seq:
299  *     play_sound(filename, flag = 0)
300  *
301  *  Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
302  *  1, play the sound asynchronously; 3, play the sound with loop asynchronously
303  */
304 static VALUE
305 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
306 {
307         VALUE fnval, flval;
308         int flag, retval;
309         char *fname;
310         rb_scan_args(argc, argv, "11", &fnval, &flval);
311         if (flval == Qnil)
312                 flag = 0;
313         else flag = NUM2INT(rb_Integer(flval));
314         fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
315         fname = StringValuePtr(fnval);
316         retval = MyAppCallback_playSound(fname, flag);
317         return (retval ? Qtrue : Qnil);
318 }
319
320 /*
321  *  call-seq:
322  *     stop_sound
323  *
324  *  Stop the sound if playing.
325  */
326 static VALUE
327 s_Kernel_StopSound(VALUE self)
328 {
329         MyAppCallback_stopSound();
330         return Qnil;
331 }
332
333 /*
334  *  call-seq:
335  *     export_to_clipboard(str)
336  *
337  *  Export the given string to clipboard.
338  */
339 static VALUE
340 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
341 {
342 #if !defined(__CMDMAC__)
343     const char *s;
344         char *ns;
345     if (!gUseGUI)
346         return Qnil;
347     s = StringValuePtr(sval);
348 #if __WXMSW__
349         /*  Convert the end-of-line characters  */
350         {       const char *p; int nc; char *np;
351                 nc = 0;
352                 for (p = s; *p != 0; p++) {
353                         if (*p == '\n')
354                                 nc++;
355                 }       
356                 ns = (char *)malloc(strlen(s) + nc + 1);
357                 for (np = ns, p = s; *p != 0; p++, np++) {
358                         if (*p == '\n')
359                                 *np++ = '\r';
360                         *np = *p;
361                 }
362                 *np = 0;
363         }
364 #else
365         ns = (char *)malloc(strlen(s) + 1);
366         strcpy(ns, s);
367 #if __WXMAC__
368         {       char *np;
369                 /*  wxMac still has Carbon code. Oops.  */
370                 for (np = ns; *np != 0; np++) {
371                         if (*np == '\n')
372                                 *np = '\r';
373                 }
374         }
375 #endif
376 #endif
377         if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
378                 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
379 #endif
380         return Qnil;
381 }
382
383 /*
384  *  call-seq:
385  *     stdout.write(str)
386  *
387  *  Put the message in the main text view in black color.
388  */
389 static VALUE
390 s_StandardOutput(VALUE self, VALUE str)
391 {
392         int n;
393         MyAppCallback_setConsoleColor(0);
394         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
395         return INT2NUM(n);
396 }
397
398 /*
399  *  call-seq:
400  *     stderr.write(str)
401  *
402  *  Put the message in the main text view in red color.
403  */
404 static VALUE
405 s_StandardErrorOutput(VALUE self, VALUE str)
406 {
407         int n;
408         MyAppCallback_setConsoleColor(1);
409         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
410         MyAppCallback_setConsoleColor(0);
411         return INT2NUM(n);
412 }
413
414 /*
415  *  call-seq:
416  *     stdout.flush
417  *     stderr.flush
418  *
419  *  Flush the standard (error) output. Actually do nothing.
420  */
421 static VALUE
422 s_FlushConsoleOutput(VALUE self)
423 {
424         return self;
425 }
426
427 /*
428  *  call-seq:
429  *     stdin.gets(rs = $/)
430  *
431  *  Read one line message via dialog box.
432  */
433 static VALUE
434 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
435 {
436         VALUE pval, rval;
437         pval = rb_str_new2("Enter a line:");
438         rval = s_Kernel_Ask(1, &pval, self);
439         if (rval == Qnil)
440                 rb_interrupt();
441         rb_str_cat2(rval, "\n");
442         return rval;
443 }
444
445 /*
446  *  call-seq:
447  *     stdin.method_missing(name, args, ...)
448  *
449  *  Throw an exception, noting only gets and readline are defined.
450  */
451 static VALUE
452 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
453 {
454         VALUE nval;
455         rb_scan_args(argc, argv, "10", &nval);
456         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
457         return Qnil;  /*  Not reached  */
458 }
459
460 #pragma mark ====== Track key events ======
461
462 /*  User interrupt handling
463  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
464  *  key events. This polling should only be enabled during "normal" execution
465  *  of scripts and must be disabled when the rest of the application (or Ruby
466  *  script itself) is handling GUI. This is ensured by appropriate calls to
467  *  enable_interrupt and disable_interrupt.  */
468
469 static VALUE s_interrupt_flag = Qfalse;
470
471 static VALUE
472 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
473 {
474         volatile VALUE message;
475         const char *p;
476         if (Ruby_GetInterruptFlag() == Qtrue) {
477                 rb_scan_args(argc, argv, "01", &message);
478                 if (message != Qnil)
479                         p = StringValuePtr(message);
480                 else
481                         p = NULL;
482                 MyAppCallback_showProgressPanel(p);
483         }
484         return Qnil;
485 }
486
487 static VALUE
488 s_HideProgressPanel(VALUE self)
489 {
490         MyAppCallback_hideProgressPanel();
491         return Qnil;
492 }
493
494 static VALUE
495 s_SetProgressValue(VALUE self, VALUE val)
496 {
497         double dval = NUM2DBL(rb_Float(val));
498         MyAppCallback_setProgressValue(dval);
499         return Qnil;
500 }
501
502 static VALUE
503 s_SetProgressMessage(VALUE self, VALUE msg)
504 {
505         const char *p;
506         if (msg == Qnil)
507                 p = NULL;
508         else p = StringValuePtr(msg);
509         MyAppCallback_setProgressMessage(p);
510         return Qnil;
511 }
512
513 static VALUE
514 s_SetInterruptFlag(VALUE self, VALUE val)
515 {
516         VALUE oldval;
517         if (val != Qundef) {
518                 if (val == Qfalse || val == Qnil)
519                         val = Qfalse;
520                 else val = Qtrue;
521         }
522         oldval = s_interrupt_flag;
523         if (val != Qundef) {
524                 s_interrupt_flag = val;
525                 if (val == Qfalse) {
526                         s_HideProgressPanel(self);
527                 }
528         }
529         return oldval;
530 }
531
532 static VALUE
533 s_GetInterruptFlag(VALUE self)
534 {
535         return s_SetInterruptFlag(self, Qundef);
536 }
537
538 VALUE
539 Ruby_SetInterruptFlag(VALUE val)
540 {
541         return s_SetInterruptFlag(Qnil, val);
542 }
543
544 VALUE
545 Ruby_GetInterruptFlag(void)
546 {
547         return s_SetInterruptFlag(Qnil, Qundef);
548 }
549
550 /*
551  *  call-seq:
552  *     check_interrupt -> integer
553  *
554  *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
555  */
556 static VALUE
557 s_Kernel_CheckInterrupt(VALUE self)
558 {
559         if (Ruby_GetInterruptFlag() == Qfalse)
560                 return INT2NUM(-1);
561         else if (MyAppCallback_checkInterrupt())
562                 return INT2NUM(1);
563         else return INT2NUM(0);
564 }
565
566 static volatile unsigned long sITimerCount = 0;
567
568 #if __WXMSW__
569 static HANDLE sITimerEvent;
570 static HANDLE sITimerThread;
571 static int sITimerInterval;
572
573 static __stdcall unsigned
574 s_ITimerThreadFunc(void *p)
575 {
576         while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
577                 sITimerCount++;
578         }
579         return 0;
580 }
581
582 #elif USE_PTHREAD_FOR_TIMER
583
584 /*  Timer thread  */
585 static pthread_t sTimerThread;
586
587 /*  -1: uninitiated; 0: active, 1: inactive, -2: request to terminate  */
588 static volatile signed char sTimerFlag = -1;
589 static volatile int sTimerIntervalMicrosec = 0;
590
591 static void *
592 s_TimerThreadEntry(void *param)
593 {
594         while (1) {
595                 usleep(sTimerIntervalMicrosec);
596                 if (sTimerFlag == 0)
597                         sITimerCount++;
598                 else if (sTimerFlag == -2)
599                         break;
600         }
601         return NULL;    
602 }
603
604 #endif
605
606 static void
607 s_SignalAction(int n)
608 {
609         sITimerCount++;
610 }
611
612 static void
613 s_SetIntervalTimer(int n, int msec)
614 {
615 #if __WXMSW__
616         if (n == 0) {
617                 /*  Start interval timer  */
618                 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
619                 sITimerInterval = msec;
620                 if (sITimerEvent) {
621                         sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
622                 }
623         } else {
624                 /*  Stop interval timer  */
625                 if (sITimerEvent)
626                         SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
627                 if (sITimerThread) {
628                         WaitForSingleObject(sITimerThread, 1000);
629                         CloseHandle(sITimerThread);
630                 }
631                 if (sITimerEvent)
632                         CloseHandle(sITimerEvent);
633                 sITimerEvent = NULL;
634                 sITimerThread = NULL;
635         }
636 #elif USE_PTHREAD_FOR_TIMER
637         if (n == 0) {
638                 if (sTimerFlag == -1) {
639                         int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
640                         if (status != 0) {
641                                 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
642                         }
643                 }
644                 sTimerFlag = 0;  /*  Active  */
645                 sTimerIntervalMicrosec = msec * 1000;
646         } else if (sTimerFlag != -1)
647                 sTimerFlag = 1;  /*  Inactive  */       
648 #else
649         static struct itimerval sOldValue;
650         static struct sigaction sOldAction;
651         struct itimerval val;
652         struct sigaction act;
653         if (n == 0) {
654                 sITimerCount = 0;
655                 act.sa_handler = s_SignalAction;
656                 act.sa_mask = 0;
657                 act.sa_flags = 0;
658                 sigaction(SIGALRM, &act, &sOldAction);
659                 val.it_value.tv_sec = 0;
660                 val.it_value.tv_usec = msec * 1000;
661                 val.it_interval.tv_sec = 0;
662                 val.it_interval.tv_usec = msec * 1000;
663                 setitimer(ITIMER_REAL, &val, &sOldValue);
664         } else {
665                 setitimer(ITIMER_REAL, &sOldValue, &val);
666                 sigaction(SIGALRM, &sOldAction, &act);
667         }
668 #endif
669 }
670
671 static unsigned long
672 s_GetTimerCount(void)
673 {
674         return sITimerCount;
675 }
676
677 static void
678 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
679 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
680 {
681         if (s_interrupt_flag != Qfalse) {
682                 static unsigned long sLastTime = 0;
683                 unsigned long currentTime;
684                 int flag;
685                 currentTime = s_GetTimerCount();
686                 if (currentTime != sLastTime) {
687                         sLastTime = currentTime;
688                         gMolbyIsCheckingInterrupt = 1;
689                         flag = MyAppCallback_checkInterrupt();
690                         gMolbyIsCheckingInterrupt = 0;
691                         if (flag) {
692                                 s_SetInterruptFlag(Qnil, Qfalse);
693                                 rb_interrupt();
694                         }
695                 }
696         }
697 }
698
699 #pragma mark ====== Menu handling ======
700
701 /*
702  *  call-seq:
703  *     register_menu(title, method, enable_proc = nil)
704  *
705  *  Register the method (specified as a symbol) in the script menu.
706  *  The method must be either an instance method of Molecule with no argument,
707  *  or a class method of Molecule with one argument (the current molecule),
708  *  or a proc object with one argument (the current molecule).
709  *  The menu associated with the class method can be invoked even when no document
710  *  is open (the argument is set to Qnil in this case). On the other hand, the
711  *  menu associated with the instance method can only be invoked when at least one 
712  *  document is active.
713  *  If enable_proc is non-nil, then it is called whenever the availability of
714  *  the menu command is tested. It is usually a proc object with one argument
715  *  (the current molecule or nil). As a special case, the following symbols can
716  *  be given; :mol (enabled when any molecule is open), :non_empty (enabled when
717  *  the top-level molecule has at least one atom), :selection (enabled when
718  *  the top-level molecule has one or more selected atoms).
719  */
720 static VALUE
721 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
722 {
723         int n, mtype = 0;
724         VALUE tval, mval, pval;
725         static VALUE sMolSym, sNonEmptySym, sSelectionSym;
726         static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
727         rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
728         tval = rb_str_to_str(tval);
729         n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
730         if (n < 0)
731                 return Qnil;
732         if (TYPE(mval) == T_SYMBOL) {
733                 /*  Create an appropriate proc object  */
734                 const char *name = rb_id2name(SYM2ID(mval));
735                 char *s;
736                 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
737                         /*  Defined as a Molecule method  */
738                         asprintf(&s, "lambda { |m| m.%s }", name);
739                         mtype = 1;
740                 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
741                         /*  Defined as a Molecule class method  */
742                         asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
743                         mtype = 2;
744                 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
745                 mval = rb_eval_string(s);
746                 free(s);
747         }
748         if (sMolSym == Qfalse) {
749                 sMolSym = ID2SYM(rb_intern("mol"));
750                 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
751                 sSelectionSym = ID2SYM(rb_intern("selection"));
752                 sMolProc = rb_eval_string("lambda { |m| m != nil }");
753                 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
754                 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
755                 sTrueProc = rb_eval_string("lambda { |m| true }");
756                 rb_global_variable(&sMolProc);
757                 rb_global_variable(&sNonEmptyProc);
758                 rb_global_variable(&sSelectionProc);
759                 rb_global_variable(&sTrueProc);
760         }
761         
762         if (pval == Qnil) {
763                 if (mtype == 1)
764                         pval = sMolProc;
765                 else
766                         pval = sTrueProc;
767         } else if (pval == sMolSym)
768                 pval = sMolProc;
769         else if (pval == sNonEmptySym)
770                 pval = sNonEmptyProc;
771         else if (pval == sSelectionSym)
772                 pval = sSelectionProc;
773         rb_ary_store(gScriptMenuCommands, n, mval);
774         rb_ary_store(gScriptMenuEnablers, n, pval);
775         return INT2NUM(n);
776 }
777
778 static VALUE
779 s_Kernel_LookupMenu(VALUE self, VALUE title)
780 {
781         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
782         return INT2NUM(n);
783 }
784
785 static VALUE
786 s_Ruby_UpdateUI_handler(VALUE data)
787 {
788         void **p = (void **)data;
789         int index = (int)p[0];
790         Molecule *mol = (Molecule *)p[1];
791         int *outChecked = (int *)p[2];
792         char **outTitle = (char **)p[3];
793         VALUE mval = ValueFromMolecule(mol);
794         VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
795         static ID call_id = 0;
796         if (call_id == 0)
797                 call_id = rb_intern("call");
798         if (pval == Qnil)
799                 return Qnil;
800         pval = rb_funcall(pval, call_id, 1, mval);
801         if (rb_obj_is_kind_of(pval, rb_cArray)) {
802                 VALUE val;
803                 if (outChecked != NULL) {
804                         val = rb_ary_entry(pval, 1);  /*  Checked or not  */
805                         *outChecked = (RTEST(val) ? 1 : 0);
806                 }
807                 if (outTitle != NULL) {
808                         val = rb_ary_entry(pval, 2);  /*  Text  */
809                         if (TYPE(val) == T_STRING) {
810                                 *outTitle = strdup(StringValuePtr(val));
811                         }
812                 }
813                 pval = rb_ary_entry(pval, 0);
814         }
815         return pval;
816 }
817
818 int
819 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
820 {
821         int status;
822         void *p[4];
823         VALUE retval;
824         p[0] = (void *)(intptr_t)index;
825         p[1] = mol;
826         p[2] = outChecked;
827         p[3] = outTitle;
828         retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
829         return (RTEST(retval) ? 1 : 0);
830 }
831
832 /*
833 static VALUE
834 s_Ruby_methodType_sub(VALUE data)
835 {
836         const char **p = (const char **)data;
837         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
838         ID mid = rb_intern(p[1]);
839         int ival;
840         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
841                 ival = 1;
842         else if (rb_respond_to(klass, mid))
843                 ival = 2;
844         else ival = 0;
845         return INT2FIX(ival);
846 }
847 */      
848 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
849     has the singleton method (class method) with the given name, 0 otherwise.  */
850 /*int
851 Ruby_methodType(const char *className, const char *methodName)
852 {
853         int status;
854         VALUE retval;
855         const char *p[2];
856         p[0] = className;
857         p[1] = methodName;
858         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
859         if (status == 0)
860                 return FIX2INT(retval);
861         else return 0;
862 }
863 */
864
865 /*
866  *  call-seq:
867  *     execute_script_file(fname)
868  *
869  *  Execute the script in the given file. If a molecule is active, then
870  *  the script is evaluated as Molecule.current.instance_eval(script).
871  *  Before entering the script, the current directory is set to the parent
872  *  directory of the script.
873  */
874 static VALUE
875 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
876 {
877         int status;
878         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
879         if (retval == (VALUE)6 && status == -1)
880                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
881         if (status != 0)
882                 rb_jump_tag(status);
883         return retval;
884 }
885
886 /*
887  *  call-seq:
888  *     document_home
889  *
890  *  Get the directory suitable for storing user documents. On Windows
891  *  it is the home directory + "My Documents". On other platforms
892  *  it is the home directory.
893  */
894 static VALUE
895 s_Kernel_DocumentHome(VALUE self)
896 {
897         char *s = MyAppCallback_getDocumentHomeDir();
898         VALUE retval = Ruby_NewFileStringValue(s);
899         free(s);
900         return retval;
901 }
902
903 /*  The callback function for call_subprocess  */
904 static int
905 s_Kernel_CallSubProcess_Callback(void *data)
906 {
907         int status;
908         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
909         if (status != 0 || retval == Qnil || retval == Qfalse)
910                 return 1;
911         else return 0;
912 }
913
914 /*
915  *  call-seq:
916  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
917  *
918  *  Call subprocess. A progress dialog window is displayed, with a message
919  *  "Running #{process_name}...".
920  *  A callback proc can be given, which is called periodically during execution. If the proc returns
921  *  nil or false, then the execution will be interrupted.
922  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
923  *  filename begins with ">>", then the message will be appended to the file.
924  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
925  *  If the argument is nil, then the message will be sent to the Ruby console.
926  */
927 static VALUE
928 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
929 {
930         VALUE cmd, procname, cproc, stdout_val, stderr_val;
931         int n, exitstatus, pid;
932         char *sout, *serr;
933         FILE *fpout, *fperr;
934
935         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
936
937         if (stdout_val == Qnil) {
938                 fpout = (FILE *)1;
939         } else {
940                 sout = StringValuePtr(stdout_val);
941                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
942                         fpout = NULL;
943                 else {
944                         if (strncmp(sout, ">>", 2) == 0) {
945                                 sout += 2;
946                                 fpout = fopen(sout, "a");
947                         } else {
948                                 if (*sout == '>')
949                                         sout++;
950                                 fpout = fopen(sout, "w");
951                         }
952                         if (fpout == NULL)
953                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
954                 }
955         }
956         if (stderr_val == Qnil) {
957                 fperr = (FILE *)1;
958         } else {
959                 serr = StringValuePtr(stderr_val);
960                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
961                         fperr = NULL;
962                 else {
963                         if (strncmp(serr, ">>", 2) == 0) {
964                                 serr += 2;
965                                 fpout = fopen(serr, "a");
966                         } else {
967                                 if (*serr == '>')
968                                         serr++;
969                                 fperr = fopen(serr, "w");
970                         }
971                         if (fperr == NULL)
972                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
973                 }
974         }
975
976         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
977         
978         if (fpout != NULL && fpout != (FILE *)1)
979                 fclose(fpout);
980         if (fperr != NULL && fperr != (FILE *)1)
981                 fclose(fperr);
982
983         return INT2NUM(n);
984
985         
986 }
987
988 /*
989  *  call-seq:
990  *     backquote(cmd)
991  *
992  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
993  */
994 static VALUE
995 s_Kernel_Backquote(VALUE self, VALUE cmd)
996 {
997         char *buf;
998         int n, exitstatus, pid;
999         VALUE val;
1000         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1001 /*      fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1002         if (n >= 0 && buf != NULL) {
1003                 val = Ruby_NewEncodedStringValue(buf, 0);
1004                 free(buf);
1005         } else {
1006                 val = Ruby_NewEncodedStringValue("", 0);
1007         }
1008         rb_last_status_set(exitstatus, pid);
1009         return val;
1010 }
1011
1012 #pragma mark ====== User defaults ======
1013
1014 /*
1015  *  call-seq:
1016  *     get_global_settings(key)
1017  *
1018  *  Get a setting data for key from the application preferences.
1019  */
1020 static VALUE
1021 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1022 {
1023         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1024         if (p != NULL) {
1025                 VALUE retval = rb_eval_string(p);
1026                 free(p);
1027                 return retval;
1028         } else return Qnil;
1029 }
1030
1031 /*
1032  *  call-seq:
1033  *     set_global_settings(key, value)
1034  *
1035  *  Set a setting data for key to the application preferences.
1036  */
1037 static VALUE
1038 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1039 {
1040         VALUE sval = rb_inspect(value);
1041         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1042         return value;
1043 }
1044
1045 #pragma mark ====== IO extension ======
1046
1047 static VALUE
1048 s_Ruby_str_encode_protected(VALUE val)
1049 {
1050         return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1051                                   ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1052 }
1053
1054 /*
1055  *  call-seq:
1056  *     gets_any_eol
1057  *
1058  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1059  */
1060 static VALUE
1061 s_IO_gets_any_eol(VALUE self)
1062 {
1063         VALUE val, val2, cval;
1064         char buf[1024];
1065         int i, c, status;
1066         static ID id_getbyte = 0, id_ungetbyte;
1067         if (id_getbyte == 0) {
1068                 id_getbyte = rb_intern("getbyte");
1069                 id_ungetbyte = rb_intern("ungetbyte");
1070         }
1071         i = 0;
1072         val = Qnil;
1073         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1074                 c = NUM2INT(rb_Integer(cval));
1075                 if (c == 0x0d) {
1076                         cval = rb_funcall(self, id_getbyte, 0);
1077                         if (cval != Qnil) {
1078                                 c = NUM2INT(rb_Integer(cval));
1079                                 if (c != 0x0a)
1080                                         rb_funcall(self, id_ungetbyte, 1, cval);
1081                         }
1082                         break;
1083                 } else if (c != 0x0a) {
1084                         buf[i++] = c;
1085                         if (i >= 1020) {
1086                                 buf[i] = 0;
1087                                 if (val == Qnil)
1088                                         val = rb_str_new(buf, i);
1089                                 else
1090                                         rb_str_append(val, rb_str_new(buf, i));
1091                                 i = 0;
1092                         }
1093                 } else break;
1094         }
1095         if (cval == Qnil && i == 0 && val == Qnil)
1096                 return Qnil;  /*  End of file  */
1097         buf[i] = 0;
1098         if (val == Qnil)
1099                 val = rb_str_new(buf, i);
1100         else if (i > 0)
1101                 rb_str_append(val, rb_str_new(buf, i));
1102         val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /*  Ignore exception  */
1103         if (status == 0)
1104                 val = val2;
1105         if (cval != Qnil) {
1106                 /*  Needs a end-of-line mark  */
1107                 cval = rb_gv_get("$/");
1108                 rb_str_append(val, cval);
1109         }
1110         rb_gv_set("$_", val);
1111         return val;
1112 }
1113
1114 #pragma mark ====== Utility functions (protected funcall) ======
1115
1116 struct Ruby_funcall2_record {
1117         VALUE recv;
1118         ID mid;
1119         int argc;
1120         VALUE *argv;
1121 };
1122
1123 static VALUE
1124 s_Ruby_funcall2_sub(VALUE data)
1125 {
1126         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1127         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1128 }
1129
1130 VALUE
1131 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1132 {
1133         struct Ruby_funcall2_record rec;
1134         rec.recv = recv;
1135         rec.mid = mid;
1136         rec.argc = argc;
1137         rec.argv = argv;
1138         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1139 }
1140
1141 RubyValue
1142 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1143 {
1144         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1145 }
1146
1147 #pragma mark ====== ParameterRef Class ======
1148
1149 static UnionPar *
1150 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1151 {
1152         UnionPar *up;
1153         ParameterRef *pref;
1154         Data_Get_Struct(self, ParameterRef, pref);
1155         if (typep != NULL)
1156                 *typep = pref->parType;
1157         if (pref->parType == kElementParType) {
1158                 up = (UnionPar *)&gElementParameters[pref->idx];
1159         } else {
1160                 up = ParameterRefGetPar(pref);
1161                 if (checkEditable) {
1162                         if (pref->idx < 0)
1163                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1164                         if (up->bond.src != 0 && up->bond.src != -1)
1165                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1166                 }
1167         }
1168         return up;
1169 }
1170
1171 static void
1172 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1173 {
1174         UnionPar *up;
1175         ParameterRef *pref;
1176         Data_Get_Struct(self, ParameterRef, pref);
1177         if (pref->mol == NULL)
1178                 return;
1179         up = ParameterRefGetPar(pref);
1180         if (key != s_SourceSym)
1181                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1182         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1183                 /*  Register undo  */
1184                 MolAction *act;
1185                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1186                 MolActionCallback_registerUndo(pref->mol, act);
1187                 MoleculeCallback_notifyModification(pref->mol, 0);
1188                 pref->mol->needsMDRebuild = 1;
1189         }
1190 }
1191
1192 VALUE
1193 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1194 {
1195         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1196         if (pref != NULL)
1197                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1198         else
1199                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1200 }
1201
1202 static int
1203 s_AtomTypeIndexFromValue(VALUE val)
1204 {
1205         if (rb_obj_is_kind_of(val, rb_cNumeric))
1206                 return NUM2INT(val);
1207         else
1208                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1209 }
1210
1211 static const char *s_ParameterTypeNames[] = {
1212         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1213 };
1214 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1215
1216 static int
1217 s_ParTypeFromValue(VALUE val)
1218 {
1219         int i, n;
1220         ID valid;
1221         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1222         if (s_ParameterTypeIDs[0] == 0) {
1223                 for (i = 0; i < n; i++)
1224                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1225         }
1226         valid = rb_to_id(val);
1227         for (i = 0; i < n; i++) {
1228                 if (valid == s_ParameterTypeIDs[i]) {
1229                         if (i == 7)
1230                                 return kElementParType;
1231                         else return kFirstParType + i;
1232                 }
1233         }
1234         return kInvalidParType;
1235 }
1236
1237 /*
1238  *  call-seq:
1239  *     index -> Integer
1240  *
1241  *  Get the index in the parameter list.
1242  */
1243 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1244         ParameterRef *pref;
1245         Data_Get_Struct(self, ParameterRef, pref);
1246         return INT2NUM(pref->idx);
1247 }
1248
1249 /*
1250  *  call-seq:
1251  *     par_type -> String
1252  *
1253  *  Get the parameter type, like "bond", "angle", etc.
1254  */
1255 static VALUE s_ParameterRef_GetParType(VALUE self) {
1256         Int tp;
1257         s_UnionParFromValue(self, &tp, 0);
1258         if (tp == kElementParType)
1259                 return rb_str_new2("element");
1260         tp -= kFirstParType;
1261         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1262                 return rb_str_new2(s_ParameterTypeNames[tp]);
1263         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1264 }
1265
1266 /*
1267  *  call-seq:
1268  *     atom_type -> String or Array of String
1269  *     atom_types -> String or Array of String
1270  *
1271  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1272  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1273  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1274  *  The atom type may be "X", which is a wildcard that matches any atom type.
1275  */
1276 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1277         UnionPar *up;
1278         Int tp, i, n;
1279         UInt types[4];
1280         VALUE vals[4];
1281         up = s_UnionParFromValue(self, &tp, 0);
1282         n = ParameterGetAtomTypes(tp, up, types);
1283         if (n == 0)
1284                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1285         for (i = 0; i < n; i++) {
1286                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1287                         vals[i] = INT2NUM(types[i]);
1288                 else
1289                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1290         }
1291         if (n == 1)
1292                 return vals[0];
1293         else
1294                 return rb_ary_new4(n, vals);
1295 }
1296
1297 /*
1298  *  call-seq:
1299  *     k -> Float
1300  *
1301  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1302  */
1303 static VALUE s_ParameterRef_GetK(VALUE self) {
1304         UnionPar *up;
1305         Int tp, i, n;
1306         VALUE vals[3];
1307         up = s_UnionParFromValue(self, &tp, 0);
1308         switch (tp) {
1309                 case kBondParType:
1310                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1311                 case kAngleParType:
1312                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1313                 case kDihedralParType:
1314                 case kImproperParType:
1315                         if (up->torsion.mult == 1)
1316                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1317                         n = up->torsion.mult;
1318                         if (n > 3)
1319                                 n = 3;
1320                         for (i = 0; i < n; i++)
1321                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1322                         return rb_ary_new4(n, vals);
1323                 default:
1324                         rb_raise(rb_eMolbyError, "invalid member k");
1325         }
1326 }
1327
1328 /*
1329  *  call-seq:
1330  *     r0 -> Float
1331  *
1332  *  Get the equilibrium bond length. Only available for bond parameters.
1333  */
1334 static VALUE s_ParameterRef_GetR0(VALUE self) {
1335         UnionPar *up;
1336         Int tp;
1337         up = s_UnionParFromValue(self, &tp, 0);
1338         if (tp == kBondParType)
1339                 return rb_float_new(up->bond.r0);
1340         else rb_raise(rb_eMolbyError, "invalid member r0");
1341 }
1342
1343 /*
1344  *  call-seq:
1345  *     a0 -> Float
1346  *
1347  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1348  */
1349 static VALUE s_ParameterRef_GetA0(VALUE self) {
1350         UnionPar *up;
1351         Int tp;
1352         up = s_UnionParFromValue(self, &tp, 0);
1353         if (tp == kAngleParType)
1354                 return rb_float_new(up->angle.a0 * kRad2Deg);
1355         else rb_raise(rb_eMolbyError, "invalid member a0");
1356 }
1357
1358 /*
1359  *  call-seq:
1360  *     mult -> Float
1361  *
1362  *  Get the multiplicity. Only available for dihedral and improper parameters.
1363  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1364  */
1365 static VALUE s_ParameterRef_GetMult(VALUE self) {
1366         UnionPar *up;
1367         Int tp;
1368         up = s_UnionParFromValue(self, &tp, 0);
1369         if (tp == kDihedralParType || tp == kImproperParType)
1370                 return rb_float_new(up->torsion.mult);
1371         else rb_raise(rb_eMolbyError, "invalid member mult");
1372 }
1373
1374 /*
1375  *  call-seq:
1376  *     period -> Integer or Array of Integers
1377  *
1378  *  Get the periodicity. Only available for dihedral and improper parameters.
1379  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1380  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1381  */
1382 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1383         UnionPar *up;
1384         Int tp, i, n;
1385         VALUE vals[3];
1386         up = s_UnionParFromValue(self, &tp, 0);
1387         if (tp == kDihedralParType || tp == kImproperParType) {
1388                 if (up->torsion.mult == 1)
1389                         return INT2NUM(up->torsion.period[0]);
1390                 n = up->torsion.mult;
1391                 if (n > 3)
1392                         n = 3;
1393                 for (i = 0; i < n; i++)
1394                         vals[i] = INT2NUM(up->torsion.period[i]);
1395                 return rb_ary_new4(n, vals);
1396         } else rb_raise(rb_eMolbyError, "invalid member period");
1397 }
1398
1399 /*
1400  *  call-seq:
1401  *     phi0 -> Float or Array of Floats
1402  *
1403  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1404  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1405  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1406  */
1407 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1408         UnionPar *up;
1409         Int tp, i, n;
1410         VALUE vals[3];
1411         up = s_UnionParFromValue(self, &tp, 0);
1412         if (tp == kDihedralParType || tp == kImproperParType) {
1413                 if (up->torsion.mult == 1)
1414                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1415                 n = up->torsion.mult;
1416                 if (n > 3)
1417                         n = 3;
1418                 for (i = 0; i < n; i++)
1419                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1420                 return rb_ary_new4(n, vals);
1421         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1422 }
1423
1424 /*
1425  *  call-seq:
1426  *     A -> Float
1427  *
1428  *  Get the "A" value for the van der Waals parameter.
1429  */
1430 /*
1431  static VALUE s_ParameterRef_GetA(VALUE self) {
1432         UnionPar *up;
1433         Int tp;
1434         up = s_UnionParFromValue(self, &tp, 0);
1435         if (tp == kVdwParType)
1436                 return rb_float_new(up->vdw.A);
1437         else if (tp == kVdwPairParType)
1438                 return rb_float_new(up->vdwp.A);
1439         else rb_raise(rb_eMolbyError, "invalid member A");
1440 }
1441 */
1442
1443 /*
1444  *  call-seq:
1445  *     B -> Float
1446  *
1447  *  Get the "B" value for the van der Waals parameter.
1448  */
1449 /*
1450 static VALUE s_ParameterRef_GetB(VALUE self) {
1451         UnionPar *up;
1452         Int tp;
1453         up = s_UnionParFromValue(self, &tp, 0);
1454         if (tp == kVdwParType)
1455                 return rb_float_new(up->vdw.B);
1456         else if (tp == kVdwPairParType)
1457                 return rb_float_new(up->vdwp.B);
1458         else rb_raise(rb_eMolbyError, "invalid member B");
1459 }
1460 */
1461
1462 /*
1463  *  call-seq:
1464  *     r_eq -> Float
1465  *
1466  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1467  */
1468 static VALUE s_ParameterRef_GetReq(VALUE self) {
1469         UnionPar *up;
1470         Int tp;
1471 /*      Double a, b, r; */
1472         Double r;
1473         up = s_UnionParFromValue(self, &tp, 0);
1474         if (tp == kVdwParType) {
1475         /*      a = up->vdw.A;
1476                 b = up->vdw.B;  */
1477                 r = up->vdw.r_eq;
1478         } else if (tp == kVdwPairParType) {
1479         /*      a = up->vdwp.A;
1480                 b = up->vdwp.B;  */
1481                 r = up->vdwp.r_eq;
1482         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1483 /*      if (a == 0.0 || b == 0.0) */
1484         return rb_float_new(r);
1485 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1486 }
1487
1488 /*
1489  *  call-seq:
1490  *     eps -> Float
1491  *
1492  *  Get the minimum energy for the van der Waals parameter.
1493  */
1494 static VALUE s_ParameterRef_GetEps(VALUE self) {
1495         UnionPar *up;
1496         Int tp;
1497 /*      Double a, b; */
1498         Double eps;
1499         up = s_UnionParFromValue(self, &tp, 0);
1500         if (tp == kVdwParType) {
1501         /*      a = up->vdw.A;
1502                 b = up->vdw.B;  */
1503                 eps = up->vdw.eps;
1504         } else if (tp == kVdwPairParType) {
1505         /*      a = up->vdwp.A;
1506                 b = up->vdwp.B; */
1507                 eps = up->vdwp.eps;
1508         } else rb_raise(rb_eMolbyError, "invalid member eps");
1509 /*      if (a == 0.0 || b == 0.0)  */
1510                 return rb_float_new(eps * INTERNAL2KCAL);
1511 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1512 }
1513
1514 /*
1515  *  call-seq:
1516  *     A14 -> Float
1517  *
1518  *  Get the "A" value for the 1-4 van der Waals parameter.
1519  */
1520 /*
1521 static VALUE s_ParameterRef_GetA14(VALUE self) {
1522         UnionPar *up;
1523         Int tp;
1524         up = s_UnionParFromValue(self, &tp, 0);
1525         if (tp == kVdwParType)
1526                 return rb_float_new(up->vdw.A14);
1527         else if (tp == kVdwPairParType)
1528                 return rb_float_new(up->vdwp.A14);
1529         else rb_raise(rb_eMolbyError, "invalid member A14");
1530 }
1531 */
1532
1533 /*
1534  *  call-seq:
1535  *     B14 -> Float
1536  *
1537  *  Get the "B" value for the 1-4 van der Waals parameter.
1538  */
1539 /*
1540 static VALUE s_ParameterRef_GetB14(VALUE self) {
1541         UnionPar *up;
1542         Int tp;
1543         up = s_UnionParFromValue(self, &tp, 0);
1544         if (tp == kVdwParType)
1545                 return rb_float_new(up->vdw.B14);
1546         else if (tp == kVdwPairParType)
1547                 return rb_float_new(up->vdwp.B14);
1548         else rb_raise(rb_eMolbyError, "invalid member B14");
1549 }
1550 */
1551
1552 /*
1553  *  call-seq:
1554  *     r_eq14 -> Float
1555  *
1556  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1557  */
1558 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1559         UnionPar *up;
1560         Int tp;
1561 /*      Double a, b, r; */
1562         Double r;
1563         up = s_UnionParFromValue(self, &tp, 0);
1564         if (tp == kVdwParType) {
1565         /*      a = up->vdw.A14;
1566                 b = up->vdw.B14; */
1567                 r = up->vdw.r_eq14;
1568         } else if (tp == kVdwPairParType) {
1569         /*      a = up->vdwp.A14;
1570                 b = up->vdwp.B14;  */
1571                 r = up->vdwp.r_eq14;
1572         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1573 /*      if (a == 0.0 || b == 0.0)  */
1574         return rb_float_new(r);
1575 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1576 }
1577
1578 /*
1579  *  call-seq:
1580  *     eps14 -> Float
1581  *
1582  *  Get the minimum energy for the 1-4 van der Waals parameter.
1583  */
1584 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1585         UnionPar *up;
1586         Int tp;
1587 /*      Double a, b;  */
1588         Double eps;
1589         up = s_UnionParFromValue(self, &tp, 0);
1590         if (tp == kVdwParType) {
1591         /*      a = up->vdw.A14;
1592                 b = up->vdw.B14;  */
1593                 eps = up->vdw.eps14;
1594         } else if (tp == kVdwPairParType) {
1595         /*      a = up->vdwp.A14;
1596                 b = up->vdwp.B14; */
1597                 eps = up->vdwp.eps14;
1598         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1599 /*      if (a == 0.0 || b == 0.0) */
1600         return rb_float_new(eps * INTERNAL2KCAL);
1601 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1602 }
1603
1604 /*
1605  *  call-seq:
1606  *     cutoff -> Float
1607  *
1608  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1609  */
1610 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1611         UnionPar *up;
1612         Int tp;
1613         up = s_UnionParFromValue(self, &tp, 0);
1614         if (tp == kVdwCutoffParType)
1615                 return rb_float_new(up->vdwcutoff.cutoff);
1616         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1617 }
1618
1619 /*
1620  *  call-seq:
1621  *     radius -> Float
1622  *
1623  *  Get the atomic (covalent) radius for the element parameter.
1624  */
1625 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1626         UnionPar *up;
1627         Int tp;
1628         up = s_UnionParFromValue(self, &tp, 0);
1629         if (tp == kElementParType)
1630                 return rb_float_new(up->atom.radius);
1631         else rb_raise(rb_eMolbyError, "invalid member radius");
1632 }
1633
1634 /*
1635  *  call-seq:
1636  *     vdw_radius -> Float
1637  *
1638  *  Get the van der Waals radius for the element parameter. (0 if not given)
1639  */
1640 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1641         UnionPar *up;
1642         Int tp;
1643         up = s_UnionParFromValue(self, &tp, 0);
1644         if (tp == kElementParType)
1645                 return rb_float_new(up->atom.vdw_radius);
1646         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1647 }
1648
1649 /*
1650  *  call-seq:
1651  *     color -> [Float, Float, Float]
1652  *
1653  *  Get the rgb color for the element parameter.
1654  */
1655 static VALUE s_ParameterRef_GetColor(VALUE self) {
1656         UnionPar *up;
1657         Int tp;
1658         up = s_UnionParFromValue(self, &tp, 0);
1659         if (tp == kElementParType)
1660                 return rb_ary_new3(3, rb_float_new(up->atom.red / 65535.0), rb_float_new(up->atom.green / 65535.0), rb_float_new(up->atom.blue / 65535.0));
1661         else rb_raise(rb_eMolbyError, "invalid member color");
1662 }
1663
1664 /*
1665  *  call-seq:
1666  *     atomic_number -> Integer
1667  *
1668  *  Get the atomic number for the vdw or element parameter.
1669  */
1670 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1671         UnionPar *up;
1672         Int tp;
1673         up = s_UnionParFromValue(self, &tp, 0);
1674         if (tp == kElementParType)
1675                 return INT2NUM(up->atom.number);
1676         else if (tp == kVdwParType)
1677                 return INT2NUM(up->vdw.atomicNumber);
1678         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1679 }
1680
1681 /*
1682  *  call-seq:
1683  *     name -> String
1684  *
1685  *  Get the name for the element parameter.
1686  */
1687 static VALUE s_ParameterRef_GetName(VALUE self) {
1688         UnionPar *up;
1689         Int tp;
1690         up = s_UnionParFromValue(self, &tp, 0);
1691         if (tp == kElementParType) {
1692                 char name[5];
1693                 strncpy(name, up->atom.name, 4);
1694                 name[4] = 0;
1695                 return rb_str_new2(name);
1696         } else rb_raise(rb_eMolbyError, "invalid member name");
1697 }
1698
1699 /*
1700  *  call-seq:
1701  *     weight -> Float
1702  *
1703  *  Get the atomic weight for the element parameter.
1704  */
1705 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1706         UnionPar *up;
1707         Int tp;
1708         up = s_UnionParFromValue(self, &tp, 0);
1709         if (tp == kElementParType)
1710                 return rb_float_new(up->atom.weight);
1711         else if (tp == kVdwParType)
1712                 return rb_float_new(up->vdw.weight);
1713         else rb_raise(rb_eMolbyError, "invalid member weight");
1714 }
1715
1716 /*
1717  *  call-seq:
1718  *     fullname -> String
1719  *
1720  *  Get the full name for the element parameter.
1721  */
1722 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1723         UnionPar *up;
1724         Int tp;
1725         up = s_UnionParFromValue(self, &tp, 0);
1726         if (tp == kElementParType) {
1727                 char fullname[16];
1728                 strncpy(fullname, up->atom.fullname, 15);
1729                 fullname[15] = 0;
1730                 return rb_str_new2(fullname);
1731         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1732 }
1733
1734 /*
1735  *  call-seq:
1736  *     comment -> String
1737  *
1738  *  Get the comment for the parameter.
1739  */
1740 static VALUE s_ParameterRef_GetComment(VALUE self) {
1741         UnionPar *up;
1742         Int tp, com;
1743         up = s_UnionParFromValue(self, &tp, 0);
1744         com = up->bond.com;
1745         if (com == 0)
1746                 return Qnil;
1747         else return rb_str_new2(ParameterGetComment(com));
1748 }
1749
1750 /*
1751  *  call-seq:
1752  *     source -> String
1753  *
1754  *  Get the source string for the parameter. Returns false for undefined parameter,
1755  *  and nil for "local" parameter that is specific for the molecule.
1756  */
1757 static VALUE s_ParameterRef_GetSource(VALUE self) {
1758         UnionPar *up;
1759         Int tp, src;
1760         up = s_UnionParFromValue(self, &tp, 0);
1761         src = up->bond.src;
1762         if (src < 0)
1763                 return Qfalse;  /* undefined */
1764         else if (src == 0)
1765                 return Qnil;  /*  local  */
1766         else return rb_str_new2(ParameterGetComment(src));
1767 }
1768
1769 static void
1770 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1771 {
1772         VALUE *valp;
1773         int i;
1774         if (n == 1)
1775                 valp = &val;
1776         else {
1777                 if (rb_obj_is_kind_of(val, rb_cString)) {
1778                         char *s = StringValuePtr(val);
1779                         char *p;
1780                         for (i = 0; i < n; i++) {
1781                                 char buf[40];
1782                                 int len;
1783                                 /*  Skip leading separaters  */
1784                                 while (*s == '-' || *s == ' ' || *s == '\t')
1785                                         s++;
1786                                 for (p = s; *p != 0; p++) {
1787                                         if (*p == '-' || *p == ' ' || *p == '\t')
1788                                                 break;
1789                                 }
1790                                 len = p - s;
1791                                 if (len >= sizeof(buf))
1792                                         len = sizeof(buf) - 1;
1793                                 strncpy(buf, s, len);
1794                                 buf[len] = 0;
1795                                 /*  Skip trailing blanks  */
1796                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1797                                         buf[len] = 0;
1798                                 if (buf[0] == 0)
1799                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1800                                 if (buf[0] >= '0' && buf[0] <= '9')
1801                                         types[i] = atoi(buf);
1802                                 else
1803                                         types[i] = AtomTypeEncodeToUInt(buf);
1804                                 if (p == NULL || *p == 0) {
1805                                         i++;
1806                                         break;
1807                                 } else s = p + 1;
1808                         }
1809                         if (i < n)
1810                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1811                         return;
1812                 }
1813                 val = rb_ary_to_ary(val);
1814                 if (RARRAY_LEN(val) != n)
1815                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1816                 valp = RARRAY_PTR(val);
1817         }
1818         for (i = 0; i < n; i++) {
1819                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1820                         types[i] = NUM2INT(rb_Integer(valp[i]));
1821                 else {
1822                         VALUE sval = valp[i];
1823                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1824                 }
1825         }
1826 }
1827
1828 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1829         UnionPar *up;
1830         VALUE oldval;
1831         Int oldsrc, tp;
1832         UInt types[4];
1833         up = s_UnionParFromValue(self, &tp, 1);
1834         oldval = s_ParameterRef_GetAtomTypes(self);
1835         oldsrc = up->bond.src;
1836         switch (tp) {
1837                 case kBondParType:
1838                         s_ScanAtomTypes(val, 2, types);
1839                         up->bond.type1 = types[0];
1840                         up->bond.type2 = types[1];
1841                         break;
1842                 case kAngleParType:
1843                         s_ScanAtomTypes(val, 3, types);
1844                         up->angle.type1 = types[0];
1845                         up->angle.type2 = types[1];
1846                         up->angle.type3 = types[2];
1847                         break;
1848                 case kDihedralParType:
1849                 case kImproperParType:
1850                         s_ScanAtomTypes(val, 4, types);
1851                         up->torsion.type1 = types[0];
1852                         up->torsion.type2 = types[1];
1853                         up->torsion.type3 = types[2];
1854                         up->torsion.type4 = types[3];
1855                         break;
1856                 case kVdwParType:
1857                         s_ScanAtomTypes(val, 1, types);
1858                         up->vdw.type1 = types[0];
1859                         break;
1860                 case kVdwPairParType:
1861                         s_ScanAtomTypes(val, 2, types);
1862                         up->vdwp.type1 = types[0];
1863                         up->vdwp.type2 = types[1];
1864                         break;
1865                 case kVdwCutoffParType:
1866                         s_ScanAtomTypes(val, 2, types);
1867                         up->vdwcutoff.type1 = types[0];
1868                         up->vdwcutoff.type2 = types[1];
1869                         break;
1870                 default:
1871                         return Qnil;
1872         }
1873         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1874         return val;
1875 }
1876
1877 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1878         UnionPar *up;
1879         Int tp, i, n, oldsrc;
1880         VALUE *valp, oldval;
1881         up = s_UnionParFromValue(self, &tp, 1);
1882         oldval = s_ParameterRef_GetK(self);
1883         oldsrc = up->bond.src;
1884         switch (tp) {
1885                 case kBondParType:
1886                         val = rb_Float(val);
1887                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1888                         break;
1889                 case kAngleParType:
1890                         val = rb_Float(val);
1891                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1892                         break;
1893                 case kDihedralParType:
1894                 case kImproperParType:
1895                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1896                                 up->torsion.mult = 1;
1897                                 val = rb_Float(val);
1898                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1899                                 break;
1900                         }
1901                         n = up->torsion.mult;
1902                         if (n > 3)
1903                                 n = 3;
1904                         val = rb_ary_to_ary(val);
1905                         if (RARRAY_LEN(val) != n)
1906                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1907                         valp = RARRAY_PTR(val);
1908                         for (i = 0; i < n; i++) {
1909                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1910                         }
1911                         break;
1912                 default:
1913                         rb_raise(rb_eMolbyError, "invalid member k");
1914         }
1915         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1916         return val;
1917 }
1918
1919 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1920         UnionPar *up;
1921         Int tp, oldsrc;
1922         VALUE oldval;
1923         up = s_UnionParFromValue(self, &tp, 1);
1924         oldval = s_ParameterRef_GetR0(self);
1925         oldsrc = up->bond.src;
1926         if (tp == kBondParType) {
1927                 val = rb_Float(val);
1928                 up->bond.r0 = NUM2DBL(val);
1929         } else rb_raise(rb_eMolbyError, "invalid member r0");
1930         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1931         return val;
1932 }
1933
1934 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1935         UnionPar *up;
1936         Int tp, oldsrc;
1937         VALUE oldval;
1938         up = s_UnionParFromValue(self, &tp, 1);
1939         oldval = s_ParameterRef_GetA0(self);
1940         oldsrc = up->bond.src;
1941         if (tp == kAngleParType) {
1942                 val = rb_Float(val);
1943                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1944         } else rb_raise(rb_eMolbyError, "invalid member a0");
1945         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1946         return val;
1947 }
1948
1949 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1950         UnionPar *up;
1951         Int tp, oldsrc;
1952         VALUE oldval;
1953         up = s_UnionParFromValue(self, &tp, 1);
1954         oldval = s_ParameterRef_GetMult(self);
1955         oldsrc = up->bond.src;
1956         if (tp == kDihedralParType || tp == kImproperParType) {
1957                 int i;
1958                 val = rb_Integer(val);
1959                 i = NUM2INT(val);
1960                 if (i < 0 || i > 3)
1961                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1962                 up->torsion.mult = i;
1963         } else rb_raise(rb_eMolbyError, "invalid member mult");
1964         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1965         return val;
1966 }
1967
1968 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1969         UnionPar *up;
1970         Int tp, i, n, oldsrc;
1971         VALUE *valp, oldval;
1972         up = s_UnionParFromValue(self, &tp, 1);
1973         oldval = s_ParameterRef_GetPeriod(self);
1974         oldsrc = up->bond.src;
1975         if (tp == kDihedralParType || tp == kImproperParType) {
1976                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1977                         up->torsion.mult = 1;
1978                         val = rb_Integer(val);
1979                         up->torsion.period[0] = NUM2INT(val);
1980                 } else {
1981                         n = up->torsion.mult;
1982                         if (n > 3)
1983                                 n = 3;
1984                         val = rb_ary_to_ary(val);
1985                         if (RARRAY_LEN(val) != n)
1986                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1987                         valp = RARRAY_PTR(val);
1988                         for (i = 0; i < n; i++) {
1989                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1990                         }
1991                 }
1992         } else rb_raise(rb_eMolbyError, "invalid member period");
1993         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1994         return val;
1995 }
1996
1997 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1998         UnionPar *up;
1999         Int tp, i, n, oldsrc;
2000         VALUE *valp, oldval;
2001         up = s_UnionParFromValue(self, &tp, 1);
2002         oldval = s_ParameterRef_GetPhi0(self);
2003         oldsrc = up->bond.src;
2004         if (tp == kDihedralParType || tp == kImproperParType) {
2005                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2006                         up->torsion.mult = 1;
2007                         val = rb_Float(val);
2008                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2009                 } else {
2010                         n = up->torsion.mult;
2011                         if (n > 3)
2012                                 n = 3;
2013                         val = rb_ary_to_ary(val);
2014                         if (RARRAY_LEN(val) != n)
2015                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2016                         valp = RARRAY_PTR(val);
2017                         for (i = 0; i < n; i++)
2018                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2019                 }
2020         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2021         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2022         return val;
2023 }
2024
2025 /*
2026 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2027         UnionPar *up;
2028         Int tp, oldsrc;
2029         double d;
2030         VALUE oldval;
2031         up = s_UnionParFromValue(self, &tp, 1);
2032         oldval = s_ParameterRef_GetA(self);
2033         oldsrc = up->bond.src;
2034         val = rb_Float(val);
2035         d = NUM2DBL(val);
2036         if (tp == kVdwParType)
2037                 up->vdw.A = d;
2038         else if (tp == kVdwPairParType)
2039                 up->vdwp.A = d;
2040         else rb_raise(rb_eMolbyError, "invalid member A");
2041         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2042         return val;
2043 }
2044
2045 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2046         UnionPar *up;
2047         Int tp, oldsrc;
2048         double d;
2049         VALUE oldval;
2050         up = s_UnionParFromValue(self, &tp, 1);
2051         oldval = s_ParameterRef_GetB(self);
2052         oldsrc = up->bond.src;
2053         val = rb_Float(val);
2054         d = NUM2DBL(val);
2055         if (tp == kVdwParType)
2056                 up->vdw.B = d;
2057         else if (tp == kVdwPairParType)
2058                 up->vdwp.B = d;
2059         else rb_raise(rb_eMolbyError, "invalid member B");
2060         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2061         return val;
2062 }
2063 */
2064
2065 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2066         UnionPar *up;
2067         Int tp, oldsrc;
2068         Double r;
2069         VALUE oldval;
2070         up = s_UnionParFromValue(self, &tp, 1);
2071         oldval = s_ParameterRef_GetReq(self);
2072         oldsrc = up->bond.src;
2073         val = rb_Float(val);
2074         r = NUM2DBL(val);
2075         if (tp == kVdwParType) {
2076                 up->vdw.r_eq = r;
2077                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2078                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2079         } else if (tp == kVdwPairParType) {
2080                 up->vdwp.r_eq = r;
2081                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2082                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2083         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2084         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2085         return val;
2086 }
2087
2088 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2089         UnionPar *up;
2090         Int tp, oldsrc;
2091         Double e;
2092         VALUE oldval;
2093         up = s_UnionParFromValue(self, &tp, 1);
2094         oldval = s_ParameterRef_GetEps(self);
2095         oldsrc = up->bond.src;
2096         val = rb_Float(val);
2097         e = NUM2DBL(val) * KCAL2INTERNAL;
2098         if (tp == kVdwParType) {
2099                 up->vdw.eps = e;
2100                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2101                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2102         } else if (tp == kVdwPairParType) {
2103                 up->vdwp.eps = e;
2104                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2105                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2106         } else rb_raise(rb_eMolbyError, "invalid member eps");
2107         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2108         return val;
2109 }
2110
2111 /*
2112 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2113         UnionPar *up;
2114         Int tp, oldsrc;
2115         double d;
2116         VALUE oldval;
2117         up = s_UnionParFromValue(self, &tp, 1);
2118         oldval = s_ParameterRef_GetA14(self);
2119         oldsrc = up->bond.src;
2120         val = rb_Float(val);
2121         d = NUM2DBL(val);
2122         if (tp == kVdwParType)
2123                 up->vdw.A14 = d;
2124         else if (tp == kVdwPairParType)
2125                 up->vdwp.A14 = d;
2126         else rb_raise(rb_eMolbyError, "invalid member A14");
2127         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2128         return val;
2129 }
2130
2131 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2132         UnionPar *up;
2133         Int tp, oldsrc;
2134         double d;
2135         VALUE oldval;
2136         up = s_UnionParFromValue(self, &tp, 1);
2137         oldval = s_ParameterRef_GetB14(self);
2138         oldsrc = up->bond.src;
2139         val = rb_Float(val);
2140         d = NUM2DBL(val);
2141         if (tp == kVdwParType)
2142                 up->vdw.B14 = d;
2143         else if (tp == kVdwPairParType)
2144                 up->vdwp.B14 = d;
2145         else rb_raise(rb_eMolbyError, "invalid member B14");
2146         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2147         return val;
2148 }
2149 */
2150
2151 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2152         UnionPar *up;
2153         Int tp, oldsrc;
2154         Double r;
2155         VALUE oldval;
2156         up = s_UnionParFromValue(self, &tp, 1);
2157         oldval = s_ParameterRef_GetReq14(self);
2158         oldsrc = up->bond.src;
2159         val = rb_Float(val);
2160         r = NUM2DBL(val);
2161         if (tp == kVdwParType) {
2162                 up->vdw.r_eq14 = r;
2163                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2164                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2165         } else if (tp == kVdwPairParType) {
2166                 up->vdwp.r_eq14 = r;
2167                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2168                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2169         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2170         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2171         return val;
2172 }
2173
2174 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2175         UnionPar *up;
2176         Int tp, oldsrc;
2177         Double e;
2178         VALUE oldval;
2179         up = s_UnionParFromValue(self, &tp, 1);
2180         oldval = s_ParameterRef_GetEps14(self);
2181         oldsrc = up->bond.src;
2182         val = rb_Float(val);
2183         e = NUM2DBL(val) * KCAL2INTERNAL;
2184         if (tp == kVdwParType) {
2185                 up->vdw.eps14 = e;
2186                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2187                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2188         } else if (tp == kVdwPairParType) {
2189                 up->vdwp.eps14 = e;
2190                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2191                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2192         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2193         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2194         return val;
2195 }
2196
2197 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2198         UnionPar *up;
2199         Int tp, oldsrc;
2200         VALUE oldval;
2201         oldval = s_ParameterRef_GetCutoff(self);
2202         oldsrc = up->bond.src;
2203         up = s_UnionParFromValue(self, &tp, 1);
2204         val = rb_Float(val);
2205         if (tp == kVdwCutoffParType) {
2206                 up->vdwcutoff.cutoff = NUM2DBL(val);
2207         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2208         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2209         return val;
2210 }
2211
2212 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2213         UnionPar *up;
2214         Int tp, oldsrc;
2215         VALUE oldval;
2216         up = s_UnionParFromValue(self, &tp, 1);
2217         oldval = s_ParameterRef_GetRadius(self);
2218         oldsrc = up->bond.src;
2219         val = rb_Float(val);
2220         if (tp == kElementParType) {
2221                 up->atom.radius = NUM2DBL(val);
2222         } else rb_raise(rb_eMolbyError, "invalid member radius");
2223         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2224         return val;
2225 }
2226
2227 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2228         UnionPar *up;
2229         Int tp, oldsrc;
2230         VALUE oldval;
2231         up = s_UnionParFromValue(self, &tp, 1);
2232         oldval = s_ParameterRef_GetVdwRadius(self);
2233         oldsrc = up->bond.src;
2234         val = rb_Float(val);
2235         if (tp == kElementParType) {
2236                 up->atom.vdw_radius = NUM2DBL(val);
2237         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2238         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2239         return val;
2240 }
2241
2242 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2243         UnionPar *up;
2244         Int tp, oldsrc;
2245         VALUE *valp, oldval;
2246         up = s_UnionParFromValue(self, &tp, 1);
2247         oldval = s_ParameterRef_GetColor(self);
2248         oldsrc = up->bond.src;
2249         val = rb_ary_to_ary(val);
2250         if (RARRAY_LEN(val) != 3)
2251                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2252         valp = RARRAY_PTR(val);
2253         if (tp == kElementParType) {
2254                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2255                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2256                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2257         } else rb_raise(rb_eMolbyError, "invalid member color");
2258         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2259         return val;
2260 }
2261
2262 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2263         UnionPar *up;
2264         Int tp, oldsrc;
2265         VALUE oldval;
2266         up = s_UnionParFromValue(self, &tp, 1);
2267         oldval = s_ParameterRef_GetAtomicNumber(self);
2268         oldsrc = up->bond.src;
2269         val = rb_Integer(val);
2270         if (tp == kElementParType)
2271                 up->atom.number = NUM2INT(val);
2272         else if (tp == kVdwParType) {
2273                 up->vdw.atomicNumber = NUM2INT(val);
2274                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2275         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2276         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2277         return val;
2278 }
2279
2280 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2281         UnionPar *up;
2282         Int tp, oldsrc;
2283         VALUE oldval;
2284         up = s_UnionParFromValue(self, &tp, 1);
2285         oldval = s_ParameterRef_GetName(self);
2286         oldsrc = up->bond.src;
2287         if (tp == kElementParType) {
2288                 strncpy(up->atom.name, StringValuePtr(val), 4);
2289         } else rb_raise(rb_eMolbyError, "invalid member name");
2290         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2291         return val;
2292 }
2293
2294 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2295         UnionPar *up;
2296         Int tp, oldsrc;
2297         VALUE oldval;
2298         val = rb_Float(val);
2299         oldval = s_ParameterRef_GetWeight(self);
2300         up = s_UnionParFromValue(self, &tp, 1);
2301         oldsrc = up->bond.src;
2302         if (tp == kElementParType)
2303                 up->atom.weight = NUM2DBL(val);
2304         else if (tp == kVdwParType)
2305                 up->vdw.weight = NUM2DBL(val);
2306         else rb_raise(rb_eMolbyError, "invalid member weight");
2307         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2308         return val;
2309 }
2310
2311 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2312         UnionPar *up;
2313         Int tp, oldsrc;
2314         VALUE oldval;
2315         up = s_UnionParFromValue(self, &tp, 1);
2316         oldval = s_ParameterRef_GetFullName(self);
2317         oldsrc = up->bond.src;
2318         if (tp == kElementParType) {
2319                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2320                 up->atom.fullname[15] = 0;
2321         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2322         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2323         return val;
2324 }
2325
2326 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2327         UnionPar *up;
2328         Int tp, com, oldsrc;
2329         VALUE oldval;
2330         up = s_UnionParFromValue(self, &tp, 1);
2331         oldval = s_ParameterRef_GetComment(self);
2332         oldsrc = up->bond.src;
2333         if (val == Qnil)
2334                 up->bond.com = 0;
2335         else {
2336                 com = ParameterCommentIndex(StringValuePtr(val));
2337                 up->bond.com = com;
2338         }
2339         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2340         return val;     
2341 }
2342
2343 /*  Only false (undefined) and nil (local) can be set  */
2344 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2345         UnionPar *up;
2346         Int tp, oldsrc;
2347         VALUE oldval;
2348         up = s_UnionParFromValue(self, &tp, 1);
2349         if (val != Qfalse && val != Qnil)
2350                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2351         oldval = s_ParameterRef_GetSource(self);
2352         oldsrc = up->bond.src;
2353         if (oldsrc != 0 && oldsrc != -1)
2354                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2355         up->bond.src = (val == Qfalse ? -1 : 0);
2356         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2357         return val;     
2358 }
2359
2360 static struct s_ParameterAttrDef {
2361         char *name;
2362         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2363         ID id;                  /*  Will be set within InitMolby()  */
2364         VALUE (*getter)(VALUE);
2365         VALUE (*setter)(VALUE, VALUE);
2366 } s_ParameterAttrDefTable[] = {
2367         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2368         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2369         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2370         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2371         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2372         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2373         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2374         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2375         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2376         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2377 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2378         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2379         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2380         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2381 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2382         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2383         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2384         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2385         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2386         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2387         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2388         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2389         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2390         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2391         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2392         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2393         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2394         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2395         {NULL} /* Sentinel */
2396 };
2397
2398 static VALUE
2399 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2400 {
2401         int i;
2402         ID kid;
2403         if (TYPE(key) != T_SYMBOL) {
2404                 kid = rb_intern(StringValuePtr(key));
2405                 key = ID2SYM(kid);
2406         } else kid = SYM2ID(key);
2407         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2408                 if (s_ParameterAttrDefTable[i].id == kid) {
2409                         if (value == Qundef)
2410                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2411                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2412                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2413                         else
2414                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2415                 }
2416         }
2417         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2418         return Qnil; /* not reached */
2419 }
2420
2421 static VALUE
2422 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2423 {
2424         return s_ParameterRef_SetAttr(self, key, Qundef);
2425 }
2426
2427 /*
2428  *  call-seq:
2429  *     keys(idx)          -> array of valid parameter attributes
2430  *  
2431  *  Returns an array of valid parameter attributes (as Symbols).
2432  */
2433 static VALUE
2434 s_ParameterRef_Keys(VALUE self)
2435 {
2436         ParameterRef *pref;
2437         Data_Get_Struct(self, ParameterRef, pref);
2438         switch (pref->parType) {
2439                 case kBondParType:
2440                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2441                 case kAngleParType:
2442                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2443                 case kDihedralParType:
2444                 case kImproperParType:
2445                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2446                 case kVdwParType:
2447                         return rb_ary_new3(11, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_AtomicNumberSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_WeightSym, s_CommentSym, s_SourceSym);
2448                 case kVdwPairParType:
2449                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2450                 case kVdwCutoffParType:
2451                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2452                 case kElementParType:
2453                         return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_VdwRadiusSym, s_CommentSym, s_SourceSym);
2454                 default:
2455                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2456         }
2457         return Qnil;  /*  Not reached  */
2458 }
2459
2460 /*
2461  *  call-seq:
2462  *     to_hash(idx)          -> Hash
2463  *  
2464  *  Returns a hash containing valid parameter names and values
2465  */
2466 static VALUE
2467 s_ParameterRef_ToHash(VALUE self)
2468 {
2469         VALUE keys = s_ParameterRef_Keys(self);
2470         VALUE retval;
2471         int i;
2472         if (keys == Qnil)
2473                 return Qnil;
2474         retval = rb_hash_new();
2475         for (i = 0; i < RARRAY_LEN(keys); i++) {
2476                 VALUE key = RARRAY_PTR(keys)[i];
2477                 VALUE val = s_ParameterRef_GetAttr(self, key);
2478                 rb_hash_aset(retval, key, val);
2479         }
2480         return retval;
2481 }
2482
2483 /*
2484  *  call-seq:
2485  *     parameter.to_s(idx)          -> String
2486  *  
2487  *  Returns a string representation of the given parameter
2488  */
2489 static VALUE
2490 s_ParameterRef_ToString(VALUE self)
2491 {
2492         Int tp, i, n;
2493         char buf[1024], types[4][8];
2494         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2495         switch (tp) {
2496                 case kBondParType:
2497                         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);
2498                         break;
2499                 case kAngleParType:
2500                         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);
2501                         break;
2502                 case kDihedralParType:
2503                 case kImproperParType:
2504                         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]));
2505                         n = strlen(buf);
2506                         for (i = 0; i < up->torsion.mult; i++) {
2507                                 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);
2508                                 n = strlen(buf);
2509                         }
2510                         break;
2511                 case kVdwParType:
2512                         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);
2513                         break;
2514                 case kVdwPairParType:
2515                         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);
2516                         break;
2517                 case kVdwCutoffParType:
2518                         snprintf(buf, sizeof buf, "vdwcutoff %4.6s %4.6s %8.4f", AtomTypeDecodeToString(up->vdwcutoff.type1, types[0]), AtomTypeDecodeToString(up->vdwcutoff.type2, types[1]), up->vdwcutoff.cutoff);
2519                         break;
2520                 case kElementParType:
2521                         snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s %6.3f", up->atom.name, up->atom.number, up->atom.radius, up->atom.red / 65535.0, up->atom.green / 65535.0, up->atom.blue / 65535.0, up->atom.weight, up->atom.fullname, up->atom.vdw_radius);
2522                         break;
2523         }
2524         return rb_str_new2(buf);
2525 }
2526
2527 /*
2528  *  call-seq:
2529  *     self == parameterRef -> boolean
2530  *  
2531  *  True if the parameters point to the same parameter record.
2532  */
2533 static VALUE
2534 s_ParameterRef_Equal(VALUE self, VALUE val)
2535 {
2536         Int tp1, tp2;
2537         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2538                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2539         } else return Qfalse;
2540 }
2541         
2542 #pragma mark ====== Parameter Class ======
2543
2544 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2545  *  is NULL, then the global parameters are looked for.  */
2546
2547 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2548 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2549 static void
2550 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2551 {
2552         Molecule *mol;
2553         Data_Get_Struct(val, Molecule, mol);
2554         if (mol == NULL)
2555                 rb_raise(rb_eMolbyError, "the molecule is empty");
2556         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2557                 /*  Do self.md_arena.prepare  */
2558                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2559                 if (val2 != Qnil)
2560                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2561         }
2562 }
2563
2564 static VALUE
2565 s_NewParameterValueFromValue(VALUE val)
2566 {
2567         Molecule *mol;
2568         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2569                 Data_Get_Struct(val, Molecule, mol);
2570                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2571                 MoleculeRetain(mol);
2572                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2573         } else {
2574                 mol = NULL;
2575                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2576         }
2577 }
2578
2579 static Molecule *
2580 s_MoleculeFromParameterValue(VALUE val)
2581 {
2582         Molecule *mol;
2583         Data_Get_Struct(val, Molecule, mol);
2584         return mol;
2585 }
2586
2587 static Parameter *
2588 s_ParameterFromParameterValue(VALUE val)
2589 {
2590         Molecule *mol;
2591         Data_Get_Struct(val, Molecule, mol);
2592         if (mol != NULL)
2593                 return mol->par;
2594         return gBuiltinParameters;
2595 }
2596
2597 /*  Forward declarations  */
2598 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2599 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2600
2601 static Molecule *
2602 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2603 {
2604         if (val == rb_cParameter) {
2605                 return NULL;  /*  Parameter class method: builtin parameters  */
2606         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2607                 return s_MoleculeFromParameterValue(val);
2608         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2609                 return s_MoleculeFromParEnumerableValue(val);
2610         } else return NULL;
2611 }
2612
2613 /*
2614  *  call-seq:
2615  *     builtin    -> Parameter
2616  *  
2617  *  Returns a parameter value that points to the global (builtin) parameters.
2618  *  Equivalent to Parameter::Builtin (constant).
2619  */
2620 static VALUE
2621 s_Parameter_Builtin(VALUE self)
2622 {
2623         static ID s_builtin_id = 0;
2624         if (s_builtin_id == 0)
2625                 s_builtin_id = rb_intern("Builtin");
2626         return rb_const_get(rb_cParameter, s_builtin_id);
2627 }
2628
2629 /*
2630  *  call-seq:
2631  *     bond(idx)          -> ParameterRef
2632  *  
2633  *  The index-th bond parameter record is returned.
2634  */
2635 static VALUE
2636 s_Parameter_Bond(VALUE self, VALUE ival)
2637 {
2638         Molecule *mol;
2639         int idx, n;
2640         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2641         idx = NUM2INT(rb_Integer(ival));
2642         if (mol == NULL)
2643                 n = gBuiltinParameters->nbondPars;
2644         else if (mol->par != NULL)
2645                 n = mol->par->nbondPars;
2646         else n = 0;
2647         if (idx < -n || idx >= n)
2648                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2649         if (idx < 0)
2650                 idx += n;
2651         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2652 }
2653
2654 /*
2655  *  call-seq:
2656  *     angle(idx)          -> ParameterRef
2657  *  
2658  *  The index-th angle parameter record is returned.
2659  */
2660 static VALUE
2661 s_Parameter_Angle(VALUE self, VALUE ival)
2662 {
2663         Molecule *mol;
2664         int idx, n;
2665         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2666         idx = NUM2INT(rb_Integer(ival));
2667         if (mol == NULL)
2668                 n = gBuiltinParameters->nanglePars;
2669         else if (mol->par != NULL)
2670                 n = mol->par->nanglePars;
2671         else n = 0;
2672         if (idx < -n || idx >= n)
2673                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2674         if (idx < 0)
2675                 idx += n;
2676         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2677 }
2678
2679 /*
2680  *  call-seq:
2681  *     dihedral(idx)          -> ParameterRef
2682  *  
2683  *  The index-th dihedral parameter record is returned.
2684  */
2685 static VALUE
2686 s_Parameter_Dihedral(VALUE self, VALUE ival)
2687 {
2688         Molecule *mol;
2689         int idx, n;
2690         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2691         idx = NUM2INT(rb_Integer(ival));
2692         if (mol == NULL)
2693                 n = gBuiltinParameters->ndihedralPars;
2694         else if (mol->par != NULL)
2695                 n = mol->par->ndihedralPars;
2696         else n = 0;
2697         if (idx < -n || idx >= n)
2698                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2699         if (idx < 0)
2700                 idx += n;
2701         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2702 }
2703
2704 /*
2705  *  call-seq:
2706  *     improper(idx)          -> ParameterRef
2707  *  
2708  *  The index-th improper parameter record is returned.
2709  */
2710 static VALUE
2711 s_Parameter_Improper(VALUE self, VALUE ival)
2712 {
2713         Molecule *mol;
2714         int idx, n;
2715         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2716         idx = NUM2INT(rb_Integer(ival));
2717         if (mol == NULL)
2718                 n = gBuiltinParameters->nimproperPars;
2719         else if (mol->par != NULL)
2720                 n = mol->par->nimproperPars;
2721         else n = 0;
2722         if (idx < -n || idx >= n)
2723                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2724         if (idx < 0)
2725                 idx += n;
2726         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2727 }
2728
2729 /*
2730  *  call-seq:
2731  *     vdw(idx)          -> ParameterRef
2732  *  
2733  *  The index-th vdw parameter record is returned.
2734  */
2735 static VALUE
2736 s_Parameter_Vdw(VALUE self, VALUE ival)
2737 {
2738         Molecule *mol;
2739         int idx, n;
2740         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2741         idx = NUM2INT(rb_Integer(ival));
2742         if (mol == NULL)
2743                 n = gBuiltinParameters->nvdwPars;
2744         else if (mol->par != NULL)
2745                 n = mol->par->nvdwPars;
2746         else n = 0;
2747         if (idx < -n || idx >= n)
2748                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2749         if (idx < 0)
2750                 idx += n;
2751         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2752 }
2753
2754 /*
2755  *  call-seq:
2756  *     vdw_pair(idx)          -> ParameterRef
2757  *  
2758  *  The index-th vdw pair parameter record is returned.
2759  */
2760 static VALUE
2761 s_Parameter_VdwPair(VALUE self, VALUE ival)
2762 {
2763         Molecule *mol;
2764         int idx, n;
2765         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2766         idx = NUM2INT(rb_Integer(ival));
2767         if (mol == NULL)
2768                 n = gBuiltinParameters->nvdwpPars;
2769         else if (mol->par != NULL)
2770                 n = mol->par->nvdwpPars;
2771         else n = 0;
2772         if (idx < -n || idx >= n)
2773                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2774         if (idx < 0)
2775                 idx += n;
2776         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2777 }
2778
2779 /*
2780  *  call-seq:
2781  *     vdw_cutoff(idx)          -> ParameterRef
2782  *  
2783  *  The index-th vdw cutoff parameter record is returned.
2784  */
2785 static VALUE
2786 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2787 {
2788         Molecule *mol;
2789         int idx, n;
2790         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2791         idx = NUM2INT(rb_Integer(ival));
2792         if (mol == NULL)
2793                 n = gBuiltinParameters->nvdwCutoffPars;
2794         else if (mol->par != NULL)
2795                 n = mol->par->nvdwCutoffPars;
2796         else n = 0;
2797         if (idx < -n || idx >= n)
2798                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2799         if (idx < 0)
2800                 idx += n;
2801         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2802 }
2803
2804 /*
2805  *  call-seq:
2806  *     element(idx)            -> ParameterRef
2807  *     element(t1)             -> ParameterRef
2808  *  
2809  *  In the first form, the index-th element parameter record is returned. In the second
2810  *  form, the element parameter for t1 is looked up (the last index first). t1
2811  *  is the element name string (up to 4 characters).
2812  *  Unlike other Parameter methods, this is used only for the global parameter.
2813  */
2814 static VALUE
2815 s_Parameter_Element(VALUE self, VALUE ival)
2816 {
2817         Int idx1;
2818         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2819                 int n = gCountElementParameters;
2820                 idx1 = NUM2INT(rb_Integer(ival));
2821                 if (idx1 < -n || idx1 >= n)
2822                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2823                 if (idx1 < 0)
2824                         idx1 += n;
2825                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2826         } else {
2827                 ElementPar *ep;
2828                 char name[6];
2829                 int i;
2830                 strncpy(name, StringValuePtr(ival), 4);
2831                 name[4] = 0;
2832                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2833                         if (strncmp(ep->name, name, 4) == 0)
2834                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2835                 }
2836                 return Qnil;
2837         }
2838 }
2839
2840 /*
2841  *  call-seq:
2842  *     nbonds          -> Integer
2843  *  
2844  *  Returns the number of bond parameters.
2845  */
2846 static VALUE
2847 s_Parameter_Nbonds(VALUE self)
2848 {
2849         Int n;
2850         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2851         if (mol == NULL)
2852                 n = gBuiltinParameters->nbondPars;
2853         else if (mol->par != NULL)
2854                 n = mol->par->nbondPars;
2855         else n = 0;
2856         return INT2NUM(n);
2857 }
2858
2859 /*
2860  *  call-seq:
2861  *     nangles          -> Integer
2862  *  
2863  *  Returns the number of angle parameters.
2864  */
2865 static VALUE
2866 s_Parameter_Nangles(VALUE self)
2867 {
2868         Int n;
2869         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2870         if (mol == NULL)
2871                 n = gBuiltinParameters->nanglePars;
2872         else if (mol->par != NULL)
2873                 n = mol->par->nanglePars;
2874         else n = 0;
2875         return INT2NUM(n);
2876 }
2877
2878 /*
2879  *  call-seq:
2880  *     ndihedrals          -> Integer
2881  *  
2882  *  Returns the number of dihedral parameters.
2883  */
2884 static VALUE
2885 s_Parameter_Ndihedrals(VALUE self)
2886 {
2887         Int n;
2888         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2889         if (mol == NULL)
2890                 n = gBuiltinParameters->ndihedralPars;
2891         else if (mol->par != NULL)
2892                 n = mol->par->ndihedralPars;
2893         else n = 0;
2894         return INT2NUM(n);
2895 }
2896
2897 /*
2898  *  call-seq:
2899  *     nimpropers          -> Integer
2900  *  
2901  *  Returns the number of improper parameters.
2902  */
2903 static VALUE
2904 s_Parameter_Nimpropers(VALUE self)
2905 {
2906         Int n;
2907         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2908         if (mol == NULL)
2909                 n = gBuiltinParameters->nimproperPars;
2910         else if (mol->par != NULL)
2911                 n = mol->par->nimproperPars;
2912         else n = 0;
2913         return INT2NUM(n);
2914 }
2915
2916 /*
2917  *  call-seq:
2918  *     nvdws          -> Integer
2919  *  
2920  *  Returns the number of vdw parameters.
2921  */
2922 static VALUE
2923 s_Parameter_Nvdws(VALUE self)
2924 {
2925         Int n;
2926         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2927         if (mol == NULL)
2928                 n = gBuiltinParameters->nvdwPars;
2929         else if (mol->par != NULL)
2930                 n = mol->par->nvdwPars;
2931         else n = 0;
2932         return INT2NUM(n);
2933 }
2934
2935 /*
2936  *  call-seq:
2937  *     nvdw_pairs          -> Integer
2938  *  
2939  *  Returns the number of vdw pair parameters.
2940  */
2941 static VALUE
2942 s_Parameter_NvdwPairs(VALUE self)
2943 {
2944         Int n;
2945         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2946         if (mol == NULL)
2947                 n = gBuiltinParameters->nvdwpPars;
2948         else if (mol->par != NULL)
2949                 n = mol->par->nvdwpPars;
2950         else n = 0;
2951         return INT2NUM(n);
2952 }
2953
2954 /*
2955  *  call-seq:
2956  *     nvdw_cutoffs          -> Integer
2957  *  
2958  *  Returns the number of vdw cutoff parameters.
2959  */
2960 static VALUE
2961 s_Parameter_NvdwCutoffs(VALUE self)
2962 {
2963         Int n;
2964         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2965         if (mol == NULL)
2966                 n = gBuiltinParameters->nvdwCutoffPars;
2967         else if (mol->par != NULL)
2968                 n = mol->par->nvdwCutoffPars;
2969         else n = 0;
2970         return INT2NUM(n);
2971 }
2972
2973 /*
2974  *  call-seq:
2975  *     nelements          -> Integer
2976  *  
2977  *  Returns the number of element parameters.
2978  */
2979 static VALUE
2980 s_Parameter_Nelements(VALUE self)
2981 {
2982         return INT2NUM(gCountElementParameters);
2983 }
2984
2985 /*
2986  *  call-seq:
2987  *     bonds          -> ParEnumerable
2988  *  
2989  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2990  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2991  *  useful when all accessible parameters should be examined by use of 'each' method.
2992  */
2993 static VALUE
2994 s_Parameter_Bonds(VALUE self)
2995 {
2996         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2997         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2998 }
2999
3000 /*
3001  *  call-seq:
3002  *     angles          -> ParEnumerable
3003  *  
3004  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3005  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3006  *  useful when all accessible parameters should be examined by use of 'each' method.
3007  */
3008 static VALUE
3009 s_Parameter_Angles(VALUE self)
3010 {
3011         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3012         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3013 }
3014
3015 /*
3016  *  call-seq:
3017  *     dihedrals          -> ParEnumerable
3018  *  
3019  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3020  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3021  *  useful when all accessible parameters should be examined by use of 'each' method.
3022  */
3023 static VALUE
3024 s_Parameter_Dihedrals(VALUE self)
3025 {
3026         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3027         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3028 }
3029
3030 /*
3031  *  call-seq:
3032  *     impropers          -> ParEnumerable
3033  *  
3034  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3035  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3036  *  useful when all accessible parameters should be examined by use of 'each' method.
3037  */
3038 static VALUE
3039 s_Parameter_Impropers(VALUE self)
3040 {
3041         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3042         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3043 }
3044
3045 /*
3046  *  call-seq:
3047  *     vdws          -> ParEnumerable
3048  *  
3049  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3050  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3051  *  useful when all accessible parameters should be examined by use of 'each' method.
3052  */
3053 static VALUE
3054 s_Parameter_Vdws(VALUE self)
3055 {
3056         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3057         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3058 }
3059
3060 /*
3061  *  call-seq:
3062  *     vdw_pairs          -> ParEnumerable
3063  *  
3064  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3065  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3066  *  useful when all accessible parameters should be examined by use of 'each' method.
3067  */
3068 static VALUE
3069 s_Parameter_VdwPairs(VALUE self)
3070 {
3071         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3072         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3073 }
3074
3075 /*
3076  *  call-seq:
3077  *     vdw_cutoffs          -> ParEnumerable
3078  *  
3079  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3080  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3081  *  useful when all accessible parameters should be examined by use of 'each' method.
3082  */
3083 static VALUE
3084 s_Parameter_VdwCutoffs(VALUE self)
3085 {
3086         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3087         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3088 }
3089
3090 /*
3091  *  call-seq:
3092  *     elements          -> ParEnumerable
3093  *  
3094  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3095  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3096  *  useful when all accessible parameters should be examined by use of 'each' method.
3097  */
3098 static VALUE
3099 s_Parameter_Elements(VALUE self)
3100 {
3101         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3102         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3103 }
3104
3105 static VALUE
3106 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3107 {
3108         VALUE atval, optval;
3109         UInt t[4];
3110         Int ii[4];
3111         int i, n, idx, flags, is_global;
3112
3113         rb_scan_args(argc, argv, "1*", &atval, &optval);
3114         
3115         /*  Get the atom types  */
3116         switch (parType) {
3117                 case kBondParType: n = 2; break;
3118                 case kAngleParType: n = 3; break;
3119                 case kDihedralParType: n = 4; break;
3120                 case kImproperParType: n = 4; break;
3121                 case kVdwParType: n = 1; break;
3122                 case kVdwPairParType: n = 2; break;
3123                 default: return Qnil;
3124         }
3125         s_ScanAtomTypes(atval, n, t);
3126         for (i = 0; i < n; i++) {
3127                 if (t[i] < kAtomTypeMinimum) {
3128                         /*  Explicit atom index  */
3129                         if (mol == NULL)
3130                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3131                         if (t[i] >= mol->natoms)
3132                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3133                         ii[i] = t[i];
3134                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3135                 } else ii[i] = -1;
3136         }
3137         
3138         /*  Analyze options  */
3139         flags = 0;
3140         n = RARRAY_LEN(optval);
3141         for (i = 0; i < n; i++) {
3142                 VALUE oval = RARRAY_PTR(optval)[i];
3143                 if (oval == ID2SYM(rb_intern("global")))
3144                         flags |= kParameterLookupGlobal;
3145                 else if (oval == ID2SYM(rb_intern("local")))
3146                         flags |= kParameterLookupLocal;
3147                 else if (oval == ID2SYM(rb_intern("missing")))
3148                         flags |= kParameterLookupMissing;
3149                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3150                         flags |= kParameterLookupNoWildcard;
3151                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3152                         flags |= kParameterLookupNoBaseAtomType;
3153                 else if (oval == ID2SYM(rb_intern("create")))
3154                         flags |= 256;
3155         }
3156         if (flags == 0)
3157                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3158         
3159         idx = -1;
3160         is_global = 0;
3161         switch (parType) {
3162                 case kBondParType: {
3163                         BondPar *bp;
3164                         if (mol != NULL) {
3165                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3166                                 if (bp != NULL) {
3167                                         idx = bp - mol->par->bondPars;
3168                                         break;
3169                                 }
3170                         }
3171                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3172                         if (bp != NULL) {
3173                                 idx = bp - gBuiltinParameters->bondPars;
3174                                 is_global = 1;
3175                         }
3176                         break;
3177                 }
3178                 case kAngleParType: {
3179                         AnglePar *ap;
3180                         if (mol != NULL) {
3181                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3182                                 if (ap != NULL) {
3183                                         idx = ap - mol->par->anglePars;
3184                                         break;
3185                                 }
3186                         }
3187                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3188                         if (ap != NULL) {
3189                                 idx = ap - gBuiltinParameters->anglePars;
3190                                 is_global = 1;
3191                         }
3192                         break;
3193                 }
3194                 case kDihedralParType: {
3195                         TorsionPar *tp;
3196                         if (mol != NULL) {
3197                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3198                                 if (tp != NULL) {
3199                                         idx = tp - mol->par->dihedralPars;
3200                                         break;
3201                                 }
3202                         }
3203                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3204                         if (tp != NULL) {
3205                                 idx = tp - gBuiltinParameters->dihedralPars;
3206                                 is_global = 1;
3207                         }
3208                         break;
3209                 }
3210                 case kImproperParType: {
3211                         TorsionPar *tp;
3212                         if (mol != NULL) {
3213                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3214                                 if (tp != NULL) {
3215                                         idx = tp - mol->par->improperPars;
3216                                         break;
3217                                 }
3218                         }
3219                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3220                         if (tp != NULL) {
3221                                 idx = tp - gBuiltinParameters->improperPars;
3222                                 is_global = 1;
3223                         }
3224                         break;
3225                 }       
3226                 case kVdwParType: {
3227                         VdwPar *vp;
3228                         if (mol != NULL) {
3229                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3230                                 if (vp != NULL) {
3231                                         idx = vp - mol->par->vdwPars;
3232                                         break;
3233                                 }
3234                         }
3235                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3236                         if (vp != NULL) {
3237                                 idx = vp - gBuiltinParameters->vdwPars;
3238                                 is_global = 1;
3239                         }
3240                         break;
3241                 }       
3242                 case kVdwPairParType: {
3243                         VdwPairPar *vp;
3244                         if (mol != NULL) {
3245                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3246                                 if (vp != NULL) {
3247                                         idx = vp - mol->par->vdwpPars;
3248                                         break;
3249                                 }
3250                         }
3251                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3252                         if (vp != NULL) {
3253                                 idx = vp - gBuiltinParameters->vdwpPars;
3254                                 is_global = 1;
3255                         }
3256                         break;
3257                 }
3258                 default:
3259                         return Qnil;
3260         }
3261         if (idx < 0) {
3262                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3263                         return Qnil;            
3264                 else {
3265                         /*  Insert a new parameter record  */
3266                         UnionPar *up;
3267                         Int count = ParameterGetCountForType(mol->par, parType);
3268                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3269                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3270                         IntGroupRelease(ig);
3271                         is_global = 0;
3272                         idx = count;
3273                         /*  Set atom types  */
3274                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3275                         if (up == NULL)
3276                                 return Qnil;
3277                         switch (parType) {
3278                                 case kBondParType:
3279                                         up->bond.type1 = t[0];
3280                                         up->bond.type2 = t[1];
3281                                         break;
3282                                 case kAngleParType:
3283                                         up->angle.type1 = t[0];
3284                                         up->angle.type2 = t[1];
3285                                         up->angle.type3 = t[2];
3286                                         break;
3287                                 case kDihedralParType:
3288                                 case kImproperParType:
3289                                         up->torsion.type1 = t[0];
3290                                         up->torsion.type2 = t[1];
3291                                         up->torsion.type3 = t[2];
3292                                         up->torsion.type4 = t[3];
3293                                         break;
3294                                 case kVdwParType:
3295                                         up->vdw.type1 = t[0];
3296                                         break;
3297                                 case kVdwPairParType:
3298                                         up->vdwp.type1 = t[0];
3299                                         up->vdwp.type2 = t[1];
3300                                         break;
3301                                 default:
3302                                         return Qnil;
3303                         }
3304                 }
3305         }
3306         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3307 }
3308
3309 /*
3310  *  call-seq:
3311  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3312  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3313  *
3314  *  Find the parameter record that matches the given atom types. The atom types are given
3315  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3316  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3317  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3318  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3319  */
3320 static VALUE
3321 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3322 {
3323         int parType;
3324         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3325         if (argc == 0)
3326                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3327         parType = s_ParTypeFromValue(argv[0]);
3328         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3329 }
3330
3331 /*
3332  *  call-seq:
3333  *     self == parameter -> boolean
3334  *  
3335  *  True if the parameters point to the same parameter table.
3336  */
3337 static VALUE
3338 s_Parameter_Equal(VALUE self, VALUE val)
3339 {
3340         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3341                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3342         } else return Qfalse;
3343 }
3344
3345 #pragma mark ====== ParEnumerable Class ======
3346
3347 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3348  and the parameter type. If the Molecule is NULL, then it refers to the
3349  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3350  the global parameters are always accessible. */
3351
3352 typedef struct ParEnumerable {
3353         Molecule *mol;
3354         Int parType;   /*  Same as parType in ParameterRef  */
3355 } ParEnumerable;
3356
3357 static ParEnumerable *
3358 s_ParEnumerableNew(Molecule *mol, Int parType)
3359 {
3360         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3361         if (pen != NULL) {
3362                 pen->mol = mol;
3363                 if (mol != NULL)
3364                         MoleculeRetain(mol);
3365                 pen->parType = parType;
3366         }
3367         return pen;
3368 }
3369
3370 static void
3371 s_ParEnumerableRelease(ParEnumerable *pen)
3372 {
3373         if (pen != NULL) {
3374                 if (pen->mol != NULL)
3375                         MoleculeRelease(pen->mol);
3376                 free(pen);
3377         }
3378 }
3379
3380 static Molecule *
3381 s_MoleculeFromParEnumerableValue(VALUE val)
3382 {
3383         ParEnumerable *pen;
3384     Data_Get_Struct(val, ParEnumerable, pen);
3385         return pen->mol;
3386 }
3387
3388 static VALUE
3389 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3390 {
3391         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3392         if (pen == NULL)
3393                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3394         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3395 }
3396
3397 /*
3398  *  call-seq:
3399  *     par_type -> String
3400  *
3401  *  Get the parameter type, like "bond", "angle", etc.
3402  */
3403 static VALUE
3404 s_ParEnumerable_ParType(VALUE self) {
3405         ParEnumerable *pen;
3406         Int tp;
3407     Data_Get_Struct(self, ParEnumerable, pen);
3408         tp = pen->parType;
3409         if (tp == kElementParType)
3410                 return rb_str_new2("element");
3411         tp -= kFirstParType;
3412         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3413                 return rb_str_new2(s_ParameterTypeNames[tp]);
3414         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3415 }
3416
3417 /*
3418  *  call-seq:
3419  *     self[idx]          -> ParameterRef
3420  *  
3421  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3422  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3423  *  parent Parameter object of self.
3424  *
3425  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3426  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3427  */
3428 static VALUE
3429 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3430 {
3431         ParEnumerable *pen;
3432     Data_Get_Struct(self, ParEnumerable, pen);
3433         switch (pen->parType) {
3434                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3435                 case kBondParType:      return s_Parameter_Bond(self, ival);
3436                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3437                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3438                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3439                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3440                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3441                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3442                 case kElementParType:   return s_Parameter_Element(self, ival);
3443                 default:
3444                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3445         }
3446         return Qnil;  /*  Not reached  */
3447 }
3448
3449 /*
3450  *  call-seq:
3451  *     length          -> Integer
3452  *  
3453  *  Returns the number of parameters included in this enumerable.
3454  */
3455 static VALUE
3456 s_ParEnumerable_Length(VALUE self)
3457 {
3458         ParEnumerable *pen;
3459     Data_Get_Struct(self, ParEnumerable, pen);
3460         switch (pen->parType) {
3461                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3462                 case kBondParType:      return s_Parameter_Nbonds(self);
3463                 case kAngleParType:     return s_Parameter_Nangles(self); 
3464                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3465                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3466                 case kVdwParType:       return s_Parameter_Nvdws(self);
3467                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3468                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3469                 case kElementParType:   return s_Parameter_Nelements(self);
3470                 default:
3471                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3472         }
3473         return Qnil;  /*  Not reached  */
3474 }
3475
3476 /*
3477  *  call-seq:
3478  *     each {|pref| ...}
3479  *  
3480  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3481  */
3482 VALUE
3483 s_ParEnumerable_Each(VALUE self)
3484 {
3485         VALUE aval;
3486         ParEnumerable *pen;
3487         ParameterRef *pref;
3488         int i, ofs, n;
3489     Data_Get_Struct(self, ParEnumerable, pen);
3490         if (pen->parType == kElementParType)
3491                 n = gCountElementParameters;
3492         else {
3493                 switch (pen->parType) {
3494                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3495                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3496                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3497                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3498                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3499                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3500                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3501                         default:
3502                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3503                 }
3504                 if (pen->mol == NULL)
3505                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3506                 else if (pen->mol->par != NULL)
3507                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3508                 else return self;
3509         }               
3510         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3511         Data_Get_Struct(aval, ParameterRef, pref);
3512         for (i = 0; i < n; i++) {
3513                 pref->idx = i;
3514                 rb_yield(aval);
3515         }
3516     return self;
3517 }
3518
3519 /*
3520  *  call-seq:
3521  *     reverse_each {|pref| ...}
3522  *  
3523  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3524  */
3525 VALUE
3526 s_ParEnumerable_ReverseEach(VALUE self)
3527 {
3528         VALUE aval;
3529         ParEnumerable *pen;
3530         ParameterRef *pref;
3531         int i, ofs, n;
3532     Data_Get_Struct(self, ParEnumerable, pen);
3533         if (pen->parType == kElementParType)
3534                 n = gCountElementParameters;
3535         else {
3536                 switch (pen->parType) {
3537                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3538                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3539                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3540                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3541                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3542                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3543                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3544                         default:
3545                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3546                 }
3547                 if (pen->mol == NULL)
3548                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3549                 else if (pen->mol->par != NULL)
3550                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3551                 else return self;
3552         }               
3553         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3554         Data_Get_Struct(aval, ParameterRef, pref);
3555         for (i = n - 1; i >= 0; i--) {
3556                 pref->idx = i;
3557                 rb_yield(aval);
3558         }
3559     return self;
3560 }
3561
3562 /*
3563  *  call-seq:
3564  *     insert(idx = nil, pref = nil)       -> ParameterRef
3565  *  
3566  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3567  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3568  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3569  *  parameter is left undefined.
3570  *  Throws an exception if ParEnumerable points to the global parameter.
3571  */
3572 static VALUE
3573 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3574 {
3575         VALUE ival, pval;
3576         ParEnumerable *pen;
3577         int i, n;
3578         IntGroup *ig;
3579         UnionPar u;
3580         MolAction *act;
3581     Data_Get_Struct(self, ParEnumerable, pen);
3582         if (pen->mol == NULL)
3583                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3584         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3585         rb_scan_args(argc, argv, "02", &ival, &pval);
3586         if (ival != Qnil) {
3587                 i = NUM2INT(rb_Integer(ival));
3588                 if (i < 0 || i > n)
3589                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3590                 n = i;
3591         }
3592         if (pval != Qnil) {
3593                 Int type;
3594                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3595                 if (up == NULL || type != pen->parType)
3596                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3597                 ParameterCopyOneWithType(&u, up, pen->parType);
3598                 u.bond.src = 0;
3599         } else {
3600                 memset(&u, 0, sizeof(u));
3601                 u.bond.src = 0;
3602         }
3603         ig = IntGroupNewWithPoints(n, 1, -1);
3604         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3605
3606         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3607         MolActionCallback_registerUndo(pen->mol, act);
3608         MolActionRelease(act);
3609         
3610         IntGroupRelease(ig);
3611         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3612 }
3613
3614 /*
3615  *  call-seq:
3616  *     delete(Integer)
3617  *     delete(IntGroup)
3618  *  
3619  *  Delete the parameter(s) specified by the argument.
3620  */
3621 static VALUE
3622 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3623 {
3624         ParEnumerable *pen;
3625         int i, n;
3626         IntGroup *ig;
3627     Data_Get_Struct(self, ParEnumerable, pen);
3628         if (pen->mol == NULL)
3629                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3630         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3631         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3632                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3633                 i = 1;
3634         } else {
3635                 ig = IntGroupFromValue(ival);
3636                 if ((i = IntGroupGetCount(ig)) == 0) {
3637                         IntGroupRelease(ig);
3638                         return Qnil;
3639                 }
3640         }
3641         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3642                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3643         n = i;
3644
3645         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3646         IntGroupRelease(ig);
3647         return ival;
3648 }
3649
3650 /*
3651  *  call-seq:
3652  *     lookup(atom_types, options, ...) -> ParameterRef
3653  *     lookup(atom_type_string, options, ...) -> ParameterRef
3654  *
3655  *  Find the parameter record that matches the given atom types. The arguments are
3656  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3657  *  specified.
3658  */
3659 static VALUE
3660 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3661 {
3662         ParEnumerable *pen;
3663     Data_Get_Struct(self, ParEnumerable, pen);
3664         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3665 }
3666
3667 /*
3668  *  call-seq:
3669  *     self == parEnumerable -> boolean
3670  *  
3671  *  True if the arguments point to the same parameter table and type.
3672  */
3673 static VALUE
3674 s_ParEnumerable_Equal(VALUE self, VALUE val)
3675 {
3676         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3677                 ParEnumerable *pen1, *pen2;
3678                 Data_Get_Struct(self, ParEnumerable, pen1);
3679                 Data_Get_Struct(val, ParEnumerable, pen2);
3680                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3681         } else return Qfalse;
3682 }
3683
3684 #pragma mark ====== AtomRef Class ======
3685
3686 /*  Forward declaration for register undo  */
3687 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3688
3689 /*  Ruby string "set_atom_attr"  */
3690 static VALUE s_SetAtomAttrString;
3691
3692 static int
3693 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3694 {
3695         AtomRef *aref;
3696         int idx;
3697         Data_Get_Struct(self, AtomRef, aref);
3698         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3699         if (idx < 0 || idx >= aref->mol->natoms)
3700                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3701         if (app != NULL)
3702                 *app = aref->mol->atoms + idx;
3703         if (mpp != NULL)
3704                 *mpp = aref->mol;
3705         return idx;
3706 }
3707
3708 static Atom *
3709 s_AtomFromValue(VALUE self)
3710 {
3711         Atom *ap;
3712         s_AtomIndexFromValue(self, &ap, NULL);
3713         return ap;
3714 }
3715
3716 static Atom *
3717 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3718 {
3719         Atom *ap;
3720         s_AtomIndexFromValue(self, &ap, mpp);
3721         return ap;
3722 }
3723
3724 static void
3725 s_NotifyModificationForAtomRef(VALUE self)
3726 {
3727         AtomRef *aref;
3728         Data_Get_Struct(self, AtomRef, aref);
3729         MoleculeIncrementModifyCount(aref->mol);
3730 }
3731
3732 static void
3733 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3734 {
3735         AtomRef *aref;
3736         Data_Get_Struct(self, AtomRef, aref);
3737         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3738                 /*  Register undo  */
3739                 MolAction *act;
3740                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3741                 MolActionCallback_registerUndo(aref->mol, act);
3742                 MoleculeCallback_notifyModification(aref->mol, 0);
3743                 /*  Request MD rebuilt if necessary  */
3744                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3745                         aref->mol->needsMDRebuild = 1;
3746         }
3747 }
3748
3749 VALUE
3750 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3751 {
3752         AtomRef *aref;
3753         aref = AtomRefNew(mol, idx);
3754         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3755 }
3756
3757 static VALUE
3758 s_AtomRef_GetMolecule(VALUE self)
3759 {
3760         Molecule *mpp;
3761         s_AtomIndexFromValue(self, NULL, &mpp);
3762         return ValueFromMolecule(mpp);
3763 }
3764
3765 static VALUE s_AtomRef_GetIndex(VALUE self) {
3766         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3767 }
3768
3769 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3770         return INT2NUM(s_AtomFromValue(self)->segSeq);
3771 }
3772
3773 static VALUE s_AtomRef_GetSegName(VALUE self) {
3774         char *p = s_AtomFromValue(self)->segName;
3775         return rb_str_new(p, strlen_limit(p, 4));
3776 }
3777
3778 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3779         return INT2NUM(s_AtomFromValue(self)->resSeq);
3780 }
3781
3782 static VALUE s_AtomRef_GetResName(VALUE self) {
3783         char *p = s_AtomFromValue(self)->resName;
3784         return rb_str_new(p, strlen_limit(p, 4));
3785 }
3786
3787 static VALUE s_AtomRef_GetName(VALUE self) {
3788         char *p = s_AtomFromValue(self)->aname;
3789         return rb_str_new(p, strlen_limit(p, 4));
3790 }
3791
3792 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3793         int type = s_AtomFromValue(self)->type;
3794         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3795         return rb_str_new(p, strlen_limit(p, 6));
3796 }
3797
3798 static VALUE s_AtomRef_GetCharge(VALUE self) {
3799         return rb_float_new(s_AtomFromValue(self)->charge);
3800 }
3801
3802 static VALUE s_AtomRef_GetWeight(VALUE self) {
3803         return rb_float_new(s_AtomFromValue(self)->weight);
3804 }
3805
3806 static VALUE s_AtomRef_GetElement(VALUE self) {
3807         char *p = s_AtomFromValue(self)->element;
3808         return rb_str_new(p, strlen_limit(p, 4));
3809 }
3810
3811 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3812         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3813 }
3814
3815 static VALUE s_AtomRef_GetConnects(VALUE self) {
3816         VALUE retval;
3817         Int i, *cp;
3818         Atom *ap = s_AtomFromValue(self);
3819         retval = rb_ary_new();
3820         cp = AtomConnectData(&ap->connect);
3821         for (i = 0; i < ap->connect.count; i++)
3822                 rb_ary_push(retval, INT2NUM(cp[i]));
3823         return retval;
3824 }
3825
3826 static VALUE s_AtomRef_GetR(VALUE self) {
3827         return ValueFromVector(&(s_AtomFromValue(self)->r));
3828 }
3829
3830 static VALUE s_AtomRef_GetX(VALUE self) {
3831         return rb_float_new(s_AtomFromValue(self)->r.x);
3832 }
3833
3834 static VALUE s_AtomRef_GetY(VALUE self) {
3835         return rb_float_new(s_AtomFromValue(self)->r.y);
3836 }
3837
3838 static VALUE s_AtomRef_GetZ(VALUE self) {
3839         return rb_float_new(s_AtomFromValue(self)->r.z);
3840 }
3841
3842 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3843         Atom *ap;
3844         Molecule *mp;
3845         Vector r1;
3846         s_AtomIndexFromValue(self, &ap, &mp);
3847         r1 = ap->r;
3848         if (mp->cell != NULL)
3849                 TransformVec(&r1, mp->cell->rtr, &r1);
3850         return r1;
3851 }
3852
3853 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3854         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3855         return ValueFromVector(&r1);
3856 }
3857
3858 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3859         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3860 }
3861
3862 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3863         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3864 }
3865
3866 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3867         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3868 }
3869
3870 static VALUE s_AtomRef_GetSigma(VALUE self) {
3871         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3872 }
3873
3874 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3875         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3876 }
3877
3878 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3879         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3880 }
3881
3882 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3883         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3884 }
3885
3886 static VALUE s_AtomRef_GetV(VALUE self) {
3887         return ValueFromVector(&(s_AtomFromValue(self)->v));
3888 }
3889
3890 static VALUE s_AtomRef_GetF(VALUE self) {
3891         Vector v = s_AtomFromValue(self)->f;
3892         VecScaleSelf(v, INTERNAL2KCAL);
3893         return ValueFromVector(&v);
3894 }
3895
3896 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3897         return rb_float_new(s_AtomFromValue(self)->occupancy);
3898 }
3899
3900 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3901         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3902 }
3903
3904 static VALUE s_AtomRef_GetAniso(VALUE self) {
3905         VALUE retval;
3906         int i;
3907         Atom *ap = s_AtomFromValue(self);
3908         if (ap->aniso == NULL)
3909                 return Qnil;
3910         retval = rb_ary_new();
3911         for (i = 0; i < 6; i++)
3912                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3913         if (ap->aniso->has_bsig) {
3914                 rb_ary_push(retval, INT2NUM(0));
3915                 for (i = 0; i < 6; i++)
3916                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3917         }
3918         return retval;
3919 }
3920
3921 static VALUE s_AtomRef_GetSymop(VALUE self) {
3922         VALUE retval;
3923         Atom *ap = s_AtomFromValue(self);
3924         if (!ap->symop.alive)
3925                 return Qnil;
3926         retval = rb_ary_new();
3927         rb_ary_push(retval, INT2NUM(ap->symop.sym));
3928         rb_ary_push(retval, INT2NUM(ap->symop.dx));
3929         rb_ary_push(retval, INT2NUM(ap->symop.dy));
3930         rb_ary_push(retval, INT2NUM(ap->symop.dz));
3931         rb_ary_push(retval, INT2NUM(ap->symbase));
3932         return retval;
3933 }
3934
3935 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3936         return INT2NUM(s_AtomFromValue(self)->intCharge);
3937 }
3938
3939 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3940         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3941 }
3942
3943 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3944         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3945 }
3946
3947 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3948         Molecule *mol;
3949         Atom *ap;
3950         int idx, i;
3951         MDExclusion *exinfo;
3952         Int *exlist;
3953         VALUE retval, aval;
3954         idx = s_AtomIndexFromValue(self, &ap, &mol);
3955         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3956                 VALUE mval = ValueFromMolecule(mol);
3957                 s_RebuildMDParameterIfNecessary(mval, Qnil);
3958         }
3959         if (mol->arena->exinfo == NULL)
3960                 return Qnil;
3961         exinfo = mol->arena->exinfo + idx;
3962         exlist = mol->arena->exlist;
3963         retval = rb_ary_new();
3964         aval = rb_ary_new();
3965         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
3966                 rb_ary_push(aval, INT2NUM(exlist[i]));
3967         rb_ary_push(retval, aval);
3968         aval = rb_ary_new();
3969         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
3970                 rb_ary_push(aval, INT2NUM(exlist[i]));
3971         rb_ary_push(retval, aval);
3972         aval = rb_ary_new();
3973         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
3974                 rb_ary_push(aval, INT2NUM(exlist[i]));
3975         rb_ary_push(retval, aval);
3976         return retval;
3977 }
3978
3979 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3980         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3981 }
3982
3983 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3984         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3985 }
3986
3987 static VALUE s_AtomRef_GetHidden(VALUE self) {
3988         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3989 }
3990
3991 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3992         VALUE retval;
3993         Int i, count, *cp;
3994         Atom *ap = s_AtomFromValue(self);
3995         if (ap->anchor == NULL)
3996                 return Qnil;
3997         count = ap->anchor->connect.count;
3998         retval = rb_ary_new2(count * 2);
3999         cp = AtomConnectData(&ap->anchor->connect);
4000         for (i = 0; i < count; i++) {
4001                 rb_ary_store(retval, i, INT2NUM(cp[i]));
4002                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4003         }
4004         return retval;
4005 }
4006
4007 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4008         char *p = s_AtomFromValue(self)->uff_type;
4009         return rb_str_new(p, strlen_limit(p, 5));
4010 }
4011
4012 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4013         rb_raise(rb_eMolbyError, "index cannot be directly set");
4014         return Qnil;
4015 }
4016
4017 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4018         VALUE oval = s_AtomRef_GetSegSeq(self);
4019         val = rb_Integer(val);
4020         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4021         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4022         return val;
4023 }
4024
4025 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4026         char *p = StringValuePtr(val);
4027         VALUE oval = s_AtomRef_GetSegName(self);
4028         strncpy(s_AtomFromValue(self)->segName, p, 4);
4029         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4030         return val;
4031 }
4032
4033 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4034         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4035         return val; /* Not reached */
4036 }
4037
4038 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4039         Atom *ap = s_AtomFromValue(self);
4040         char *p = StringValuePtr(val);
4041         VALUE oval = s_AtomRef_GetName(self);
4042         if (ap->anchor != NULL && p[0] == '_')
4043                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4044         strncpy(ap->aname, p, 4);
4045         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4046         return val;
4047 }
4048
4049 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4050         Molecule *mp;
4051         char *p = StringValuePtr(val);
4052         VALUE oval = s_AtomRef_GetAtomType(self);
4053         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4054         if (type != 0 && type < kAtomTypeMinimum)
4055                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4056         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4057         mp->needsMDRebuild = 1;
4058         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4059         return val;
4060 }
4061
4062 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4063         Molecule *mp;
4064         VALUE oval = s_AtomRef_GetCharge(self);
4065         val = rb_Float(val);
4066         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4067         mp->needsMDRebuild = 1;
4068         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4069         return val;
4070 }
4071
4072 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4073         Molecule *mp;
4074         VALUE oval = s_AtomRef_GetWeight(self);
4075         val = rb_Float(val);
4076         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4077         mp->needsMDRebuild = 1;
4078         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4079         return val;
4080 }
4081
4082 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4083         Double w;
4084         Molecule *mp;
4085         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4086         char *p = StringValuePtr(val);
4087         VALUE oval = s_AtomRef_GetElement(self);
4088         ap->atomicNumber = ElementToInt(p);
4089         ElementToString(ap->atomicNumber, ap->element);
4090         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4091                 ap->weight = w;
4092         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4093         mp->needsMDRebuild = 1;
4094         return val;
4095 }
4096
4097 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4098         Double w;
4099         Molecule *mp;
4100         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4101         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4102         val = rb_Integer(val);
4103         ap->atomicNumber = NUM2INT(val);
4104         ElementToString(ap->atomicNumber, ap->element);
4105         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4106                 ap->weight = w;
4107         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4108         mp->needsMDRebuild = 1;
4109         return val;
4110 }
4111
4112 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4113         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4114         return val; /* Not reached */
4115 }
4116
4117 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4118         Vector v;
4119         Molecule *mp;
4120         VALUE oval = s_AtomRef_GetR(self);
4121         VectorFromValue(val, &v);
4122         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4123         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4124         mp->needsMDCopyCoordinates = 1;
4125         return val;
4126 }
4127
4128 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4129         Double f;
4130         Molecule *mp;
4131         VALUE oval = s_AtomRef_GetX(self);
4132         val = rb_Float(val);
4133         f = NUM2DBL(val);
4134         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4135         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4136         mp->needsMDCopyCoordinates = 1;
4137         return val;
4138 }
4139
4140 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4141         Double f;
4142         Molecule *mp;
4143         VALUE oval = s_AtomRef_GetY(self);
4144         val = rb_Float(val);
4145         f = NUM2DBL(val);
4146         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4147         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4148         mp->needsMDCopyCoordinates = 1;
4149         return val;
4150 }
4151
4152 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4153         Double f;
4154         Molecule *mp;
4155         VALUE oval = s_AtomRef_GetZ(self);
4156         val = rb_Float(val);
4157         f = NUM2DBL(val);
4158         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4159         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4160         mp->needsMDCopyCoordinates = 1;
4161         return val;
4162 }
4163
4164 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4165         Vector v, ov;
4166         Atom *ap;
4167         Molecule *mp;
4168         s_AtomIndexFromValue(self, &ap, &mp);
4169         ov = ap->r;
4170         VectorFromValue(val, &v);
4171         if (mp->cell != NULL)
4172                 TransformVec(&v, mp->cell->tr, &v);
4173         ap->r = v;
4174         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4175         mp->needsMDCopyCoordinates = 1;
4176         return val;
4177 }
4178
4179 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4180         double f;
4181         Vector v, ov;
4182         Atom *ap;
4183         Molecule *mp;
4184         s_AtomIndexFromValue(self, &ap, &mp);
4185         ov = v = ap->r;
4186         val = rb_Float(val);
4187         f = NUM2DBL(val);
4188         if (mp->cell != NULL) {
4189                 TransformVec(&v, mp->cell->rtr, &v);
4190                 v.x = f;
4191                 TransformVec(&v, mp->cell->tr, &v);
4192         } else v.x = f;
4193         ap->r = v;
4194         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4195         mp->needsMDCopyCoordinates = 1;
4196         return val;
4197 }
4198
4199 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4200         double f;
4201         Vector v, ov;
4202         Atom *ap;
4203         Molecule *mp;
4204         s_AtomIndexFromValue(self, &ap, &mp);
4205         ov = v = ap->r;
4206         val = rb_Float(val);
4207         f = NUM2DBL(val);
4208         if (mp->cell != NULL) {
4209                 TransformVec(&v, mp->cell->rtr, &v);
4210                 v.y = f;
4211                 TransformVec(&v, mp->cell->tr, &v);
4212         } else v.y = f;
4213         ap->r = v;
4214         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4215         mp->needsMDCopyCoordinates = 1;
4216         return val;
4217 }
4218
4219 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4220         double f;
4221         Vector v, ov;
4222         Atom *ap;
4223         Molecule *mp;
4224         s_AtomIndexFromValue(self, &ap, &mp);
4225         ov = v = ap->r;
4226         val = rb_Float(val);
4227         f = NUM2DBL(val);
4228         if (mp->cell != NULL) {
4229                 TransformVec(&v, mp->cell->rtr, &v);
4230                 v.z = f;
4231                 TransformVec(&v, mp->cell->tr, &v);
4232         } else v.z = f;
4233         ap->r = v;
4234         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4235         mp->needsMDCopyCoordinates = 1;
4236         return val;
4237 }
4238
4239 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4240         Vector v;
4241         Molecule *mp;
4242         VALUE oval = s_AtomRef_GetSigma(self);
4243         VectorFromValue(val, &v);
4244         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4245         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4246         mp->needsMDCopyCoordinates = 1;
4247         return val;
4248 }
4249
4250 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4251         Double f;
4252         Molecule *mp;
4253         VALUE oval = s_AtomRef_GetSigmaX(self);
4254         val = rb_Float(val);
4255         f = NUM2DBL(val);
4256         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4257         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4258         mp->needsMDCopyCoordinates = 1;
4259         return val;
4260 }
4261
4262 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4263         Double f;
4264         Molecule *mp;
4265         VALUE oval = s_AtomRef_GetSigmaY(self);
4266         val = rb_Float(val);
4267         f = NUM2DBL(val);
4268         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4269         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4270         mp->needsMDCopyCoordinates = 1;
4271         return val;
4272 }
4273
4274 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4275         Double f;
4276         Molecule *mp;
4277         VALUE oval = s_AtomRef_GetSigmaZ(self);
4278         val = rb_Float(val);
4279         f = NUM2DBL(val);
4280         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4281         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4282         mp->needsMDCopyCoordinates = 1;
4283         return val;
4284 }
4285
4286 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4287         Vector v;
4288         Atom *ap;
4289         Molecule *mp;
4290         VALUE oval = s_AtomRef_GetV(self);
4291         VectorFromValue(val, &v);
4292         s_AtomIndexFromValue(self, &ap, &mp);
4293         ap->v = v;
4294         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4295         mp->needsMDCopyCoordinates = 1;
4296         return val;
4297 }
4298
4299 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4300         Vector v;
4301         Molecule *mp;
4302         VALUE oval = s_AtomRef_GetF(self);
4303         VectorFromValue(val, &v);
4304         VecScaleSelf(v, KCAL2INTERNAL);
4305         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4306         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4307         mp->needsMDCopyCoordinates = 1;
4308         return val;
4309 }
4310
4311 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4312         VALUE oval = s_AtomRef_GetOccupancy(self);
4313         Molecule *mp;
4314         val = rb_Float(val);
4315         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4316         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4317         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4318         return val;
4319 }
4320
4321 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4322         VALUE oval = s_AtomRef_GetTempFactor(self);
4323         val = rb_Float(val);
4324         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4325         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4326         return val;
4327 }
4328
4329 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4330         AtomRef *aref;
4331         int i, n, type;
4332         VALUE *valp;
4333         Double f[12];
4334         VALUE oval = s_AtomRef_GetAniso(self);
4335         Data_Get_Struct(self, AtomRef, aref);
4336         val = rb_funcall(val, rb_intern("to_a"), 0);
4337         n = RARRAY_LEN(val);
4338         valp = RARRAY_PTR(val);
4339         for (i = 0; i < 6; i++) {
4340                 if (i < n)
4341                         f[i] = NUM2DBL(rb_Float(valp[i]));
4342                 else f[i] = 0.0;
4343         }
4344         if (n >= 7)
4345                 type = NUM2INT(rb_Integer(valp[6]));
4346         else type = 0;
4347         if (n >= 13) {
4348                 for (i = 0; i < 6; i++)
4349                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4350         } else {
4351                 for (i = 0; i < 6; i++)
4352                         f[i + 6] = 0.0;
4353         }
4354         i = s_AtomIndexFromValue(self, NULL, NULL);
4355         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4356         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4357         return val;
4358 }
4359
4360 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4361         Molecule *mol;
4362         Atom *ap;
4363         int i, n;
4364         VALUE *valp;
4365         Int ival[5];
4366         VALUE oval = s_AtomRef_GetSymop(self);
4367         i = s_AtomIndexFromValue(self, &ap, &mol);
4368         if (val == Qnil) {
4369                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4370         } else {
4371                 val = rb_funcall(val, rb_intern("to_a"), 0);
4372                 n = RARRAY_LEN(val);
4373                 valp = RARRAY_PTR(val);
4374                 for (i = 0; i < 5; i++) {
4375                         if (i < n) {
4376                                 if (valp[i] == Qnil)
4377                                         ival[i] = -100000;
4378                                 else 
4379                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4380                         } else ival[i] = -100000;
4381                 }
4382         }
4383         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4384                 rb_raise(rb_eMolbyError, "index of symmetry (%d) is out of range (should be 0..%d)", ival[0], (mol->nsyms == 0 ? 0 : mol->nsyms - 1));
4385         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4386                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4387         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4388                 return val;  /*  No need to change  */
4389         if (ival[0] != -100000)
4390                 ap->symop.sym = ival[0];
4391         if (ival[1] != -100000)
4392                 ap->symop.dx = ival[1];
4393         if (ival[2] != -100000)
4394                 ap->symop.dy = ival[2];
4395         if (ival[3] != -100000)
4396                 ap->symop.dz = ival[3];
4397         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4398         if (ival[4] != -100000)
4399                 ap->symbase = ival[4];
4400         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4401                 /*  The anisotropic parameters should be recalculated  */
4402                 VALUE oaval = s_AtomRef_GetAniso(self);
4403                 MoleculeSetAnisoBySymop(mol, i);
4404                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4405         }
4406         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4407         return val;
4408 }
4409
4410 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4411         VALUE oval = s_AtomRef_GetIntCharge(self);
4412         val = rb_Integer(val);
4413         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4414         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4415         return val;
4416 }
4417
4418 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4419         Molecule *mp;
4420         VALUE oval = s_AtomRef_GetFixForce(self);
4421         val = rb_Float(val);
4422         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4423         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4424         mp->needsMDRebuild = 1;
4425         return val;
4426 }
4427
4428 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4429         Vector v;
4430         Molecule *mp;
4431         VALUE oval = s_AtomRef_GetFixPos(self);
4432         VectorFromValue(val, &v);
4433         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4434         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4435         mp->needsMDRebuild = 1;
4436         return val;
4437 }
4438
4439 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4440         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4441         return val; /* Not reached */
4442 }
4443
4444 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4445         VALUE oval = s_AtomRef_GetIntCharge(self);
4446         val = rb_Integer(val);
4447         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4448         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4449         return val;
4450 }
4451
4452 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4453         VALUE oval = s_AtomRef_GetIntCharge(self);
4454         val = rb_Integer(val);
4455         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4456         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4457         return val;
4458 }
4459
4460 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4461         Atom *ap = s_AtomFromValue(self);
4462         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4463         if (RTEST(val)) {
4464                 ap->exflags |= kAtomHiddenFlag;
4465         } else {
4466                 ap->exflags &= ~kAtomHiddenFlag;
4467         }
4468         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4469         return val;
4470 }
4471
4472 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4473         Int idx, i, j, k, n, *ip;
4474         Double *dp;
4475         Atom *ap;
4476         Molecule *mol;
4477         VALUE oval, v;
4478         AtomConnect ac;
4479         Int nUndoActions;
4480         MolAction **undoActions;
4481         memset(&ac, 0, sizeof(ac));
4482         idx = s_AtomIndexFromValue(self, &ap, &mol);
4483         oval = s_AtomRef_GetAnchorList(self);
4484         if (val != Qnil) {
4485                 val = rb_ary_to_ary(val);
4486                 n = RARRAY_LEN(val);
4487         } else n = 0;
4488         if (n == 0) {
4489                 if (ap->anchor != NULL) {
4490                         AtomConnectResize(&ap->anchor->connect, 0);
4491                         free(ap->anchor->coeffs);
4492                         free(ap->anchor);
4493                         ap->anchor = NULL;
4494                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4495                 }
4496                 return val;
4497         }
4498         if (n < 2)
4499                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4500         if (ap->aname[0] == '_')
4501                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4502         ip = (Int *)malloc(sizeof(Int) * n);
4503         dp = NULL;
4504         for (i = 0; i < n; i++) {
4505                 v = RARRAY_PTR(val)[i];
4506                 if (rb_obj_is_kind_of(v, rb_cFloat))
4507                         break;
4508                 j = NUM2INT(rb_Integer(v));
4509                 if (j < 0 || j >= mol->natoms)
4510                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4511                 for (k = 0; k < i; k++) {
4512                         if (ip[k] == j)
4513                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4514                 }
4515                 ip[i] = j;
4516         }
4517         if (i < n) {
4518                 if (i < 2)
4519                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4520                 else if (i * 2 != n)
4521                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4522                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4523                 for (i = 0; i < n / 2; i++) {
4524                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4525                         if (dp[i] <= 0.0)
4526                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4527                 }
4528                 n /= 2;
4529         }
4530         nUndoActions = 0;
4531         undoActions = NULL;
4532         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4533         free(dp);
4534         free(ip);
4535         if (i != 0)
4536                 rb_raise(rb_eMolbyError, "invalid argument");
4537         if (nUndoActions > 0) {
4538                 for (i = 0; i < nUndoActions; i++) {
4539                         MolActionCallback_registerUndo(mol, undoActions[i]);
4540                         MolActionRelease(undoActions[i]);
4541                 }
4542                 free(undoActions);
4543         }
4544         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4545         return val;
4546 }
4547
4548 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4549         Atom *ap = s_AtomFromValue(self);
4550         char *p = StringValuePtr(val);
4551         VALUE oval = s_AtomRef_GetUFFType(self);
4552         strncpy(ap->uff_type, p, 5);
4553         ap->uff_type[5] = 0;
4554         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4555         return val;
4556 }
4557
4558 static struct s_AtomAttrDef {
4559         char *name;
4560         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4561         ID id;                  /*  Will be set within InitMolby()  */
4562         VALUE (*getter)(VALUE);
4563         VALUE (*setter)(VALUE, VALUE);
4564 } s_AtomAttrDefTable[] = {
4565         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4566         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4567         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4568         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4569         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4570         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4571         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4572         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4573         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4574         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4575         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4576         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4577         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4578         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4579         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4580     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4581         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4582         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4583         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4584         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4585         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4586         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4587         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4588         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4589         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4590         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4591         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4592         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4593         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4594         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4595         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4596         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4597         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4598         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4599         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4600         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4601         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4602         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4603         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4604         {NULL} /* Sentinel */
4605 };
4606
4607 static VALUE
4608 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4609 {
4610         int i;
4611         ID kid;
4612         if (TYPE(key) != T_SYMBOL) {
4613                 kid = rb_intern(StringValuePtr(key));
4614                 key = ID2SYM(kid);
4615         } else kid = SYM2ID(key);
4616         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4617                 if (s_AtomAttrDefTable[i].id == kid) {
4618                         if (value == Qundef)
4619                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4620                         else
4621                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4622                 }
4623         }
4624         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4625         return Qnil; /* not reached */
4626 }
4627
4628 static VALUE
4629 s_AtomRef_GetAttr(VALUE self, VALUE key)
4630 {
4631         return s_AtomRef_SetAttr(self, key, Qundef);
4632 }
4633
4634 /*
4635  *  call-seq:
4636  *     self == atomRef -> boolean
4637  *
4638  *  True if the two references point to the same atom.
4639  */
4640 static VALUE
4641 s_AtomRef_Equal(VALUE self, VALUE val)
4642 {
4643         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4644                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4645         } else return Qfalse;
4646 }
4647
4648 #pragma mark ====== MolEnumerable Class ======
4649
4650 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4651
4652 /*
4653  *  call-seq:
4654  *     self[idx] -> AtomRef or Array of Integers
4655  *  
4656  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4657  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4658  *  value is a String. Otherwise, the return value is an Array of Integers.
4659  */
4660 static VALUE
4661 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4662 {
4663         MolEnumerable *mseq;
4664         Molecule *mol;
4665         int idx1, idx2;
4666     Data_Get_Struct(self, MolEnumerable, mseq);
4667         mol = mseq->mol;
4668         if (mseq->kind == kAtomKind) {
4669                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4670         }
4671         idx1 = NUM2INT(arg1);
4672         switch (mseq->kind) {
4673                 case kBondKind: {
4674                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4675                         if (idx2 < 0 || idx2 >= mol->nbonds)
4676                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4677                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4678                 }
4679                 case kAngleKind: {
4680                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4681                         if (idx2 < 0 || idx2 >= mol->nangles)
4682                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4683                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4684                 }
4685                 case kDihedralKind: {
4686                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4687                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4688                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4689                         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]));
4690                 }
4691                 case kImproperKind: {
4692                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4693                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4694                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4695                         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]));
4696                 }
4697                 case kResidueKind: {
4698                         char *p;
4699                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4700                         if (idx2 < 0 || idx2 >= mol->nresidues)
4701                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4702                         p = mol->residues[idx2];
4703                         return rb_str_new(p, strlen_limit(p, 4));
4704                 }
4705         }
4706         return Qnil;
4707 }
4708
4709 /*
4710  *  call-seq:
4711  *     length          -> Integer
4712  *  
4713  *  Returns the number of objects included in this enumerable.
4714  */
4715 static VALUE
4716 s_MolEnumerable_Length(VALUE self)
4717 {
4718         MolEnumerable *mseq;
4719     Data_Get_Struct(self, MolEnumerable, mseq);
4720         switch (mseq->kind) {
4721                 case kAtomKind:
4722                         return INT2NUM(mseq->mol->natoms);
4723                 case kBondKind:
4724                         return INT2NUM(mseq->mol->nbonds);
4725                 case kAngleKind:
4726                         return INT2NUM(mseq->mol->nangles);
4727                 case kDihedralKind:
4728                         return INT2NUM(mseq->mol->ndihedrals);
4729                 case kImproperKind:
4730                         return INT2NUM(mseq->mol->nimpropers);
4731                 case kResidueKind:
4732                         return INT2NUM(mseq->mol->nresidues);
4733         }
4734         return INT2NUM(-1);
4735 }
4736
4737 /*
4738  *  call-seq:
4739  *     each {|obj| ...}
4740  *  
4741  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4742  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4743  *  For the atoms, a same AtomRef object is passed (with different internal information)
4744  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4745  *  for each iteration.
4746  */
4747 VALUE
4748 s_MolEnumerable_Each(VALUE self)
4749 {
4750         MolEnumerable *mseq;
4751         int i;
4752         int len = NUM2INT(s_MolEnumerable_Length(self));
4753     Data_Get_Struct(self, MolEnumerable, mseq);
4754         if (mseq->kind == kAtomKind) {
4755                 /*  The same AtomRef object will be used during the loop  */
4756                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4757                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4758                 for (i = 0; i < len; i++) {
4759                         aref->idx = i;
4760                         rb_yield(arval);
4761                 }
4762     } else {
4763                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4764                 for (i = 0; i < len; i++) {
4765                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4766                 }
4767         }
4768     return self;
4769 }
4770
4771 /*
4772  *  call-seq:
4773  *     self == molEnumerable -> boolean
4774  *
4775  *  True if the two arguments point to the same molecule and enumerable type.
4776  */
4777 static VALUE
4778 s_MolEnumerable_Equal(VALUE self, VALUE val)
4779 {
4780         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4781                 MolEnumerable *mseq1, *mseq2;
4782                 Data_Get_Struct(self, MolEnumerable, mseq1);
4783                 Data_Get_Struct(val, MolEnumerable, mseq2);
4784                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4785         } else return Qfalse;
4786 }
4787
4788
4789 #pragma mark ====== Molecule Class ======
4790
4791 #pragma mark ------ Allocate/Release/Accessor ------
4792
4793 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method.  */
4794 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4795 char *gLoadSaveErrorMessage = NULL;
4796
4797 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4798
4799 Molecule *
4800 MoleculeFromValue(VALUE val)
4801 {
4802         Molecule *mol;
4803         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4804                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4805     Data_Get_Struct(val, Molecule, mol);
4806         return mol;
4807 }
4808
4809 static VALUE sMoleculeRetainArray = Qnil;
4810
4811 /*  The function is called from MoleculeRelease()  */
4812 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4813 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4814 /*  object is always returned for the same Molecule.  */
4815 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4816 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4817 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4818 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4819 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4820
4821 /*  Register/unregister the exmolobj Ruby object  */
4822 void
4823 MoleculeReleaseExternalObj(Molecule *mol)
4824 {
4825         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4826                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4827                 mol->exmolobjProtected = 0;
4828         }
4829 }
4830
4831 void
4832 MoleculeRetainExternalObj(Molecule *mol)
4833 {
4834         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4835                 if (sMoleculeRetainArray == Qnil) {
4836                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4837                         sMoleculeRetainArray = rb_ary_new();
4838                 }
4839                 
4840                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4841                 mol->exmolobjProtected = 1;
4842         }
4843 }
4844
4845 /*  Release hook function for Ruby  */
4846 void
4847 MoleculeReleaseHook(Molecule *mol)
4848 {
4849         if (mol->exmolobj != NULL) {
4850                 /*  No need to remove from sMoleculeRetainArray  */
4851                 mol->exmolobj = NULL;
4852                 mol->exmolobjProtected = 0;
4853         }
4854         MoleculeRelease(mol);
4855 }
4856
4857 VALUE
4858 ValueFromMolecule(Molecule *mol)
4859 {
4860         if (mol == NULL)
4861                 return Qnil;
4862         if (mol->exmolobj != NULL)
4863                 return (VALUE)mol->exmolobj;
4864         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4865         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4866         return (VALUE)mol->exmolobj;
4867 }
4868
4869 /*  Allocator  */
4870 static VALUE
4871 s_Molecule_Alloc(VALUE klass)
4872 {
4873         VALUE val;
4874         Molecule *mol = MoleculeNew();
4875         val = ValueFromMolecule(mol);
4876         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4877         return val;
4878 }
4879
4880 static int
4881 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4882 {
4883         int n;
4884         char *p = "";
4885         if (FIXNUM_P(val)) {
4886                 n = FIX2INT(val);
4887                 if (n >= 0 && n < mol->natoms)
4888                         return n;
4889                 n = -1; /*  No such atom  */
4890                 val = rb_inspect(val);
4891         } else {
4892                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4893         }
4894         if (n >= 0 && n < mol->natoms)
4895                 return n;
4896         p = StringValuePtr(val);
4897         if (n == -1)
4898                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4899         else if (n == -2)
4900                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4901         else
4902                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4903         return 0; /* Not reached */
4904 }
4905
4906 static IntGroup *
4907 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4908 {
4909         IntGroup *ig;
4910         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4911         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4912                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4913         Data_Get_Struct(val, IntGroup, ig);
4914         IntGroupRetain(ig);
4915         return ig;
4916 }
4917
4918 /*
4919  *  call-seq:
4920  *     dup          -> Molecule
4921  *
4922  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
4923  *  created object does not affect the old object in any sense.
4924  */
4925 static VALUE
4926 s_Molecule_InitCopy(VALUE self, VALUE arg)
4927 {
4928         Molecule *mp1, *mp2;
4929         Data_Get_Struct(self, Molecule, mp1);
4930         mp2 = MoleculeFromValue(arg);
4931         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4932                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4933         return self;
4934 }
4935
4936 /*
4937  *  call-seq:
4938  *     atom_index(val)       -> Integer
4939  *
4940  *  Returns the atom index represented by val. val can be either a non-negative integer
4941  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4942  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
4943  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
4944  *  If val is a string and multiple atoms match the description, the atom with the lowest index
4945  *  is returned.
4946  */
4947 static VALUE
4948 s_Molecule_AtomIndex(VALUE self, VALUE val)
4949 {
4950     Molecule *mol;
4951     Data_Get_Struct(self, Molecule, mol);
4952         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4953 }
4954
4955 /*
4956  *  call-seq:
4957  *     self == Molecule -> boolean
4958  *
4959  *  True if the two arguments point to the same molecule.
4960  */
4961 static VALUE
4962 s_Molecule_Equal(VALUE self, VALUE val)
4963 {
4964         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4965                 Molecule *mol1, *mol2;
4966                 Data_Get_Struct(self, Molecule, mol1);
4967                 Data_Get_Struct(val, Molecule, mol2);
4968                 return (mol1 == mol2 ? Qtrue : Qfalse);
4969         } else return Qfalse;
4970 }
4971
4972 #pragma mark ------ Load/Save ------
4973
4974 static void
4975 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
4976 {
4977         if (gLoadSaveErrorMessage != NULL) {
4978                 MyAppCallback_setConsoleColor(1);
4979                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
4980                 MyAppCallback_setConsoleColor(0);
4981         }
4982         if (status != 0)
4983                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4984 }
4985
4986 /*
4987  *  call-seq:
4988  *     loadmbsf(file)       -> bool
4989  *
4990  *  Read a structure from a mbsf file.
4991  *  Return true if successful.
4992  */
4993 static VALUE
4994 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4995 {
4996         VALUE fname;
4997         char *fstr;
4998         Molecule *mol;
4999         int retval;
5000         MoleculeClearLoadSaveErrorMessage();
5001         Data_Get_Struct(self, Molecule, mol);
5002         rb_scan_args(argc, argv, "1", &fname);
5003         fstr = FileStringValuePtr(fname);
5004         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5005         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5006         return Qtrue;   
5007 }
5008
5009 /*
5010  *  call-seq:
5011  *     loadpsf(file, pdbfile = nil)       -> bool
5012  *
5013  *  Read a structure from a psf file. molecule must be empty. The psf may be
5014  *  an "extended" version, which also contains coordinates. If pdbfile 
5015  *  is given, then atomic coordinates are read from that file.
5016  *  Return true if successful.
5017  */
5018 static VALUE
5019 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5020 {
5021         VALUE fname, pdbname;
5022         char *fstr, *pdbstr;
5023         Molecule *mol;
5024         int retval;
5025         Data_Get_Struct(self, Molecule, mol);
5026         if (mol->natoms > 0)
5027                 return Qnil;  /*  Must be a new molecule  */
5028         MoleculeClearLoadSaveErrorMessage();
5029         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5030         fstr = FileStringValuePtr(fname);
5031         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5032         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5033         pdbstr = NULL;
5034         if (!NIL_P(pdbname)) {
5035                 pdbstr = strdup(FileStringValuePtr(pdbname));
5036                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5037                 free(pdbstr);
5038                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5039         }
5040         return Qtrue;
5041 }
5042
5043 /*
5044  *  call-seq:
5045  *     loadpdb(file)       -> bool
5046  *
5047  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5048  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5049  *  Return true if successful.
5050  */
5051 static VALUE
5052 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5053 {
5054         VALUE fname;
5055         char *fstr;
5056         Molecule *mol;
5057         int retval;
5058         Data_Get_Struct(self, Molecule, mol);
5059         rb_scan_args(argc, argv, "1", &fname);
5060         MoleculeClearLoadSaveErrorMessage();
5061         fstr = FileStringValuePtr(fname);
5062         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5063         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5064         return Qtrue;   
5065 }
5066
5067 /*
5068  *  call-seq:
5069  *     loaddcd(file)       -> bool
5070  *
5071  *  Read coordinates from a dcd file. The molecule should not empty.
5072  *  Return true if successful.
5073  */
5074 static VALUE
5075 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5076 {
5077         VALUE fname;
5078         char *fstr;
5079         Molecule *mol;
5080         int retval;
5081         Data_Get_Struct(self, Molecule, mol);
5082         rb_scan_args(argc, argv, "1", &fname);
5083         MoleculeClearLoadSaveErrorMessage();
5084         fstr = FileStringValuePtr(fname);
5085         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5086         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5087         return Qtrue;   
5088 }
5089
5090 /*
5091  *  call-seq:
5092  *     loadtep(file)       -> bool
5093  *
5094  *  Read coordinates from an ortep .tep file.
5095  *  Return true if successful.
5096  */
5097 static VALUE
5098 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5099 {
5100         VALUE fname;
5101         char *fstr;
5102         Molecule *mol;
5103         int retval;
5104         Data_Get_Struct(self, Molecule, mol);
5105         rb_scan_args(argc, argv, "1", &fname);
5106         MoleculeClearLoadSaveErrorMessage();
5107         fstr = FileStringValuePtr(fname);
5108         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5109         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5110         return Qtrue;   
5111 }
5112
5113 /*
5114  *  call-seq:
5115  *     loadres(file)       -> bool
5116  *
5117  *  Read coordinates from a shelx .res file.
5118  *  Return true if successful.
5119  */
5120 static VALUE
5121 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5122 {
5123         VALUE fname;
5124         char *fstr;
5125         Molecule *mol;
5126         int retval;
5127         Data_Get_Struct(self, Molecule, mol);
5128         rb_scan_args(argc, argv, "1", &fname);
5129         MoleculeClearLoadSaveErrorMessage();
5130         fstr = FileStringValuePtr(fname);
5131         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5132         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5133         return Qtrue;   
5134 }
5135
5136 /*
5137  *  call-seq:
5138  *     loadfchk(file)       -> bool
5139  *
5140  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5141  *  Return true if successful.
5142  */
5143 static VALUE
5144 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5145 {
5146         VALUE fname;
5147         char *fstr;
5148         Molecule *mol;
5149         int retval;
5150         Data_Get_Struct(self, Molecule, mol);
5151         rb_scan_args(argc, argv, "1", &fname);
5152         MoleculeClearLoadSaveErrorMessage();
5153         fstr = FileStringValuePtr(fname);
5154         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5155         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5156         return Qtrue;   
5157 }
5158
5159 /*
5160  *  call-seq:
5161  *     loaddat(file)       -> bool
5162  *
5163  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5164  *  Return true if successful.
5165  */
5166 static VALUE
5167 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5168 {
5169         VALUE fname;
5170         char *fstr;
5171         Molecule *mol;
5172         int retval;
5173         Data_Get_Struct(self, Molecule, mol);
5174         rb_scan_args(argc, argv, "1", &fname);
5175         MoleculeClearLoadSaveErrorMessage();
5176         fstr = FileStringValuePtr(fname);
5177         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5178         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5179         MyAppCallback_hideProgressPanel();
5180         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5181         return Qtrue;   
5182 }
5183
5184 /*
5185  *  call-seq:
5186  *     savembsf(file)       -> bool
5187  *
5188  *  Write structure as a mbsf file. Returns true if successful.
5189  */
5190 static VALUE
5191 s_Molecule_Savembsf(VALUE self, VALUE fname)
5192 {
5193         char *fstr;
5194     Molecule *mol;
5195         int retval;
5196     Data_Get_Struct(self, Molecule, mol);
5197         MoleculeClearLoadSaveErrorMessage();
5198         fstr = FileStringValuePtr(fname);
5199         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5200         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5201         return Qtrue;
5202 }
5203
5204 /*
5205  *  call-seq:
5206  *     savepsf(file)       -> bool
5207  *
5208  *  Write structure as a psf file. Returns true if successful.
5209  */
5210 static VALUE
5211 s_Molecule_Savepsf(VALUE self, VALUE fname)
5212 {
5213         char *fstr;
5214     Molecule *mol;
5215         int retval;
5216     Data_Get_Struct(self, Molecule, mol);
5217         MoleculeClearLoadSaveErrorMessage();
5218         fstr = FileStringValuePtr(fname);
5219         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5220         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5221         return Qtrue;
5222 }
5223
5224 /*
5225  *  call-seq:
5226  *     savepdb(file)       -> bool
5227  *
5228  *  Write coordinates as a pdb file. Returns true if successful.
5229  */
5230 static VALUE
5231 s_Molecule_Savepdb(VALUE self, VALUE fname)
5232 {
5233         char *fstr;
5234     Molecule *mol;
5235         int retval;
5236     Data_Get_Struct(self, Molecule, mol);
5237         MoleculeClearLoadSaveErrorMessage();
5238         fstr = FileStringValuePtr(fname);
5239         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5240         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5241         return Qtrue;
5242 }
5243
5244 /*
5245  *  call-seq:
5246  *     savedcd(file)       -> bool
5247  *
5248  *  Write coordinates as a dcd file. Returns true if successful.
5249  */
5250 static VALUE
5251 s_Molecule_Savedcd(VALUE self, VALUE fname)
5252 {
5253         char *fstr;
5254     Molecule *mol;
5255         int retval;
5256     Data_Get_Struct(self, Molecule, mol);
5257         MoleculeClearLoadSaveErrorMessage();
5258         fstr = FileStringValuePtr(fname);
5259         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5260         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5261         return Qtrue;
5262 }
5263
5264 /*  load([ftype, ] fname, ...)  */
5265 static VALUE
5266 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5267 {
5268         VALUE rval;
5269         char *argstr, *methname, *p, *type = "";
5270         ID mid = 0;
5271         int i;
5272         const char *ls = (loadFlag ? "load" : "save");
5273         int lslen = strlen(ls);
5274
5275         if (argc == 0)
5276                 return Qnil;
5277
5278         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5279                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5280         if (argstr[0] == ':') {
5281                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5282                 methname = ALLOC_N(char, lslen + strlen(argstr));
5283                 strcpy(methname, ls);
5284                 strcat(methname, argstr + 1);
5285                 type = argstr + 1;
5286                 for (i = lslen; methname[i] != 0; i++)
5287                         methname[i] = tolower(methname[i]);
5288                 mid = rb_intern(methname);
5289                 xfree(methname);
5290                 argc--;
5291                 argv++;
5292                 rval = rb_funcall2(self, mid, argc, argv);
5293                 if (rval == Qnil)
5294                         goto failure;
5295                 else
5296                         goto success;
5297         }
5298         /*  Guess file type from extension  */
5299         p = strrchr(argstr, '.');
5300         if (p != NULL) {
5301                 p++;
5302                 type = p;
5303                 for (methname = p; *methname != 0; methname++) {
5304                         if (!isalpha(*methname))
5305                                 break;
5306                 }
5307                 if (*methname == 0) {
5308                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5309                         if (methname == NULL)
5310                                 rb_raise(rb_eMolbyError, "Low memory");
5311                         strcpy(methname, ls);
5312                         strcat(methname, p);
5313                         for (i = lslen; methname[i] != 0; i++)
5314                                 methname[i] = tolower(methname[i]);
5315                         mid = rb_intern(methname);
5316                         xfree(methname);
5317                         if (loadFlag) {
5318                                 if (rb_respond_to(self, mid)) {
5319                                         /*  Load: try to call the load procedure only if it is available  */
5320                                         rval = rb_funcall2(self, mid, argc, argv);
5321                                         if (rval != Qnil)
5322                                                 goto success;
5323                                 }
5324                         } else {
5325                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5326                                 rval = rb_funcall2(self, mid, argc, argv);
5327                                 if (rval != Qnil)
5328                                         goto success;
5329                         }
5330                 }
5331         }
5332 failure:
5333         rval = rb_str_to_str(argv[0]);
5334         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5335         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5336         return Qnil;  /*  Does not reach here  */
5337
5338 success:
5339         {
5340                 /*  Register the path  */
5341                 Molecule *mol;
5342         /*      Atom *ap; */
5343                 Data_Get_Struct(self, Molecule, mol);
5344                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5345                 
5346                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5347         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5348                         if (ap->occupancy != 0.0)
5349                                 break;
5350                 }
5351                 if (i == mol->natoms) {
5352                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5353                                 ap->occupancy = 1.0;
5354                         }
5355                 } */
5356         }
5357         return rval;
5358 }
5359
5360 /*
5361  *  call-seq:
5362  *     molload(file, *args)       -> bool
5363  *
5364  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5365  *  file type given by the extension). If this method fails, then all defined (public)
5366  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5367  */
5368 static VALUE
5369 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5370 {
5371         return s_Molecule_LoadSave(argc, argv, self, 1);
5372 }
5373
5374 /*
5375  *  call-seq:
5376  *     molsave(file, *args)       -> bool
5377  *
5378  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5379  *  (XXX is the file type given by the extension).
5380  */
5381 static VALUE
5382 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5383 {
5384         return s_Molecule_LoadSave(argc, argv, self, 0);
5385 }
5386
5387 /*
5388  *  call-seq:
5389  *     open        -> Molecule
5390  *     open(file)  -> Molecule
5391  *
5392  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5393  */
5394 static VALUE
5395 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5396 {
5397         VALUE fname;
5398         const char *p;
5399         Molecule *mp;
5400         VALUE iflag;
5401     
5402     if (!gUseGUI) {
5403         rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5404     }
5405     
5406         rb_scan_args(argc, argv, "01", &fname);
5407         if (NIL_P(fname))
5408                 p = NULL;
5409         else
5410                 p = FileStringValuePtr(fname);
5411         iflag = Ruby_SetInterruptFlag(Qfalse);
5412         mp = MoleculeCallback_openNewMolecule(p);
5413         Ruby_SetInterruptFlag(iflag);
5414         if (mp == NULL) {
5415                 if (p == NULL)
5416                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5417                 else
5418                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5419         }
5420         return ValueFromMolecule(mp);
5421 }
5422
5423 /*
5424  *  call-seq:
5425  *     new  -> Molecule
5426  *     new(file, *args)  -> Molecule
5427  *
5428  *  Create a new molecule and call "load" method with the same arguments.
5429  */
5430 static VALUE
5431 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5432 {
5433         if (argc > 0)
5434                 return s_Molecule_Load(argc, argv, self);
5435         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5436 }
5437
5438 /*
5439  *  call-seq:
5440  *     error_message       -> String
5441  *
5442  *  Get the error_message from the last load/save method. If no error, returns nil.
5443  */
5444 static VALUE
5445 s_Molecule_ErrorMessage(VALUE klass)
5446 {
5447         if (gLoadSaveErrorMessage == NULL)
5448                 return Qnil;
5449         else return rb_str_new2(gLoadSaveErrorMessage);
5450 }
5451
5452 /*
5453  *  call-seq:
5454  *     set_error_message(String)
5455  *     Molecule.error_message = String
5456  *
5457  *  Set the error_message for the present load/save method.
5458  */
5459 static VALUE
5460 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5461 {
5462         if (gLoadSaveErrorMessage != NULL) {
5463                 free(gLoadSaveErrorMessage);
5464                 gLoadSaveErrorMessage = NULL;
5465         }
5466         if (sval != Qnil) {
5467                 sval = rb_str_to_str(sval);
5468                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5469         }
5470         return sval;
5471 }
5472
5473 /*
5474  *  call-seq:
5475  *     set_molecule(Molecule)
5476  *
5477  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5478  *  This method is exclusively used for associating a new document with an existing molecule.
5479  */
5480 static VALUE
5481 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5482 {
5483         Molecule *mp1, *mp2;
5484         Data_Get_Struct(self, Molecule, mp1);
5485         mp2 = MoleculeFromValue(mval);
5486         MoleculeInitWithMolecule(mp1, mp2);
5487         MoleculeCallback_notifyModification(mp1, 1);
5488         return self;
5489 }
5490
5491 #pragma mark ------ Name attributes ------
5492
5493 /*
5494  *  call-seq:
5495  *     name       -> String
5496  *
5497  *  Returns the display name of the molecule. If the molecule has no associated
5498  *  document, then returns nil.
5499  */
5500 static VALUE
5501 s_Molecule_Name(VALUE self)
5502 {
5503     Molecule *mol;
5504         char buf[1024];
5505     Data_Get_Struct(self, Molecule, mol);
5506         MoleculeCallback_displayName(mol, buf, sizeof buf);
5507         if (buf[0] == 0)
5508                 return Qnil;
5509         else
5510                 return rb_str_new2(buf);
5511 }
5512
5513 /*
5514  *  call-seq:
5515  *     set_name(string) -> self
5516  *
5517  *  Set the name of an untitled molecule. If the molecule is not associated with window
5518  *  or it already has an associated file, then exception is thrown.
5519  */
5520 static VALUE
5521 s_Molecule_SetName(VALUE self, VALUE nval)
5522 {
5523     Molecule *mol;
5524     Data_Get_Struct(self, Molecule, mol);
5525         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5526                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5527         return self;
5528 }
5529
5530
5531 /*
5532  *  call-seq:
5533  *     path       -> String
5534  *
5535  *  Returns the full path name of the molecule, if it is associated with a file.
5536  *  If the molecule has no associated file, then returns nil.
5537  */
5538 static VALUE
5539 s_Molecule_Path(VALUE self)
5540 {
5541     Molecule *mol;
5542         char buf[1024];
5543     Data_Get_Struct(self, Molecule, mol);
5544         MoleculeCallback_pathName(mol, buf, sizeof buf);
5545         if (buf[0] == 0)
5546                 return Qnil;
5547         else
5548                 return Ruby_NewFileStringValue(buf);
5549 }
5550
5551 /*
5552  *  call-seq:
5553  *     dir       -> String
5554  *
5555  *  Returns the full path name of the directory in which the file associated with the
5556  *  molecule is located. If the molecule has no associated file, then returns nil.
5557  */
5558 static VALUE
5559 s_Molecule_Dir(VALUE self)
5560 {
5561     Molecule *mol;
5562         char buf[1024], *p;
5563     Data_Get_Struct(self, Molecule, mol);
5564         MoleculeCallback_pathName(mol, buf, sizeof buf);
5565 #if __WXMSW__
5566         translate_char(buf, '\\', '/');
5567 #endif
5568         if (buf[0] == 0)
5569                 return Qnil;
5570         else {
5571                 p = strrchr(buf, '/');
5572                 if (p != NULL)
5573                         *p = 0;
5574                 return rb_str_new2(buf);
5575         }
5576 }
5577
5578 /*
5579  *  call-seq:
5580  *     inspect       -> String
5581  *
5582  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5583  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5584  *  the Molecule structure) is returned.
5585  */
5586 static VALUE
5587 s_Molecule_Inspect(VALUE self)
5588 {
5589     Molecule *mol;
5590         char buf[256];
5591     Data_Get_Struct(self, Molecule, mol);
5592         MoleculeCallback_displayName(mol, buf, sizeof buf);
5593         if (buf[0] == 0) {
5594                 /*  No associated document  */
5595                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5596                 return rb_str_new2(buf);
5597         } else {
5598                 /*  Check whether the document name is duplicate  */
5599                 char buf2[256];
5600                 int idx, k, k2;
5601                 Molecule *mol2;
5602                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5603                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5604                         if (strcmp(buf, buf2) == 0) {
5605                                 k++;
5606                                 if (mol == mol2)
5607                                         k2 = k;
5608                         }
5609                 }
5610                 if (k > 1) {
5611                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5612                 } else {
5613                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5614                 }
5615                 return rb_str_new2(buf2);
5616         }
5617 }
5618
5619 #pragma mark ------ MolEnumerables ------
5620
5621 static VALUE
5622 s_Molecule_MolEnumerable(VALUE self, int kind)
5623 {
5624     Molecule *mol;
5625         MolEnumerable *mseq;
5626     Data_Get_Struct(self, Molecule, mol);
5627         mseq = MolEnumerableNew(mol, kind);
5628         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5629 }
5630
5631 /*
5632  *  call-seq:
5633  *     atoms       -> MolEnumerable
5634  *
5635  *  Returns a MolEnumerable object representing the array of atoms.
5636  */
5637 static VALUE
5638 s_Molecule_Atoms(VALUE self)
5639 {
5640         return s_Molecule_MolEnumerable(self, kAtomKind);
5641 }
5642
5643 /*
5644  *  call-seq:
5645  *     bonds       -> MolEnumerable
5646  *
5647  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5648  *  by an array of two atom indices.
5649  */
5650 static VALUE
5651 s_Molecule_Bonds(VALUE self)
5652 {
5653         return s_Molecule_MolEnumerable(self, kBondKind);
5654 }
5655
5656 /*
5657  *  call-seq:
5658  *     angles       -> MolEnumerable
5659  *
5660  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5661  *  by an array of three atom indices.
5662  */
5663 static VALUE
5664 s_Molecule_Angles(VALUE self)
5665 {
5666         return s_Molecule_MolEnumerable(self, kAngleKind);
5667 }
5668
5669 /*
5670  *  call-seq:
5671  *     dihedrals       -> MolEnumerable
5672  *
5673  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5674  *  by an array of four atom indices.
5675  */
5676 static VALUE
5677 s_Molecule_Dihedrals(VALUE self)
5678 {
5679         return s_Molecule_MolEnumerable(self, kDihedralKind);
5680 }
5681
5682 /*
5683  *  call-seq:
5684  *     impropers       -> MolEnumerable
5685  *
5686  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5687  *  by an array of four atom indices.
5688  */
5689 static VALUE
5690 s_Molecule_Impropers(VALUE self)
5691 {
5692         return s_Molecule_MolEnumerable(self, kImproperKind);
5693 }
5694
5695 /*
5696  *  call-seq:
5697  *     residues       -> MolEnumerable
5698  *
5699  *  Returns a MolEnumerable object representing the array of residue names.
5700  */
5701 static VALUE
5702 s_Molecule_Residues(VALUE self)
5703 {
5704         return s_Molecule_MolEnumerable(self, kResidueKind);
5705 }
5706
5707 /*
5708  *  call-seq:
5709  *     natoms       -> Integer
5710  *
5711  *  Returns the number of atoms.
5712  */
5713 static VALUE
5714 s_Molecule_Natoms(VALUE self)
5715 {
5716     Molecule *mol;
5717     Data_Get_Struct(self, Molecule, mol);
5718         return INT2NUM(mol->natoms);
5719 }
5720
5721 /*
5722  *  call-seq:
5723  *     nbonds       -> Integer
5724  *
5725  *  Returns the number of bonds.
5726  */
5727 static VALUE
5728 s_Molecule_Nbonds(VALUE self)
5729 {
5730     Molecule *mol;
5731     Data_Get_Struct(self, Molecule, mol);
5732         return INT2NUM(mol->nbonds);
5733 }
5734
5735 /*
5736  *  call-seq:
5737  *     nangles       -> Integer
5738  *
5739  *  Returns the number of angles.
5740  */
5741 static VALUE
5742 s_Molecule_Nangles(VALUE self)
5743 {
5744     Molecule *mol;
5745     Data_Get_Struct(self, Molecule, mol);
5746         return INT2NUM(mol->nangles);
5747 }
5748
5749 /*
5750  *  call-seq:
5751  *     ndihedrals       -> Integer
5752  *
5753  *  Returns the number of dihedrals.
5754  */
5755 static VALUE
5756 s_Molecule_Ndihedrals(VALUE self)
5757 {
5758     Molecule *mol;
5759     Data_Get_Struct(self, Molecule, mol);
5760         return INT2NUM(mol->ndihedrals);
5761 }
5762
5763 /*
5764  *  call-seq:
5765  *     nimpropers       -> Integer
5766  *
5767  *  Returns the number of impropers.
5768  */
5769 static VALUE
5770 s_Molecule_Nimpropers(VALUE self)
5771 {
5772     Molecule *mol;
5773     Data_Get_Struct(self, Molecule, mol);
5774         return INT2NUM(mol->nimpropers);
5775 }
5776
5777 /*
5778  *  call-seq:
5779  *     nresidues       -> Integer
5780  *
5781  *  Returns the number of residues.
5782  */
5783 static VALUE
5784 s_Molecule_Nresidues(VALUE self)
5785 {
5786     Molecule *mol;
5787     Data_Get_Struct(self, Molecule, mol);
5788         return INT2NUM(mol->nresidues);
5789 }
5790
5791 /*
5792  *  call-seq:
5793  *     nresidues = Integer
5794  *
5795  *  Change the number of residues.
5796  */
5797 static VALUE
5798 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5799 {
5800     Molecule *mol;
5801         int ival = NUM2INT(val);
5802     Data_Get_Struct(self, Molecule, mol);
5803         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5804         if (ival != mol->nresidues)
5805                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5806         return val;
5807 }
5808
5809 /*
5810  *  call-seq:
5811  *     max_residue_number(atom_group = nil)     -> Integer
5812  *
5813  *  Returns the maximum residue number actually used. If an atom group is given, only
5814  *  these atoms are examined. If no atom is present, nil is returned.
5815  */
5816 static VALUE
5817 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5818 {
5819     Molecule *mol;
5820         VALUE gval;
5821         int maxSeq;
5822         IntGroup *ig;
5823     Data_Get_Struct(self, Molecule, mol);
5824         rb_scan_args(argc, argv, "01", &gval);
5825         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5826         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5827         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5828 }
5829
5830 /*
5831  *  call-seq:
5832  *     min_residue_number(atom_group = nil)     -> Integer
5833  *
5834  *  Returns the minimum residue number actually used. If an atom group is given, only
5835  *  these atoms are examined. If no atom is present, nil is returned.
5836  */
5837 static VALUE
5838 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5839 {
5840     Molecule *mol;
5841         VALUE gval;
5842         int minSeq;
5843         IntGroup *ig;
5844     Data_Get_Struct(self, Molecule, mol);
5845         rb_scan_args(argc, argv, "01", &gval);
5846         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5847         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5848         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5849 }
5850
5851 /*
5852  *  call-seq:
5853  *     each_atom(atom_group = nil) {|aref| ...}
5854  *
5855  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5856  *  group is given, only these atoms are processed.
5857  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5858  *  is self (a Molecule object).
5859  */
5860 static VALUE
5861 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5862 {
5863         int i;
5864     Molecule *mol;
5865         AtomRef *aref;
5866         VALUE arval;
5867         VALUE gval;
5868         IntGroup *ig;
5869     Data_Get_Struct(self, Molecule, mol);
5870         rb_scan_args(argc, argv, "01", &gval);
5871         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5872         arval = ValueFromMoleculeAndIndex(mol, 0);
5873         Data_Get_Struct(arval, AtomRef, aref);
5874         for (i = 0; i < mol->natoms; i++) {
5875                 aref->idx = i;
5876                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5877                         rb_yield(arval);
5878         }
5879         if (ig != NULL)
5880                 IntGroupRelease(ig);
5881     return self;
5882 }
5883
5884 #pragma mark ------ Atom Group ------
5885
5886 static VALUE
5887 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5888 {
5889         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5890         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5891         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5892         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5893         return Qnil;
5894 }
5895
5896 /*
5897  *  call-seq:
5898  *     atom_group
5899  *     atom_group {|aref| ...}
5900  *     atom_group(arg1, arg2, ...)
5901  *     atom_group(arg1, arg2, ...) {|aref| ...}
5902  *
5903  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5904  *  If arguments are given, then the atoms reprensented by the arguments are added to the
5905  *  group. For a conversion of a string to an atom index, see the description
5906  *  of Molecule#atom_index.
5907  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
5908  *  representing each atom, and the atoms are removed from the result if the block returns false.
5909  *
5910  */
5911 static VALUE
5912 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5913 {
5914         IntGroup *ig1, *ig2;
5915     Molecule *mol;
5916         Int i, startPt, interval;
5917         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5918         Data_Get_Struct(retval, IntGroup, ig1);
5919     Data_Get_Struct(self, Molecule, mol);
5920         if (argc == 0) {
5921                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5922         } else {
5923                 while (argc > 0) {
5924                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5925                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5926                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5927                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5928                                 ig2 = IntGroupFromValue(*argv);
5929                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5930                                         interval = IntGroupGetInterval(ig2, i);
5931                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5932                                 }
5933                                 IntGroupRelease(ig2);
5934                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
5935                                 VALUE values[2];
5936                                 values[0] = (VALUE)mol;
5937                                 values[1] = (VALUE)ig1;
5938                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5939                         } else
5940                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5941                         argc--;
5942                         argv++;
5943                 }
5944         }
5945         if (rb_block_given_p()) {
5946                 /*  Evaluate the given block with an AtomRef as the argument, and delete
5947                         the index if the block returns false  */
5948                 AtomRef *aref = AtomRefNew(mol, 0);
5949                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5950                 ig2 = IntGroupNew();
5951                 IntGroupCopy(ig2, ig1);
5952                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5953                         VALUE resval;
5954                         if (startPt >= mol->natoms)
5955                                 break;
5956                         aref->idx = startPt;
5957                         resval = rb_yield(arval);
5958                         if (!RTEST(resval))
5959                                 IntGroupRemove(ig1, startPt, 1);
5960                 }
5961                 IntGroupRelease(ig2);
5962         }
5963         
5964         /*  Remove points that are out of bounds */
5965         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5966
5967         return retval;                  
5968 }
5969
5970 /*
5971  *  call-seq:
5972  *     selection       -> IntGroup
5973  *
5974  *  Returns the current selection.
5975  */
5976 static VALUE
5977 s_Molecule_Selection(VALUE self)
5978 {
5979     Molecule *mol;
5980         IntGroup *ig;
5981         VALUE val;
5982     Data_Get_Struct(self, Molecule, mol);
5983         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5984                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
5985                 val = ValueFromIntGroup(ig);
5986                 IntGroupRelease(ig);
5987         } else {
5988                 val = IntGroup_Alloc(rb_cIntGroup);
5989         }
5990         return val;
5991 }
5992
5993 static VALUE
5994 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5995 {
5996     Molecule *mol;
5997         IntGroup *ig;
5998     Data_Get_Struct(self, Molecule, mol);
5999         if (val == Qnil)
6000                 ig = NULL;
6001         else
6002                 ig = s_Molecule_AtomGroupFromValue(self, val);
6003         if (undoable)
6004                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6005         else
6006                 MoleculeSetSelection(mol, ig);
6007         if (ig != NULL)
6008                 IntGroupRelease(ig);
6009         return val;
6010 }
6011
6012 /*
6013  *  call-seq:
6014  *     selection = IntGroup
6015  *
6016  *  Set the current selection. The right-hand operand may be nil.
6017  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6018  */
6019 static VALUE
6020 s_Molecule_SetSelection(VALUE self, VALUE val)
6021 {
6022         return s_Molecule_SetSelectionSub(self, val, 0);
6023 }
6024
6025 /*
6026  *  call-seq:
6027  *     set_undoable_selection(IntGroup)
6028  *
6029  *  Set the current selection with undo registration. The right-hand operand may be nil.
6030  *  This operation is undoable.
6031  */
6032 static VALUE
6033 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6034 {
6035         return s_Molecule_SetSelectionSub(self, val, 1);
6036 }
6037
6038 #pragma mark ------ Editing ------
6039
6040 /*
6041  *  call-seq:
6042  *     extract(group, dummy_flag = nil)       -> Molecule
6043  *
6044  *  Extract the atoms given by group and return as a new molecule object.
6045  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6046  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6047  *  names beginning with an underscore) and included in the new molecule object.
6048  */
6049 static VALUE
6050 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6051 {
6052     Molecule *mol1, *mol2;
6053         IntGroup *ig;
6054         VALUE group, dummy_flag, retval;
6055     Data_Get_Struct(self, Molecule, mol1);
6056         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6057         ig = s_Molecule_AtomGroupFromValue(self, group);
6058         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6059                 retval = Qnil;
6060         } else {
6061                 retval = ValueFromMolecule(mol2);
6062         }
6063         IntGroupRelease(ig);
6064         return retval;
6065 }
6066
6067 /*
6068  *  call-seq:
6069  *     add(molecule2)       -> self
6070  *
6071  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6072     conflicts.
6073     This operation is undoable.
6074  */
6075 static VALUE
6076 s_Molecule_Add(VALUE self, VALUE val)
6077 {
6078     Molecule *mol1, *mol2;
6079     Data_Get_Struct(self, Molecule, mol1);
6080         mol2 = MoleculeFromValue(val);
6081         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6082         return self; 
6083 }
6084
6085 /*
6086  *  call-seq:
6087  *     remove(group)       -> Molecule
6088  *
6089  *  The atoms designated by the given group are removed from the molecule.
6090  *  This operation is undoable.
6091  */
6092 static VALUE
6093 s_Molecule_Remove(VALUE self, VALUE group)
6094 {
6095     Molecule *mol1;
6096         IntGroup *ig, *bg;
6097         Int i;
6098         IntGroupIterator iter;
6099
6100     Data_Get_Struct(self, Molecule, mol1);
6101         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6102         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6103                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6104         Data_Get_Struct(group, IntGroup, ig);
6105
6106         /*  Remove the bonds between the two fragments  */
6107         /*  (This is necessary for undo to work correctly)  */
6108         IntGroupIteratorInit(ig, &iter);
6109         bg = NULL;
6110         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6111                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6112                 Int j, *cp;
6113                 cp = AtomConnectData(&ap->connect);
6114                 for (j = 0; j < ap->connect.count; j++) {
6115                         int n = cp[j];
6116                         if (!IntGroupLookup(ig, n, NULL)) {
6117                                 /*  bond i-n, i is in ig and n is not  */
6118                                 int k = MoleculeLookupBond(mol1, i, n);
6119                                 if (k >= 0) {
6120                                         if (bg == NULL)
6121                                                 bg = IntGroupNew();
6122                                         IntGroupAdd(bg, k, 1);
6123                                 }
6124                         }
6125                 }
6126         }
6127         IntGroupIteratorRelease(&iter);
6128         if (bg != NULL) {
6129                 /*  Remove bonds  */
6130                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6131                 IntGroupRelease(bg);
6132         }
6133         /*  Remove atoms  */
6134         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6135                 return Qnil;
6136         return self;
6137 }
6138
6139 /*
6140  *  call-seq:
6141  *     create_atom(name, pos = -1)  -> AtomRef
6142  *
6143  *  Create a new atom with the specified name (may contain residue 
6144  *  information) and position (if position is out of range, the atom is appended at
6145  *  the end). Returns the reference to the new atom.
6146  *  This operation is undoable.
6147  */
6148 static VALUE
6149 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6150 {
6151     Molecule *mol;
6152     Int i, pos;
6153         VALUE name, ival;
6154     Atom arec;
6155     AtomRef *aref;
6156         char *p, resName[6], atomName[6];
6157         int resSeq;
6158     Data_Get_Struct(self, Molecule, mol);
6159         rb_scan_args(argc, argv, "02", &name, &ival);
6160         if (ival != Qnil)
6161                 pos = NUM2INT(rb_Integer(ival));
6162         else pos = -1;
6163         if (name != Qnil) {
6164                 p = StringValuePtr(name);
6165                 if (p[0] != 0) {
6166                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6167                         if (atomName[0] == 0)
6168                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6169                 }
6170         } else p = NULL;
6171         if (p == NULL || p[0] == 0) {
6172                 memset(atomName, 0, 4);
6173                 resSeq = -1;
6174         }
6175     memset(&arec, 0, sizeof(arec));
6176     strncpy(arec.aname, atomName, 4);
6177     if (resSeq >= 0) {
6178       strncpy(arec.resName, resName, 4);
6179       arec.resSeq = resSeq;
6180     }
6181         arec.occupancy = 1.0;
6182 //    i = MoleculeCreateAnAtom(mol, &arec);
6183         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6184                 return Qnil;
6185     aref = AtomRefNew(mol, pos);
6186     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6187 }
6188
6189 /*
6190  *  call-seq:
6191  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6192  *
6193  *  Create a new atom with the same attributes (but no bonding information)
6194  *  with the specified atom. Returns the reference to the new atom.
6195  *  This operation is undoable.
6196  */
6197 static VALUE
6198 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6199 {
6200     Molecule *mol;
6201         const Atom *apsrc;
6202     Atom arec;
6203         AtomRef *aref;
6204         VALUE retval, aval, ival;
6205         Int pos;
6206     Data_Get_Struct(self, Molecule, mol);
6207         rb_scan_args(argc, argv, "11", &aval, &ival);
6208         if (FIXNUM_P(aval)) {
6209                 int idx = NUM2INT(aval);
6210                 if (idx < 0 || idx >= mol->natoms)
6211                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6212                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6213         } else {
6214                 apsrc = s_AtomFromValue(aval);
6215         }
6216         if (apsrc == NULL)
6217                 rb_raise(rb_eMolbyError, "bad atom specification");
6218         if (ival != Qnil)
6219                 pos = NUM2INT(rb_Integer(ival));
6220         else pos = -1;
6221         AtomDuplicate(&arec, apsrc);
6222         arec.connect.count = 0;
6223         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6224                 retval = Qnil;
6225         else {
6226                 aref = AtomRefNew(mol, pos);
6227                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6228         }
6229         AtomClean(&arec);
6230         return retval;
6231 }
6232
6233 /*
6234  *  call-seq:
6235  *     create_bond(n1, n2, ...)       -> Integer
6236  *
6237  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6238  *  do nothing for that pair. Returns the number of bonds actually created.
6239  *  This operation is undoable.
6240  */
6241 static VALUE
6242 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6243 {
6244     Molecule *mol;
6245         Int i, j, k, *ip, old_nbonds;
6246         if (argc == 0)
6247                 rb_raise(rb_eMolbyError, "missing arguments");
6248         if (argc % 2 != 0)
6249                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6250     Data_Get_Struct(self, Molecule, mol);
6251         ip = ALLOC_N(Int, argc + 1);
6252         for (i = j = 0; i < argc; i++, j++) {
6253                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6254                 if (i % 2 == 1) {
6255                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6256                                 j -= 2;  /*  This bond is already present: skip it  */
6257                         else {
6258                                 for (k = 0; k < j - 1; k += 2) {
6259                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6260                                                 j -= 2;   /*  The same entry is already in the argument  */
6261                                                 break;
6262                                         }
6263                                 }
6264                         }
6265                 }
6266         }
6267         old_nbonds = mol->nbonds;
6268         if (j > 0) {
6269                 ip[j] = kInvalidIndex;
6270                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6271         } else i = 0;
6272         xfree(ip);
6273         if (i == -1)
6274                 rb_raise(rb_eMolbyError, "atom index out of range");
6275         else if (i == -2)
6276                 rb_raise(rb_eMolbyError, "too many bonds");
6277         else if (i == -3)
6278                 rb_raise(rb_eMolbyError, "duplicate bonds");
6279         else if (i == -5)
6280                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6281         else if (i != 0)
6282                 rb_raise(rb_eMolbyError, "error in creating bonds");
6283         return INT2NUM(mol->nbonds - old_nbonds);
6284 }
6285
6286 /*
6287  *  call-seq:
6288  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6289  *
6290  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6291  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6292  *  This operation is undoable.
6293  */
6294 static VALUE
6295 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6296 {
6297     Molecule *mol;
6298         Int i, j, n[2];
6299         IntGroup *bg;
6300         if (argc == 0)
6301                 rb_raise(rb_eMolbyError, "missing arguments");
6302         if (argc % 2 != 0)
6303                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6304     Data_Get_Struct(self, Molecule, mol);
6305         bg = NULL;
6306         for (i = j = 0; i < argc; i++, j = 1 - j) {
6307                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6308                 if (j == 1) {
6309                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6310                         if (k >= 0) {
6311                                 if (bg == NULL)
6312                                         bg = IntGroupNew();
6313                                 IntGroupAdd(bg, k, 1);
6314                         }
6315                 }
6316         }
6317         if (bg != NULL) {
6318                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6319                 i = IntGroupGetCount(bg);
6320                 IntGroupRelease(bg);
6321         } else i = 0;
6322         return INT2NUM(i);
6323 }
6324
6325 /*
6326  *  call-seq:
6327  *     assign_bond_order(idx, d1)
6328  *     assign_bond_orders(group, [d1, d2, ...])
6329  *
6330  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6331  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6332  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6333  *  (This may change in the future)
6334  *  This operation is undoable.
6335  */
6336 static VALUE
6337 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6338 {
6339     Molecule *mol;
6340         IntGroup *ig;
6341     Data_Get_Struct(self, Molecule, mol);
6342         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6343                 /*  The first form  */
6344                 Int idx = NUM2INT(rb_Integer(idxval));
6345                 Double d1 = NUM2DBL(rb_Float(dval));
6346                 if (idx < 0 || idx >= mol->nbonds)
6347                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6348                 ig = IntGroupNewWithPoints(idx, 1, -1);
6349                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6350                 IntGroupRelease(ig);
6351         } else {
6352                 Int i, n;
6353                 Double *dp;
6354                 ig = IntGroupFromValue(idxval);
6355                 n = IntGroupGetCount(ig);
6356                 if (n == 0)
6357                         rb_raise(rb_eMolbyError, "the bond index is empty");
6358                 dval = rb_ary_to_ary(dval);
6359                 dp = (Double *)calloc(sizeof(Double), n);
6360                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6361                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6362                 }
6363                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6364                 free(dp);
6365                 IntGroupRelease(ig);
6366         }
6367         return self;
6368 }
6369
6370 /*
6371  *  call-seq:
6372  *     get_bond_order(idx) -> Float
6373  *     get_bond_orders(group) -> Array
6374  *
6375  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6376  *  In the second form, the bond orders at the indices in the group are returned as an array.
6377  *  If no bond order information have been assigned, returns nil (the first form)
6378  *  or an empty array (the second form).
6379  */
6380 static VALUE
6381 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6382 {
6383     Molecule *mol;
6384         IntGroup *ig;
6385         Double *dp;
6386         VALUE retval;
6387         Int i, n, numericArg;
6388     Data_Get_Struct(self, Molecule, mol);
6389         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6390                 /*  The first form  */
6391                 Int idx = NUM2INT(rb_Integer(idxval));
6392                 if (idx < 0 || idx >= mol->nbonds)
6393                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6394                 if (mol->bondOrders == NULL)
6395                         return Qnil;
6396                 ig = IntGroupNewWithPoints(idx, 1, -1);
6397                 n = 1;
6398                 numericArg = 1;
6399         } else {
6400                 if (mol->bondOrders == NULL)
6401                         return rb_ary_new();
6402                 ig = IntGroupFromValue(idxval);
6403                 n = IntGroupGetCount(ig);
6404                 if (n == 0)
6405                         rb_raise(rb_eMolbyError, "the bond index is empty");
6406                 numericArg = 0;
6407         }
6408         dp = (Double *)calloc(sizeof(Double), n);
6409         MoleculeGetBondOrders(mol, dp, ig);
6410         if (numericArg)
6411                 retval = rb_float_new(dp[0]);
6412         else {
6413                 retval = rb_ary_new();
6414                 for (i = 0; i < n; i++)
6415                         rb_ary_push(retval, rb_float_new(dp[i]));
6416         }
6417         free(dp);
6418         IntGroupRelease(ig);
6419         return retval;
6420 }
6421
6422 /*
6423  *  call-seq:
6424  *     bond_exist?(idx1, idx2) -> bool
6425  *
6426  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6427  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6428  */
6429 static VALUE
6430 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6431 {
6432         Molecule *mol;
6433         Int idx1, idx2, i;
6434         Atom *ap;
6435         Int *cp;
6436     Data_Get_Struct(self, Molecule, mol);
6437         idx1 = NUM2INT(rb_Integer(ival1));
6438         idx2 = NUM2INT(rb_Integer(ival2));
6439         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6440                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6441         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6442         cp = AtomConnectData(&ap->connect);
6443         for (i = 0; i < ap->connect.count; i++) {
6444                 if (cp[i] == idx2)
6445                         return Qtrue;
6446         }
6447         return Qfalse;
6448 }
6449
6450 /*
6451  *  call-seq:
6452  *     add_angle(n1, n2, n3)       -> Molecule
6453  *
6454  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6455  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6456  *  This operation is undoable.
6457  */
6458 static VALUE
6459 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6460 {
6461         Int n[4];
6462     Molecule *mol;
6463     Data_Get_Struct(self, Molecule, mol);
6464         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6465         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6466         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6467         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6468                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6469         n[3] = kInvalidIndex;
6470         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6471         return self;
6472 }
6473
6474 /*
6475  *  call-seq:
6476  *     remove_angle(n1, n2, n3)       -> Molecule
6477  *
6478  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6479  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6480  *  This operation is undoable.
6481  */
6482 static VALUE
6483 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6484 {
6485         Int n[4];
6486     Molecule *mol;
6487         IntGroup *ig;
6488     Data_Get_Struct(self, Molecule, mol);
6489         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6490         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6491         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6492         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6493                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6494         ig = IntGroupNewWithPoints(n[3], 1, -1);
6495         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6496         IntGroupRelease(ig);
6497         return self;
6498 }
6499
6500 /*
6501  *  call-seq:
6502  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6503  *
6504  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6505  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6506  *  This operation is undoable.
6507  */
6508 static VALUE
6509 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6510 {
6511         Int n[5];
6512     Molecule *mol;
6513     Data_Get_Struct(self, Molecule, mol);
6514         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6515         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6516         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6517         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6518         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6519                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6520         n[4] = kInvalidIndex;
6521         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6522         return self;
6523 }
6524
6525 /*
6526  *  call-seq:
6527  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6528  *
6529  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6530  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6531  *  This operation is undoable.
6532  */
6533 static VALUE
6534 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6535 {
6536         Int n[5];
6537     Molecule *mol;
6538         IntGroup *ig;
6539     Data_Get_Struct(self, Molecule, mol);
6540         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6541         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6542         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6543         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6544         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6545                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6546         ig = IntGroupNewWithPoints(n[4], 1, -1);
6547         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6548         IntGroupRelease(ig);
6549         return self;
6550 }
6551
6552 /*
6553  *  call-seq:
6554  *     add_improper(n1, n2, n3, n4)       -> Molecule
6555  *
6556  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6557  *  not automatically added when a new bond is created, so this method is more useful than
6558  *  the angle/dihedral counterpart.
6559  *  This operation is undoable.
6560  */
6561 static VALUE
6562 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6563 {
6564         Int n[5];
6565     Molecule *mol;
6566     Data_Get_Struct(self, Molecule, mol);
6567         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6568         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6569         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6570         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6571         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6572                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6573         n[4] = kInvalidIndex;
6574         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6575         return self;
6576 }
6577
6578 /*
6579  *  call-seq:
6580  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6581  *     remove_improper(intgroup)             -> Molecule
6582  *
6583  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6584  *  Returns self. Unlike angles and dihedrals, impropers are
6585  *  not automatically added when a new bond is created, so this method is more useful than
6586  *  the angle/dihedral counterpart.
6587  *  This operation is undoable.
6588  */
6589 static VALUE
6590 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6591 {
6592         Int n[5];
6593         VALUE v1, v2, v3, v4;
6594     Molecule *mol;
6595         IntGroup *ig;
6596     Data_Get_Struct(self, Molecule, mol);
6597         if (argc == 1) {
6598                 ig = IntGroupFromValue(argv[0]);
6599         } else {
6600                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6601                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6602                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6603                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6604                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6605                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6606                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6607                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6608         }
6609         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6610         IntGroupRelease(ig);
6611         return self;
6612 }
6613
6614 /*
6615  *  call-seq:
6616  *     assign_residue(group, res)       -> Molecule
6617  *
6618  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6619  *  or "resname.resno". When the residue number is not specified, the residue number of
6620  *  the first atom in the group is used.
6621  *  This operation is undoable.
6622  */
6623 static VALUE
6624 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6625 {
6626     Molecule *mol;
6627         IntGroup *ig;
6628         char *p, *pp, buf[16];
6629         Int resid, n;
6630         Atom *ap;
6631     Data_Get_Struct(self, Molecule, mol);
6632         
6633         /*  Parse the argument res  */
6634         if (FIXNUM_P(res)) {
6635                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6636                 resid = NUM2INT(res);
6637                 buf[0] = 0;
6638         } else {
6639                 p = StringValuePtr(res);
6640                 pp = strchr(p, '.');
6641                 if (pp != NULL) {
6642                         resid = atoi(pp + 1);
6643                         n = pp - p;
6644                 } else {
6645                         resid = -1;
6646                         n = strlen(p);
6647                 }
6648                 if (n > sizeof buf - 1)
6649                         n = sizeof buf - 1;
6650                 strncpy(buf, p, n);
6651                 buf[n] = 0;
6652         }
6653         ig = s_Molecule_AtomGroupFromValue(self, range);
6654         if (ig == NULL || IntGroupGetCount(ig) == 0)
6655                 return Qnil;
6656
6657         if (resid < 0) {
6658                 /*  Use the residue number of the first specified atom  */
6659                 n = IntGroupGetNthPoint(ig, 0);
6660                 if (n >= mol->natoms)
6661                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6662                 ap = ATOM_AT_INDEX(mol->atoms, n);
6663                 resid = ap->resSeq;
6664         }
6665         /*  Change the residue number  */
6666         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6667         /*  Change the residue name if necessary  */
6668         if (buf[0] != 0) {
6669         /*      Int seqs[2];
6670                 seqs[0] = resid;
6671                 seqs[1] = kInvalidIndex; */
6672                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6673         }
6674         IntGroupRelease(ig);
6675         return self;
6676 }
6677
6678 /*
6679  *  call-seq:
6680  *     offset_residue(group, offset)       -> Molecule
6681  *
6682  *  Offset the residue number of the specified atoms. If any of the residue number gets
6683  *  negative, then exception is thrown.
6684  *  This operation is undoable.
6685  */
6686 static VALUE
6687 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6688 {
6689     Molecule *mol;
6690         IntGroup *ig;
6691         int ofs, result;
6692     Data_Get_Struct(self, Molecule, mol);
6693         ig = s_Molecule_AtomGroupFromValue(self, range);
6694         ofs = NUM2INT(offset);
6695         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6696         if (result > 0)
6697                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6698         IntGroupRelease(ig);
6699         return self;
6700 }
6701
6702 /*
6703  *  call-seq:
6704  *     renumber_atoms(array)       -> IntGroup
6705  *
6706  *  Change the order of atoms so that the atoms specified in the array argument appear
6707  *  in this order from the top of the molecule. The atoms that are not included in array
6708  *  are placed after these atoms, and these atoms are returned as an intGroup.
6709  *  This operation is undoable.
6710  */
6711 static VALUE
6712 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6713 {
6714     Molecule *mol;
6715         Int *new2old;
6716         IntGroup *ig;
6717         int i, n;
6718         VALUE *valp, retval;
6719     Data_Get_Struct(self, Molecule, mol);
6720         if (TYPE(array) != T_ARRAY)
6721                 array = rb_funcall(array, rb_intern("to_a"), 0);
6722         n = RARRAY_LEN(array);
6723         valp = RARRAY_PTR(array);
6724         new2old = ALLOC_N(Int, n + 1);
6725         for (i = 0; i < n; i++)
6726                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6727         new2old[i] = kInvalidIndex;
6728         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6729         if (i == 1)
6730                 rb_raise(rb_eMolbyError, "Atom index out of range");
6731         else if (i == 2)
6732                 rb_raise(rb_eMolbyError, "Duplicate entry");
6733         else if (i == 3)
6734                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6735         retval = IntGroup_Alloc(rb_cIntGroup);
6736         Data_Get_Struct(retval, IntGroup, ig);
6737         if (mol->natoms > n)
6738                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6739         xfree(new2old);
6740         return retval;
6741 }
6742
6743 /*
6744  *  call-seq:
6745  *     set_atom_attr(index, key, value)
6746  *
6747  *  Set the atom attribute for the specified atom.
6748  *  This operation is undoable.
6749  */
6750 static VALUE
6751 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6752 {
6753         Molecule *mol;
6754         VALUE aref, oldval;
6755     Data_Get_Struct(self, Molecule, mol);
6756         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6757         oldval = s_AtomRef_GetAttr(aref, key);
6758         if (val == Qundef)
6759                 return oldval;
6760         s_AtomRef_SetAttr(aref, key, val);
6761         return val;
6762 }
6763
6764 /*
6765  *  call-seq:
6766  *     get_atom_attr(index, key)
6767  *
6768  *  Get the atom attribute for the specified atom.
6769  */
6770 static VALUE
6771 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6772 {
6773         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6774 }
6775
6776 #pragma mark ------ Undo Support ------
6777
6778 /*
6779  *  call-seq:
6780  *     register_undo(script, *args)
6781  *
6782  *  Register an undo operation with the current molecule.
6783  */
6784 static VALUE
6785 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6786 {
6787         Molecule *mol;
6788         VALUE script, args;
6789         MolAction *act;
6790     Data_Get_Struct(self, Molecule, mol);
6791         rb_scan_args(argc, argv, "1*", &script, &args);
6792         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6793         MolActionCallback_registerUndo(mol, act);
6794         return script;
6795 }
6796
6797 /*
6798  *  call-seq:
6799  *     undo_enabled? -> bool
6800  *
6801  *  Returns true if undo is enabled for this molecule; otherwise no.
6802  */
6803 static VALUE
6804 s_Molecule_UndoEnabled(VALUE self)
6805 {
6806     Molecule *mol;
6807     Data_Get_Struct(self, Molecule, mol);
6808         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6809                 return Qtrue;
6810         else return Qfalse;
6811 }
6812
6813 /*
6814  *  call-seq:
6815  *     undo_enabled = bool
6816  *
6817  *  Enable or disable undo.
6818  */
6819 static VALUE
6820 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6821 {
6822     Molecule *mol;
6823     Data_Get_Struct(self, Molecule, mol);
6824         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6825         return val;
6826 }
6827
6828 #pragma mark ------ Measure ------
6829
6830 static void
6831 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6832 {
6833         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6834                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6835                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6836                 case 0: break;
6837                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6838         }
6839 }
6840
6841 /*
6842  *  call-seq:
6843  *     center_of_mass(group = nil)       -> Vector3D
6844  *
6845  *  Calculate the center of mass for the given set of atoms. The argument
6846  *  group is null, then all atoms are considered.
6847  */
6848 static VALUE
6849 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6850 {
6851     Molecule *mol;
6852         VALUE group;
6853         IntGroup *ig;
6854         Vector v;
6855     Data_Get_Struct(self, Molecule, mol);
6856         rb_scan_args(argc, argv, "01", &group);
6857         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6858         s_Molecule_DoCenterOfMass(mol, &v, ig);
6859         if (ig != NULL)
6860                 IntGroupRelease(ig);
6861         return ValueFromVector(&v);
6862 }
6863
6864 /*
6865  *  call-seq:
6866  *     centralize(group = nil)       -> self
6867  *
6868  *  Translate the molecule so that the center of mass of the given group is located
6869  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6870  */
6871 static VALUE
6872 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6873 {
6874     Molecule *mol;
6875         VALUE group;
6876         IntGroup *ig;
6877         Vector v;
6878     Data_Get_Struct(self, Molecule, mol);
6879         rb_scan_args(argc, argv, "01", &group);
6880         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6881         s_Molecule_DoCenterOfMass(mol, &v, ig);
6882         if (ig != NULL)
6883                 IntGroupRelease(ig);
6884         v.x = -v.x;
6885         v.y = -v.y;
6886         v.z = -v.z;
6887         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6888         return self;
6889 }
6890
6891 /*
6892  *  call-seq:
6893  *     bounds(group = nil)       -> [min, max]
6894  *
6895  *  Calculate the boundary. The return value is an array of two Vector3D objects.
6896  */
6897 static VALUE
6898 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6899 {
6900     Molecule *mol;
6901         VALUE group;
6902         IntGroup *ig;
6903         Vector vmin, vmax;
6904         int n;
6905         Atom *ap;
6906     Data_Get_Struct(self, Molecule, mol);
6907         rb_scan_args(argc, argv, "01", &group);
6908         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6909         if (ig != NULL && IntGroupGetCount(ig) == 0)
6910                 rb_raise(rb_eMolbyError, "atom group is empty");
6911         vmin.x = vmin.y = vmin.z = 1e30;
6912         vmax.x = vmax.y = vmax.z = -1e30;
6913         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6914                 Vector r;
6915                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6916                         continue;
6917                 r = ap->r;
6918                 if (r.x < vmin.x)
6919                         vmin.x = r.x;
6920                 if (r.y < vmin.y)
6921                         vmin.y = r.y;
6922                 if (r.z < vmin.z)
6923                         vmin.z = r.z;
6924                 if (r.x > vmax.x)
6925                         vmax.x = r.x;
6926                 if (r.y > vmax.y)
6927                         vmax.y = r.y;
6928                 if (r.z > vmax.z)
6929                         vmax.z = r.z;
6930         }
6931         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6932 }
6933
6934 /*  Get atom position or a vector  */
6935 static void
6936 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6937 {
6938         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6939                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6940                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6941         } else {
6942                 VectorFromValue(val, vp);
6943         }
6944 }
6945
6946 /*
6947  *  call-seq:
6948  *     measure_bond(n1, n2)       -> Float
6949  *
6950  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
6951  *  or Vector3D values.
6952  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6953  */
6954 static VALUE
6955 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6956 {
6957     Molecule *mol;
6958         Vector v1, v2;
6959     Data_Get_Struct(self, Molecule, mol);
6960         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6961         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6962         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6963 }
6964
6965 /*
6966  *  call-seq:
6967  *     measure_angle(n1, n2, n3)       -> Float
6968  *
6969  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
6970  *  or Vector3D values. The return value is in degree.
6971  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6972  */
6973 static VALUE
6974 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6975 {
6976     Molecule *mol;
6977         Vector v1, v2, v3;
6978         Double d;
6979     Data_Get_Struct(self, Molecule, mol);
6980         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6981         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6982         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
6983         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6984         if (isnan(d))
6985                 return Qnil;  /*  Cannot define  */
6986         else return rb_float_new(d);
6987 }
6988
6989 /*
6990  *  call-seq:
6991  *     measure_dihedral(n1, n2, n3, n4)       -> Float
6992  *
6993  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
6994  *  or Vector3D values. The return value is in degree.
6995  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6996  */
6997 static VALUE
6998 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
6999 {
7000     Molecule *mol;
7001         Vector v1, v2, v3, v4;
7002         Double d;
7003     Data_Get_Struct(self, Molecule, mol);
7004         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7005         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7006         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7007         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
7008         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7009         if (isnan(d))
7010                 return Qnil;  /*  Cannot define  */
7011         else return rb_float_new(d);
7012 }
7013
7014 /*
7015  *  call-seq:
7016  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7017  *
7018  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7019  *  first and second atom in the pair should belong to group1 and group2, respectively.
7020  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7021  */
7022 static VALUE
7023 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7024 {
7025     Molecule *mol;
7026         VALUE limval, gval1, gval2, rval, igval;
7027         IntGroup *ig1, *ig2;
7028         IntGroupIterator iter1, iter2;
7029         Int npairs, *pairs;
7030         Int n[2], i;
7031         Double lim;
7032         Vector r1;
7033         Atom *ap1, *ap2;
7034         MDExclusion *exinfo;
7035         Int *exlist;
7036         
7037     Data_Get_Struct(self, Molecule, mol);
7038         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7039         lim = NUM2DBL(rb_Float(limval));
7040         if (lim <= 0.0)
7041                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7042         if (gval1 != Qnil)
7043                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7044         else
7045                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7046         if (gval2 != Qnil)
7047                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7048         else
7049                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7050         
7051         if (!RTEST(igval)) {
7052                 /*  Use the exclusion table in MDArena  */
7053                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7054                         VALUE mval = ValueFromMolecule(mol);
7055                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7056                 }
7057                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7058                 exlist = mol->arena->exlist;    
7059         } else {
7060                 exinfo = NULL;
7061                 exlist = NULL;
7062         }
7063         IntGroupIteratorInit(ig1, &iter1);
7064         IntGroupIteratorInit(ig2, &iter2);
7065         npairs = 0;
7066         pairs = NULL;
7067         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7068                 Int exn1, exn2;
7069                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7070                 r1 = ap1->r;
7071                 if (exinfo != NULL) {
7072                         exn1 = exinfo[n[0]].index1;
7073                         exn2 = exinfo[n[0] + 1].index1;
7074                 } else exn1 = exn2 = -1;
7075                 IntGroupIteratorReset(&iter2);
7076                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7077                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7078                         if (n[0] == n[1])
7079                                 continue;  /*  Same atom  */
7080                         if (exinfo != NULL) {
7081                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7082                                 for (i = exn1; i < exn2; i++) {
7083                                         if (exlist[i] == n[1])
7084                                                 break;
7085                                 }
7086                                 if (i < exn2)
7087                                         continue;  /*  Should be excluded  */
7088                         }
7089                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7090                                 /*  Is this pair already registered?  */
7091                                 Int *ip;
7092                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7093                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7094                                                 break;
7095                                 }
7096                                 if (i >= npairs) {
7097                                         /*  Not registered yet  */
7098                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7099                                 }
7100                         }
7101                 }
7102         }
7103         IntGroupIteratorRelease(&iter2);
7104         IntGroupIteratorRelease(&iter1);
7105         IntGroupRelease(ig2);
7106         IntGroupRelease(ig1);
7107         rval = rb_ary_new2(npairs);
7108         if (pairs != NULL) {
7109                 for (i = 0; i < npairs; i++) {
7110                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7111                 }
7112                 free(pairs);
7113         }
7114         return rval;
7115 }
7116
7117 /*
7118  *  call-seq:
7119  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7120  *
7121  *  Find atoms that are within the threshold distance from the given atom.
7122  *  (The atom argument can also be a vector, representing a cartesian coordinate. In that case, the van der Waals of the atom can also be specified.)
7123  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7124  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7125  *  If limit is not given, a default value of 1.2 is used.
7126  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7127  */
7128 static VALUE
7129 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7130 {
7131     Molecule *mol;
7132         VALUE aval, limval, radval;
7133         double limit, radius;
7134         Int n1, nbonds, *bonds, an;
7135         Vector v;
7136     Data_Get_Struct(self, Molecule, mol);
7137         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7138         if (rb_obj_is_kind_of(aval, rb_cVector3D) || rb_obj_is_kind_of(aval, rb_cLAMatrix) || rb_obj_is_kind_of(aval, rb_mEnumerable)) {
7139                 VectorFromValue(aval, &v);
7140                 if (radval == Qnil)
7141                         radius = gElementParameters[6].radius;
7142                 else
7143                         radius = NUM2DBL(rb_Float(radval));
7144                 n1 = mol->natoms;
7145         } else {
7146                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7147                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7148                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7149                 if (an >= 0 && an < gCountElementParameters)
7150                         radius = gElementParameters[an].radius;
7151                 else radius = gElementParameters[6].radius;
7152         }
7153         if (limval == Qnil)
7154                 limit = 1.2;
7155         else
7156                 limit = NUM2DBL(rb_Float(limval));
7157         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7158         bonds = NULL;
7159         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7160         aval = rb_ary_new();
7161         if (nbonds > 0) {
7162                 for (n1 = 0; n1 < nbonds; n1++)
7163                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7164                 free(bonds);
7165         }
7166         return aval;
7167 }
7168
7169 /*
7170  *  call-seq:
7171  *     guess_bonds(limit = 1.2)       -> Integer
7172  *
7173  *  Create bonds between atoms that are within the threshold distance.
7174  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7175  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7176  *  If limit is not given, a default value of 1.2 is used.
7177  *  The number of the newly created bonds is returned.
7178  *  This operation is undoable.
7179  */
7180 static VALUE
7181 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7182 {
7183     Molecule *mol;
7184         VALUE limval;
7185         double limit;
7186         Int nbonds, *bonds;
7187     Data_Get_Struct(self, Molecule, mol);
7188         rb_scan_args(argc, argv, "01", &limval);
7189         if (limval == Qnil)
7190                 limit = 1.2;
7191         else
7192                 limit = NUM2DBL(rb_Float(limval));
7193         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7194         if (nbonds > 0) {
7195                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7196                 free(bonds);
7197         }
7198         return INT2NUM(nbonds);
7199 }
7200
7201 #pragma mark ------ Cell and Symmetry ------
7202
7203 /*
7204  *  call-seq:
7205  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7206  *
7207  *  Returns the unit cell parameters. If cell is not set, returns nil.
7208  */
7209 static VALUE
7210 s_Molecule_Cell(VALUE self)
7211 {
7212     Molecule *mol;
7213         int i;
7214         VALUE val;
7215     Data_Get_Struct(self, Molecule, mol);
7216         if (mol->cell == NULL)
7217                 return Qnil;
7218         val = rb_ary_new2(6);
7219         for (i = 0; i < 6; i++)
7220                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7221         if (mol->cell->has_sigma) {
7222                 for (i = 0; i < 6; i++) {
7223                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7224                 }
7225         }
7226         return val;
7227 }
7228
7229 /*
7230  *  call-seq:
7231  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7232  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7233  *
7234  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7235  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7236  This operation is undoable.
7237  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7238  */
7239 static VALUE
7240 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7241 {
7242     Molecule *mol;
7243         VALUE val, cval;
7244         int i, convert_coord, n;
7245         double d[12];
7246     Data_Get_Struct(self, Molecule, mol);
7247         rb_scan_args(argc, argv, "11", &val, &cval);
7248         if (val == Qnil) {
7249                 n = 0;
7250         } else {
7251                 int len;
7252                 val = rb_ary_to_ary(val);
7253                 len = RARRAY_LEN(val);
7254                 if (len >= 12) {
7255                         n = 12;
7256                 } else if (len >= 6) {
7257                         n = 6;
7258                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7259                 for (i = 0; i < n; i++)
7260                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7261         }
7262         convert_coord = (RTEST(cval) ? 1 : 0);
7263         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7264         return val;
7265 }
7266
7267 /*
7268  *  call-seq:
7269  *     box -> [avec, bvec, cvec, origin, flags]
7270  *
7271  *  Get the unit cell information in the form of a periodic bounding box.
7272  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7273  *  Integers which define whether the system is periodic along the axis.
7274  *  If no unit cell is defined, nil is returned.
7275  */
7276 static VALUE
7277 s_Molecule_Box(VALUE self)
7278 {
7279     Molecule *mol;
7280         VALUE v[5], val;
7281     Data_Get_Struct(self, Molecule, mol);
7282         if (mol == NULL || mol->cell == NULL)
7283                 return Qnil;
7284         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7285         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7286         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7287         v[3] = ValueFromVector(&(mol->cell->origin));
7288         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7289         val = rb_ary_new4(5, v);
7290         return val;
7291 }
7292
7293 /*
7294  *  call-seq:
7295  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7296  *     set_box(d, origin = [0, 0, 0])
7297  *     set_box
7298  *
7299  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7300  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7301  as the box vector.
7302  Flags, if present, is a 3-member array of Integers defining whether the system is
7303  periodic along the axis.
7304  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7305  In the second form, an isotropic box with cell-length d is set.
7306  In the third form, the existing box is cleared.
7307  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7308  */
7309 static VALUE
7310 s_Molecule_SetBox(VALUE self, VALUE aval)
7311 {
7312     Molecule *mol;
7313         VALUE v[6];
7314         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7315         Vector vv[3];
7316         Vector origin = {0, 0, 0};
7317         char flags[3];
7318         Double d;
7319         int i, convertCoordinates = 0;
7320     Data_Get_Struct(self, Molecule, mol);
7321         if (aval == Qnil) {
7322                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7323                 return self;
7324         }
7325         aval = rb_ary_to_ary(aval);
7326         for (i = 0; i < 6; i++) {
7327                 if (i < RARRAY_LEN(aval))
7328                         v[i] = (RARRAY_PTR(aval))[i];
7329                 else v[i] = Qnil;
7330         }
7331         if (v[0] == Qnil) {
7332                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7333                 return self;
7334         }
7335         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7336                 d = NUM2DBL(rb_Float(v[0]));
7337                 for (i = 0; i < 3; i++)
7338                         VecScale(vv[i], ax[i], d);
7339                 if (v[1] != Qnil)
7340                         VectorFromValue(v[1], &origin);
7341                 flags[0] = flags[1] = flags[2] = 1;
7342         } else {
7343                 for (i = 0; i < 3; i++) {
7344                         if (v[i] == Qnil) {
7345                                 VecZero(vv[i]);
7346                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7347                                 d = NUM2DBL(rb_Float(v[i]));
7348                                 VecScale(vv[i], ax[i], d);
7349                         } else {
7350                                 VectorFromValue(v[i], &vv[i]);
7351                         }
7352                         flags[i] = (VecLength2(vv[i]) > 0.0);
7353                 }
7354                 if (v[3] != Qnil)
7355                         VectorFromValue(v[3], &origin);
7356                 if (v[4] != Qnil) {
7357                         for (i = 0; i < 3; i++) {
7358                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7359                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7360                         }
7361                 }
7362                 if (RTEST(v[5]))
7363                         convertCoordinates = 1;
7364         }
7365         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7366         return self;
7367 }
7368
7369 /*
7370  *  call-seq:
7371  *     cell_periodicity -> [n1, n2, n3]
7372  *
7373  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7374  *  nil is returned.
7375  */
7376 static VALUE
7377 s_Molecule_CellPeriodicity(VALUE self)
7378 {
7379     Molecule *mol;
7380     Data_Get_Struct(self, Molecule, mol);
7381         if (mol->cell == NULL)
7382                 return Qnil;
7383         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7384 }
7385
7386 /*
7387  *  call-seq:
7388  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7389  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7390  *
7391  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7392  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7393  *  If cell is not defined, exception is raised.
7394  *  This operation is undoable.
7395  */
7396 static VALUE
7397 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7398 {
7399     Molecule *mol;
7400         Int flag;
7401     Data_Get_Struct(self, Molecule, mol);
7402         if (mol->cell == NULL)
7403                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7404         if (arg == Qnil)
7405                 flag = 0;
7406         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7407                 flag = NUM2INT(rb_Integer(arg));
7408         else {
7409                 Int i;
7410                 VALUE arg0;
7411                 arg = rb_ary_to_ary(arg);
7412                 flag = 0;
7413                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7414                         arg0 = RARRAY_PTR(arg)[i];
7415                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7416                                 flag |= (1 << (2 - i));
7417                 }
7418         }
7419         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7420         return arg;
7421 }
7422
7423 /*
7424  *  call-seq:
7425  *     cell_flexibility -> bool
7426  *
7427  *  Returns the unit cell is flexible or not
7428  */
7429 static VALUE
7430 s_Molecule_CellFlexibility(VALUE self)
7431 {
7432         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7433         return Qtrue;
7434         /*    Molecule *mol;
7435          Data_Get_Struct(self, Molecule, mol);
7436          if (mol->cell == NULL)
7437          return Qfalse;
7438          if (mol->useFlexibleCell)
7439          return Qtrue;
7440          else return Qfalse; */
7441 }
7442
7443 /*
7444  *  call-seq:
7445  *     self.cell_flexibility = bool
7446  *     set_cell_flexibility(bool)
7447  *
7448  *  Change the unit cell is flexible or not
7449  */
7450 static VALUE
7451 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7452 {
7453         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7454         return self;
7455         /*    Molecule *mol;
7456          Data_Get_Struct(self, Molecule, mol);
7457          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7458          return self; */
7459 }
7460
7461 /*
7462  *  call-seq:
7463  *     cell_transform -> Transform
7464  *
7465  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7466  *  If cell is not defined, nil is returned.
7467  */
7468 static VALUE
7469 s_Molecule_CellTransform(VALUE self)
7470 {
7471     Molecule *mol;
7472     Data_Get_Struct(self, Molecule, mol);
7473         if (mol == NULL || mol->cell == NULL)
7474                 return Qnil;
7475         return ValueFromTransform(&(mol->cell->tr));
7476 }
7477
7478 /*
7479  *  call-seq:
7480  *     symmetry -> Array of Transforms
7481  *     symmetries -> Array of Transforms
7482  *
7483  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7484  *  returns an empty array.
7485  */
7486 static VALUE
7487 s_Molecule_Symmetry(VALUE self)
7488 {
7489     Molecule *mol;
7490         VALUE val;
7491         int i;
7492     Data_Get_Struct(self, Molecule, mol);
7493         if (mol->nsyms <= 0)
7494                 return rb_ary_new();
7495         val = rb_ary_new2(mol->nsyms);
7496         for (i = 0; i < mol->nsyms; i++) {
7497                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7498         }
7499         return val;
7500 }
7501
7502 /*
7503  *  call-seq:
7504  *     nsymmetries -> Integer
7505  *
7506  *  Get the number of currently defined symmetry operations.
7507  */
7508 static VALUE
7509 s_Molecule_Nsymmetries(VALUE self)
7510 {
7511     Molecule *mol;
7512     Data_Get_Struct(self, Molecule, mol);
7513         return INT2NUM(mol->nsyms);
7514 }
7515
7516 /*
7517  *  call-seq:
7518  *     add_symmetry(Transform) -> Integer
7519  *
7520  *  Add a new symmetry operation. If no symmetry operation is defined and the
7521  *  given argument is not an identity transform, then also add an identity
7522  *  transform at the index 0.
7523  *  Returns the total number of symmetries after operation.
7524  */
7525 static VALUE
7526 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7527 {
7528     Molecule *mol;
7529         Transform tr;
7530     Data_Get_Struct(self, Molecule, mol);
7531         TransformFromValue(trans, &tr);
7532         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7533         return INT2NUM(mol->nsyms);
7534 }
7535
7536 /*
7537  *  call-seq:
7538  *     remove_symmetry(count = nil) -> Integer
7539  *     remove_symmetries(count = nil) -> Integer
7540  *
7541  *  Remove the specified number of symmetry operations. The last added ones are removed
7542  *  first. If count is nil, then all symmetry operations are removed. Returns the
7543  *  number of leftover symmetries.
7544  */
7545 static VALUE
7546 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7547 {
7548     Molecule *mol;
7549         VALUE cval;
7550         int i, n;
7551     Data_Get_Struct(self, Molecule, mol);
7552         rb_scan_args(argc, argv, "01", &cval);
7553         if (cval == Qnil)
7554                 n = mol->nsyms - 1;
7555         else {
7556                 n = NUM2INT(rb_Integer(cval));
7557                 if (n < 0 || n > mol->nsyms)
7558                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7559                 if (n == mol->nsyms)
7560                         n = mol->nsyms - 1;
7561         }
7562         for (i = 0; i < n; i++)
7563                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7564         return INT2NUM(mol->nsyms);
7565 }
7566
7567 /*
7568  *  call-seq:
7569  *     wrap_unit_cell(group) -> Vector3D
7570  *
7571  *  Move the specified group so that the center of mass of the group is within the
7572  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7573  *  exception is raised.
7574  */
7575 static VALUE
7576 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7577 {
7578     Molecule *mol;
7579         IntGroup *ig;
7580         Vector v, cv, dv;
7581     Data_Get_Struct(self, Molecule, mol);
7582         if (mol->cell == NULL)
7583                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7584         ig = s_Molecule_AtomGroupFromValue(self, gval);
7585         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7586         TransformVec(&v, mol->cell->rtr, &cv);
7587         if (mol->cell->flags[0])
7588                 v.x -= floor(v.x);
7589         if (mol->cell->flags[1])
7590                 v.y -= floor(v.y);
7591         if (mol->cell->flags[2])
7592                 v.z -= floor(v.z);
7593         TransformVec(&dv, mol->cell->tr, &v);
7594         VecDec(dv, cv);
7595         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7596         IntGroupRelease(ig);
7597         return ValueFromVector(&dv);
7598 }
7599
7600 /*
7601  *  call-seq:
7602  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7603  *
7604  *  Expand the specified part of the molecule by the given symmetry operation.
7605  *  Returns the array of atom indices corresponding to the expanded atoms.
7606  *  If allow_overlap is true, then new atoms are created even when the
7607  *  coordinates coincide with the some other atom (special position) of the
7608  *  same element; otherwise, such atom will not be created and the index of the
7609  *  existing atom is given in the returned array.
7610  */
7611 static VALUE
7612 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7613 {
7614     Molecule *mol;
7615         VALUE gval, sval, xval, yval, zval, rval, oval;
7616         IntGroup *ig;
7617         Int n[4], allow_overlap;
7618         Int natoms;
7619         Int nidx, *idx;
7620         
7621     Data_Get_Struct(self, Molecule, mol);
7622         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7623         n[0] = NUM2INT(rb_Integer(sval));
7624         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7625         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7626         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7627         allow_overlap = (RTEST(oval) ? 1 : 0);
7628         ig = s_Molecule_AtomGroupFromValue(self, gval);
7629         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7630                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7631         natoms = mol->natoms;
7632         
7633         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7634         
7635         rval = rb_ary_new2(nidx);
7636         while (--nidx >= 0) {
7637                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7638         }
7639         /*      if (natoms == mol->natoms)
7640          rval = Qnil;
7641          else {
7642          rval = IntGroup_Alloc(rb_cIntGroup);
7643          Data_Get_Struct(rval, IntGroup, ig);
7644          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7645          } */
7646         return rval;
7647 }
7648
7649 /*
7650  *  call-seq:
7651  *     amend_by_symmetry(group = nil) -> IntGroup
7652  *
7653  *  Expand the specified part of the molecule by the given symmetry operation.
7654  *  Returns an IntGroup containing the added atoms.
7655  */
7656 static VALUE
7657 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7658 {
7659     Molecule *mol;
7660         IntGroup *ig, *ig2;
7661         VALUE rval, gval;
7662     Data_Get_Struct(self, Molecule, mol);
7663         rb_scan_args(argc, argv, "01", &gval);
7664         if (gval != Qnil)
7665                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7666         else ig = NULL;
7667         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7668         rval = ValueFromIntGroup(ig2);
7669         IntGroupRelease(ig2);
7670         return rval;
7671 }
7672
7673 #pragma mark ------ Transforms ------
7674
7675 /*
7676  *  call-seq:
7677  *     translate(vec, group = nil)       -> Molecule
7678  *
7679  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7680  *  This operation is undoable.
7681  */
7682 static VALUE
7683 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7684 {
7685     Molecule *mol;
7686         VALUE vec, group;
7687         Vector v;
7688         IntGroup *ig;
7689     Data_Get_Struct(self, Molecule, mol);
7690         rb_scan_args(argc, argv, "11", &vec, &group);
7691         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7692         VectorFromValue(vec, &v);
7693         //      MoleculeTranslate(mol, &v, ig);
7694         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7695         if (ig != NULL)
7696                 IntGroupRelease(ig);
7697         return self;
7698 }
7699
7700 /*
7701  *  call-seq:
7702  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7703  *
7704  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7705  *  If group is given, only atoms in the group are moved.
7706  *  This operation is undoable.
7707  */
7708 static VALUE
7709 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7710 {
7711     Molecule *mol;
7712         volatile VALUE aval, anval, cval, gval;
7713         Double angle;
7714         Vector av, cv;
7715         Transform tr;
7716         IntGroup *ig;
7717     Data_Get_Struct(self, Molecule, mol);
7718         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7719         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7720         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7721         VectorFromValue(aval, &av);
7722         if (NIL_P(cval))
7723                 cv.x = cv.y = cv.z = 0.0;
7724         else
7725                 VectorFromValue(cval, &cv);
7726         if (TransformForRotation(tr, &av, angle, &cv))
7727                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7728         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7729         if (ig != NULL)
7730                 IntGroupRelease(ig);
7731         return self;
7732 }
7733
7734 /*
7735  *  call-seq:
7736  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7737  *
7738  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7739  *  axis must not be a zero vector.
7740  *  If group is given, only atoms in the group are moved.
7741  *  This operation is undoable.
7742  */
7743 static VALUE
7744 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7745 {
7746     Molecule *mol;
7747         volatile VALUE aval, cval, gval;
7748         Vector av, cv;
7749         Transform tr;
7750         IntGroup *ig;
7751     Data_Get_Struct(self, Molecule, mol);
7752         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7753         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7754         VectorFromValue(aval, &av);
7755         if (NIL_P(cval))
7756                 cv.x = cv.y = cv.z = 0.0;
7757         else
7758                 VectorFromValue(cval, &cv);
7759         if (TransformForReflection(tr, &av, &cv))
7760                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7761         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7762         if (ig != NULL)
7763                 IntGroupRelease(ig);
7764         return self;
7765 }
7766
7767 /*
7768  *  call-seq:
7769  *     invert(center = [0,0,0], group = nil)       -> Molecule
7770  *
7771  *  Invert the molecule with the given center.
7772  *  If group is given, only atoms in the group are moved.
7773  *  This operation is undoable.
7774  */
7775 static VALUE
7776 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7777 {
7778         Molecule *mol;
7779         volatile VALUE cval, gval;
7780         Vector cv;
7781         Transform tr;
7782         IntGroup *ig;
7783     Data_Get_Struct(self, Molecule, mol);
7784         rb_scan_args(argc, argv, "02", &cval, &gval);
7785         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7786         if (NIL_P(cval))
7787                 cv.x = cv.y = cv.z = 0.0;
7788         else
7789                 VectorFromValue(cval, &cv);
7790         TransformForInversion(tr, &cv);
7791         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7792         if (ig != NULL)
7793                 IntGroupRelease(ig);
7794         return self;
7795 }
7796
7797 /*
7798  *  call-seq:
7799  *     transform(transform, group = nil)       -> Molecule
7800  *
7801  *  Transform the molecule by the given Transform object.
7802  *  If group is given, only atoms in the group are moved.
7803  *  This operation is undoable.
7804  */
7805 static VALUE
7806 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7807 {
7808     Molecule *mol;
7809         VALUE trans, group;
7810         Transform tr;
7811         IntGroup *ig;
7812     Data_Get_Struct(self, Molecule, mol);
7813         rb_scan_args(argc, argv, "11", &trans, &group);
7814         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7815         TransformFromValue(trans, &tr);
7816         /*      MoleculeTransform(mol, tr, ig); */
7817         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7818         if (ig != NULL)
7819                 IntGroupRelease(ig);
7820         return self;
7821 }
7822
7823 /*
7824  *  call-seq:
7825  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7826  *
7827  *  Get the transform corresponding to the symmetry operation. The symop can either be
7828  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7829  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7830  *  Otherwise, the returned transform is for fractional coordinates.
7831  *  Raises exception when no cell or no transform are defined.
7832  */
7833 static VALUE
7834 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7835 {
7836     Molecule *mol;
7837         VALUE sval, fval;
7838         Symop symop;
7839         Transform tr;
7840     Data_Get_Struct(self, Molecule, mol);
7841         if (mol->cell == NULL)
7842                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7843         if (mol->nsyms == 0)
7844                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7845         rb_scan_args(argc, argv, "11", &sval, &fval);
7846         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7847                 symop.sym = NUM2INT(rb_Integer(sval));
7848                 symop.dx = symop.dy = symop.dz = 0;
7849         } else {
7850                 sval = rb_ary_to_ary(sval);
7851                 if (RARRAY_LEN(sval) < 4)
7852                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7853                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7854                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7855                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7856                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7857         }
7858         if (symop.sym >= mol->nsyms)
7859                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7860         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7861         return ValueFromTransform(&tr);
7862 }
7863
7864 /*
7865  *  call-seq:
7866  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7867  *
7868  *  Get the symmetry operation corresponding to the given transform.
7869  *  If is_cartesian is true, the given transform is for cartesian coordinates.
7870  *  Otherwise, the given transform is for fractional coordinates.
7871  *  Raises exception when no cell or no transform are defined.
7872  */
7873 static VALUE
7874 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7875 {
7876     Molecule *mol;
7877         VALUE tval, fval;
7878         Symop symop;
7879         Transform tr;
7880         int n;
7881     Data_Get_Struct(self, Molecule, mol);
7882         if (mol->cell == NULL)
7883                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7884         if (mol->nsyms == 0)
7885                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7886         rb_scan_args(argc, argv, "11", &tval, &fval);
7887         TransformFromValue(tval, &tr);
7888         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7889         if (n == 0) {
7890                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7891         } else {
7892                 return Qnil;  /*  Not found  */
7893         }
7894 }
7895
7896 #pragma mark ------ Frames ------
7897
7898 /*
7899  *  call-seq:
7900  *     select_frame(index)
7901  *     frame = index
7902  *
7903  *  Select the specified frame. If successful, returns true, otherwise returns false.
7904  */
7905 static VALUE
7906 s_Molecule_SelectFrame(VALUE self, VALUE val)
7907 {
7908     Molecule *mol;
7909         int ival = NUM2INT(val);
7910     Data_Get_Struct(self, Molecule, mol);
7911         ival = MoleculeSelectFrame(mol, ival, 1);
7912         if (ival >= 0)
7913                 return Qtrue;
7914         else return Qfalse;
7915 }
7916
7917 /*
7918  *  call-seq:
7919  *     frame -> Integer
7920  *
7921  *  Get the current frame.
7922  */
7923 static VALUE
7924 s_Molecule_Frame(VALUE self)
7925 {
7926     Molecule *mol;
7927     Data_Get_Struct(self, Molecule, mol);
7928         return INT2NUM(mol->cframe);
7929 }
7930
7931 /*
7932  *  call-seq:
7933  *     nframes -> Integer
7934  *
7935  *  Get the number of frames.
7936  */
7937 static VALUE
7938 s_Molecule_Nframes(VALUE self)
7939 {
7940     Molecule *mol;
7941     Data_Get_Struct(self, Molecule, mol);
7942         return INT2NUM(MoleculeGetNumberOfFrames(mol));
7943 }
7944
7945 /*
7946  *  call-seq:
7947  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7948  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7949  *
7950  *  Insert new frames at the indices specified by the intGroup. If the first argument is
7951  *  an integer, a single new frame is inserted at that index. If the first argument is 
7952  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7953  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
7954  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
7955  *  to the new frame.
7956  *  Returns an intGroup representing the inserted frames if successful, nil if not.
7957  */
7958 static VALUE
7959 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7960 {
7961         VALUE val, coords, cells;
7962     Molecule *mol;
7963         IntGroup *ig;
7964         int count, ival, i, j, len, len_c, len2, nframes;
7965         VALUE *ptr, *ptr2;
7966         Vector *vp, *vp2;
7967     Data_Get_Struct(self, Molecule, mol);
7968         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7969         if (coords != Qnil) {
7970                 if (TYPE(coords) != T_ARRAY)
7971                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7972                 len = RARRAY_LEN(coords);
7973         } else len = 0;
7974         if (cells != Qnil) {
7975                 if (mol->cell == NULL)
7976                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7977                 if (TYPE(cells) != T_ARRAY)
7978                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7979                 len_c = RARRAY_LEN(cells);
7980         } else len_c = 0;
7981         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
7982         nframes = MoleculeGetNumberOfFrames(mol);
7983         if (val == Qnil) {
7984                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7985                 val = ValueFromIntGroup(ig);
7986         } else {
7987                 ig = IntGroupFromValue(val);
7988         }
7989         count = IntGroupGetCount(ig);  /*  Count is updated here  */
7990         vp = ALLOC_N(Vector, mol->natoms * count);
7991         if (cells != Qnil)
7992                 vp2 = ALLOC_N(Vector, 4 * count);
7993         else vp2 = NULL;
7994         if (len > 0) {
7995                 if (len < count)
7996                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7997                 ptr = RARRAY_PTR(coords);
7998                 for (i = 0; i < count; i++) {
7999                         if (TYPE(ptr[i]) != T_ARRAY)
8000                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8001                         len2 = RARRAY_LEN(ptr[i]);
8002                         if (len2 < mol->natoms)
8003                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8004                         ptr2 = RARRAY_PTR(ptr[i]);
8005                         for (j = 0; j < mol->natoms; j++)
8006                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8007                 }
8008         } else {
8009                 Atom *ap;
8010                 for (i = 0; i < count; i++) {
8011                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8012                                 vp[i * mol->natoms + j] = ap->r;
8013                         }
8014                 }
8015         }
8016         if (len_c > 0) {
8017                 if (len_c < count)
8018                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8019                 ptr = RARRAY_PTR(cells);
8020                 for (i = 0; i < count; i++) {
8021                         if (TYPE(ptr[i]) != T_ARRAY)
8022                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8023                         len2 = RARRAY_LEN(ptr[i]);
8024                         if (len2 < 4)
8025                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8026                         ptr2 = RARRAY_PTR(ptr[i]);
8027                         for (j = 0; j < 4; j++)
8028                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8029                 }
8030         }
8031         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8032         IntGroupRelease(ig);
8033         xfree(vp);
8034         if (vp2 != NULL)
8035                 xfree(vp2);
8036         return (ival >= 0 ? val : Qnil);
8037 }
8038
8039 /*
8040  *  call-seq:
8041  *     create_frame(coordinates = nil) -> Integer
8042  *     create_frames(coordinates = nil) -> Integer
8043  *
8044  *  Same as molecule.insert_frames(nil, coordinates).
8045  */
8046 static VALUE
8047 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8048 {
8049         VALUE vals[3];
8050         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8051         vals[0] = Qnil;
8052         return s_Molecule_InsertFrames(3, vals, self);
8053 }
8054
8055 /*
8056  *  call-seq:
8057  *     remove_frames(IntGroup, wantCoordinates = false)
8058  *
8059  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8060  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8061  *  removed frames is returned if operation is successful.
8062  */
8063 static VALUE
8064 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8065 {
8066         VALUE val, flag;
8067         VALUE retval;
8068     Molecule *mol;
8069         IntGroup *ig;
8070         int count;
8071     Data_Get_Struct(self, Molecule, mol);
8072         rb_scan_args(argc, argv, "11", &val, &flag);
8073         ig = IntGroupFromValue(val);
8074         count = IntGroupGetCount(ig);
8075         if (RTEST(flag)) {
8076                 /*  Create return value before removing frames  */
8077                 VALUE coords;
8078                 int i, j, n;
8079                 Atom *ap;
8080                 Vector v;
8081                 retval = rb_ary_new2(count);
8082                 for (i = 0; i < count; i++) {
8083                         n = IntGroupGetNthPoint(ig, i);
8084                         coords = rb_ary_new2(mol->natoms);
8085                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8086                                 if (n < ap->nframes && n != mol->cframe)
8087                                         v = ap->frames[n];
8088                                 else v = ap->r;
8089                                 rb_ary_push(coords, ValueFromVector(&v));
8090                         }
8091                         rb_ary_push(retval, coords);
8092                 }
8093         } else retval = Qtrue;
8094         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8095                 return retval;
8096         else return Qnil;
8097 }
8098
8099 /*
8100  *  call-seq:
8101  *     each_frame {|n| ...}
8102  *
8103  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8104  *  the frame number. After completion, the original frame number is restored.
8105  */
8106 static VALUE
8107 s_Molecule_EachFrame(VALUE self)
8108 {
8109         int i, cframe, nframes;
8110     Molecule *mol;
8111     Data_Get_Struct(self, Molecule, mol);
8112         cframe = mol->cframe;
8113         nframes = MoleculeGetNumberOfFrames(mol);
8114         if (nframes > 0) {
8115                 for (i = 0; i < nframes; i++) {
8116                         MoleculeSelectFrame(mol, i, 1);
8117                         rb_yield(INT2NUM(i));
8118                 }
8119                 MoleculeSelectFrame(mol, cframe, 1);
8120         }
8121     return self;
8122 }
8123
8124 /*
8125  *  call-seq:
8126  *     get_coord_from_frame(index, group = nil)
8127  *
8128  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8129  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8130  *  copied; now they are always copied)
8131  */
8132 static VALUE
8133 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8134 {
8135         Molecule *mol;
8136         VALUE ival, gval, cval;
8137         Int index, i, j, n, nn;
8138         IntGroup *ig;
8139         IntGroupIterator iter;
8140         Atom *ap;
8141         Vector *vp;
8142     Data_Get_Struct(self, Molecule, mol);
8143         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8144         if (argc == 3)
8145                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8146         index = NUM2INT(rb_Integer(ival));
8147         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8148                 if (n == 0)
8149                         rb_raise(rb_eMolbyError, "No frame is present");
8150                 else
8151                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8152         }
8153         if (gval == Qnil) {
8154                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8155         } else {
8156                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8157         }
8158         n = IntGroupGetCount(ig);
8159         if (n > 0) {
8160                 vp = (Vector *)calloc(sizeof(Vector), n);
8161                 IntGroupIteratorInit(ig, &iter);
8162                 j = 0;
8163                 nn = 0;
8164                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8165                         ap = ATOM_AT_INDEX(mol->atoms, i);
8166                         if (index < ap->nframes) {
8167                                 vp[j] = ap->frames[index];
8168                                 nn++;
8169                         } else {
8170                                 vp[j] = ap->r;
8171                         }
8172                         j++;
8173                 }
8174                 if (nn > 0)
8175                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8176                 free(vp);
8177                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8178                         vp = mol->frame_cells + index * 4;
8179                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8180                 }
8181                 IntGroupIteratorRelease(&iter);
8182         }
8183         /*  Copy the extra properties  */
8184         IntGroupRelease(ig);
8185         for (i = 0; i < mol->nmolprops; i++) {
8186                 Double *dp = (Double *)malloc(sizeof(Double));
8187                 ig = IntGroupNew();
8188                 IntGroupAdd(ig, mol->cframe, 1);
8189                 *dp = mol->molprops[i].propvals[index];
8190                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8191                 free(dp);
8192                 IntGroupRelease(ig);
8193         }
8194         
8195         return self;
8196 }
8197
8198 /*
8199  *  call-seq:
8200  *     reorder_frames(old_indices)
8201  *
8202  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8203  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8204  *  same as the old frames 2/0/1, respectively.
8205  *  The argument must have the same number of integers as the number of frames.
8206  */
8207 static VALUE
8208 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8209 {
8210         Molecule *mol;
8211         Int *ip, *ip2, i, n, nframes;
8212     Data_Get_Struct(self, Molecule, mol);
8213         aval = rb_ary_to_ary(aval);
8214         nframes = MoleculeGetNumberOfFrames(mol);
8215         if (RARRAY_LEN(aval) != nframes)
8216                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8217         ip2 = (Int *)calloc(sizeof(Int), nframes);
8218         ip = (Int *)calloc(sizeof(Int), nframes);
8219         for (i = 0; i < nframes; i++) {
8220                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8221                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8222                         free(ip2);
8223                         free(ip);
8224                         if (n < 0 || n >= nframes)
8225                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8226                         else
8227                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8228                 }
8229                 ip2[n] = 1;
8230                 ip[i] = n;
8231         }
8232         free(ip2);
8233         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8234         free(ip);
8235         return self;
8236 }
8237
8238 #pragma mark ------ Fragments ------
8239
8240 /*
8241  *  call-seq:
8242  *     fragment(n1, *exatoms)  -> IntGroup
8243  *     fragment(group, *exatoms)  -> IntGroup
8244  *
8245  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8246  *  those atoms will not be counted during the search.
8247  */
8248 static VALUE
8249 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8250 {
8251     Molecule *mol;
8252         IntGroup *baseg, *ig, *exatoms;
8253         int n;
8254         volatile VALUE nval, exval;
8255     Data_Get_Struct(self, Molecule, mol);
8256         rb_scan_args(argc, argv, "1*", &nval, &exval);
8257         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8258                 baseg = NULL;
8259                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8260         } else {
8261                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8262         }
8263         if (RARRAY_LEN(exval) == 0) {
8264                 exatoms = NULL;
8265         } else {
8266                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8267                 Data_Get_Struct(exval, IntGroup, exatoms);
8268         }
8269         if (baseg == NULL) {
8270                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8271         } else {
8272                 IntGroupIterator iter;
8273                 IntGroupIteratorInit(baseg, &iter);
8274                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8275                         ig = IntGroupNew();
8276                 } else {
8277                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8278                         if (ig != NULL) {
8279                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8280                                         IntGroup *subg;
8281                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8282                                         if (subg != NULL) {
8283                                                 IntGroupAddIntGroup(ig, subg);
8284                                                 IntGroupRelease(subg);
8285                                         }
8286                                 }
8287                         }
8288                 }
8289                 IntGroupIteratorRelease(&iter);
8290                 IntGroupRelease(baseg);
8291         }
8292         if (ig == NULL)
8293                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8294         nval = ValueFromIntGroup(ig);
8295         IntGroupRelease(ig);
8296         return nval;
8297 }
8298
8299 /*
8300  *  call-seq:
8301  *     fragments(exclude = nil)
8302  *
8303  *  Returns the fragments as an array of IntGroups. 
8304  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8305  *  in defining the fragment.
8306  */
8307 static VALUE
8308 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8309 {
8310     Molecule *mol;
8311         IntGroup *ag, *fg, *eg;
8312         VALUE gval, exval, retval;
8313     Data_Get_Struct(self, Molecule, mol);
8314         if (mol == NULL)
8315                 return Qnil;
8316         if (mol->natoms == 0)
8317                 return rb_ary_new();
8318         rb_scan_args(argc, argv, "01", &exval);
8319         if (exval == Qnil)
8320                 eg = NULL;
8321         else
8322                 eg = IntGroupFromValue(exval);
8323         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8324         if (eg != NULL)
8325                 IntGroupRemoveIntGroup(ag, eg);
8326         retval = rb_ary_new();
8327         while (IntGroupGetCount(ag) > 0) {
8328                 int n = IntGroupGetNthPoint(ag, 0);
8329                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8330                 if (fg == NULL)
8331                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8332                 gval = ValueFromIntGroup(fg);
8333                 rb_ary_push(retval, gval);
8334                 IntGroupRemoveIntGroup(ag, fg);
8335                 IntGroupRelease(fg);
8336         }
8337         IntGroupRelease(ag);
8338         if (eg != NULL)
8339                 IntGroupRelease(eg);
8340         return retval;
8341 }
8342
8343 /*
8344  *  call-seq:
8345  *     each_fragment(exclude = nil) {|group| ...}
8346  *
8347  *  Execute the block, with the IntGroup object for each fragment as the argument.
8348  *  Atoms or bonds should not be added or removed during the execution of the block.
8349  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8350  *  in defining the fragment.
8351  */
8352 static VALUE
8353 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8354 {
8355     Molecule *mol;
8356         IntGroup *ag, *fg, *eg;
8357         VALUE gval, exval;
8358     Data_Get_Struct(self, Molecule, mol);
8359         if (mol == NULL || mol->natoms == 0)
8360                 return self;
8361         rb_scan_args(argc, argv, "01", &exval);
8362         if (exval == Qnil)
8363                 eg = NULL;
8364         else
8365                 eg = IntGroupFromValue(exval);
8366         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8367         if (eg != NULL)
8368                 IntGroupRemoveIntGroup(ag, eg);
8369         while (IntGroupGetCount(ag) > 0) {
8370                 int n = IntGroupGetNthPoint(ag, 0);
8371                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8372                 if (fg == NULL)
8373                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8374                 gval = ValueFromIntGroup(fg);
8375                 rb_yield(gval);
8376                 IntGroupRemoveIntGroup(ag, fg);
8377                 IntGroupRelease(fg);
8378         }
8379         IntGroupRelease(ag);
8380         if (eg != NULL)
8381                 IntGroupRelease(eg);
8382         return self;
8383 }
8384
8385 /*
8386  *  call-seq:
8387  *     detachable?(group)  -> [n1, n2]
8388  *
8389  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8390  *  of the molecule via only one bond. If it is, then the indices of the atoms
8391  *  belonging to the bond is returned, the first element being the atom included
8392  *  in the fragment. Otherwise, Qnil is returned.
8393  */
8394 static VALUE
8395 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8396 {
8397         Molecule *mol;
8398         IntGroup *ig;
8399         int n1, n2;
8400         VALUE retval;
8401     Data_Get_Struct(self, Molecule, mol);
8402         ig = s_Molecule_AtomGroupFromValue(self, gval);
8403         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8404                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8405         } else retval = Qnil;
8406         IntGroupRelease(ig);
8407         return retval;
8408 }
8409
8410 /*
8411  *  call-seq:
8412  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8413  *
8414  *  Returns an array of bonds that connect an atom in the group and an atom out
8415  *  of the group. The first atom in the bond always belongs to the group. If no
8416  *  such bonds are present, an empty array is returned.
8417  */
8418 static VALUE
8419 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8420 {
8421         Molecule *mol;
8422         IntGroup *ig, *bg;
8423         VALUE gval, retval;
8424     Data_Get_Struct(self, Molecule, mol);
8425         rb_scan_args(argc, argv, "01", &gval);
8426         if (gval == Qnil) {
8427                 ig = MoleculeGetSelection(mol);
8428                 if (ig != NULL)
8429                         IntGroupRetain(ig);
8430         } else {
8431                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8432         }
8433         retval = rb_ary_new();
8434         if (ig == NULL)
8435                 return retval;
8436         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8437         if (bg != NULL) {
8438                 IntGroupIterator iter;
8439                 Int i;
8440                 IntGroupIteratorInit(bg, &iter);
8441                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8442                         /*  The atoms at the border  */
8443                         Int n1, n2;
8444                         n1 = mol->bonds[i * 2];
8445                         n2 = mol->bonds[i * 2 + 1];
8446                         if (IntGroupLookupPoint(ig, n1) < 0) {
8447                                 int w = n1;
8448                                 n1 = n2;
8449                                 n2 = w;
8450                                 if (IntGroupLookupPoint(ig, n1) < 0)
8451                                         continue;  /*  Actually this is an internal error  */
8452                         }
8453                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8454                 }
8455                 IntGroupIteratorRelease(&iter);
8456         }
8457         IntGroupRelease(bg);
8458         IntGroupRelease(ig);
8459         return retval;
8460 }
8461
8462 /*  Calculate the transform that moves the current coordinates to the reference
8463  coordinates with least displacements.   */
8464 static Double
8465 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8466 {
8467         Atom *ap, *ap1;
8468         Int natoms, nn;
8469         Vector org1, org2;
8470         Int i, in, j, k;
8471         Double w, w1;
8472         Mat33 r, q, u;
8473         Double eigen_val[3];
8474         Vector eigen_vec[3];
8475         Vector s[3];
8476         IntGroupIterator iter;
8477
8478         natoms = mol->natoms;
8479         ap = mol->atoms;
8480         IntGroupIteratorInit(ig, &iter);
8481         
8482         /*  Calculate the weighted center  */
8483         VecZero(org1);
8484         VecZero(org2);
8485         w = 0.0;
8486         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8487                 ap1 = ATOM_AT_INDEX(ap, in);
8488                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8489                 VecScaleInc(org1, ap1->r, w1);
8490                 VecScaleInc(org2, ref[i], w1);
8491                 w += w1;
8492         }
8493         w = 1.0 / w;
8494         VecScaleSelf(org1, w);
8495         VecScaleSelf(org2, w);
8496
8497     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8498     /*  Matrix to diagonalize = R * tR    */
8499         memset(r, 0, sizeof(Mat33));
8500         memset(q, 0, sizeof(Mat33));
8501         memset(u, 0, sizeof(Mat33));
8502         nn = 0;
8503         IntGroupIteratorReset(&iter);
8504         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8505                 Vector v1, v2;
8506                 ap1 = ATOM_AT_INDEX(ap, in);
8507                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8508                 w1 *= w1;
8509                 VecSub(v1, ap1->r, org1);
8510                 VecSub(v2, ref[i], org2);
8511                 r[0] += w1 * v1.x * v2.x;
8512                 r[1] += w1 * v1.y * v2.x;
8513                 r[2] += w1 * v1.z * v2.x;
8514                 r[3] += w1 * v1.x * v2.y;
8515                 r[4] += w1 * v1.y * v2.y;
8516                 r[5] += w1 * v1.z * v2.y;
8517                 r[6] += w1 * v1.x * v2.z;
8518                 r[7] += w1 * v1.y * v2.z;
8519                 r[8] += w1 * v1.z * v2.z;
8520                 nn++;
8521         }
8522         for (i = 0; i < 9; i++)
8523                 r[i] /= (nn * nn);
8524         for (i = 0; i < 3; i++) {
8525                 for (j = 0; j < 3; j++) {
8526                         for (k = 0; k < 3; k++) {
8527                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8528                         }
8529                 }
8530         }
8531         
8532         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8533                 IntGroupIteratorRelease(&iter);
8534                 return -1.0;  /*  Cannot determine the eigenvector  */
8535         }
8536
8537     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8538     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8539         MatrixTranspose(r, r);
8540         for (i = 0; i < 3; i++) {
8541                 MatrixVec(&s[i], r, &eigen_vec[i]);
8542                 w1 = 1.0 / sqrt(eigen_val[i]);
8543                 VecScaleSelf(s[i], w1);
8544         }
8545         for (k = 0; k < 3; k++) {
8546                 u[0] += s[k].x * eigen_vec[k].x;
8547                 u[1] += s[k].y * eigen_vec[k].x;
8548                 u[2] += s[k].z * eigen_vec[k].x;
8549                 u[3] += s[k].x * eigen_vec[k].y;
8550                 u[4] += s[k].y * eigen_vec[k].y;
8551                 u[5] += s[k].z * eigen_vec[k].y;
8552                 u[6] += s[k].x * eigen_vec[k].z;
8553                 u[7] += s[k].y * eigen_vec[k].z;
8554                 u[8] += s[k].z * eigen_vec[k].z;
8555         }
8556         
8557         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8558         MatrixVec(&org1, u, &org1);
8559         VecDec(org2, org1);
8560         for (i = 0; i < 9; i++)
8561                 trans[i] = u[i];
8562         trans[9] = org2.x;
8563         trans[10] = org2.y;
8564         trans[11] = org2.z;
8565         
8566         /*  Calculate rmsd  */
8567         IntGroupIteratorReset(&iter);
8568         w = 0.0;
8569         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8570                 Vector tv;
8571                 ap1 = ATOM_AT_INDEX(ap, in);
8572                 TransformVec(&tv, trans, &ap1->r);
8573                 VecDec(tv, ref[i]);
8574                 w += VecLength2(tv);
8575         }
8576         w = sqrt(w / nn);
8577         IntGroupIteratorRelease(&iter);
8578         return w;
8579 }
8580
8581 /*
8582  *  call-seq:
8583  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8584  *
8585  *  Calculate the transform to fit the given group to the set of reference coordinates.
8586  *  The reference coordinates ref is given as either a frame number, an array of
8587  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8588  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8589  *  Return values are the transform (that converts the present coordinates to the
8590  *  target coordinates) and root mean square deviation (without weight).
8591  */
8592 static VALUE
8593 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8594 {
8595         Molecule *mol;
8596         Atom *ap;
8597         VALUE gval, rval, wval;
8598         IntGroup *ig;
8599         IntGroupIterator iter;
8600         int nn, errno, i, j, in, status;
8601         Vector *ref;
8602         Double *weights, dval[3];
8603         Transform tr;
8604
8605         Data_Get_Struct(self, Molecule, mol);
8606         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8607         if (gval == Qnil)
8608                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8609         else
8610                 ig = IntGroupFromValue(gval);
8611         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8612                 IntGroupRelease(ig);
8613                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8614         }
8615         ref = (Vector *)calloc(sizeof(Vector), nn);
8616         weights = (Double *)calloc(sizeof(Double), nn);
8617         IntGroupIteratorInit(ig, &iter);
8618         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8619                 int fn = NUM2INT(rb_Integer(rval));
8620                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8621                         errno = 1;
8622                         status = fn;
8623                         goto err;
8624                 }
8625                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8626                         ap = ATOM_AT_INDEX(mol->atoms, in);
8627                         if (fn < ap->nframes)
8628                                 ref[i] = ap->frames[fn];
8629                         else ref[i] = ap->r;
8630                 }
8631         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8632                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8633                 if (m->row * m->column < nn * 3) {
8634                         errno = 2;
8635                         goto err;
8636                 }
8637                 for (i = 0; i < nn; i++) {
8638                         ref[i].x = m->data[i * 3];
8639                         ref[i].y = m->data[i * 3 + 1];
8640                         ref[i].z = m->data[i * 3 + 2];
8641                 }
8642         } else {
8643                 VALUE aval;
8644                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8645                 if (status != 0) {
8646                         errno = 3;
8647                         goto err;
8648                 }
8649                 if (RARRAY_LEN(rval) < nn) {
8650                         errno = 2;
8651                         goto err;
8652                 }
8653                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8654                         /*  Array of 3*nn numbers  */
8655                         if (RARRAY_LEN(rval) < nn * 3) {
8656                                 errno = 2;
8657                                 goto err;
8658                         }
8659                         for (i = 0; i < nn; i++) {
8660                                 for (j = 0; j < 3; j++) {
8661                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8662                                         if (status != 0) {
8663                                                 errno = 3;
8664                                                 goto err;
8665                                         }
8666                                         dval[j] = NUM2DBL(aval);
8667                                 }
8668                                 ref[i].x = dval[0];
8669                                 ref[i].y = dval[1];
8670                                 ref[i].z = dval[2];
8671                         }
8672                 } else {
8673                         /*  Array of nn Vector3Ds or Arrays  */
8674                         for (i = 0; i < nn; i++) {
8675                                 aval = (RARRAY_PTR(rval))[i];
8676                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8677                                         VectorFromValue(aval, &ref[i]);
8678                                 } else {
8679                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8680                                         if (status != 0) {
8681                                                 errno = 3;
8682                                                 goto err;
8683                                         }
8684                                         if (RARRAY_LEN(aval) < 3) {
8685                                                 errno = 4;
8686                                                 status = i;
8687                                                 goto err;
8688                                         }
8689                                         for (j = 0; j < 3; j++) {
8690                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8691                                                 if (status != 0) {
8692                                                         errno = 3;
8693                                                         goto err;
8694                                                 }
8695                                                 dval[j] = NUM2DBL(aaval);
8696                                         }
8697                                         ref[i].x = dval[0];
8698                                         ref[i].y = dval[1];
8699                                         ref[i].z = dval[2];
8700                                 }
8701                         }
8702                 }
8703         }
8704         if (wval == Qnil) {
8705                 /*  Use atomic weights  */
8706                 IntGroupIteratorReset(&iter);
8707                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8708                         ap = ATOM_AT_INDEX(mol->atoms, in);
8709                         weights[i] = ap->weight;
8710                 }
8711         } else {
8712                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8713                 if (status != 0) {
8714                         errno = 3;
8715                         goto err;
8716                 }
8717                 if (RARRAY_LEN(wval) < nn) {
8718                         errno = 5;
8719                         goto err;
8720                 }
8721                 for (i = 0; i < nn; i++) {
8722                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8723                         if (status != 0) {
8724                                 errno = 3;
8725                                 goto err;
8726                         }
8727                         weights[i] = NUM2DBL(wwval);
8728                 }
8729         }
8730         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8731         if (dval[0] < 0) {
8732                 errno = 6;
8733                 goto err;
8734         }
8735         errno = 0;
8736 err:
8737         IntGroupIteratorRelease(&iter);
8738         free(ref);
8739         free(weights);
8740         if (errno == 0) {
8741                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8742         } else if (errno == 1) {
8743                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8744         } else if (errno == 2) {
8745                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8746         } else if (errno == 3) {
8747                 rb_jump_tag(status);
8748         } else if (errno == 4) {
8749                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8750         } else if (errno == 5) {
8751                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8752         } else if (errno == 6) {
8753                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8754         }
8755         return Qnil;  /*  Not reached  */
8756 }
8757
8758 #pragma mark ------ Screen Display ------
8759
8760 /*
8761  *  call-seq:
8762  *     display
8763  *
8764  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8765  */
8766 static VALUE
8767 s_Molecule_Display(VALUE self)
8768 {
8769     Molecule *mol;
8770     Data_Get_Struct(self, Molecule, mol);
8771         if (mol->mview != NULL)
8772                 MainViewCallback_display(mol->mview);
8773         return Qnil;
8774 }
8775
8776 /*
8777  *  call-seq:
8778  *     make_front
8779  *
8780  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8781  */
8782 static VALUE
8783 s_Molecule_MakeFront(VALUE self)
8784 {
8785     Molecule *mol;
8786     Data_Get_Struct(self, Molecule, mol);
8787         if (mol->mview != NULL)
8788                 MainViewCallback_makeFront(mol->mview);
8789         return Qnil;
8790 }
8791
8792 /*
8793  *  call-seq:
8794  *     update_enabled? -> bool
8795  *
8796  *  Returns true if screen update is enabled; otherwise no.
8797  */
8798 static VALUE
8799 s_Molecule_UpdateEnabled(VALUE self)
8800 {
8801     Molecule *mol;
8802     Data_Get_Struct(self, Molecule, mol);
8803         if (mol->mview != NULL && !mol->mview->freezeScreen)
8804                 return Qtrue;
8805         else return Qfalse;
8806 }
8807
8808 /*
8809  *  call-seq:
8810  *     update_enabled = bool
8811  *
8812  *  Enable or disable screen update. This is effective for automatic update on modification.
8813  *  Explicit call to molecule.display() always updates the screen.
8814  */
8815 static VALUE
8816 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8817 {
8818     Molecule *mol;
8819     Data_Get_Struct(self, Molecule, mol);
8820         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8821         if (mol->mview != NULL)
8822                 mol->mview->freezeScreen = (val == Qfalse);
8823         else val = Qfalse;
8824         return val;
8825 }
8826
8827 /*
8828  *  call-seq:
8829  *     show_unitcell
8830  *     show_unitcell(bool)
8831  *     show_unitcell = bool
8832  *
8833  *  Set the flag whether to show the unit cell. If no argument is given, the
8834  *  current flag is returned.
8835  */
8836 static VALUE
8837 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8838 {
8839     Molecule *mol;
8840     Data_Get_Struct(self, Molecule, mol);
8841         if (mol->mview == NULL)
8842                 return Qnil;
8843         if (argc > 0) {
8844                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8845                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8846         }
8847         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8848 }
8849
8850 /*
8851  *  call-seq:
8852  *     show_hydrogens
8853  *     show_hydrogens(bool)
8854  *     show_hydrogens = bool
8855  *
8856  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8857  *  current flag is returned.
8858  */
8859 static VALUE
8860 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8861 {
8862     Molecule *mol;
8863     Data_Get_Struct(self, Molecule, mol);
8864         if (mol->mview == NULL)
8865                 return Qnil;
8866         if (argc > 0) {
8867                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8868                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8869         }
8870         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8871 }
8872
8873 /*
8874  *  call-seq:
8875  *     show_dummy_atoms
8876  *     show_dummy_atoms(bool)
8877  *     show_dummy_atoms = bool
8878  *
8879  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8880  *  current flag is returned.
8881  */
8882 static VALUE
8883 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8884 {
8885     Molecule *mol;
8886     Data_Get_Struct(self, Molecule, mol);
8887         if (mol->mview == NULL)
8888                 return Qnil;
8889         if (argc > 0) {
8890                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8891                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8892         }
8893         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8894 }
8895
8896 /*
8897  *  call-seq:
8898  *     show_expanded
8899  *     show_expanded(bool)
8900  *     show_expanded = bool
8901  *
8902  *  Set the flag whether to show the expanded atoms. If no argument is given, the
8903  *  current flag is returned.
8904  */
8905 static VALUE
8906 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8907 {
8908     Molecule *mol;
8909     Data_Get_Struct(self, Molecule, mol);
8910         if (mol->mview == NULL)
8911                 return Qnil;
8912         if (argc > 0) {
8913                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8914                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8915         }
8916         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8917 }
8918
8919 /*
8920  *  call-seq:
8921  *     show_ellipsoids
8922  *     show_ellipsoids(bool)
8923  *     show_ellipsoids = bool
8924  *
8925  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8926  *  current flag is returned.
8927  */
8928 static VALUE
8929 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8930 {
8931     Molecule *mol;
8932     Data_Get_Struct(self, Molecule, mol);
8933         if (mol->mview == NULL)
8934                 return Qnil;
8935         if (argc > 0) {
8936                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8937                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8938         }
8939         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8940 }
8941
8942 /*
8943  *  call-seq:
8944  *     is_atom_visible(index)  -> Boolean
8945  *
8946  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8947  *  as well as the molecule attributes (showHydrogens, etc.)
8948  */
8949 static VALUE
8950 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8951 {
8952         Molecule *mol;
8953         Int idx;
8954         Atom *ap;
8955     Data_Get_Struct(self, Molecule, mol);
8956         idx = s_Molecule_AtomIndexFromValue(mol, ival);
8957         if (idx < 0 || idx >= mol->natoms)
8958                 return Qnil;
8959         ap = ATOM_AT_INDEX(mol->atoms, idx);
8960         if (mol->mview != NULL) {
8961                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8962                         return Qfalse;
8963                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8964                         return Qfalse;
8965                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8966                         return Qfalse;
8967         }
8968         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8969 }
8970
8971 /*
8972  *  call-seq:
8973  *     hidden_atoms       -> IntGroup
8974  *
8975  *  Returns the currently hidden atoms.
8976  */
8977 static VALUE
8978 s_Molecule_HiddenAtoms(VALUE self)
8979 {
8980         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8981         return Qnil;  /*  Not reached  */
8982 }
8983
8984 /*
8985  *  call-seq:
8986  *     set_hidden_atoms(IntGroup)
8987  *     self.hidden_atoms = IntGroup
8988  *
8989  *  Hide the specified atoms. This operation is _not_ undoable.
8990  */
8991 static VALUE
8992 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8993 {
8994         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8995         return Qnil;  /*  Not reached  */
8996 }
8997
8998 /*
8999  *  call-seq:
9000  *     show_graphite -> Integer
9001  *     show_graphite = Integer
9002  *     show_graphite = boolean
9003  *
9004  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9005  *  number of rings to display for each direction.
9006  *  If the argument is boolean, only the show/hide flag is set.
9007  */
9008 static VALUE
9009 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9010 {
9011     Molecule *mol;
9012     Data_Get_Struct(self, Molecule, mol);
9013         if (mol->mview == NULL)
9014                 return Qnil;
9015         if (argc > 0) {
9016                 if (argv[0] == Qnil || argv[0] == Qfalse)
9017                         mol->mview->showGraphiteFlag = 0;
9018                 else if (argv[0] == Qtrue)
9019                         mol->mview->showGraphiteFlag = 1;
9020                 else {
9021                         int n = NUM2INT(rb_Integer(argv[0]));
9022                         if (n < 0)
9023                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9024                         mol->mview->showGraphite = n;
9025                 }
9026                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9027         }
9028         return INT2NUM(mol->mview->showGraphite);
9029 }
9030
9031 /*
9032  *  call-seq:
9033  *     show_graphite? -> boolean
9034  *
9035  *  Return whether the graphite is set visible or not.
9036 */
9037 static VALUE
9038 s_Molecule_ShowGraphiteFlag(VALUE self)
9039 {
9040     Molecule *mol;
9041     Data_Get_Struct(self, Molecule, mol);
9042         if (mol->mview == NULL)
9043                 return Qnil;
9044         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9045 }
9046         
9047 /*
9048  *  call-seq:
9049  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9050  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9051  *     show_periodic_image = boolean
9052  *
9053  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9054  *  set but no visual effects are observed.
9055  *  If the argument is boolean, only the show/hide flag is modified.
9056  */
9057 static VALUE
9058 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9059 {
9060     Molecule *mol;
9061         VALUE val;
9062         int ival[6];
9063         int i;
9064     Data_Get_Struct(self, Molecule, mol);
9065         if (mol->mview == NULL)
9066                 return Qnil;
9067         rb_scan_args(argc, argv, "01", &val);
9068         if (argc > 0) {
9069                 /*  Change current settings  */
9070                 if (val == Qnil || val == Qfalse)
9071                         mol->mview->showPeriodicImageFlag = 0;
9072                 else if (val == Qtrue)
9073                         mol->mview->showPeriodicImageFlag = 1;
9074                 else {
9075                         val = rb_ary_to_ary(val);
9076                         for (i = 0; i < 6; i++) {
9077                                 if (i < RARRAY_LEN(val))
9078                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9079                         }
9080                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9081                                 rb_raise(rb_eMolbyError, "bad arguments");
9082                         for (i = 0; i < 6; i++)
9083                                 mol->mview->showPeriodicImage[i] = ival[i];
9084                 }
9085                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9086         }
9087         val = rb_ary_new();
9088         for (i = 0; i < 6; i++)
9089                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9090         return val;
9091 }
9092
9093 /*
9094  *  call-seq:
9095  *     show_periodic_image? -> boolean
9096  *
9097  *  Return whether the periodic images are set to visible or not. This flag is
9098  *  independent from the show_periodic_image settings.
9099  */
9100 static VALUE
9101 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9102 {
9103     Molecule *mol;
9104     Data_Get_Struct(self, Molecule, mol);
9105         if (mol->mview == NULL)
9106                 return Qnil;
9107         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9108 }
9109
9110 /*
9111  *  call-seq:
9112  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9113  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9114  *     show_rotation_center = boolean
9115  *
9116  *  Set to show the rotation center of the screen.
9117  *  If the argument is boolean, only the show/hide flag is modified.
9118  */
9119 static VALUE
9120 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9121 {
9122     Molecule *mol;
9123     Data_Get_Struct(self, Molecule, mol);
9124         if (mol->mview == NULL)
9125                 return Qnil;
9126         if (argc > 0) {
9127                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9128                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9129         }
9130         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9131 }
9132
9133 /*
9134  *  call-seq:
9135  *     line_mode
9136  *     line_mode(bool)
9137  *     line_mode = bool
9138  *
9139  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9140  *  current flag is returned.
9141  */
9142 static VALUE
9143 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9144 {
9145     Molecule *mol;
9146     Data_Get_Struct(self, Molecule, mol);
9147         if (mol->mview == NULL)
9148                 return Qnil;
9149         if (argc > 0) {
9150                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9151                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9152         }
9153         return (mol->mview->lineMode ? Qtrue : Qfalse);
9154 }
9155
9156 /*
9157  *  call-seq:
9158  *     atom_radius = float
9159  *     atom_radius
9160  *
9161  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9162  *  (Default = 0.4)
9163  *  If no argument is given, the current value is returned.
9164  */
9165 static VALUE
9166 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9167 {
9168     Molecule *mol;
9169     Data_Get_Struct(self, Molecule, mol);
9170         if (mol->mview == NULL)
9171                 return Qnil;
9172         if (argc > 0) {
9173                 double rad = NUM2DBL(rb_Float(argv[0]));
9174                 if (rad > 0.0) {
9175                         mol->mview->atomRadius = rad;
9176                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9177                 }
9178                 return argv[0];
9179         }
9180         return rb_float_new(mol->mview->atomRadius);
9181 }
9182
9183 /*
9184  *  call-seq:
9185  *     bond_radius = float
9186  *     bond_radius
9187  *
9188  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9189  *  If no argument is given, the current value is returned.
9190  */
9191 static VALUE
9192 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9193 {
9194     Molecule *mol;
9195     Data_Get_Struct(self, Molecule, mol);
9196         if (mol->mview == NULL)
9197                 return Qnil;
9198         if (argc > 0) {
9199                 double rad = NUM2DBL(rb_Float(argv[0]));
9200                 if (rad > 0.0) {
9201                         mol->mview->bondRadius = rad;
9202                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9203                 }
9204                 return argv[0];
9205         }
9206         return rb_float_new(mol->mview->bondRadius);
9207 }
9208
9209 /*
9210  *  call-seq:
9211  *     atom_resolution = integer
9212  *     atom_resolution
9213  *
9214  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9215  *  (Default = 12; minimum = 6)
9216  *  If no argument is given, the current value is returned.
9217  */
9218 static VALUE
9219 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9220 {
9221     Molecule *mol;
9222     Data_Get_Struct(self, Molecule, mol);
9223         if (mol->mview == NULL)
9224                 return Qnil;
9225         if (argc > 0) {
9226                 int res = NUM2INT(rb_Integer(argv[0]));
9227                 if (res < 6)
9228                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9229                 mol->mview->atomResolution = res;
9230                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9231                 return INT2NUM(res);
9232         }
9233         return INT2NUM(mol->mview->atomResolution);
9234 }
9235
9236 /*
9237  *  call-seq:
9238  *     bond_resolution = integer
9239  *     bond_resolution
9240  *
9241  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9242  *  (Default = 8; minimum = 4)
9243  *  If no argument is given, the current value is returned.
9244  */
9245 static VALUE
9246 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9247 {
9248     Molecule *mol;
9249     Data_Get_Struct(self, Molecule, mol);
9250         if (mol->mview == NULL)
9251                 return Qnil;
9252         if (argc > 0) {
9253                 int res = NUM2INT(rb_Integer(argv[0]));
9254                 if (res < 4)
9255                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9256                 mol->mview->bondResolution = res;
9257                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9258                 return INT2NUM(res);
9259         }
9260         return INT2NUM(mol->mview->bondResolution);
9261 }
9262
9263 /*
9264  *  call-seq:
9265  *     resize_to_fit
9266  *
9267  *  Resize the model drawing to fit in the window.
9268  */
9269 static VALUE
9270 s_Molecule_ResizeToFit(VALUE self)
9271 {
9272     Molecule *mol;
9273     Data_Get_Struct(self, Molecule, mol);
9274         if (mol->mview != NULL)
9275                 MainView_resizeToFit(mol->mview);
9276         return self;    
9277 }
9278
9279 /*
9280  *  call-seq:
9281  *     get_view_rotation -> [[ax, ay, az], angle]
9282  *
9283  *  Get the current rotation for the view. Angle is in degree, not radian.
9284  */
9285 static VALUE
9286 s_Molecule_GetViewRotation(VALUE self)
9287 {
9288     Molecule *mol;
9289         double f[4];
9290         Vector v;
9291     Data_Get_Struct(self, Molecule, mol);
9292         if (mol->mview == NULL)
9293                 return Qnil;
9294         TrackballGetRotate(mol->mview->track, f);
9295         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9296         v.x = f[1];
9297         v.y = f[2];
9298         v.z = f[3];
9299         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9300 }
9301
9302 /*
9303  *  call-seq:
9304  *     get_view_scale -> float
9305  *
9306  *  Get the current scale for the view.
9307  */
9308 static VALUE
9309 s_Molecule_GetViewScale(VALUE self)
9310 {
9311     Molecule *mol;
9312     Data_Get_Struct(self, Molecule, mol);
9313         if (mol->mview == NULL)
9314                 return Qnil;
9315         return rb_float_new(TrackballGetScale(mol->mview->track));
9316 }
9317
9318 /*
9319  *  call-seq:
9320  *     get_view_center -> Vector
9321  *
9322  *  Get the current center point of the view.
9323  */
9324 static VALUE
9325 s_Molecule_GetViewCenter(VALUE self)
9326 {
9327     Molecule *mol;
9328         double f[4];
9329         Vector v;
9330     Data_Get_Struct(self, Molecule, mol);
9331         if (mol->mview == NULL)
9332                 return Qnil;
9333         TrackballGetTranslate(mol->mview->track, f);
9334         v.x = -f[0] * mol->mview->dimension;
9335         v.y = -f[1] * mol->mview->dimension;
9336         v.z = -f[2] * mol->mview->dimension;
9337         return ValueFromVector(&v);
9338 }
9339
9340 /*
9341  *  call-seq:
9342  *     set_view_rotation([ax, ay, az], angle) -> self
9343  *
9344  *  Set the current rotation for the view. Angle is in degree, not radian.
9345  */
9346 static VALUE
9347 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9348 {
9349     Molecule *mol;
9350         double f[4];
9351         Vector v;
9352     Data_Get_Struct(self, Molecule, mol);
9353         if (mol->mview == NULL)
9354                 return Qnil;
9355         VectorFromValue(aval, &v);
9356         if (NormalizeVec(&v, &v))
9357                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9358         f[1] = v.x;
9359         f[2] = v.y;
9360         f[3] = v.z;
9361         f[0] = -NUM2DBL(rb_Float(angval));
9362         TrackballSetRotate(mol->mview->track, f);
9363         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9364         return self;
9365 }
9366
9367 /*
9368  *  call-seq:
9369  *     set_view_scale(scale) -> self
9370  *
9371  *  Set the current scale for the view.
9372  */
9373 static VALUE
9374 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9375 {
9376     Molecule *mol;
9377     Data_Get_Struct(self, Molecule, mol);
9378         if (mol->mview == NULL)
9379                 return Qnil;
9380         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9381         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9382         return self;
9383 }
9384
9385 /*
9386  *  call-seq:
9387  *     set_view_center(vec) -> self
9388  *
9389  *  Set the current center point of the view.
9390  */
9391 static VALUE
9392 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9393 {
9394     Molecule *mol;
9395         Vector v;
9396         double f[4];
9397     Data_Get_Struct(self, Molecule, mol);
9398         if (mol->mview == NULL)
9399                 return Qnil;
9400         VectorFromValue(aval, &v);
9401         f[0] = -v.x / mol->mview->dimension;
9402         f[1] = -v.y / mol->mview->dimension;
9403         f[2] = -v.z / mol->mview->dimension;
9404         TrackballSetTranslate(mol->mview->track, f);
9405         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9406         return self;
9407 }
9408
9409 /*
9410  *  call-seq:
9411  *     set_background_color(red, green, blue)
9412  *
9413  *  Set the background color of the model window.
9414  */
9415 static VALUE
9416 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9417 {
9418     Molecule *mol;
9419     Data_Get_Struct(self, Molecule, mol);
9420         if (mol->mview != NULL) {
9421                 VALUE rval, gval, bval;
9422                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9423                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9424         }
9425         return self;    
9426 }
9427
9428 /*
9429  *  call-seq:
9430  *     export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9431  *
9432  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9433  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9434  *  If either width or height is not specified, then the screen width/height is used instead.
9435  */
9436 static VALUE
9437 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9438 {
9439         Molecule *mol;
9440         VALUE fval, sval, bval, wval, hval;
9441         char *fname;
9442         float scale;
9443         int bg_color, width, height;
9444     Data_Get_Struct(self, Molecule, mol);
9445         if (mol->mview == NULL)
9446                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9447         rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9448         fname = FileStringValuePtr(fval);
9449         if (sval == Qnil)
9450                 scale = 1.0;
9451         else scale = NUM2DBL(rb_Float(sval));
9452         if (bval == Qnil)
9453                 bg_color = -1;
9454         else bg_color = NUM2INT(rb_Integer(bval));
9455         if (wval == Qnil)
9456                 width = 0;
9457         else width = NUM2INT(rb_Integer(wval));
9458         if (hval == Qnil)
9459                 height = 0;
9460         else height = NUM2INT(rb_Integer(hval));
9461         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9462                 return fval;
9463         else return Qnil;
9464 }
9465
9466 #pragma mark ------ Graphics ------
9467
9468 static void
9469 s_CalculateGraphicNormals(MainViewGraphic *gp)
9470 {
9471         int i;
9472         Vector v1, v2, v3;
9473         if (gp == NULL || gp->npoints < 3)
9474                 return;
9475         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9476         v1.x = gp->points[3] - gp->points[0];
9477         v1.y = gp->points[4] - gp->points[1];
9478         v1.z = gp->points[5] - gp->points[2];
9479         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9480         for (i = 2; i < gp->npoints; i++) {
9481                 v2.x = gp->points[i * 3] - gp->points[0];
9482                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9483                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9484                 VecCross(v3, v1, v2);
9485                 NormalizeVec(&v3, &v3);
9486                 gp->normals[i * 3] = v3.x;
9487                 gp->normals[i * 3 + 1] = v3.y;
9488                 gp->normals[i * 3 + 2] = v3.z;
9489                 v1 = v2;
9490         }
9491         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9492         VecZero(v1);
9493         for (i = 2; i < gp->npoints; i++) {
9494                 v1.x += gp->normals[i * 3];
9495                 v1.y += gp->normals[i * 3 + 1];
9496                 v1.z += gp->normals[i * 3 + 2];
9497         }
9498         NormalizeVec(&v1, &v1);
9499         gp->normals[0] = v1.x;
9500         gp->normals[1] = v1.y;
9501         gp->normals[2] = v1.z;
9502         /*  normals[1] = nv[2].normalize  */
9503         v2.x = gp->normals[6];
9504         v2.y = gp->normals[7];
9505         v2.z = gp->normals[8];
9506         NormalizeVec(&v1, &v2);
9507         gp->normals[3] = v1.x;
9508         gp->normals[4] = v1.y;
9509         gp->normals[5] = v1.z;
9510         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9511         for (i = 2; i < gp->npoints; i++) {
9512                 if (i == gp->npoints - 1)
9513                         VecZero(v3);
9514                 else {
9515                         v3.x = gp->normals[i * 3 + 3];
9516                         v3.y = gp->normals[i * 3 + 4];
9517                         v3.z = gp->normals[i * 3 + 5];
9518                 }
9519                 VecInc(v2, v3);
9520                 NormalizeVec(&v1, &v2);
9521                 gp->normals[i * 3] = v1.x;
9522                 gp->normals[i * 3 + 1] = v1.y;
9523                 gp->normals[i * 3 + 2] = v1.z;
9524                 v2 = v3;
9525         }
9526 }
9527
9528 /*
9529  *  call-seq:
9530  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9531  *
9532  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9533  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9534  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9535  *   points: an array of Vectors
9536  *   
9537  */
9538 static VALUE
9539 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9540 {
9541     Molecule *mol;
9542         MainViewGraphic g;
9543         int i, n, ni, idx;
9544         const char *p;
9545         VALUE kval, cval, pval, fval, ival;
9546     Data_Get_Struct(self, Molecule, mol);
9547         if (mol->mview == NULL)
9548                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9549         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9550         idx = NUM2INT(rb_Integer(ival));
9551         if (idx == -1)
9552                 idx = mol->mview->ngraphics;
9553         else if (idx < 0 || idx > mol->mview->ngraphics)
9554                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9555         memset(&g, 0, sizeof(g));
9556         g.visible = 1;
9557         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9558                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9559         } else {
9560                 kval = rb_obj_as_string(kval);
9561                 p = StringValuePtr(kval);
9562                 if (strcmp(p, "line") == 0)
9563                         g.kind = kMainViewGraphicLine;
9564                 else if (strcmp(p, "poly") == 0)
9565                         g.kind = kMainViewGraphicPoly;
9566                 else if (strcmp(p, "cylinder") == 0)
9567                         g.kind = kMainViewGraphicCylinder;
9568                 else if (strcmp(p, "cone") == 0)
9569                         g.kind = kMainViewGraphicCone;
9570                 else if (strcmp(p, "ellipsoid") == 0)
9571                         g.kind = kMainViewGraphicEllipsoid;
9572                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9573         }
9574         g.closed = (RTEST(fval) ? 1 : 0);
9575         cval = rb_ary_to_ary(cval);
9576         n = RARRAY_LEN(cval);
9577         if (n < 3 || n >= 5)
9578                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9579         if (n == 3)
9580                 g.rgba[3] = 1.0;
9581         for (i = 0; i < n; i++)
9582                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9583         pval = rb_ary_to_ary(pval);
9584         n = RARRAY_LEN(pval);
9585         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9586         if (n <= 0)
9587                 rb_raise(rb_eArgError, "no control points are given");
9588         switch (g.kind) {
9589                 case kMainViewGraphicLine:
9590                         if (n < 2)
9591                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9592                         break;
9593                 case kMainViewGraphicPoly:
9594                         if (n < 3)
9595                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9596                         break;
9597                 case kMainViewGraphicCylinder:
9598                 case kMainViewGraphicCone:
9599                         if (n != 3)
9600                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9601                         ni = 2;
9602                         break;
9603                 case kMainViewGraphicEllipsoid:
9604                         if (n == 2) {
9605                                 ni = 1;
9606                         } else if (n != 4)
9607                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9608                         break;
9609         }
9610         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9611         for (i = 0; i < n; i++) {
9612                 Vector v;
9613                 VALUE rval = RARRAY_PTR(pval)[i];
9614                 if (i == ni) {
9615                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9616                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9617                                 VectorFromValue(rval, &v);
9618                         } else {
9619                                 v.x = NUM2DBL(rb_Float(rval));
9620                         }
9621                         v.y = v.z = 0;
9622                 } else {
9623                         VectorFromValue(rval, &v);
9624                 }
9625                 g.points[i * 3] = v.x;
9626                 g.points[i * 3 + 1] = v.y;
9627                 g.points[i * 3 + 2] = v.z;
9628         }
9629         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9630                 /*  Sphere  */
9631                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9632                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9633                 g.points[7] = g.points[11] = g.points[3];
9634         }
9635         if (g.kind == kMainViewGraphicPoly) {
9636                 /*  Calculate normals  */
9637                 s_CalculateGraphicNormals(&g);
9638         }
9639         MainView_insertGraphic(mol->mview, idx, &g);
9640         
9641         {
9642                 /*  Register undo  */
9643                 MolAction *act;
9644                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9645                 MolActionCallback_registerUndo(mol, act);
9646                 MolActionRelease(act);
9647         }
9648
9649         return INT2NUM(idx);    
9650 }
9651
9652 /*
9653  *  call-seq:
9654  *     create_graphic(kind, color, points, fill = nil) -> integer
9655  *
9656  *  Create a new graphic object. The arguments are similar as insert_graphic.
9657  */
9658 static VALUE
9659 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9660 {
9661         VALUE args[5];
9662         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9663         args[0] = INT2NUM(-1);
9664         return s_Molecule_InsertGraphic(argc + 1, args, self);
9665 }
9666
9667 /*
9668  *  call-seq:
9669  *     remove_graphic(index) -> integer
9670  *
9671  *  Remove a graphic object.
9672  */
9673 static VALUE
9674 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9675 {
9676     Molecule *mol;
9677         int i;
9678     Data_Get_Struct(self, Molecule, mol);
9679         if (mol->mview == NULL)
9680                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9681         i = NUM2INT(rb_Integer(ival));
9682         if (i < 0 || i >= mol->mview->ngraphics)
9683                 rb_raise(rb_eArgError, "graphic index is out of range");
9684         {
9685                 /*  Prepare data for undo  */
9686                 MainViewGraphic *gp;
9687                 Vector *vp;
9688                 MolAction *act;
9689                 double col[4];
9690                 int n;
9691                 gp = mol->mview->graphics + i;
9692                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9693                 for (n = 0; n < gp->npoints; n++) {
9694                         vp[n].x = gp->points[n * 3];
9695                         vp[n].y = gp->points[n * 3 + 1];
9696                         vp[n].z = gp->points[n * 3 + 2];
9697                 }
9698                 col[0] = gp->rgba[0];
9699                 col[1] = gp->rgba[1];
9700                 col[2] = gp->rgba[2];
9701                 col[3] = gp->rgba[3];
9702                 if (gp->visible == 0) {
9703                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9704                         MolActionCallback_registerUndo(mol, act);
9705                         MolActionRelease(act);
9706                 }
9707                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9708                 MolActionCallback_registerUndo(mol, act);
9709                 free(vp);
9710                 MolActionRelease(act);
9711         }
9712         MainView_removeGraphic(mol->mview, i);
9713         return ival;
9714 }
9715
9716 /*
9717  *  call-seq:
9718  *     ngraphics -> integer
9719  *
9720  *  Get the number of graphic objects.
9721  */
9722 static VALUE
9723 s_Molecule_NGraphics(VALUE self)
9724 {
9725     Molecule *mol;
9726     Data_Get_Struct(self, Molecule, mol);
9727         if (mol->mview == NULL)
9728                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9729         return INT2NUM(mol->mview->ngraphics);
9730 }
9731
9732 /*
9733  *  call-seq:
9734  *     get_graphic_point(graphic_index, point_index) -> value
9735  *     get_graphic_points(graphic_index) -> values
9736  *
9737  *  Get the point_index-th control point of graphic_index-th graphic object.
9738  *  Get an array of all control points with the given values.
9739  *   
9740  */
9741 static VALUE
9742 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9743 {
9744         MainViewGraphic *gp;
9745     Molecule *mol;
9746         int index, pindex;
9747         Vector v;
9748         VALUE gval, pval;
9749     Data_Get_Struct(self, Molecule, mol);
9750         if (mol->mview == NULL)
9751                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9752         rb_scan_args(argc, argv, "11", &gval, &pval);
9753         index = NUM2INT(rb_Integer(gval));
9754         if (index < 0 || index >= mol->mview->ngraphics)
9755                 rb_raise(rb_eArgError, "the graphic index is out of range");
9756         gp = mol->mview->graphics + index;
9757         if (pval != Qnil) {
9758                 pindex = NUM2INT(rb_Integer(pval));
9759                 if (pindex < 0 || pindex >= gp->npoints)
9760                         rb_raise(rb_eArgError, "the point index is out of range");
9761                 v.x = gp->points[pindex * 3];
9762                 v.y = gp->points[pindex * 3 + 1];
9763                 v.z = gp->points[pindex * 3 + 2];
9764                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9765                         return rb_float_new(v.x);
9766                 } else {
9767                         return ValueFromVector(&v);
9768                 }
9769         } else {
9770                 pval = rb_ary_new();
9771                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9772                         v.x = gp->points[pindex * 3];
9773                         v.y = gp->points[pindex * 3 + 1];
9774                         v.z = gp->points[pindex * 3 + 2];
9775                         rb_ary_push(pval, ValueFromVector(&v));
9776                 }
9777                 return pval;
9778         }
9779 }
9780
9781 /*
9782  *  call-seq:
9783  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9784  *     set_graphic_points(graphic_index, new_values) -> new_values
9785  *
9786  *  Change the point_index-th control point of graphic_index-th graphic object.
9787  *  Replace the control points with the given values.
9788  *   
9789  */
9790 static VALUE
9791 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9792 {
9793         MainViewGraphic *gp;
9794     Molecule *mol;
9795         int index, pindex;
9796         Vector v, v0;
9797         VALUE gval, pval, nval;
9798         MolAction *act;
9799     Data_Get_Struct(self, Molecule, mol);
9800         if (mol->mview == NULL)
9801                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9802         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9803         index = NUM2INT(rb_Integer(gval));
9804         if (index < 0 || index >= mol->mview->ngraphics)
9805                 rb_raise(rb_eArgError, "the graphic index is out of range");
9806         gp = mol->mview->graphics + index;
9807         if (nval != Qnil) {
9808                 pindex = NUM2INT(rb_Integer(pval));
9809                 if (pindex < 0 || pindex >= gp->npoints)
9810                         rb_raise(rb_eArgError, "the point index is out of range");
9811                 v0.x = gp->points[pindex * 3];
9812                 v0.y = gp->points[pindex * 3 + 1];
9813                 v0.z = gp->points[pindex * 3 + 2];
9814                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9815                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9816                                 v.x = NUM2DBL(rb_Float(nval));
9817                                 v.y = v.z = 0;
9818                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9819                                 v.x = NUM2DBL(rb_Float(nval));
9820                                 v.y = v.z = 0;
9821                                 gp->points[7] = gp->points[11] = v.x;
9822                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9823                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9824                 } else {
9825                         if (nval == Qnil) {
9826                                 v.x = kInvalidFloat;
9827                                 v.y = v.z = 0.0;
9828                         } else VectorFromValue(nval, &v);
9829                 }
9830                 gp->points[pindex * 3] = v.x;
9831                 gp->points[pindex * 3 + 1] = v.y;
9832                 gp->points[pindex * 3 + 2] = v.z;
9833                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9834         } else {
9835                 VALUE aval;
9836                 int len;
9837                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9838                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9839                         vp[pindex].x = gp->points[pindex * 3];
9840                         vp[pindex].y = gp->points[pindex * 3 + 1];
9841                         vp[pindex].z = gp->points[pindex * 3 + 2];
9842                 }
9843                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9844                 free(vp);
9845                 pval = rb_ary_to_ary(pval);
9846                 len = RARRAY_LEN(pval);
9847                 if (gp->npoints < len) {
9848                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9849                         gp->npoints = len;
9850                 } else if (gp->npoints > len) {
9851                         int len2 = 3;
9852                         switch (gp->kind) {
9853                                 case kMainViewGraphicLine: len2 = 2; break;
9854                                 case kMainViewGraphicPoly: len2 = 3; break;
9855                                 case kMainViewGraphicCylinder: len2 = 3; break;
9856                                 case kMainViewGraphicCone: len2 = 3; break;
9857                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9858                         }
9859                         if (len2 < len)
9860                                 len2 = len;
9861                         gp->npoints = len2;
9862                 }
9863                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9864                         aval = RARRAY_PTR(pval)[pindex];
9865                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9866                                 v.x = NUM2DBL(rb_Float(aval));
9867                                 v.y = v.z = 0;
9868                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9869                                 v.x = NUM2DBL(rb_Float(aval));
9870                                 v.y = v.z = 0;
9871                                 gp->points[7] = gp->points[11] = v.x;
9872                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9873                                 break;
9874                         } else VectorFromValue(aval, &v);
9875                         gp->points[pindex * 3] = v.x;
9876                         gp->points[pindex * 3 + 1] = v.y;
9877                         gp->points[pindex * 3 + 2] = v.z;
9878                 }
9879         }
9880         if (gp->kind == kMainViewGraphicPoly) {
9881                 /*  Calculate normals  */
9882                 s_CalculateGraphicNormals(gp);
9883         }
9884         MolActionCallback_registerUndo(mol, act);
9885         MolActionRelease(act);          
9886         MoleculeCallback_notifyModification(mol, 0);
9887         return nval;
9888 }
9889
9890 /*
9891  *  call-seq:
9892  *     get_graphic_color(graphic_index) -> value
9893  *
9894  *  Get the color of graphic_index-th graphic object
9895  */
9896 static VALUE
9897 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9898 {
9899         MainViewGraphic *gp;
9900     Molecule *mol;
9901         int index;
9902     Data_Get_Struct(self, Molecule, mol);
9903         if (mol->mview == NULL)
9904                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9905         index = NUM2INT(rb_Integer(gval));
9906         if (index < 0 || index >= mol->mview->ngraphics)
9907                 rb_raise(rb_eArgError, "the graphic index is out of range");
9908         gp = mol->mview->graphics + index;
9909         return rb_ary_new3(4, rb_float_new(gp->rgba[0]), rb_float_new(gp->rgba[1]), rb_float_new(gp->rgba[2]), rb_float_new(gp->rgba[3]));
9910 }
9911
9912 /*
9913  *  call-seq:
9914  *     set_graphic_color(graphic_index, new_value) -> new_value
9915  *
9916  *  Change the color of graphic_index-th graphic object
9917  *   
9918  */
9919 static VALUE
9920 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9921 {
9922         MainViewGraphic *gp;
9923     Molecule *mol;
9924         MolAction *act;
9925         double c[4];
9926         int index, i, n;
9927     Data_Get_Struct(self, Molecule, mol);
9928         if (mol->mview == NULL)
9929                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9930         index = NUM2INT(rb_Integer(gval));
9931         if (index < 0 || index >= mol->mview->ngraphics)
9932                 rb_raise(rb_eArgError, "the graphic index is out of range");
9933         gp = mol->mview->graphics + index;
9934         for (i = 0; i < 4; i++)
9935                 c[i] = gp->rgba[i];
9936         cval = rb_ary_to_ary(cval);
9937         n = RARRAY_LEN(cval);
9938         if (n != 3 && n != 4)
9939                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9940
9941         for (i = 0; i < n; i++) {
9942                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9943         }
9944         if (n == 3)
9945                 gp->rgba[3] = 1.0;
9946         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9947         MolActionCallback_registerUndo(mol, act);
9948         MolActionRelease(act);          
9949         MoleculeCallback_notifyModification(mol, 0);
9950         return cval;
9951 }
9952
9953 /*
9954  *  call-seq:
9955  *     show_graphic(graphic_index) -> self
9956  *
9957  *  Enable the visible flag of the graphic_index-th graphic object
9958  *   
9959  */
9960 static VALUE
9961 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9962 {
9963         MainViewGraphic *gp;
9964     Molecule *mol;
9965         int index;
9966     Data_Get_Struct(self, Molecule, mol);
9967         if (mol->mview == NULL)
9968                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9969         index = NUM2INT(rb_Integer(gval));
9970         if (index < 0 || index >= mol->mview->ngraphics)
9971                 rb_raise(rb_eArgError, "the graphic index is out of range");
9972         gp = mol->mview->graphics + index;
9973         gp->visible = 1;
9974         MoleculeCallback_notifyModification(mol, 0);
9975         return self;
9976 }
9977
9978 /*
9979  *  call-seq:
9980  *     hide_graphic(graphic_index) -> self
9981  *
9982  *  Disable the visible flag of the graphic_index-th graphic object
9983  *   
9984  */
9985 static VALUE
9986 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9987 {
9988         MainViewGraphic *gp;
9989     Molecule *mol;
9990         int index;
9991     Data_Get_Struct(self, Molecule, mol);
9992         if (mol->mview == NULL)
9993                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9994         index = NUM2INT(rb_Integer(gval));
9995         if (index < 0 || index >= mol->mview->ngraphics)
9996                 rb_raise(rb_eArgError, "the graphic index is out of range");
9997         gp = mol->mview->graphics + index;
9998         gp->visible = 0;
9999         MoleculeCallback_notifyModification(mol, 0);
10000         return self;
10001 }
10002
10003 /*
10004  *  call-seq:
10005  *     show_text(string)
10006  *
10007  *  Show the string in the info text box.
10008  */
10009 static VALUE
10010 s_Molecule_ShowText(VALUE self, VALUE arg)
10011 {
10012     Molecule *mol;
10013     Data_Get_Struct(self, Molecule, mol);
10014         if (mol->mview != NULL)
10015                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10016         return Qnil;
10017 }
10018
10019 #pragma mark ------ MD Support ------
10020
10021 /*
10022  *  call-seq:
10023  *     md_arena -> MDArena
10024  *
10025  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10026  *  this molecule, a new arena is created.
10027  */
10028 static VALUE
10029 s_Molecule_MDArena(VALUE self)
10030 {
10031     Molecule *mol;
10032         VALUE retval;
10033     Data_Get_Struct(self, Molecule, mol);
10034         if (mol->arena == NULL)
10035                 md_arena_new(mol);
10036         retval = ValueFromMDArena(mol->arena);
10037         return retval;
10038 }
10039
10040 /*
10041  *  call-seq:
10042  *     set_parameter_attr(type, index, key, value, src) -> value
10043  *
10044  *  This method is used only internally.
10045  */
10046 static VALUE
10047 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10048 {
10049         /*  This method is called from MolAction to change a MM parameter attribute.  */
10050     Molecule *mol;
10051         VALUE pval;
10052         ParameterRef *pref;
10053         UnionPar *up;
10054     Data_Get_Struct(self, Molecule, mol);
10055         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10056         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10057         
10058         /*  This is the special part of this method; it allows modification of the src field. */
10059         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10060         Data_Get_Struct(pval, ParameterRef, pref);
10061         up = ParameterRefGetPar(pref);
10062         up->bond.src = FIX2INT(sval);
10063         
10064         return vval;
10065 }
10066
10067 /*
10068  *  call-seq:
10069  *     parameter -> Parameter
10070  *
10071  *  Get the local parameter of this molecule. If not defined, returns nil.
10072  */
10073 static VALUE
10074 s_Molecule_Parameter(VALUE self)
10075 {
10076     Molecule *mol;
10077     Data_Get_Struct(self, Molecule, mol);
10078 /*      if (mol->par == NULL)
10079                 return Qnil; */
10080         return s_NewParameterValueFromValue(self);
10081 }
10082
10083 /*
10084  *  call-seq:
10085  *     start_step       -> Integer
10086  *
10087  *  Returns the start step (defined by dcd format).
10088  */
10089 static VALUE
10090 s_Molecule_StartStep(VALUE self)
10091 {
10092     Molecule *mol;
10093     Data_Get_Struct(self, Molecule, mol);
10094         return INT2NUM(mol->startStep);
10095 }
10096
10097 /*
10098  *  call-seq:
10099  *     start_step = Integer
10100  *
10101  *  Set the start step (defined by dcd format).
10102  */
10103 static VALUE
10104 s_Molecule_SetStartStep(VALUE self, VALUE val)
10105 {
10106     Molecule *mol;
10107     Data_Get_Struct(self, Molecule, mol);
10108         mol->startStep = NUM2INT(rb_Integer(val));
10109         return val;
10110 }
10111
10112 /*
10113  *  call-seq:
10114  *     steps_per_frame       -> Integer
10115  *
10116  *  Returns the number of steps between frames (defined by dcd format).
10117  */
10118 static VALUE
10119 s_Molecule_StepsPerFrame(VALUE self)
10120 {
10121     Molecule *mol;
10122     Data_Get_Struct(self, Molecule, mol);
10123         return INT2NUM(mol->stepsPerFrame);
10124 }
10125
10126 /*
10127  *  call-seq:
10128  *     steps_per_frame = Integer
10129  *
10130  *  Set the number of steps between frames (defined by dcd format).
10131  */
10132 static VALUE
10133 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10134 {
10135     Molecule *mol;
10136     Data_Get_Struct(self, Molecule, mol);
10137         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10138         return val;
10139 }
10140
10141 /*
10142  *  call-seq:
10143  *     ps_per_step       -> Float
10144  *
10145  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10146  */
10147 static VALUE
10148 s_Molecule_PsPerStep(VALUE self)
10149 {
10150     Molecule *mol;
10151     Data_Get_Struct(self, Molecule, mol);
10152         return rb_float_new(mol->psPerStep);
10153 }
10154
10155 /*
10156  *  call-seq:
10157  *     ps_per_step = Float
10158  *
10159  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10160  */
10161 static VALUE
10162 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10163 {
10164     Molecule *mol;
10165     Data_Get_Struct(self, Molecule, mol);
10166         mol->psPerStep = NUM2DBL(rb_Float(val));
10167         return val;
10168 }
10169
10170 static VALUE
10171 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10172 {
10173         rb_raise(rb_eMolbyError, "Molecule#bond_par, angle_par, dihedral_par, improper_par, vdw_par are now obsolete. You can use MDArena#bond_par, angle_par, dihedral_par, improper_par, vdw_par instead, and probably these are what you really want.");
10174 }
10175
10176 #pragma mark ------ MO Handling ------
10177
10178 /*
10179  *  call-seq:
10180  *     selectedMO -> IntGroup
10181  *
10182  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10183  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10184  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10185  */
10186 static VALUE
10187 s_Molecule_SelectedMO(VALUE self)
10188 {
10189     Molecule *mol;
10190         IntGroup *ig;
10191         VALUE val;
10192     Data_Get_Struct(self, Molecule, mol);
10193         if (mol->mview == NULL)
10194                 return Qnil;
10195         ig = MainView_selectedMO(mol->mview);
10196         if (ig == NULL)
10197                 return Qnil;
10198         IntGroupOffset(ig, 1);
10199         val = ValueFromIntGroup(ig);
10200         IntGroupRelease(ig);
10201         return val;
10202 }
10203
10204 /*
10205  *  call-seq:
10206  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10207  *
10208  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10209  *  If the molecule does not contain a basis set information, then returns nil.
10210  */
10211 static VALUE
10212 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10213 {
10214     Molecule *mol;
10215         Vector o, dx, dy, dz;
10216         Int nx, ny, nz;
10217         VALUE nval;
10218         Int npoints = 80 * 80 * 80;
10219     Data_Get_Struct(self, Molecule, mol);
10220         if (mol->bset == NULL)
10221                 return Qnil;
10222         rb_scan_args(argc, argv, "01", &nval);
10223         if (nval != Qnil)
10224                 npoints = NUM2INT(rb_Integer(nval));
10225         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10226                 return Qnil;
10227         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));
10228 }
10229
10230 static int
10231 s_Cubegen_callback(double progress, void *ref)
10232 {
10233         MyAppCallback_setProgressValue(progress);
10234         if (MyAppCallback_checkInterrupt())
10235                 return 1;
10236         else return 0;
10237 }
10238
10239 /*
10240  *  call-seq:
10241  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10242  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10243  *
10244  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10245  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10246  *  second form, the cube dimension is explicitly given.
10247  *  Returns fname when successful, nil otherwise.
10248  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10249  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10250  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10251  */
10252 static VALUE
10253 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10254 {
10255         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10256     Molecule *mol;
10257         Int mono, nx, ny, nz, npoints;
10258         Vector o, dx, dy, dz;
10259         int index, n;
10260         char buf[1024];
10261     Data_Get_Struct(self, Molecule, mol);
10262         if (mol->bset == NULL)
10263                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10264         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10265         
10266         /*  Set up parameters  */
10267         mono = NUM2INT(rb_Integer(mval));
10268         if (mono < 0 || mono > mol->bset->ncomps)
10269                 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d, or 0 as 'arbitrary vector')", mono, mol->bset->ncomps);
10270         if (RTEST(bval)) {
10271                 if (mol->bset->rflag != 0)
10272                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10273                 mono += mol->bset->ncomps;
10274         }
10275                 
10276         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10277                 /*  Automatic grid formation  */
10278                 if (oval != Qnil)
10279                         npoints = NUM2INT(rb_Integer(oval));
10280                 else npoints = 0;
10281                 if (npoints == 0)
10282                         npoints = 1000000;
10283                 else if (npoints < 8)
10284                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10285                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10286                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10287                 ival = dxval;
10288                 bval = dyval;
10289         } else {
10290                 VectorFromValue(oval, &o);
10291                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10292                         VectorFromValue(dxval, &dx);
10293                 else {
10294                         dx.x = NUM2DBL(rb_Float(dxval));
10295                         dx.y = dx.z = 0.0;
10296                 }
10297                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10298                         VectorFromValue(dyval, &dy);
10299                 else {
10300                         dy.y = NUM2DBL(rb_Float(dyval));
10301                         dy.x = dy.z = 0.0;
10302                 }
10303                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10304                         VectorFromValue(dzval, &dz);
10305                 else {
10306                         dz.z = NUM2DBL(rb_Float(dzval));
10307                         dz.x = dz.y = 0.0;
10308                 }
10309                 nx = NUM2INT(rb_Integer(nxval));
10310                 ny = NUM2INT(rb_Integer(nyval));
10311                 nz = NUM2INT(rb_Integer(nzval));
10312                 if (nx <= 0 || ny <= 0 || nz <= 0)
10313                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10314                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10315                         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);
10316         }
10317         
10318         /*  Calc MO  */
10319         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10320         if (index == -2)
10321                 rb_interrupt();
10322         else if (index < 0)
10323                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10324         
10325         /*  Output to file  */
10326         MoleculeCallback_displayName(mol, buf, sizeof buf);
10327         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10328         if (n != 0)
10329                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10330         
10331         /*  Discard the cube  */
10332         MoleculeClearCubeAtIndex(mol, index);
10333         return fval;
10334 }
10335
10336 /*
10337  *  call-seq:
10338  *     clear_surface
10339  *
10340  *  Clear the MO surface if present.
10341  */
10342 static VALUE
10343 s_Molecule_ClearSurface(VALUE self)
10344 {
10345     Molecule *mol;
10346     Data_Get_Struct(self, Molecule, mol);
10347         if (mol->mcube != NULL)
10348                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10349         return self;
10350 }
10351
10352 /*
10353  *  call-seq:
10354  *     hide_surface
10355  *
10356  *  Hide the MO surface if present.
10357  */
10358 static VALUE
10359 s_Molecule_HideSurface(VALUE self)
10360 {
10361     Molecule *mol;
10362     Data_Get_Struct(self, Molecule, mol);
10363         if (mol->mcube != NULL) {
10364                 mol->mcube->hidden = 1;
10365                 MoleculeCallback_notifyModification(mol, 0);
10366         }
10367         return self;
10368 }
10369
10370 /*
10371  *  call-seq:
10372  *     show_surface
10373  *
10374  *  Show the MO surface if present.
10375  */
10376 static VALUE
10377 s_Molecule_ShowSurface(VALUE self)
10378 {
10379     Molecule *mol;
10380     Data_Get_Struct(self, Molecule, mol);
10381         if (mol->mcube != NULL) {
10382                 mol->mcube->hidden = 0;
10383                 MoleculeCallback_notifyModification(mol, 0);
10384         }
10385         return self;
10386 }
10387
10388 /*
10389  *  call-seq:
10390  *     create_surface(mo, attr = nil)
10391  *
10392  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10393  *  then it denotes the beta orbital.
10394  *  If mo is nil, then the attributes of the current surface are modified.
10395  *  Attributes:
10396  *    :npoints : the approximate number of grid points
10397  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10398  *    :thres   : the threshold for the isovalue surface
10399  *  If the molecule does not contain MO information, raises exception.
10400  */
10401 static VALUE
10402 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10403 {
10404     Molecule *mol;
10405         Vector o, dx, dy, dz;
10406         Int nmo, nx, ny, nz, i;
10407         Int need_recalc = 0;
10408         VALUE nval, hval, aval;
10409         Int npoints;
10410         Double expand;
10411         Double thres;
10412         Double d[4];
10413     Data_Get_Struct(self, Molecule, mol);
10414         rb_scan_args(argc, argv, "11", &nval, &hval);
10415         if (mol->bset == NULL)
10416                 rb_raise(rb_eMolbyError, "No MO information is given");
10417         if (nval == Qnil) {
10418                 nmo = -1;
10419         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10420                 nmo = mol->bset->nmos + 1;
10421         } else {
10422                 nmo = NUM2INT(rb_Integer(nval));
10423                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10424                         rb_raise(rb_eMolbyError, "MO index (%d) is out of range; should be 1..%d (or -1..-%d for beta orbitals); (0 is acceptable as arbitrary vector)", nmo, mol->bset->nmos, mol->bset->ncomps);
10425                 if (nmo < 0)
10426                         nmo = -nmo + mol->bset->ncomps;
10427         }
10428         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10429                 npoints = NUM2INT(rb_Integer(aval));
10430                 need_recalc = 1;
10431         } else if (mol->mcube != NULL) {
10432                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10433         } else npoints = 80 * 80 * 80;
10434         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10435                 expand = NUM2DBL(rb_Float(aval));
10436         } else if (mol->mcube != NULL) {
10437                 expand = mol->mcube->expand;
10438         } else expand = 1.0;
10439         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10440                 thres = NUM2DBL(rb_Float(aval));
10441         } else if (mol->mcube != NULL) {
10442                 thres = mol->mcube->thres;
10443         } else thres = 0.05;
10444         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10445                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10446                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10447                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10448                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10449         }
10450         for (nx = 0; nx < 2; nx++) {
10451                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10452                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10453                         aval = rb_ary_to_ary(aval);
10454                         if (RARRAY_LEN(aval) < 3) {
10455                         raise:
10456                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10457                         }
10458                         for (i = 0; i < 4; i++)
10459                                 d[i] = mol->mcube->c[nx].rgba[i];
10460                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10461                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10462                                 if (d[i] < 0.0 && d[i] > 1.0)
10463                                         goto raise;
10464                         }
10465                         for (i = 0; i < 4; i++)
10466                                 mol->mcube->c[nx].rgba[i] = d[i];
10467                 }
10468         }
10469         if (mol->mcube->expand != expand)
10470                 need_recalc = 1;
10471         mol->mcube->thres = thres;
10472         mol->mcube->expand = expand;
10473         if (nmo < 0) {
10474                 if (mol->mcube->idn < 0)
10475                         return self;  /*  Only set attributes for now  */
10476                 if (need_recalc)
10477                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10478         }
10479         if (MoleculeUpdateMCube(mol, nmo) != 0)
10480                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10481         return self;
10482 }
10483
10484 /*
10485  *  call-seq:
10486  *     set_surface_attr(attr = nil)
10487  *
10488  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10489  */
10490 static VALUE
10491 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10492 {
10493         VALUE args[2];
10494         args[0] = Qnil;
10495         args[1] = hval;
10496         return s_Molecule_CreateSurface(2, args, self);
10497 }
10498
10499 /*
10500  *  call-seq:
10501  *     nelpots
10502  *
10503  *  Get the number of electrostatic potential info.
10504  */
10505 static VALUE
10506 s_Molecule_NElpots(VALUE self)
10507 {
10508         Molecule *mol;
10509     Data_Get_Struct(self, Molecule, mol);
10510         return INT2NUM(mol->nelpots);
10511 }
10512
10513 /*
10514  *  call-seq:
10515  *     elpot(idx)
10516  *
10517  *  Get the electrostatic potential info at the given index. If present, then the
10518  *  return value is [Vector, Float] (position and potential). If not present, then
10519  *  returns nil.
10520  */
10521 static VALUE
10522 s_Molecule_Elpot(VALUE self, VALUE ival)
10523 {
10524         Molecule *mol;
10525         int idx;
10526     Data_Get_Struct(self, Molecule, mol);
10527         idx = NUM2INT(rb_Integer(ival));
10528         if (idx < 0 || idx >= mol->nelpots)
10529                 return Qnil;
10530         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10531 }
10532
10533 /*
10534  *  call-seq:
10535  *     clear_basis_set
10536  *
10537  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10538  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10539  */
10540 static VALUE
10541 s_Molecule_ClearBasisSet(VALUE self)
10542 {
10543         Molecule *mol;
10544     Data_Get_Struct(self, Molecule, mol);
10545         if (mol != NULL) {
10546                 if (mol->bset != NULL) {
10547                         BasisSetRelease(mol->bset);
10548                         mol->bset = NULL;
10549                 }
10550                 if (mol->mcube != NULL) {
10551                         MoleculeDeallocateMCube(mol->mcube);
10552                         mol->mcube = NULL;
10553                 }
10554         }
10555         return self;
10556 }
10557
10558 /*
10559  *  call-seq:
10560  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10561  *
10562  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10563  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10564  *  -2, D5-type.
10565  */
10566 static VALUE
10567 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10568 {
10569         Molecule *mol;
10570         int sym, nprims, a_idx, n;
10571     Data_Get_Struct(self, Molecule, mol);
10572         a_idx = NUM2INT(rb_Integer(aval));
10573         sym = NUM2INT(rb_Integer(symval));
10574         nprims = NUM2INT(rb_Integer(npval));
10575         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10576         if (n == -1)
10577                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10578         else if (n == -2)
10579                 rb_raise(rb_eMolbyError, "Low memory");
10580         else if (n == -3)
10581                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10582         else if (n != 0)
10583                 rb_raise(rb_eMolbyError, "Unknown error");
10584         return self;
10585 }
10586
10587 /*
10588  *  call-seq:
10589  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10590  *
10591  *  To be used internally. Add a gaussian primitive coefficients.
10592  */
10593 static VALUE
10594 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10595 {
10596         Molecule *mol;
10597         Int n;
10598         Double exponent, contraction, contraction_sp;
10599     Data_Get_Struct(self, Molecule, mol);
10600         exponent = NUM2DBL(rb_Float(expval));
10601         contraction = NUM2DBL(rb_Float(cval));
10602         contraction_sp = NUM2DBL(rb_Float(cspval));
10603         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10604         if (n == -1)
10605                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10606         else if (n == -2)
10607                 rb_raise(rb_eMolbyError, "Low memory");
10608         else if (n != 0)
10609                 rb_raise(rb_eMolbyError, "Unknown error");
10610         return self;
10611 }
10612
10613 /*
10614  *  call-seq:
10615  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10616  *
10617  *  Get the Gaussian shell information for the given MO coefficient index.
10618  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10619  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10620  *  is the number of MO component belonging to this shell.
10621  */
10622 static VALUE
10623 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10624 {
10625         Molecule *mol;
10626         ShellInfo *sp;
10627         int s_idx, sym;
10628     Data_Get_Struct(self, Molecule, mol);
10629         if (mol->bset == NULL)
10630                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10631         s_idx = NUM2INT(rb_Integer(sval));
10632         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10633                 return Qnil;
10634         sp = mol->bset->shells + s_idx;
10635         sym = sp->sym;
10636         switch (sym) {
10637                 case kGTOType_S:  sym = 0;  break;
10638                 case kGTOType_SP: sym = -1; break;
10639                 case kGTOType_P:  sym = 1;  break;
10640                 case kGTOType_D:  sym = 2;  break;
10641                 case kGTOType_D5: sym = -2; break;
10642                 case kGTOType_F:  sym = 3;  break;
10643                 case kGTOType_F7: sym = -3; break;
10644                 case kGTOType_G:  sym = 4;  break;
10645                 case kGTOType_G9: sym = -4; break;
10646                 default:
10647                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10648         }
10649         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10650 }
10651
10652 /*
10653  *  call-seq:
10654  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10655  *
10656  *  Get the Gaussian primitive coefficients for the given MO component.
10657  */
10658 static VALUE
10659 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10660 {
10661         Molecule *mol;
10662         ShellInfo *sp;
10663         PrimInfo *pp;
10664         int s_idx, i;
10665         VALUE retval, aval;
10666     Data_Get_Struct(self, Molecule, mol);
10667         if (mol->bset == NULL)
10668                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10669         s_idx = NUM2INT(rb_Integer(sval));
10670         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10671                 return Qnil;
10672         sp = mol->bset->shells + s_idx;
10673         pp = mol->bset->priminfos + sp->p_idx;
10674         retval = rb_ary_new2(sp->nprim);
10675         for (i = 0; i < sp->nprim; i++) {
10676                 if (sp->sym == kGTOType_SP) {
10677                         /*  With P contraction coefficient  */
10678                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10679                 } else {
10680                         /*  Without P contraction coefficient  */
10681                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10682                 }
10683                 rb_ary_store(retval, i, aval);
10684         }
10685         return retval;
10686 }
10687
10688 /*
10689  *  call-seq:
10690  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10691  *
10692  *  Get the Gaussian shell information for the given MO coefficient index.
10693  */
10694 static VALUE
10695 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10696 {
10697         Molecule *mol;
10698         Int n, c, atom_idx, shell_idx;
10699         char label[32];
10700     Data_Get_Struct(self, Molecule, mol);
10701         if (mol->bset == NULL)
10702                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10703         c = NUM2INT(rb_Integer(cval));
10704         if (c < 0 || c >= mol->bset->ncomps)
10705                 return Qnil;
10706         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10707         if (n != 0)
10708                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10709         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10710 }
10711
10712 /*
10713  *  call-seq:
10714  *     clear_mo_coefficients
10715  *
10716  *  Clear the existing MO coefficients.
10717  */
10718 static VALUE
10719 s_Molecule_ClearMOCoefficients(VALUE self)
10720 {
10721         Molecule *mol;
10722         Data_Get_Struct(self, Molecule, mol);
10723         if (mol->bset != NULL) {
10724                 if (mol->bset->moenergies != NULL) {
10725                         free(mol->bset->moenergies);
10726                         mol->bset->moenergies = NULL;
10727                 }
10728                 if (mol->bset->mo != NULL) {
10729                         free(mol->bset->mo);
10730                         mol->bset->mo = NULL;
10731                 }
10732                 mol->bset->nmos = 0;
10733         }
10734         return self;
10735 }
10736
10737 /*
10738  *  call-seq:
10739  *     set_mo_coefficients(idx, energy, coefficients)
10740  *
10741  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10742  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10743  *  Energy is the MO energy, and coefficients is an array
10744  *  of MO coefficients.
10745  */
10746 static VALUE
10747 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10748 {
10749         Molecule *mol;
10750         Int idx, ncomps, i;
10751         Double energy;
10752         Double *coeffs;
10753     Data_Get_Struct(self, Molecule, mol);
10754         idx = NUM2INT(rb_Integer(ival));
10755         energy = NUM2DBL(rb_Float(eval));
10756         aval = rb_ary_to_ary(aval);
10757         ncomps = RARRAY_LEN(aval);
10758         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10759         if (coeffs == NULL) {
10760                 i = -2;
10761                 goto end;
10762         }
10763         for (i = 0; i < ncomps; i++)
10764                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10765         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10766 end:
10767         if (i == -1)
10768                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10769         else if (i == -2)
10770                 rb_raise(rb_eMolbyError, "Low memory");
10771         else if (i == -3)
10772                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10773         else if (i == -4)
10774                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10775         else if (i == -5)
10776                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10777         else if (i != 0)
10778                 rb_raise(rb_eMolbyError, "Unknown error");
10779         return self;
10780 }
10781
10782 /*
10783  *  call-seq:
10784  *     get_mo_coefficients(idx)
10785  *
10786  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10787  */
10788 static VALUE
10789 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10790 {
10791         Molecule *mol;
10792         Int idx, ncomps, n;
10793         Double energy;
10794         Double *coeffs;
10795         VALUE retval;
10796     Data_Get_Struct(self, Molecule, mol);
10797         idx = NUM2INT(rb_Integer(ival));
10798         ncomps = 0;
10799         coeffs = NULL;
10800         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10801         if (n == -1)
10802                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10803         else if (n == -2)
10804                 rb_raise(rb_eMolbyError, "No basis set information is present");
10805         else if (n == -3)
10806                 return Qnil;  /*  Silently returns nil  */
10807         retval = rb_ary_new2(ncomps);
10808         for (n = 0; n < ncomps; n++)
10809                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10810         free(coeffs);
10811         return retval;
10812 }
10813
10814 /*
10815  *  call-seq:
10816  *     get_mo_energy(idx)
10817  *
10818  *  To be used internally. Get the MO energy for the given MO index (1-based).
10819  */
10820 static VALUE
10821 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10822 {
10823         Molecule *mol;
10824         Int idx, n;
10825         Double energy;
10826     Data_Get_Struct(self, Molecule, mol);
10827         idx = NUM2INT(rb_Integer(ival));
10828         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10829         if (n == -1)
10830                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10831         else if (n == -2)
10832                 rb_raise(rb_eMolbyError, "No basis set information is present");
10833         else if (n == -3)
10834                 return Qnil;
10835         return rb_float_new(energy);
10836 }
10837
10838 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10839
10840 static inline void
10841 s_InitMOInfoKeys(void)
10842 {
10843         if (sTypeSym == 0) {
10844                 sTypeSym = ID2SYM(rb_intern("type"));
10845                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10846                 sBetaSym = ID2SYM(rb_intern("beta"));
10847                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10848                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10849         }
10850 }
10851
10852 /*
10853  *  call-seq:
10854  *     set_mo_info(hash)
10855  *
10856  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10857  *  :alpha=>integer, :beta=>integer
10858  */
10859 static VALUE
10860 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10861 {
10862         Molecule *mol;
10863         VALUE aval;
10864         Int rflag, na, nb, n;
10865         char *s;
10866     Data_Get_Struct(self, Molecule, mol);
10867         if (mol->bset != NULL) {
10868                 rflag = mol->bset->rflag;
10869                 na = mol->bset->ne_alpha;
10870                 nb = mol->bset->ne_beta;
10871         } else {
10872                 rflag = 1;
10873                 na = 0;
10874                 nb = 0;
10875         }
10876         if (hval != Qnil) {
10877                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10878                         s = StringValuePtr(aval);
10879                         if (strcasecmp(s, "RHF") == 0)
10880                                 rflag = 1;
10881                         else if (strcasecmp(s, "UHF") == 0)
10882                                 rflag = 0;
10883                         else if (strcasecmp(s, "ROHF") == 0)
10884                                 rflag = 2;
10885                 }
10886                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10887                         n = NUM2INT(rb_Integer(aval));
10888                         if (n >= 0)
10889                                 na = n;
10890                 }
10891                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10892                         n = NUM2INT(rb_Integer(aval));
10893                         if (n >= 0)
10894                                 nb = n;
10895                 }
10896                 MoleculeSetMOInfo(mol, rflag, na, nb);
10897         }
10898         return self;
10899 }
10900
10901 /*
10902  *  call-seq:
10903  *     get_mo_info(key)
10904  *
10905  *  Get the MO info. The key is as described in set_mo_info.
10906  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10907  */
10908 static VALUE
10909 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10910 {
10911         Molecule *mol;
10912     Data_Get_Struct(self, Molecule, mol);
10913         if (mol->bset == NULL)
10914                 return Qnil;
10915         if (kval == sTypeSym) {
10916                 switch (mol->bset->rflag) {
10917                         case 0: return rb_str_new2("UHF");
10918                         case 1: return rb_str_new2("RHF");
10919                         case 2: return rb_str_new2("ROHF");
10920                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10921                 }
10922         } else if (kval == sAlphaSym) {
10923                 return INT2NUM(mol->bset->ne_alpha);
10924         } else if (kval == sBetaSym) {
10925                 return INT2NUM(mol->bset->ne_beta);
10926         } else if (kval == sNcompsSym) {
10927                 return INT2NUM(mol->bset->ncomps);
10928         } else if (kval == sNshellsSym) {
10929                 return INT2NUM(mol->bset->nshells);
10930         } else {
10931                 kval = rb_inspect(kval);
10932                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10933                 return Qnil;  /*  Does not reach here  */
10934         }
10935 }
10936
10937 /*
10938  *  call-seq:
10939  *     mo_type
10940  *
10941  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10942  */
10943 static VALUE
10944 s_Molecule_MOType(VALUE self)
10945 {
10946         return s_Molecule_GetMOInfo(self, sTypeSym);
10947 }
10948
10949 #pragma mark ------ Molecular Topology ------
10950
10951 /*
10952  *  call-seq:
10953  *     search_equivalent_atoms(ig = nil)
10954  *
10955  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
10956  */
10957 static VALUE
10958 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10959 {
10960         Molecule *mol;
10961         Int *result, i;
10962         VALUE val;
10963         IntGroup *ig;
10964     Data_Get_Struct(self, Molecule, mol);
10965         if (mol->natoms == 0)
10966                 return Qnil;
10967         rb_scan_args(argc, argv, "01", &val);
10968         if (val != Qnil)
10969                 ig = IntGroupFromValue(val);
10970         else ig = NULL;
10971         result = MoleculeSearchEquivalentAtoms(mol, ig);
10972         if (result == NULL)
10973                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10974         if (ig != NULL)
10975                 IntGroupRelease(ig);
10976         val = rb_ary_new2(mol->natoms);
10977         for (i = 0; i < mol->natoms; i++)
10978                 rb_ary_push(val, INT2NUM(result[i]));
10979         free(result);
10980         return val;
10981 }
10982
10983 /*
10984  *  call-seq:
10985  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10986  *
10987  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10988  *  Name is the name of the new pi anchor, and group is the atoms that define
10989  *  the pi system. Type (a String) is an atom type for MM implementation.
10990  *  Weights represent the relative significance of the component atoms; if omitted, then
10991  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
10992  *  The weight values will be normalized so that the sum of the weights is 1.0.
10993  *  The weight values must be positive.
10994  *  Index is the atom index where the created pi-anchor is inserted in the 
10995  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
10996  *  having the largest index.
10997  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
10998  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10999  */
11000 static VALUE
11001 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11002 {
11003         Molecule *mol;
11004         VALUE nval, gval;
11005         IntGroup *ig;
11006         Int i, n, idx, last_component;
11007         Atom a, *ap;
11008         PiAnchor an;
11009         AtomRef *aref;
11010         if (argc < 2 || argc >= 6)
11011                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11012         nval = *argv++;
11013         gval = *argv++;
11014         argc -= 2;
11015     Data_Get_Struct(self, Molecule, mol);
11016         ig = IntGroupFromValue(gval);
11017         memset(&a, 0, sizeof(a));
11018         memset(&an, 0, sizeof(an));
11019         strncpy(a.aname, StringValuePtr(nval), 4);
11020         if (a.aname[0] == '_')
11021                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11022         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11023         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11024                 if (n >= mol->natoms) {
11025                         AtomConnectResize(&an.connect, 0);
11026                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11027                 }
11028                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11029                 last_component = n;
11030         }
11031         if (an.connect.count == 0)
11032                 rb_raise(rb_eMolbyError, "no atoms are specified");
11033         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11034         for (i = 0; i < an.connect.count; i++) {
11035                 an.coeffs[i] = 1.0 / an.connect.count;
11036         }
11037         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11038                 /*  Atom type  */
11039                 if (argv[0] != Qnil)
11040                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11041                 argc--;
11042                 argv++;
11043         }
11044         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11045                 if (argv[0] != Qnil) {
11046                         VALUE aval = rb_ary_to_ary(argv[0]);
11047                         Double d, sum;
11048                         if (RARRAY_LEN(aval) != an.connect.count)
11049                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11050                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11051                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11052                                 if (d <= 0.0)
11053                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11054                                 sum += d;
11055                                 an.coeffs[i] = d;
11056                         }
11057                         for (i = 0; i < an.connect.count; i++)
11058                                 an.coeffs[i] /= sum;
11059                 }
11060                 argc--;
11061                 argv++;
11062         }
11063         if (argc > 0 && argv[0] != Qnil) {
11064                 /*  Index  */
11065                 idx = NUM2INT(rb_Integer(argv[0]));
11066         } else idx = -1;
11067         if (idx < 0 || idx > mol->natoms) {
11068                 /*  Immediately after the last specified atom  */
11069                 idx = last_component + 1;
11070         }
11071         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11072         memmove(a.anchor, &an, sizeof(PiAnchor));
11073         /*  Use residue information of the last specified atom  */
11074         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11075         a.resSeq = ap->resSeq;
11076         strncpy(a.resName, ap->resName, 4);
11077         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11078                 return Qnil;
11079         MoleculeCalculatePiAnchorPosition(mol, idx);
11080     aref = AtomRefNew(mol, idx);
11081     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11082 }
11083
11084 #pragma mark ------ Molecular Properties ------
11085
11086 /*
11087  *  call-seq:
11088  *     set_property(name, value[, index]) -> value
11089  *     set_property(name, values, group) -> values
11090  *
11091  *  Set molecular property. A property is a floating-point number with a specified name,
11092  *  and can be set for each frame separately. The name of the property is given as a String.
11093  *  The value can be a single floating point number, which is set to the current frame.
11094  *  
11095  */
11096 static VALUE
11097 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11098 {
11099         Molecule *mol;
11100         VALUE nval, vval, ival;
11101         char *name;
11102         IntGroup *ig;
11103         Int i, n, idx, fidx;
11104         Double *dp;
11105         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11106     Data_Get_Struct(self, Molecule, mol);
11107         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11108                 idx = NUM2INT(rb_Integer(nval));
11109                 if (idx < 0 || idx >= mol->nmolprops)
11110                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11111         } else {
11112                 name = StringValuePtr(nval);
11113                 idx = MoleculeLookUpProperty(mol, name);
11114                 if (idx < 0) {
11115                         idx = MoleculeCreateProperty(mol, name);
11116                         if (idx < 0)
11117                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11118                 }
11119         }
11120         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11121                 if (ival == Qnil)
11122                         fidx = mol->cframe;
11123                 else {
11124                         fidx = NUM2INT(rb_Integer(ival));
11125                         n = MoleculeGetNumberOfFrames(mol);
11126                         if (fidx < 0 || fidx >= n)
11127                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11128                 }
11129                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11130                 dp = (Double *)malloc(sizeof(Double));
11131                 *dp = NUM2DBL(rb_Float(vval));
11132                 n = 1;
11133         } else {
11134                 vval = rb_ary_to_ary(vval);
11135                 ig = IntGroupFromValue(ival);
11136                 n = IntGroupGetCount(ig);
11137                 if (n == 0)
11138                         rb_raise(rb_eMolbyError, "No frames are specified");
11139                 if (RARRAY_LEN(vval) < n)
11140                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11141                 dp = (Double *)calloc(sizeof(Double), n);
11142                 for (i = 0; i < n; i++)
11143                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11144         }
11145         
11146         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11147         free(dp);
11148         IntGroupRelease(ig);
11149         return self;
11150 }
11151
11152 /*
11153  *  call-seq:
11154  *     get_property(name[, index]) -> value
11155  *     get_property(name, group) -> values
11156  *
11157  *  Get molecular property. In the first form, a property value for a single frame is returned.
11158  *  (If index is omitted, then the value for the current frame is given)
11159  *  In the second form, an array of property values for the given frames is returned.
11160  *  If name is not one of known properties or a valid index integer, exception is raised.
11161  */
11162 static VALUE
11163 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11164 {
11165         Molecule *mol;
11166         VALUE nval, ival;
11167         char *name;
11168         IntGroup *ig;
11169         Int i, n, idx, fidx;
11170         Double *dp;
11171         rb_scan_args(argc, argv, "11", &nval, &ival);
11172     Data_Get_Struct(self, Molecule, mol);
11173         if (mol->nmolprops == 0)
11174                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11175         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11176                 idx = NUM2INT(rb_Integer(nval));
11177                 if (idx < 0 || idx >= mol->nmolprops)
11178                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11179         } else {
11180                 name = StringValuePtr(nval);
11181                 idx = MoleculeLookUpProperty(mol, name);
11182                 if (idx < 0)
11183                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11184         }
11185         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11186                 if (ival == Qnil)
11187                         fidx = mol->cframe;
11188                 else {
11189                         fidx = NUM2INT(rb_Integer(ival));
11190                         n = MoleculeGetNumberOfFrames(mol);
11191                         if (fidx < 0 || fidx >= n)
11192                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11193                 }
11194                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11195                 ival = INT2FIX(fidx);
11196                 n = 1;
11197         } else {
11198                 ig = IntGroupFromValue(ival);
11199                 n = IntGroupGetCount(ig);
11200                 if (n == 0)
11201                         return rb_ary_new();
11202         }
11203         dp = (Double *)calloc(sizeof(Double), n);
11204         MoleculeGetProperty(mol, idx, ig, dp);  
11205         if (FIXNUM_P(ival))
11206                 ival = rb_float_new(dp[0]);
11207         else {
11208                 ival = rb_ary_new();
11209                 for (i = n - 1; i >= 0; i--) {
11210                         nval = rb_float_new(dp[i]);
11211                         rb_ary_store(ival, i, nval);
11212                 }
11213         }
11214         free(dp);
11215         IntGroupRelease(ig);
11216         return ival;
11217 }
11218
11219 /*
11220  *  call-seq:
11221  *     property_names -> Array
11222  *
11223  *  Get an array of property names.
11224  */
11225 static VALUE
11226 s_Molecule_PropertyNames(VALUE self)
11227 {
11228         Molecule *mol;
11229         VALUE rval, nval;
11230         int i;
11231     Data_Get_Struct(self, Molecule, mol);
11232         rval = rb_ary_new();
11233         for (i = mol->nmolprops - 1; i >= 0; i--) {
11234                 nval = rb_str_new2(mol->molprops[i].propname);
11235                 rb_ary_store(rval, i, nval);
11236         }
11237         return rval;
11238 }
11239
11240 #pragma mark ------ Class methods ------
11241
11242 /*
11243  *  call-seq:
11244  *     current       -> Molecule
11245  *
11246  *  Get the currently "active" molecule.
11247  */
11248 static VALUE
11249 s_Molecule_Current(VALUE klass)
11250 {
11251         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11252 }
11253
11254 /*
11255  *  call-seq:
11256  *     Molecule[]          -> Molecule
11257  *     Molecule[n]         -> Molecule
11258  *     Molecule[name]      -> Molecule
11259  *     Molecule[name, k]   -> Molecule
11260  *     Molecule[regex]     -> Molecule
11261  *     Molecule[regex, k]  -> Molecule
11262  *
11263  *  Molecule[] is equivalent to Molecule.current.
11264  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11265  *  Molecule[name] gives the first document (in the order of creation time) that has
11266  *  the given name. If a second argument (k) is given, the k-th document that has the
11267  *  given name is returned.
11268  *  Molecule[regex] gives the first document (in the order of creation time) that
11269  *  has a name matching the regular expression. If a second argument (k) is given, 
11270  *  the k-th document that has a name matching the re is returned.
11271  */
11272 static VALUE
11273 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11274 {
11275         VALUE val, kval;
11276         int idx, k;
11277         Molecule *mol;
11278         char buf[1024];
11279         rb_scan_args(argc, argv, "02", &val, &kval);
11280         if (val == Qnil)
11281                 return s_Molecule_Current(klass);
11282         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11283                 idx = NUM2INT(val);
11284                 mol = MoleculeCallback_moleculeAtIndex(idx);
11285         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11286                 char *p = StringValuePtr(val);
11287                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11288                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11289                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11290                         if (strcmp(buf, p) == 0 && --k == 0)
11291                                 break;
11292                 }
11293         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11294                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11295                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11296                         VALUE name;
11297                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11298                         name = rb_str_new2(buf);
11299                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11300                                 break;
11301                 }       
11302         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11303         
11304         if (mol == NULL)
11305                 return Qnil;
11306         else return ValueFromMolecule(mol);
11307 }
11308
11309 /*
11310  *  call-seq:
11311  *     list         -> array of Molecules
11312  *
11313  *  Get the list of molecules associated to the documents, in the order of creation
11314  *  time of the document. If no document is open, returns an empry array.
11315  */
11316 static VALUE
11317 s_Molecule_List(VALUE klass)
11318 {
11319         Molecule *mol;
11320         int i;
11321         VALUE ary;
11322         i = 0;
11323         ary = rb_ary_new();
11324         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11325                 rb_ary_push(ary, ValueFromMolecule(mol));
11326                 i++;
11327         }
11328         return ary;
11329 }
11330
11331 /*
11332  *  call-seq:
11333  *     ordered_list         -> array of Molecules
11334  *
11335  *  Get the list of molecules associated to the documents, in the order of front-to-back
11336  *  ordering of the associated window. If no document is open, returns an empry array.
11337  */
11338 static VALUE
11339 s_Molecule_OrderedList(VALUE klass)
11340 {
11341         Molecule *mol;
11342         int i;
11343         VALUE ary;
11344         i = 0;
11345         ary = rb_ary_new();
11346         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11347                 rb_ary_push(ary, ValueFromMolecule(mol));
11348                 i++;
11349         }
11350         return ary;
11351 }
11352
11353 #pragma mark ------ Call Subprocess ------
11354
11355 /*  The callback functions for call_subprocess_async  */
11356 static int
11357 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11358 {
11359         int ruby_status;
11360         VALUE procval, retval, args[2];
11361         args[0] = ValueFromMolecule(mol);
11362         args[1] = INT2NUM(status);
11363         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11364         if (procval != Qnil) {
11365                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11366                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11367                         return 1;
11368         }
11369         return 0;
11370 }
11371
11372 static int
11373 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11374 {
11375         int ruby_status;
11376         VALUE procval, retval, args[2];
11377         args[0] = ValueFromMolecule(mol);
11378         args[1] = INT2NUM(tcount);
11379         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11380         if (procval != Qnil) {
11381                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11382                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11383                         return 1;
11384         }
11385         return 0;
11386 }
11387
11388 /*
11389  *  call-seq:
11390  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11391  *
11392  *  Call subprocess asynchronically.
11393  *  If end_callback is given, it will be called (with two arguments self and termination status)
11394  *  when the subprocess terminated.
11395  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11396  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11397  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11398  *  filename begins with ">>", then the message will be appended to the file.
11399  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11400  *  If the argument is nil, then the message will be sent to the Ruby console.
11401  *  Returns the process ID as an integer.
11402  */
11403 static VALUE
11404 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11405 {
11406         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11407         Molecule *mol;
11408         char *sout, *serr;
11409         int n;
11410         FILE *fpout, *fperr;
11411         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11412         Data_Get_Struct(self, Molecule, mol);
11413
11414         if (stdout_val == Qnil) {
11415                 fpout = (FILE *)1;
11416         } else {
11417                 sout = StringValuePtr(stdout_val);
11418                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11419                         fpout = NULL;
11420                 else {
11421                         if (strncmp(sout, ">>", 2) == 0) {
11422                                 sout += 2;
11423                                 fpout = fopen(sout, "a");
11424                         } else {
11425                                 if (*sout == '>')
11426                                         sout++;
11427                                 fpout = fopen(sout, "w");
11428                         }
11429                         if (fpout == NULL)
11430                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11431                 }
11432         }
11433         if (stderr_val == Qnil) {
11434                 fperr = (FILE *)1;
11435         } else {
11436                 serr = StringValuePtr(stderr_val);
11437                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11438                         fperr = NULL;
11439                 else {
11440                         if (strncmp(serr, ">>", 2) == 0) {
11441                                 serr += 2;
11442                                 fpout = fopen(serr, "a");
11443                         } else {
11444                                 if (*serr == '>')
11445                                         serr++;
11446                                 fperr = fopen(serr, "w");
11447                         }
11448                         if (fperr == NULL)
11449                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11450                 }
11451         }
11452         
11453         /*  Register procs as instance variables  */
11454         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11455         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11456         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11457         if (fpout != NULL && fpout != (FILE *)1)
11458                 fclose(fpout);
11459         if (fperr != NULL && fperr != (FILE *)1)
11460                 fclose(fperr);
11461         return INT2NUM(n);
11462 }
11463
11464 #pragma mark ====== Define Molby Classes ======
11465
11466 void
11467 Init_Molby(void)
11468 {
11469         int i;
11470         
11471         /*  Define module Molby  */
11472         rb_mMolby = rb_define_module("Molby");
11473         
11474         /*  Define Vector3D, Transform, IntGroup  */
11475         Init_MolbyTypes();
11476         
11477         /*  Define MDArena  */
11478         Init_MolbyMDTypes();
11479
11480         /*  class Molecule  */
11481         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11482
11483         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11484     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11485     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11486         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11487         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11488
11489     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11490     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11491     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11492     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11493     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11494     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11495     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11496     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11497     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11498     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11499         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11500     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11501     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11502     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11503     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11504     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11505     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11506     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11507         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11508         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11509         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11510         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11511         
11512     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11513         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11514     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11515     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11516     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11517
11518     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11519     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11520     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11521     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11522     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11523     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11524         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11525         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11526         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11527         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11528         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11529         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11530         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11531         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11532         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11533         
11534         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11535         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11536         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11537         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11538         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11539         
11540         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11541     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11542         rb_define_alias(rb_cMolecule, "+", "add");
11543     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11544         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11545         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11546         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11547         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11548         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11549         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11550         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11551         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11552         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11553         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11554         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11555         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11556         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11557         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11558         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11559         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11560         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11561         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11562         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11563         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11564
11565         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11566         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11567         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11568         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11569         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11570
11571         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11572         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11573         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11574         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11575         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11576         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11577         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11578         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11579         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11580
11581         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11582         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11583         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11584         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11585         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11586         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11587         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11588         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11589         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11590         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11591         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11592         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11593         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11594         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11595         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11596         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11597         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11598         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11599         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11600         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11601         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11602         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11603
11604         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11605         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11606         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11607         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11608         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11609         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11610         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11611
11612         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11613         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11614         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11615         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11616         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11617         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11618         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11619         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11620         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11621         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11622         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11623         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11624         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11625
11626         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11627         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11628         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11629         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11630         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11631         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11632
11633         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11634         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11635         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11636         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11637         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11638         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11639         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11640         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11641         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11642         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11643         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11644         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11645         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11646         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11647         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11648         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11649         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11650         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11651         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11652         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11653         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11654         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11655         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11656         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11657         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11658         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11659         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11660         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11661         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11662         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11663         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11664         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11665         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11666         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11667         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11668         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11669         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11670         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11671         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11672         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11673         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11674         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11675         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11676         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11677         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11678         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11679         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11680         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11681         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11682         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11683         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11684         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11685         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11686         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11687         
11688         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11689         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11690         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11691         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11692         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11693         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11694         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11695         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11696         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11697         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11698         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11699         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11700         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11701
11702         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11703         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11704         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11705         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11706         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11707         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11708         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11709         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11710         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11711         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11712         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11713         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11714         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11715         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11716                 
11717         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11718         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11719         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11720         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11721         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11722         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11723         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11724         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11725         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11726         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11727         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11728         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11729         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11730         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11731         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11732         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11733         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11734         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11735         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11736         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11737         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11738         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11739         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11740
11741         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11742         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11743         
11744         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11745         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11746         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11747                 
11748         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11749         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11750         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11751         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11752         
11753         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11754         
11755         /*  class MolEnumerable  */
11756         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11757     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11758         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11759         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11760     rb_define_alias(rb_cMolEnumerable, "size", "length");
11761         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11762         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11763
11764         /*  class AtomRef  */
11765         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11766         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11767                 char buf[64];
11768                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11769                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11770                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11771                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11772                 strcat(buf, "=");
11773                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11774         }
11775         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11776         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11777         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11778         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11779         s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11780         rb_global_variable(&s_SetAtomAttrString);
11781         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11782         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11783
11784         /*  class Parameter  */
11785         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11786         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11787         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11788         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11789         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11790         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11791         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11792         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11793         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11794         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11795         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11796         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11797         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11798         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11799         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11800         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11801         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11802         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11803         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11804         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11805         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11806         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11807         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11808         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11809         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11810         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11811         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11812         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11813         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11814         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11815         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11816         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11817         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11818         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11819         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11820         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11821         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11822         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11823         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11824         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11825         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11826         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11827         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11828         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11829         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11830         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11831         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11832         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11833         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11834         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11835         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11836         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11837         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11838         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11839
11840         /*  class ParEnumerable  */
11841         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11842     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11843         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11844         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11845         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11846         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11847         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11848         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11849         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11850         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11851         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11852         
11853         /*  class ParameterRef  */
11854         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11855         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11856                 char buf[64];
11857                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11858                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11859                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11860                 if (s_ParameterAttrDefTable[i].symref != NULL)
11861                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11862                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11863                         strcat(buf, "=");
11864                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11865                 }
11866         }
11867         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11868         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11869         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11870         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11871         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11872         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11873         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11874         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11875
11876         /*  class MolbyError  */
11877         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11878
11879         /*  module Kernel  */
11880         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11881         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11882         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11883         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11884         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11885         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11886         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11887         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11888         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11889         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11890         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11891         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11892         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11893         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11894         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11895         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11896         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11897         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11898         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11899         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11900         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11901         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11902         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11903         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11904
11905         /*  class IO  */
11906         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11907         
11908         s_ID_equal = rb_intern("==");
11909         g_RubyID_call = rb_intern("call");
11910         
11911         s_InitMOInfoKeys();
11912         
11913         /*  Symbols for graphics  */
11914         s_LineSym = ID2SYM(rb_intern("line"));
11915         s_PolySym = ID2SYM(rb_intern("poly"));
11916         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11917         s_ConeSym = ID2SYM(rb_intern("cone"));
11918         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11919 }
11920
11921 #pragma mark ====== Interface with RubyDialog class ======
11922
11923 RubyValue
11924 RubyDialogCallback_parentModule(void)
11925 {
11926         return (RubyValue)rb_mMolby;
11927 }
11928
11929 #pragma mark ====== External functions ======
11930
11931 static VALUE s_ruby_top_self = Qfalse;
11932 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11933 static VALUE s_ruby_export_local_variables = Qfalse;
11934
11935 static VALUE
11936 s_evalRubyScriptOnMoleculeSub(VALUE val)
11937 {
11938         void **ptr = (void **)val;
11939         Molecule *mol = (Molecule *)ptr[1];
11940         VALUE sval, fnval, lnval, retval;
11941         VALUE binding;
11942
11943         /*  Clear the error information (store in the history array if necessary)  */
11944         sval = rb_errinfo();
11945         if (sval != Qnil) {
11946                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11947                 rb_set_errinfo(Qnil);
11948         }
11949
11950         if (s_ruby_top_self == Qfalse) {
11951                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11952         }
11953         if (s_ruby_get_binding_for_molecule == Qfalse) {
11954                 const char *s1 =
11955                  "lambda { |_mol_, _bind_| \n"
11956                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11957                  "  _proc_.call(_mol_) } ";
11958                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11959                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11960         }
11961         if (s_ruby_export_local_variables == Qfalse) {
11962                 const char *s2 =
11963                 "lambda { |_bind_| \n"
11964                 "   # find local variables newly defined in _bind_ \n"
11965                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11966                 " _a_.each { |_vsym_| \n"
11967                 "   _vname_ = _vsym_.to_s \n"
11968                 "   _vval_ = _bind_.eval(_vname_) \n"
11969                 "   #  Define local variable \n"
11970                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11971                 "   #  Then set value  \n"
11972                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11973                 " } \n"
11974                 "}";
11975                 s_ruby_export_local_variables = rb_eval_string(s2);
11976                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11977         }
11978         if (ptr[2] == NULL) {
11979                 char *scr;
11980                 /*  String literal: we need to specify string encoding  */
11981 #if defined(__WXMSW__)
11982                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11983 #else
11984                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11985 #endif
11986                 sval = rb_str_new2(scr);
11987                 free(scr);
11988                 fnval = rb_str_new2("(eval)");
11989                 lnval = INT2FIX(0);
11990         } else {
11991                 sval = rb_str_new2((char *)ptr[0]);
11992                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11993                 lnval = INT2FIX(1);
11994         }
11995         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11996         if (mol != NULL) {
11997                 VALUE mval = ValueFromMolecule(mol);
11998                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11999         }
12000         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12001         if (mol != NULL) {
12002                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12003         }
12004         return retval;
12005 }
12006
12007 RubyValue
12008 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12009 {
12010         RubyValue retval;
12011         void *args[3];
12012         VALUE save_interrupt_flag;
12013 /*      char *save_ruby_sourcefile;
12014         int save_ruby_sourceline; */
12015         if (gMolbyIsCheckingInterrupt) {
12016                 MolActionAlertRubyIsRunning();
12017                 *status = -1;
12018                 return (RubyValue)Qnil;
12019         }
12020         gMolbyRunLevel++;
12021         args[0] = (void *)script;
12022         args[1] = (void *)mol;
12023         args[2] = (void *)fname;
12024         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12025 /*      save_ruby_sourcefile = ruby_sourcefile;
12026         save_ruby_sourceline = ruby_sourceline; */
12027         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12028         if (*status != 0) {
12029                 /*  Is this 'exit' exception?  */
12030                 VALUE last_exception = rb_gv_get("$!");
12031                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12032                         /*  Capture exit and return the status value  */
12033                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12034                         *status = 0;
12035                         rb_set_errinfo(Qnil);
12036                 }
12037         }
12038         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12039 /*      ruby_sourcefile = save_ruby_sourcefile;
12040         ruby_sourceline = save_ruby_sourceline; */
12041         gMolbyRunLevel--;
12042         return retval;
12043 }
12044
12045 int
12046 Ruby_showValue(RubyValue value, char **outValueString)
12047 {
12048         VALUE val = (VALUE)value;
12049         if (gMolbyIsCheckingInterrupt) {
12050                 MolActionAlertRubyIsRunning();
12051                 return 0;
12052         }
12053         if (val != Qnil) {
12054                 int status;
12055                 char *str;
12056                 gMolbyRunLevel++;
12057                 val = rb_protect(rb_inspect, val, &status);
12058                 gMolbyRunLevel--;
12059                 if (status != 0)
12060                         return status;
12061                 str = StringValuePtr(val);
12062                 if (outValueString != NULL)
12063                         *outValueString = strdup(str);
12064                 MyAppCallback_showScriptMessage("%s", str);
12065         } else {
12066                 if (outValueString != NULL)
12067                         *outValueString = NULL;
12068         }
12069         return 0;
12070 }
12071
12072 void
12073 Ruby_showError(int status)
12074 {
12075         static const int tag_raise = 6;
12076     char *main_message = "Molby script error";
12077         char *msg = NULL, *msg2;
12078         VALUE val, backtrace;
12079         int interrupted = 0;
12080     int exit_status = -1;
12081         if (status == tag_raise) {
12082                 VALUE errinfo = rb_errinfo();
12083                 VALUE eclass = CLASS_OF(errinfo);
12084                 if (eclass == rb_eInterrupt) {
12085             main_message = "Molby script interrupted";
12086             msg = "Interrupt";
12087                         interrupted = 1;
12088         } else if (eclass == rb_eSystemExit) {
12089             main_message = "Molby script exit";
12090             interrupted = 2;
12091             val = rb_eval_string_protect("$!.status", &status);
12092             if (status == 0) {
12093                 exit_status = NUM2INT(rb_Integer(val));
12094                 asprintf(&msg, "Molby script exit with status %d", exit_status);
12095             } else {
12096                 asprintf(&msg, "Molby script exit with unknown status");
12097             }
12098         }
12099         }
12100         gMolbyRunLevel++;
12101     if (exit_status != 0) {
12102         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12103         if (msg == NULL) {
12104             val = rb_eval_string_protect("$!.to_s", &status);
12105             if (status == 0)
12106                 msg = RSTRING_PTR(val);
12107             else
12108                 msg = "(message not available)";
12109         }
12110         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12111     } else {
12112         msg2 = strdup(msg);
12113     }
12114         MyAppCallback_messageBox(msg2, main_message, 0, 3);
12115         free(msg2);
12116     if (interrupted == 2) {
12117         free(msg);
12118         if (!gUseGUI && exit_status == 0)
12119             exit(0);  // Capture exit(0) here and force exit
12120     }
12121         gMolbyRunLevel--;
12122 }
12123
12124 /*  Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode.  */
12125 int
12126 Molby_loadScript(const char *script, int from_file)
12127 {
12128     int status;
12129     gMolbyRunLevel++;
12130     if (from_file)
12131         rb_load_protect(rb_str_new2(script), 0, &status);
12132     else
12133         rb_eval_string_protect(script, &status);
12134     gMolbyRunLevel--;
12135     return status;
12136 }
12137
12138 void
12139 Molby_getDescription(char **versionString, char **auxString)
12140 {
12141         extern const char *gVersionString, *gCopyrightString;
12142         extern int gRevisionNumber;
12143         extern char *gLastBuildString;
12144     char *s1, *s2;
12145         char *revisionString;
12146         if (gRevisionNumber > 0) {
12147                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12148         } else revisionString = "";
12149
12150     asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12151 #if defined(__WXMSW__)
12152     #if TARGET_ARCH == 64
12153              "Molby (64 bit)",
12154     #else
12155              "Molby (32 bit)",
12156     #endif
12157 #else
12158              "Molby",
12159 #endif
12160              gVersionString, revisionString, gCopyrightString, gLastBuildString);
12161     if (gUseGUI) {
12162         asprintf(&s2,
12163                  "\nIncluding:\n"
12164                  "%s"
12165                  "ruby %s, http://www.ruby-lang.org/\n"
12166                  "%s\n"
12167                  "FFTW 3.3.2, http://www.fftw.org/\n"
12168                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12169                  "  and Massachusetts Institute of Technology",
12170                  MyAppCallback_getGUIDescriptionString(),
12171                  gRubyVersion, gRubyCopyright);
12172     } else {
12173         asprintf(&s2,
12174                  "Including "
12175                  "ruby %s, http://www.ruby-lang.org/\n"
12176                  "%s\n"
12177                  "FFTW 3.3.2, http://www.fftw.org/\n"
12178                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12179                  "  and Massachusetts Institute of Technology",
12180                  gRubyVersion, gRubyCopyright);
12181
12182     }
12183         if (revisionString[0] != 0)
12184                 free(revisionString);
12185         if (versionString != NULL)
12186         *versionString = s1;
12187     if (auxString != NULL)
12188         *auxString = s2;
12189 }
12190
12191 void
12192 Molby_startup(const char *script, const char *dir)
12193 {
12194         VALUE val;
12195         int status;
12196         char *libpath;
12197         char *respath, *p, *wbuf;
12198
12199         /*  Get version/copyright string from Ruby interpreter  */
12200         {
12201                 gRubyVersion = strdup(ruby_version);
12202                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12203                                  "  ",  /*  Indent for displaying in About dialog  */
12204                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12205         }
12206         
12207         /*  Read build and revision information for Molby  */
12208 /*      {
12209                 char buf[200];
12210                 extern int gRevisionNumber;
12211                 extern char *gLastBuildString;
12212                 FILE *fp = fopen("../buildInfo.txt", "r");
12213                 gLastBuildString = "";
12214                 if (fp != NULL) {
12215                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12216                                 char *p1 = strchr(buf, '\"');
12217                                 char *p2 = strrchr(buf, '\"');
12218                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12219                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12220                                         buf[p2 - p1 - 1] = 0;
12221                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12222                                 }
12223                         }
12224                         fclose(fp);
12225                 }
12226                 fp = fopen("../revisionInfo.txt", "r");
12227                 gRevisionNumber = 0;
12228                 if (fp != NULL) {
12229                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12230                                 gRevisionNumber = strtol(buf, NULL, 0);
12231                         }
12232                         fclose(fp);
12233                 }
12234     } */
12235
12236     if (!gUseGUI) {
12237         char *wbuf2;
12238         Molby_getDescription(&wbuf, &wbuf2);
12239         MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12240         free(wbuf);
12241         free(wbuf2);
12242     }
12243         
12244         /*  Read atom display parameters  */
12245         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12246         MyAppCallback_setConsoleColor(1);
12247         MyAppCallback_showScriptMessage("%s", wbuf);
12248         MyAppCallback_setConsoleColor(0);
12249                 free(wbuf);
12250         }
12251         
12252         /*  Read default parameters  */
12253         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12254         if (wbuf != NULL) {
12255         MyAppCallback_setConsoleColor(1);
12256         MyAppCallback_showScriptMessage("%s", wbuf);
12257         MyAppCallback_setConsoleColor(0);
12258                 free(wbuf);
12259         }
12260                 
12261         /*  Initialize Ruby interpreter  */
12262 #if __WXMSW__
12263         if (gUseGUI) {
12264                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12265                     it causes rb_bug() (= fatal error) during ruby_init().
12266                     As a workaround, these standard streams are reopend as
12267                     NUL stream.  */
12268                 freopen("NUL", "r", stdin);
12269                 freopen("NUL", "w", stdout);
12270                 freopen("NUL", "w", stderr);
12271         }
12272 #endif
12273         ruby_init();
12274
12275         {
12276                 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12277                 Init_shift_jis();
12278                 Init_trans_japanese_sjis();
12279         }
12280         
12281         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12282         ruby_incpush(".");
12283         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12284         ruby_incpush(libpath);
12285         free(libpath);
12286         ruby_incpush(dir);
12287
12288         ruby_script("Molby");
12289         
12290         /*  Find the resource path (the parent directory of the given directory)  */
12291         respath = strdup(dir);
12292         p = strrchr(respath, '/');
12293         if (p == NULL && PATH_SEPARATOR != '/')
12294                 p = strrchr(respath, PATH_SEPARATOR);
12295         if (p != NULL)
12296                 *p = 0;
12297         val = Ruby_NewFileStringValue(respath);
12298         rb_define_global_const("MolbyResourcePath", val);
12299         free(respath);
12300
12301         /*  Define Molby classes  */
12302         Init_Molby();
12303     if (gUseGUI)
12304         RubyDialogInitClass();
12305
12306         rb_define_const(rb_mMolby, "ResourcePath", val);
12307         val = Ruby_NewFileStringValue(dir);
12308         rb_define_const(rb_mMolby, "ScriptPath", val);
12309         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12310         val = Ruby_NewFileStringValue(p);
12311         rb_define_const(rb_mMolby, "MbsfPath", val);    
12312         free(p);
12313         
12314         p = MyAppCallback_getHomeDir();
12315         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12316         rb_define_const(rb_mMolby, "HomeDirectory", val);
12317         free(p);
12318         p = MyAppCallback_getDocumentHomeDir();
12319         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12320         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12321         free(p);
12322         
12323     if (gUseGUI)
12324         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12325     else
12326         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12327
12328     {
12329         /*  Create objects for stdout and stderr  */
12330         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12331         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12332         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12333         rb_gv_set("$stdout", val);
12334         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12335         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12336         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12337         rb_gv_set("$stderr", val);
12338
12339         /*  Create objects for stdin  */
12340         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12341         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12342         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12343         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12344         rb_gv_set("$stdin", val);
12345     }
12346         
12347         /*  Global variable to hold error information  */
12348         rb_define_variable("$backtrace", &gMolbyBacktrace);
12349         rb_define_variable("$error_history", &gMolbyErrorHistory);
12350         gMolbyErrorHistory = rb_ary_new();
12351         
12352         /*  Global variables for script menus  */
12353         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12354         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12355         gScriptMenuCommands = rb_ary_new();
12356         gScriptMenuEnablers = rb_ary_new();
12357         
12358     if (gUseGUI) {
12359         /*  Register interrupt check code  */
12360         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12361         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12362         s_SetIntervalTimer(0, 50);
12363     }
12364         
12365         /*  Read the startup script  */
12366         if (script != NULL && script[0] != 0) {
12367                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12368                 gMolbyRunLevel++;
12369                 rb_load_protect(rb_str_new2(script), 0, &status);
12370                 gMolbyRunLevel--;
12371                 if (status != 0)
12372                         Ruby_showError(status);
12373                 else
12374                         MyAppCallback_showScriptMessage("Done.\n");
12375         }
12376 }
12377
12378 void
12379 Molby_buildARGV(int argc, const char **argv)
12380 {
12381         int i;
12382     rb_ary_clear(rb_argv);
12383     for (i = 0; i < argc; i++) {
12384                 VALUE arg = rb_tainted_str_new2(argv[i]);
12385                 OBJ_FREEZE(arg);
12386                 rb_ary_push(rb_argv, arg);
12387     }
12388 }