OSDN Git Service

Cleanup Makefiles. Molby_command (on Mac) build target is restored.
[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 <node.h>     /*  for rb_add_event_hook()  */
30
31 #if defined(__WXMAC__) || defined(__CMDMAC__)
32 #define USE_PTHREAD_FOR_TIMER 1
33 #endif
34
35 #if !__WXMSW__
36 #if USE_PTHREAD_FOR_TIMER
37 #include <unistd.h>   /*  for usleep()  */
38 #include <pthread.h>  /*  for pthread  */
39 #else
40 #include <signal.h>   /*  for sigaction()  */
41 #endif
42 #endif
43
44 #include "../Missing.h"
45
46 #pragma mark ====== Global Values ======
47
48 VALUE rb_eMolbyError;
49 VALUE rb_mMolby;
50 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
51 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
52
53 VALUE gMolbyBacktrace;
54
55 int gMolbyRunLevel = 0;
56 int gMolbyIsCheckingInterrupt = 0;
57
58 char *gRubyVersion, *gRubyCopyright;
59
60 /*  For convenience  */
61 static ID s_ID_equal;  /*  rb_intern("==")  */
62
63 int g_RubyID_call;
64
65 /*  Symbols for atom attributes  */
66 static VALUE
67         s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
68         s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
69         s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
70         s_RSym, s_XSym, s_YSym, s_ZSym,
71         s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
72         s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
73         s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
74         s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
75         s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
76         s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
77
78 /*  Symbols for parameter attributes  */
79 static VALUE
80         s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
81         s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
82         /* s_ASym, s_BSym, */
83         s_ReqSym, s_EpsSym,
84         /* s_A14Sym, s_B14Sym, */
85         s_Req14Sym, s_Eps14Sym,
86         s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
87         s_CommentSym, s_SourceSym;
88
89 /*
90  *  Utility function
91  *  Get ary[i] by calling "[]" method
92  */
93 VALUE
94 Ruby_ObjectAtIndex(VALUE ary, int idx)
95 {
96         static ID index_method = 0;
97         if (TYPE(ary) == T_ARRAY) {
98                 int len = RARRAY_LEN(ary);
99                 if (idx >= 0 && idx < len)
100                         return (RARRAY_PTR(ary))[idx];
101                 else return Qnil;
102         }
103         if (index_method == 0)
104                 index_method = rb_intern("[]");
105         return rb_funcall(ary, index_method, 1, INT2NUM(idx));
106 }
107
108 char *
109 Ruby_FileStringValuePtr(VALUE *valp)
110 {
111 #if __WXMSW__
112         char *p = strdup(StringValuePtr(*valp));
113         translate_char(p, '/', '\\');
114         *valp = rb_str_new2(p);
115         free(p);
116         return StringValuePtr(*valp);
117 #else
118         return StringValuePtr(*valp);
119 #endif
120 }
121
122 VALUE
123 Ruby_NewFileStringValue(const char *fstr)
124 {
125 #if __WXMSW__
126         VALUE retval;
127         char *p = strdup(fstr);
128         translate_char(p, '\\', '/');
129         retval = rb_str_new2(p);
130         free(p);
131         return retval;
132 #else
133         return rb_str_new2(fstr);
134 #endif
135 }
136
137 VALUE
138 Ruby_ObjToStringObj(VALUE val)
139 {
140         switch (TYPE(val)) {
141                 case T_STRING:
142                         return val;
143                 case T_SYMBOL:
144                         return rb_str_new2(rb_id2name(SYM2ID(val)));
145                 default:
146                         return rb_str_to_str(val);
147         }
148 }
149
150 #pragma mark ====== Message input/output ======
151
152 /*
153  *  call-seq:
154  *     message_box(str, title, button = nil, icon = :info)
155  *
156  *  Show a message box.
157  *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
158  *  Icon: :info, :warning, :error
159  */
160 static VALUE
161 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
162 {
163         char *str, *title, *s;
164         int buttons, icon;
165         VALUE sval, tval, bval, ival;
166         rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
167         str = StringValuePtr(sval);
168         title = StringValuePtr(tval);
169         if (bval != Qnil) {
170                 bval = Ruby_ObjToStringObj(bval);
171                 s = RSTRING_PTR(bval);
172                 if (strncmp(s, "ok", 2) == 0)
173                         buttons = 1;
174                 else if (strncmp(s, "cancel", 6) == 0)
175                         buttons = 2;
176                 else
177                         rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
178         } else buttons = 3;
179         if (ival != Qnil) {
180                 ival = Ruby_ObjToStringObj(ival);
181                 s = RSTRING_PTR(ival);
182                 if (strncmp(s, "info", 4) == 0)
183                         icon = 1;
184                 else if (strncmp(s, "warn", 4) == 0)
185                         icon = 2;
186                 else if (strncmp(s, "err", 3) == 0)
187                         icon = 3;
188                 else
189                         rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
190         } else icon = 1;
191         MyAppCallback_messageBox(str, title, buttons, icon);
192         return Qnil;
193 }
194
195 /*
196  *  call-seq:
197  *     error_message_box(str)
198  *
199  *  Show an error message box.
200  */
201 static VALUE
202 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
203 {
204         char *str = StringValuePtr(sval);
205         MyAppCallback_errorMessageBox("%s", str);
206         return Qnil;
207 }
208
209 /*
210  *  call-seq:
211  *     ask(prompt, default = nil) -> string
212  *
213  *  Open a modal dialog and get a line of text.
214  */
215 static VALUE
216 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
217 {
218         volatile VALUE prompt, message;
219         char buf[1024];
220         int retval;
221         rb_scan_args(argc, argv, "11", &prompt, &message);
222         if (message != Qnil) {
223                 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
224                 buf[sizeof buf - 1] = 0;
225         } else buf[0] = 0;
226         retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
227         if (retval)
228                 return rb_str_new2(buf);
229         else
230                 return Qnil;    
231 }
232
233 /*
234  *  call-seq:
235  *     show_console_window
236  *
237  *  Show the console window and bring to the front.
238  */
239 static VALUE
240 s_Kernel_ShowConsoleWindow(VALUE self)
241 {
242         MyAppCallback_showConsoleWindow();
243         return Qnil;
244 }
245
246 /*
247  *  call-seq:
248  *     hide_console_window
249  *
250  *  Hide the console window.
251  */
252 static VALUE
253 s_Kernel_HideConsoleWindow(VALUE self)
254 {
255         MyAppCallback_hideConsoleWindow();
256         return Qnil;
257 }
258
259 /*
260  *  call-seq:
261  *     bell
262  *
263  *  Ring the system bell.
264  */
265 static VALUE
266 s_Kernel_Bell(VALUE self)
267 {
268         MyAppCallback_bell();
269         return Qnil;
270 }
271
272 /*
273  *  call-seq:
274  *     play_sound(filename, flag = 0)
275  *
276  *  Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
277  *  1, play the sound asynchronously; 3, play the sound with loop asynchronously
278  */
279 static VALUE
280 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
281 {
282         VALUE fnval, flval;
283         int flag, retval;
284         char *fname;
285         rb_scan_args(argc, argv, "11", &fnval, &flval);
286         if (flval == Qnil)
287                 flag = 0;
288         else flag = NUM2INT(rb_Integer(flval));
289         fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
290         fname = StringValuePtr(fnval);
291         retval = MyAppCallback_playSound(fname, flag);
292         return (retval ? Qtrue : Qnil);
293 }
294
295 /*
296  *  call-seq:
297  *     stop_sound
298  *
299  *  Stop the sound if playing.
300  */
301 static VALUE
302 s_Kernel_StopSound(VALUE self)
303 {
304         MyAppCallback_stopSound();
305         return Qnil;
306 }
307
308 /*
309  *  call-seq:
310  *     export_to_clipboard(str)
311  *
312  *  Export the given string to clipboard.
313  */
314 static VALUE
315 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
316 {
317 #if !defined(__CMDMAC__)
318         const char *s = StringValuePtr(sval);
319         char *ns;
320 #if __WXMSW__
321         /*  Convert the end-of-line characters  */
322         {       const char *p; int nc; char *np;
323                 nc = 0;
324                 for (p = s; *p != 0; p++) {
325                         if (*p == '\n')
326                                 nc++;
327                 }       
328                 ns = (char *)malloc(strlen(s) + nc + 1);
329                 for (np = ns, p = s; *p != 0; p++, np++) {
330                         if (*p == '\n')
331                                 *np++ = '\r';
332                         *np = *p;
333                 }
334                 *np = 0;
335         }
336 #else
337         ns = (char *)malloc(strlen(s) + 1);
338         strcpy(ns, s);
339 #if __WXMAC__
340         {       char *np;
341                 /*  wxMac still has Carbon code. Oops.  */
342                 for (np = ns; *np != 0; np++) {
343                         if (*np == '\n')
344                                 *np = '\r';
345                 }
346         }
347 #endif
348 #endif
349         if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
350                 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
351 #endif
352         return Qnil;
353 }
354
355 /*
356  *  call-seq:
357  *     stdout.write(str)
358  *
359  *  Put the message in the main text view in black color.
360  */
361 static VALUE
362 s_StandardOutput(VALUE self, VALUE str)
363 {
364         int n;
365         MyAppCallback_setConsoleColor(0);
366         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
367         return INT2NUM(n);
368 }
369
370 /*
371  *  call-seq:
372  *     stderr.write(str)
373  *
374  *  Put the message in the main text view in red color.
375  */
376 static VALUE
377 s_StandardErrorOutput(VALUE self, VALUE str)
378 {
379         int n;
380         MyAppCallback_setConsoleColor(1);
381         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
382         MyAppCallback_setConsoleColor(0);
383         return INT2NUM(n);
384 }
385
386 /*
387  *  call-seq:
388  *     stdin.gets(rs = $/)
389  *
390  *  Read one line message via dialog box.
391  */
392 static VALUE
393 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
394 {
395         VALUE pval, rval;
396         pval = rb_str_new2("Enter a line:");
397         rval = s_Kernel_Ask(1, &pval, self);
398         if (rval == Qnil)
399                 rb_interrupt();
400         rb_str_cat2(rval, "\n");
401         return rval;
402 }
403
404 /*
405  *  call-seq:
406  *     stdin.method_missing(name, args, ...)
407  *
408  *  Throw an exception, noting only gets and readline are defined.
409  */
410 static VALUE
411 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
412 {
413         VALUE nval;
414         rb_scan_args(argc, argv, "10", &nval);
415         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
416         return Qnil;  /*  Not reached  */
417 }
418
419 #pragma mark ====== Track key events ======
420
421 /*  User interrupt handling
422  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
423  *  key events. This polling should only be enabled during "normal" execution
424  *  of scripts and must be disabled when the rest of the application (or Ruby
425  *  script itself) is handling GUI. This is ensured by appropriate calls to
426  *  enable_interrupt and disable_interrupt.  */
427
428 static VALUE s_interrupt_flag = Qfalse;
429
430 static VALUE
431 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
432 {
433         volatile VALUE message;
434         const char *p;
435         if (Ruby_GetInterruptFlag() == Qtrue) {
436                 rb_scan_args(argc, argv, "01", &message);
437                 if (message != Qnil)
438                         p = StringValuePtr(message);
439                 else
440                         p = NULL;
441                 MyAppCallback_showProgressPanel(p);
442         }
443         return Qnil;
444 }
445
446 static VALUE
447 s_HideProgressPanel(VALUE self)
448 {
449         MyAppCallback_hideProgressPanel();
450         return Qnil;
451 }
452
453 static VALUE
454 s_SetProgressValue(VALUE self, VALUE val)
455 {
456         double dval = NUM2DBL(rb_Float(val));
457         MyAppCallback_setProgressValue(dval);
458         return Qnil;
459 }
460
461 static VALUE
462 s_SetProgressMessage(VALUE self, VALUE msg)
463 {
464         const char *p;
465         if (msg == Qnil)
466                 p = NULL;
467         else p = StringValuePtr(msg);
468         MyAppCallback_setProgressMessage(p);
469         return Qnil;
470 }
471
472 static VALUE
473 s_SetInterruptFlag(VALUE self, VALUE val)
474 {
475         VALUE oldval;
476         if (val != Qundef) {
477                 if (val == Qfalse || val == Qnil)
478                         val = Qfalse;
479                 else val = Qtrue;
480         }
481         oldval = s_interrupt_flag;
482         if (val != Qundef) {
483                 s_interrupt_flag = val;
484                 if (val == Qfalse) {
485                         s_HideProgressPanel(self);
486                 }
487         }
488         return oldval;
489 }
490
491 static VALUE
492 s_GetInterruptFlag(VALUE self)
493 {
494         return s_SetInterruptFlag(self, Qundef);
495 }
496
497 #if 0
498 static VALUE
499 s_Ruby_CallMethod(VALUE val)
500 {
501         void **ptr = (void **)val;
502         VALUE receiver = (VALUE)ptr[0];
503         ID method_id = (ID)ptr[1];
504         VALUE args = (VALUE)ptr[2];
505         VALUE retval;
506         if (method_id == 0) {
507                 /*  args should be a string, which is evaluated  */
508                 if (receiver == Qnil) {
509                         retval = rb_eval_string(StringValuePtr(args));
510                 } else {
511                         retval = rb_obj_instance_eval(1, &args, receiver);
512                 }
513         } else {
514                 /*  args should be an array of arguments  */
515                 retval = rb_apply(receiver, method_id, args);
516         }
517         return retval;
518 }
519
520 VALUE
521 Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
522 {
523         VALUE retval, save_interrupt_flag;
524         void *ptr[3];
525         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
526         ptr[0] = (void *)receiver;
527         ptr[1] = (void *)method_id;
528         ptr[2] = (void *)args;
529         MyAppCallback_beginUndoGrouping();
530         retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
531         MyAppCallback_endUndoGrouping();
532         s_SetInterruptFlag(Qnil, save_interrupt_flag);
533         MyAppCallback_hideProgressPanel();  /*  In case when the progress panel is still onscreen */\v
534         return retval;
535 }
536 #endif
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 {
680         if (s_interrupt_flag != Qfalse) {
681                 static unsigned long sLastTime = 0;
682                 unsigned long currentTime;
683                 int flag;
684                 currentTime = s_GetTimerCount();
685                 if (currentTime != sLastTime) {
686                         sLastTime = currentTime;
687                         gMolbyIsCheckingInterrupt = 1;
688                         flag = MyAppCallback_checkInterrupt();
689                         gMolbyIsCheckingInterrupt = 0;
690                         if (flag) {
691                                 s_SetInterruptFlag(Qnil, Qfalse);
692                                 rb_interrupt();
693                         }
694                 }
695         }
696 }
697
698 #pragma mark ====== Menu handling ======
699
700 /*
701  *  call-seq:
702  *     register_menu(title, method)
703  *
704  *  Register the method (specified as a symbol) in the script menu.
705  *  The method must be either an instance method of Molecule with no argument,
706  *  or a class method of Molecule with one argument (the current molecule).
707  *  The menu associated with the class method can be invoked even when no document
708  *  is open (the argument is set to Qnil in this case). On the other hand, the
709  *  menu associated with the instance method can only be invoked when at least one 
710  *  document is active.
711  */
712 static VALUE
713 s_Kernel_RegisterMenu(VALUE self, VALUE title, VALUE method)
714 {
715         if (TYPE(method) == T_SYMBOL) {
716                 method = rb_funcall(method, rb_intern("to_s"), 0);
717         }
718         MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title));
719         return self;
720 }
721
722 static VALUE
723 s_Kernel_LookupMenu(VALUE self, VALUE title)
724 {
725         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
726         return INT2NUM(n);
727 }
728
729 static VALUE
730 s_Ruby_methodType_sub(VALUE data)
731 {
732         const char **p = (const char **)data;
733         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
734         ID mid = rb_intern(p[1]);
735         int ival;
736         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
737                 ival = 1;
738         else if (rb_respond_to(klass, mid))
739                 ival = 2;
740         else ival = 0;
741         return INT2FIX(ival);
742 }
743         
744 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
745     has the singleton method (class method) with the given name, 0 otherwise.  */
746 int
747 Ruby_methodType(const char *className, const char *methodName)
748 {
749         int status;
750         VALUE retval;
751         const char *p[2];
752         p[0] = className;
753         p[1] = methodName;
754         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
755         if (status == 0)
756                 return FIX2INT(retval);
757         else return 0;
758 }
759
760 /*
761  *  call-seq:
762  *     execute_script_file(fname)
763  *
764  *  Execute the script in the given file. If a molecule is active, then
765  *  the script is evaluated as Molecule.current.instance_eval(script).
766  *  Before entering the script, the current directory is set to the parent
767  *  directory of the script.
768  */
769 static VALUE
770 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
771 {
772         int status;
773         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
774         if (retval == (VALUE)6 && status == -1)
775                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
776         if (status != 0)
777                 rb_jump_tag(status);
778         return retval;
779 }
780
781 /*
782  *  call-seq:
783  *     document_home
784  *
785  *  Get the directory suitable for storing user documents. On Windows
786  *  it is the home directory + "My Documents". On other platforms
787  *  it is the home directory.
788  */
789 static VALUE
790 s_Kernel_DocumentHome(VALUE self)
791 {
792         char *s = MyAppCallback_getDocumentHomeDir();
793         VALUE retval = Ruby_NewFileStringValue(s);
794         free(s);
795         return retval;
796 }
797
798 /*  The callback function for call_subprocess  */
799 static int
800 s_Kernel_CallSubProcess_Callback(void *data)
801 {
802         int status;
803         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
804         if (status != 0 || retval == Qnil || retval == Qfalse)
805                 return 1;
806         else return 0;
807 }
808
809 /*
810  *  call-seq:
811  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
812  *
813  *  Call subprocess. A progress dialog window is displayed, with a message
814  *  "Running #{process_name}...".
815  *  A callback proc can be given, which is called periodically during execution. If the proc returns
816  *  nil or false, then the execution will be interrupted.
817  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
818  *  filename begins with ">>", then the message will be appended to the file.
819  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
820  *  If the argument is nil, then the message will be sent to the Ruby console.
821  */
822 static VALUE
823 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
824 {
825         VALUE cmd, procname, cproc, stdout_val, stderr_val;
826         int n;
827         char *sout, *serr;
828         FILE *fpout, *fperr;
829
830         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
831
832         if (stdout_val == Qnil) {
833                 fpout = (FILE *)1;
834         } else {
835                 sout = StringValuePtr(stdout_val);
836                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
837                         fpout = NULL;
838                 else {
839                         if (strncmp(sout, ">>", 2) == 0) {
840                                 sout += 2;
841                                 fpout = fopen(sout, "a");
842                         } else {
843                                 if (*sout == '>')
844                                         sout++;
845                                 fpout = fopen(sout, "w");
846                         }
847                         if (fpout == NULL)
848                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
849                 }
850         }
851         if (stderr_val == Qnil) {
852                 fperr = (FILE *)1;
853         } else {
854                 serr = StringValuePtr(stderr_val);
855                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
856                         fperr = NULL;
857                 else {
858                         if (strncmp(serr, ">>", 2) == 0) {
859                                 serr += 2;
860                                 fpout = fopen(serr, "a");
861                         } else {
862                                 if (*serr == '>')
863                                         serr++;
864                                 fperr = fopen(serr, "w");
865                         }
866                         if (fperr == NULL)
867                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
868                 }
869         }
870
871         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
872         
873         if (fpout != NULL && fpout != (FILE *)1)
874                 fclose(fpout);
875         if (fperr != NULL && fperr != (FILE *)1)
876                 fclose(fperr);
877
878         return INT2NUM(n);
879
880         
881 }
882
883 /*
884  *  call-seq:
885  *     backquote(cmd)
886  *
887  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
888  */
889 static VALUE
890 s_Kernel_Backquote(VALUE self, VALUE cmd)
891 {
892         char *buf;
893         int n;
894         VALUE val;
895         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
896         if (n != 0)
897                 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
898         if (buf != NULL) {
899                 val = rb_str_new2(buf);
900                 free(buf);
901         } else {
902                 val = rb_str_new2("");
903         }
904         return val;
905 }
906
907 #pragma mark ====== User defaults ======
908
909 /*
910  *  call-seq:
911  *     get_global_settings(key)
912  *
913  *  Get a setting data for key from the application preferences.
914  */
915 static VALUE
916 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
917 {
918         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
919         if (p != NULL) {
920                 VALUE retval = rb_eval_string(p);
921                 free(p);
922                 return retval;
923         } else return Qnil;
924 }
925
926 /*
927  *  call-seq:
928  *     set_global_settings(key, value)
929  *
930  *  Set a setting data for key to the application preferences.
931  */
932 static VALUE
933 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
934 {
935         VALUE sval = rb_inspect(value);
936         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
937         return value;
938 }
939
940 #pragma mark ====== Utility functions (protected funcall) ======
941
942 struct Ruby_funcall2_record {
943         VALUE recv;
944         ID mid;
945         int argc;
946         VALUE *argv;
947 };
948
949 static VALUE
950 s_Ruby_funcall2_sub(VALUE data)
951 {
952         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
953         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
954 }
955
956 VALUE
957 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
958 {
959         struct Ruby_funcall2_record rec;
960         rec.recv = recv;
961         rec.mid = mid;
962         rec.argc = argc;
963         rec.argv = argv;
964         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
965 }
966
967 RubyValue
968 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
969 {
970         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
971 }
972
973 #pragma mark ====== ParameterRef Class ======
974
975 static UnionPar *
976 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
977 {
978         UnionPar *up;
979         ParameterRef *pref;
980         Data_Get_Struct(self, ParameterRef, pref);
981         if (typep != NULL)
982                 *typep = pref->parType;
983         if (pref->parType == kElementParType) {
984                 up = (UnionPar *)&gElementParameters[pref->idx];
985         } else {
986                 up = ParameterRefGetPar(pref);
987                 if (checkEditable) {
988                         if (pref->idx < 0)
989                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
990                         if (up->bond.src != 0 && up->bond.src != -1)
991                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
992                 }
993         }
994         return up;
995 }
996
997 static void
998 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
999 {
1000         UnionPar *up;
1001         ParameterRef *pref;
1002         Data_Get_Struct(self, ParameterRef, pref);
1003         if (pref->mol == NULL)
1004                 return;
1005         up = ParameterRefGetPar(pref);
1006         if (key != s_SourceSym)
1007                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1008         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1009                 /*  Register undo  */
1010                 MolAction *act;
1011                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1012                 MolActionCallback_registerUndo(pref->mol, act);
1013                 MoleculeCallback_notifyModification(pref->mol, 0);
1014                 pref->mol->needsMDRebuild = 1;
1015         }
1016 }
1017
1018 VALUE
1019 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1020 {
1021         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1022         if (pref != NULL)
1023                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1024         else
1025                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1026 }
1027
1028 static int
1029 s_AtomTypeIndexFromValue(VALUE val)
1030 {
1031         if (rb_obj_is_kind_of(val, rb_cNumeric))
1032                 return NUM2INT(val);
1033         else
1034                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1035 }
1036
1037 static const char *s_ParameterTypeNames[] = {
1038         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1039 };
1040 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1041
1042 static int
1043 s_ParTypeFromValue(VALUE val)
1044 {
1045         int i, n;
1046         ID valid;
1047         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1048         if (s_ParameterTypeIDs[0] == 0) {
1049                 for (i = 0; i < n; i++)
1050                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1051         }
1052         valid = rb_to_id(val);
1053         for (i = 0; i < n; i++) {
1054                 if (valid == s_ParameterTypeIDs[i]) {
1055                         if (i == 7)
1056                                 return kElementParType;
1057                         else return kFirstParType + i;
1058                 }
1059         }
1060         return kInvalidParType;
1061 }
1062
1063 /*
1064  *  call-seq:
1065  *     index -> Integer
1066  *
1067  *  Get the index in the parameter list.
1068  */
1069 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1070         ParameterRef *pref;
1071         Data_Get_Struct(self, ParameterRef, pref);
1072         return INT2NUM(pref->idx);
1073 }
1074
1075 /*
1076  *  call-seq:
1077  *     par_type -> String
1078  *
1079  *  Get the parameter type, like "bond", "angle", etc.
1080  */
1081 static VALUE s_ParameterRef_GetParType(VALUE self) {
1082         Int tp;
1083         s_UnionParFromValue(self, &tp, 0);
1084         if (tp == kElementParType)
1085                 return rb_str_new2("element");
1086         tp -= kFirstParType;
1087         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1088                 return rb_str_new2(s_ParameterTypeNames[tp]);
1089         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1090 }
1091
1092 /*
1093  *  call-seq:
1094  *     atom_type -> String or Array of String
1095  *     atom_types -> String or Array of String
1096  *
1097  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1098  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1099  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1100  *  The atom type may be "X", which is a wildcard that matches any atom type.
1101  */
1102 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1103         UnionPar *up;
1104         Int tp, i, n;
1105         UInt types[4];
1106         VALUE vals[4];
1107         up = s_UnionParFromValue(self, &tp, 0);
1108         n = ParameterGetAtomTypes(tp, up, types);
1109         if (n == 0)
1110                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1111         for (i = 0; i < n; i++) {
1112                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1113                         vals[i] = INT2NUM(types[i]);
1114                 else
1115                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1116         }
1117         if (n == 1)
1118                 return vals[0];
1119         else
1120                 return rb_ary_new4(n, vals);
1121 }
1122
1123 /*
1124  *  call-seq:
1125  *     k -> Float
1126  *
1127  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1128  */
1129 static VALUE s_ParameterRef_GetK(VALUE self) {
1130         UnionPar *up;
1131         Int tp, i, n;
1132         VALUE vals[3];
1133         up = s_UnionParFromValue(self, &tp, 0);
1134         switch (tp) {
1135                 case kBondParType:
1136                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1137                 case kAngleParType:
1138                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1139                 case kDihedralParType:
1140                 case kImproperParType:
1141                         if (up->torsion.mult == 1)
1142                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1143                         n = up->torsion.mult;
1144                         if (n > 3)
1145                                 n = 3;
1146                         for (i = 0; i < n; i++)
1147                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1148                         return rb_ary_new4(n, vals);
1149                 default:
1150                         rb_raise(rb_eMolbyError, "invalid member k");
1151         }
1152 }
1153
1154 /*
1155  *  call-seq:
1156  *     r0 -> Float
1157  *
1158  *  Get the equilibrium bond length. Only available for bond parameters.
1159  */
1160 static VALUE s_ParameterRef_GetR0(VALUE self) {
1161         UnionPar *up;
1162         Int tp;
1163         up = s_UnionParFromValue(self, &tp, 0);
1164         if (tp == kBondParType)
1165                 return rb_float_new(up->bond.r0);
1166         else rb_raise(rb_eMolbyError, "invalid member r0");
1167 }
1168
1169 /*
1170  *  call-seq:
1171  *     a0 -> Float
1172  *
1173  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1174  */
1175 static VALUE s_ParameterRef_GetA0(VALUE self) {
1176         UnionPar *up;
1177         Int tp;
1178         up = s_UnionParFromValue(self, &tp, 0);
1179         if (tp == kAngleParType)
1180                 return rb_float_new(up->angle.a0 * kRad2Deg);
1181         else rb_raise(rb_eMolbyError, "invalid member a0");
1182 }
1183
1184 /*
1185  *  call-seq:
1186  *     mult -> Float
1187  *
1188  *  Get the multiplicity. Only available for dihedral and improper parameters.
1189  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1190  */
1191 static VALUE s_ParameterRef_GetMult(VALUE self) {
1192         UnionPar *up;
1193         Int tp;
1194         up = s_UnionParFromValue(self, &tp, 0);
1195         if (tp == kDihedralParType || tp == kImproperParType)
1196                 return rb_float_new(up->torsion.mult);
1197         else rb_raise(rb_eMolbyError, "invalid member mult");
1198 }
1199
1200 /*
1201  *  call-seq:
1202  *     period -> Integer or Array of Integers
1203  *
1204  *  Get the periodicity. Only available for dihedral and improper parameters.
1205  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1206  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1207  */
1208 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1209         UnionPar *up;
1210         Int tp, i, n;
1211         VALUE vals[3];
1212         up = s_UnionParFromValue(self, &tp, 0);
1213         if (tp == kDihedralParType || tp == kImproperParType) {
1214                 if (up->torsion.mult == 1)
1215                         return INT2NUM(up->torsion.period[0]);
1216                 n = up->torsion.mult;
1217                 if (n > 3)
1218                         n = 3;
1219                 for (i = 0; i < n; i++)
1220                         vals[i] = INT2NUM(up->torsion.period[i]);
1221                 return rb_ary_new4(n, vals);
1222         } else rb_raise(rb_eMolbyError, "invalid member period");
1223 }
1224
1225 /*
1226  *  call-seq:
1227  *     phi0 -> Float or Array of Floats
1228  *
1229  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1230  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1231  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1232  */
1233 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1234         UnionPar *up;
1235         Int tp, i, n;
1236         VALUE vals[3];
1237         up = s_UnionParFromValue(self, &tp, 0);
1238         if (tp == kDihedralParType || tp == kImproperParType) {
1239                 if (up->torsion.mult == 1)
1240                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1241                 n = up->torsion.mult;
1242                 if (n > 3)
1243                         n = 3;
1244                 for (i = 0; i < n; i++)
1245                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1246                 return rb_ary_new4(n, vals);
1247         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1248 }
1249
1250 /*
1251  *  call-seq:
1252  *     A -> Float
1253  *
1254  *  Get the "A" value for the van der Waals parameter.
1255  */
1256 /*
1257  static VALUE s_ParameterRef_GetA(VALUE self) {
1258         UnionPar *up;
1259         Int tp;
1260         up = s_UnionParFromValue(self, &tp, 0);
1261         if (tp == kVdwParType)
1262                 return rb_float_new(up->vdw.A);
1263         else if (tp == kVdwPairParType)
1264                 return rb_float_new(up->vdwp.A);
1265         else rb_raise(rb_eMolbyError, "invalid member A");
1266 }
1267 */
1268
1269 /*
1270  *  call-seq:
1271  *     B -> Float
1272  *
1273  *  Get the "B" value for the van der Waals parameter.
1274  */
1275 /*
1276 static VALUE s_ParameterRef_GetB(VALUE self) {
1277         UnionPar *up;
1278         Int tp;
1279         up = s_UnionParFromValue(self, &tp, 0);
1280         if (tp == kVdwParType)
1281                 return rb_float_new(up->vdw.B);
1282         else if (tp == kVdwPairParType)
1283                 return rb_float_new(up->vdwp.B);
1284         else rb_raise(rb_eMolbyError, "invalid member B");
1285 }
1286 */
1287
1288 /*
1289  *  call-seq:
1290  *     r_eq -> Float
1291  *
1292  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1293  */
1294 static VALUE s_ParameterRef_GetReq(VALUE self) {
1295         UnionPar *up;
1296         Int tp;
1297 /*      Double a, b, r; */
1298         Double r;
1299         up = s_UnionParFromValue(self, &tp, 0);
1300         if (tp == kVdwParType) {
1301         /*      a = up->vdw.A;
1302                 b = up->vdw.B;  */
1303                 r = up->vdw.r_eq;
1304         } else if (tp == kVdwPairParType) {
1305         /*      a = up->vdwp.A;
1306                 b = up->vdwp.B;  */
1307                 r = up->vdwp.r_eq;
1308         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1309 /*      if (a == 0.0 || b == 0.0) */
1310         return rb_float_new(r);
1311 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1312 }
1313
1314 /*
1315  *  call-seq:
1316  *     eps -> Float
1317  *
1318  *  Get the minimum energy for the van der Waals parameter.
1319  */
1320 static VALUE s_ParameterRef_GetEps(VALUE self) {
1321         UnionPar *up;
1322         Int tp;
1323 /*      Double a, b; */
1324         Double eps;
1325         up = s_UnionParFromValue(self, &tp, 0);
1326         if (tp == kVdwParType) {
1327         /*      a = up->vdw.A;
1328                 b = up->vdw.B;  */
1329                 eps = up->vdw.eps;
1330         } else if (tp == kVdwPairParType) {
1331         /*      a = up->vdwp.A;
1332                 b = up->vdwp.B; */
1333                 eps = up->vdwp.eps;
1334         } else rb_raise(rb_eMolbyError, "invalid member eps");
1335 /*      if (a == 0.0 || b == 0.0)  */
1336                 return rb_float_new(eps * INTERNAL2KCAL);
1337 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1338 }
1339
1340 /*
1341  *  call-seq:
1342  *     A14 -> Float
1343  *
1344  *  Get the "A" value for the 1-4 van der Waals parameter.
1345  */
1346 /*
1347 static VALUE s_ParameterRef_GetA14(VALUE self) {
1348         UnionPar *up;
1349         Int tp;
1350         up = s_UnionParFromValue(self, &tp, 0);
1351         if (tp == kVdwParType)
1352                 return rb_float_new(up->vdw.A14);
1353         else if (tp == kVdwPairParType)
1354                 return rb_float_new(up->vdwp.A14);
1355         else rb_raise(rb_eMolbyError, "invalid member A14");
1356 }
1357 */
1358
1359 /*
1360  *  call-seq:
1361  *     B14 -> Float
1362  *
1363  *  Get the "B" value for the 1-4 van der Waals parameter.
1364  */
1365 /*
1366 static VALUE s_ParameterRef_GetB14(VALUE self) {
1367         UnionPar *up;
1368         Int tp;
1369         up = s_UnionParFromValue(self, &tp, 0);
1370         if (tp == kVdwParType)
1371                 return rb_float_new(up->vdw.B14);
1372         else if (tp == kVdwPairParType)
1373                 return rb_float_new(up->vdwp.B14);
1374         else rb_raise(rb_eMolbyError, "invalid member B14");
1375 }
1376 */
1377
1378 /*
1379  *  call-seq:
1380  *     r_eq14 -> Float
1381  *
1382  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1383  */
1384 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1385         UnionPar *up;
1386         Int tp;
1387 /*      Double a, b, r; */
1388         Double r;
1389         up = s_UnionParFromValue(self, &tp, 0);
1390         if (tp == kVdwParType) {
1391         /*      a = up->vdw.A14;
1392                 b = up->vdw.B14; */
1393                 r = up->vdw.r_eq14;
1394         } else if (tp == kVdwPairParType) {
1395         /*      a = up->vdwp.A14;
1396                 b = up->vdwp.B14;  */
1397                 r = up->vdwp.r_eq14;
1398         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1399 /*      if (a == 0.0 || b == 0.0)  */
1400         return rb_float_new(r);
1401 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1402 }
1403
1404 /*
1405  *  call-seq:
1406  *     eps14 -> Float
1407  *
1408  *  Get the minimum energy for the 1-4 van der Waals parameter.
1409  */
1410 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1411         UnionPar *up;
1412         Int tp;
1413 /*      Double a, b;  */
1414         Double eps;
1415         up = s_UnionParFromValue(self, &tp, 0);
1416         if (tp == kVdwParType) {
1417         /*      a = up->vdw.A14;
1418                 b = up->vdw.B14;  */
1419                 eps = up->vdw.eps14;
1420         } else if (tp == kVdwPairParType) {
1421         /*      a = up->vdwp.A14;
1422                 b = up->vdwp.B14; */
1423                 eps = up->vdwp.eps14;
1424         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1425 /*      if (a == 0.0 || b == 0.0) */
1426         return rb_float_new(eps * INTERNAL2KCAL);
1427 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1428 }
1429
1430 /*
1431  *  call-seq:
1432  *     cutoff -> Float
1433  *
1434  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1435  */
1436 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1437         UnionPar *up;
1438         Int tp;
1439         up = s_UnionParFromValue(self, &tp, 0);
1440         if (tp == kVdwCutoffParType)
1441                 return rb_float_new(up->vdwcutoff.cutoff);
1442         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1443 }
1444
1445 /*
1446  *  call-seq:
1447  *     radius -> Float
1448  *
1449  *  Get the atomic radius for the atom display parameter.
1450  */
1451 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1452         UnionPar *up;
1453         Int tp;
1454         up = s_UnionParFromValue(self, &tp, 0);
1455         if (tp == kElementParType)
1456                 return rb_float_new(up->atom.radius);
1457         else rb_raise(rb_eMolbyError, "invalid member radius");
1458 }
1459
1460 /*
1461  *  call-seq:
1462  *     color -> [Float, Float, Float]
1463  *
1464  *  Get the rgb color for the atom display parameter.
1465  */
1466 static VALUE s_ParameterRef_GetColor(VALUE self) {
1467         UnionPar *up;
1468         Int tp;
1469         up = s_UnionParFromValue(self, &tp, 0);
1470         if (tp == kElementParType)
1471                 return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
1472         else rb_raise(rb_eMolbyError, "invalid member color");
1473 }
1474
1475 /*
1476  *  call-seq:
1477  *     atomic_number -> Integer
1478  *
1479  *  Get the atomic number for the vdw or atom parameter.
1480  */
1481 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1482         UnionPar *up;
1483         Int tp;
1484         up = s_UnionParFromValue(self, &tp, 0);
1485         if (tp == kElementParType)
1486                 return INT2NUM(up->atom.number);
1487         else if (tp == kVdwParType)
1488                 return INT2NUM(up->vdw.atomicNumber);
1489         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1490 }
1491
1492 /*
1493  *  call-seq:
1494  *     name -> String
1495  *
1496  *  Get the name for the atom display parameter.
1497  */
1498 static VALUE s_ParameterRef_GetName(VALUE self) {
1499         UnionPar *up;
1500         Int tp;
1501         up = s_UnionParFromValue(self, &tp, 0);
1502         if (tp == kElementParType) {
1503                 char name[5];
1504                 strncpy(name, up->atom.name, 4);
1505                 name[4] = 0;
1506                 return rb_str_new2(name);
1507         } else rb_raise(rb_eMolbyError, "invalid member name");
1508 }
1509
1510 /*
1511  *  call-seq:
1512  *     weight -> Float
1513  *
1514  *  Get the atomic weight for the atom display parameter.
1515  */
1516 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1517         UnionPar *up;
1518         Int tp;
1519         up = s_UnionParFromValue(self, &tp, 0);
1520         if (tp == kElementParType)
1521                 return rb_float_new(up->atom.weight);
1522         else if (tp == kVdwParType)
1523                 return rb_float_new(up->vdw.weight);
1524         else rb_raise(rb_eMolbyError, "invalid member weight");
1525 }
1526
1527 /*
1528  *  call-seq:
1529  *     fullname -> String
1530  *
1531  *  Get the full name for the atom display parameter.
1532  */
1533 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1534         UnionPar *up;
1535         Int tp;
1536         up = s_UnionParFromValue(self, &tp, 0);
1537         if (tp == kElementParType) {
1538                 char fullname[16];
1539                 strncpy(fullname, up->atom.fullname, 15);
1540                 fullname[15] = 0;
1541                 return rb_str_new2(fullname);
1542         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1543 }
1544
1545 /*
1546  *  call-seq:
1547  *     comment -> String
1548  *
1549  *  Get the comment for the parameter.
1550  */
1551 static VALUE s_ParameterRef_GetComment(VALUE self) {
1552         UnionPar *up;
1553         Int tp, com;
1554         up = s_UnionParFromValue(self, &tp, 0);
1555         com = up->bond.com;
1556         if (com == 0)
1557                 return Qnil;
1558         else return rb_str_new2(ParameterGetComment(com));
1559 }
1560
1561 /*
1562  *  call-seq:
1563  *     source -> String
1564  *
1565  *  Get the source string for the parameter. Returns false for undefined parameter,
1566  *  and nil for "local" parameter that is specific for the molecule.
1567  */
1568 static VALUE s_ParameterRef_GetSource(VALUE self) {
1569         UnionPar *up;
1570         Int tp, src;
1571         up = s_UnionParFromValue(self, &tp, 0);
1572         src = up->bond.src;
1573         if (src < 0)
1574                 return Qfalse;  /* undefined */
1575         else if (src == 0)
1576                 return Qnil;  /*  local  */
1577         else return rb_str_new2(ParameterGetComment(src));
1578 }
1579
1580 static void
1581 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1582 {
1583         VALUE *valp;
1584         int i;
1585         if (n == 1)
1586                 valp = &val;
1587         else {
1588                 if (rb_obj_is_kind_of(val, rb_cString)) {
1589                         char *s = StringValuePtr(val);
1590                         char *p;
1591                         for (i = 0; i < n; i++) {
1592                                 char buf[40];
1593                                 int len;
1594                                 /*  Skip leading separaters  */
1595                                 while (*s == '-' || *s == ' ' || *s == '\t')
1596                                         s++;
1597                                 for (p = s; *p != 0; p++) {
1598                                         if (*p == '-' || *p == ' ' || *p == '\t')
1599                                                 break;
1600                                 }
1601                                 len = p - s;
1602                                 if (len >= sizeof(buf))
1603                                         len = sizeof(buf) - 1;
1604                                 strncpy(buf, s, len);
1605                                 buf[len] = 0;
1606                                 /*  Skip trailing blanks  */
1607                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1608                                         buf[len] = 0;
1609                                 if (buf[0] == 0)
1610                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1611                                 if (buf[0] >= '0' && buf[0] <= '9')
1612                                         types[i] = atoi(buf);
1613                                 else
1614                                         types[i] = AtomTypeEncodeToUInt(buf);
1615                                 if (p == NULL || *p == 0) {
1616                                         i++;
1617                                         break;
1618                                 } else s = p + 1;
1619                         }
1620                         if (i < n)
1621                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1622                         return;
1623                 }
1624                 val = rb_ary_to_ary(val);
1625                 if (RARRAY_LEN(val) != n)
1626                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1627                 valp = RARRAY_PTR(val);
1628         }
1629         for (i = 0; i < n; i++) {
1630                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1631                         types[i] = NUM2INT(rb_Integer(valp[i]));
1632                 else {
1633                         VALUE sval = valp[i];
1634                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1635                 }
1636         }
1637 }
1638
1639 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1640         UnionPar *up;
1641         VALUE oldval;
1642         Int oldsrc, tp;
1643         UInt types[4];
1644         up = s_UnionParFromValue(self, &tp, 1);
1645         oldval = s_ParameterRef_GetAtomTypes(self);
1646         oldsrc = up->bond.src;
1647         switch (tp) {
1648                 case kBondParType:
1649                         s_ScanAtomTypes(val, 2, types);
1650                         up->bond.type1 = types[0];
1651                         up->bond.type2 = types[1];
1652                         break;
1653                 case kAngleParType:
1654                         s_ScanAtomTypes(val, 3, types);
1655                         up->angle.type1 = types[0];
1656                         up->angle.type2 = types[1];
1657                         up->angle.type3 = types[2];
1658                         break;
1659                 case kDihedralParType:
1660                 case kImproperParType:
1661                         s_ScanAtomTypes(val, 4, types);
1662                         up->torsion.type1 = types[0];
1663                         up->torsion.type2 = types[1];
1664                         up->torsion.type3 = types[2];
1665                         up->torsion.type4 = types[3];
1666                         break;
1667                 case kVdwParType:
1668                         s_ScanAtomTypes(val, 1, types);
1669                         up->vdw.type1 = types[0];
1670                         break;
1671                 case kVdwPairParType:
1672                         s_ScanAtomTypes(val, 2, types);
1673                         up->vdwp.type1 = types[0];
1674                         up->vdwp.type2 = types[1];
1675                         break;
1676                 case kVdwCutoffParType:
1677                         s_ScanAtomTypes(val, 2, types);
1678                         up->vdwcutoff.type1 = types[0];
1679                         up->vdwcutoff.type2 = types[1];
1680                         break;
1681                 default:
1682                         return Qnil;
1683         }
1684         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1685         return val;
1686 }
1687
1688 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1689         UnionPar *up;
1690         Int tp, i, n, oldsrc;
1691         VALUE *valp, oldval;
1692         up = s_UnionParFromValue(self, &tp, 1);
1693         oldval = s_ParameterRef_GetK(self);
1694         oldsrc = up->bond.src;
1695         switch (tp) {
1696                 case kBondParType:
1697                         val = rb_Float(val);
1698                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1699                         break;
1700                 case kAngleParType:
1701                         val = rb_Float(val);
1702                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1703                         break;
1704                 case kDihedralParType:
1705                 case kImproperParType:
1706                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1707                                 up->torsion.mult = 1;
1708                                 val = rb_Float(val);
1709                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1710                                 break;
1711                         }
1712                         n = up->torsion.mult;
1713                         if (n > 3)
1714                                 n = 3;
1715                         val = rb_ary_to_ary(val);
1716                         if (RARRAY_LEN(val) != n)
1717                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1718                         valp = RARRAY_PTR(val);
1719                         for (i = 0; i < n; i++) {
1720                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1721                         }
1722                         break;
1723                 default:
1724                         rb_raise(rb_eMolbyError, "invalid member k");
1725         }
1726         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1727         return val;
1728 }
1729
1730 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1731         UnionPar *up;
1732         Int tp, oldsrc;
1733         VALUE oldval;
1734         up = s_UnionParFromValue(self, &tp, 1);
1735         oldval = s_ParameterRef_GetR0(self);
1736         oldsrc = up->bond.src;
1737         if (tp == kBondParType) {
1738                 val = rb_Float(val);
1739                 up->bond.r0 = NUM2DBL(val);
1740         } else rb_raise(rb_eMolbyError, "invalid member r0");
1741         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1742         return val;
1743 }
1744
1745 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1746         UnionPar *up;
1747         Int tp, oldsrc;
1748         VALUE oldval;
1749         up = s_UnionParFromValue(self, &tp, 1);
1750         oldval = s_ParameterRef_GetA0(self);
1751         oldsrc = up->bond.src;
1752         if (tp == kAngleParType) {
1753                 val = rb_Float(val);
1754                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1755         } else rb_raise(rb_eMolbyError, "invalid member a0");
1756         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1757         return val;
1758 }
1759
1760 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1761         UnionPar *up;
1762         Int tp, oldsrc;
1763         VALUE oldval;
1764         up = s_UnionParFromValue(self, &tp, 1);
1765         oldval = s_ParameterRef_GetMult(self);
1766         oldsrc = up->bond.src;
1767         if (tp == kDihedralParType || tp == kImproperParType) {
1768                 int i;
1769                 val = rb_Integer(val);
1770                 i = NUM2INT(val);
1771                 if (i < 0 || i > 3)
1772                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1773                 up->torsion.mult = i;
1774         } else rb_raise(rb_eMolbyError, "invalid member mult");
1775         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1776         return val;
1777 }
1778
1779 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1780         UnionPar *up;
1781         Int tp, i, n, oldsrc;
1782         VALUE *valp, oldval;
1783         up = s_UnionParFromValue(self, &tp, 1);
1784         oldval = s_ParameterRef_GetPeriod(self);
1785         oldsrc = up->bond.src;
1786         if (tp == kDihedralParType || tp == kImproperParType) {
1787                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1788                         up->torsion.mult = 1;
1789                         val = rb_Integer(val);
1790                         up->torsion.period[0] = NUM2INT(val);
1791                 } else {
1792                         n = up->torsion.mult;
1793                         if (n > 3)
1794                                 n = 3;
1795                         val = rb_ary_to_ary(val);
1796                         if (RARRAY_LEN(val) != n)
1797                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1798                         valp = RARRAY_PTR(val);
1799                         for (i = 0; i < n; i++) {
1800                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1801                         }
1802                 }
1803         } else rb_raise(rb_eMolbyError, "invalid member period");
1804         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1805         return val;
1806 }
1807
1808 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1809         UnionPar *up;
1810         Int tp, i, n, oldsrc;
1811         VALUE *valp, oldval;
1812         up = s_UnionParFromValue(self, &tp, 1);
1813         oldval = s_ParameterRef_GetPhi0(self);
1814         oldsrc = up->bond.src;
1815         if (tp == kDihedralParType || tp == kImproperParType) {
1816                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1817                         up->torsion.mult = 1;
1818                         val = rb_Float(val);
1819                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1820                 } else {
1821                         n = up->torsion.mult;
1822                         if (n > 3)
1823                                 n = 3;
1824                         val = rb_ary_to_ary(val);
1825                         if (RARRAY_LEN(val) != n)
1826                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1827                         valp = RARRAY_PTR(val);
1828                         for (i = 0; i < n; i++)
1829                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
1830                 }
1831         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1832         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
1833         return val;
1834 }
1835
1836 /*
1837 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
1838         UnionPar *up;
1839         Int tp, oldsrc;
1840         double d;
1841         VALUE oldval;
1842         up = s_UnionParFromValue(self, &tp, 1);
1843         oldval = s_ParameterRef_GetA(self);
1844         oldsrc = up->bond.src;
1845         val = rb_Float(val);
1846         d = NUM2DBL(val);
1847         if (tp == kVdwParType)
1848                 up->vdw.A = d;
1849         else if (tp == kVdwPairParType)
1850                 up->vdwp.A = d;
1851         else rb_raise(rb_eMolbyError, "invalid member A");
1852         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
1853         return val;
1854 }
1855
1856 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
1857         UnionPar *up;
1858         Int tp, oldsrc;
1859         double d;
1860         VALUE oldval;
1861         up = s_UnionParFromValue(self, &tp, 1);
1862         oldval = s_ParameterRef_GetB(self);
1863         oldsrc = up->bond.src;
1864         val = rb_Float(val);
1865         d = NUM2DBL(val);
1866         if (tp == kVdwParType)
1867                 up->vdw.B = d;
1868         else if (tp == kVdwPairParType)
1869                 up->vdwp.B = d;
1870         else rb_raise(rb_eMolbyError, "invalid member B");
1871         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
1872         return val;
1873 }
1874 */
1875
1876 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
1877         UnionPar *up;
1878         Int tp, oldsrc;
1879         Double r;
1880         VALUE oldval;
1881         up = s_UnionParFromValue(self, &tp, 1);
1882         oldval = s_ParameterRef_GetReq(self);
1883         oldsrc = up->bond.src;
1884         val = rb_Float(val);
1885         r = NUM2DBL(val);
1886         if (tp == kVdwParType) {
1887                 up->vdw.r_eq = r;
1888                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
1889                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
1890         } else if (tp == kVdwPairParType) {
1891                 up->vdwp.r_eq = r;
1892                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
1893                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
1894         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1895         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
1896         return val;
1897 }
1898
1899 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
1900         UnionPar *up;
1901         Int tp, oldsrc;
1902         Double e;
1903         VALUE oldval;
1904         up = s_UnionParFromValue(self, &tp, 1);
1905         oldval = s_ParameterRef_GetEps(self);
1906         oldsrc = up->bond.src;
1907         val = rb_Float(val);
1908         e = NUM2DBL(val) * KCAL2INTERNAL;
1909         if (tp == kVdwParType) {
1910                 up->vdw.eps = e;
1911                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
1912                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
1913         } else if (tp == kVdwPairParType) {
1914                 up->vdwp.eps = e;
1915                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
1916                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
1917         } else rb_raise(rb_eMolbyError, "invalid member eps");
1918         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
1919         return val;
1920 }
1921
1922 /*
1923 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
1924         UnionPar *up;
1925         Int tp, oldsrc;
1926         double d;
1927         VALUE oldval;
1928         up = s_UnionParFromValue(self, &tp, 1);
1929         oldval = s_ParameterRef_GetA14(self);
1930         oldsrc = up->bond.src;
1931         val = rb_Float(val);
1932         d = NUM2DBL(val);
1933         if (tp == kVdwParType)
1934                 up->vdw.A14 = d;
1935         else if (tp == kVdwPairParType)
1936                 up->vdwp.A14 = d;
1937         else rb_raise(rb_eMolbyError, "invalid member A14");
1938         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
1939         return val;
1940 }
1941
1942 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
1943         UnionPar *up;
1944         Int tp, oldsrc;
1945         double d;
1946         VALUE oldval;
1947         up = s_UnionParFromValue(self, &tp, 1);
1948         oldval = s_ParameterRef_GetB14(self);
1949         oldsrc = up->bond.src;
1950         val = rb_Float(val);
1951         d = NUM2DBL(val);
1952         if (tp == kVdwParType)
1953                 up->vdw.B14 = d;
1954         else if (tp == kVdwPairParType)
1955                 up->vdwp.B14 = d;
1956         else rb_raise(rb_eMolbyError, "invalid member B14");
1957         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
1958         return val;
1959 }
1960 */
1961
1962 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
1963         UnionPar *up;
1964         Int tp, oldsrc;
1965         Double r;
1966         VALUE oldval;
1967         up = s_UnionParFromValue(self, &tp, 1);
1968         oldval = s_ParameterRef_GetReq14(self);
1969         oldsrc = up->bond.src;
1970         val = rb_Float(val);
1971         r = NUM2DBL(val);
1972         if (tp == kVdwParType) {
1973                 up->vdw.r_eq14 = r;
1974                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1975                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1976         } else if (tp == kVdwPairParType) {
1977                 up->vdwp.r_eq14 = r;
1978                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1979                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1980         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1981         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
1982         return val;
1983 }
1984
1985 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
1986         UnionPar *up;
1987         Int tp, oldsrc;
1988         Double e;
1989         VALUE oldval;
1990         up = s_UnionParFromValue(self, &tp, 1);
1991         oldval = s_ParameterRef_GetEps14(self);
1992         oldsrc = up->bond.src;
1993         val = rb_Float(val);
1994         e = NUM2DBL(val) * KCAL2INTERNAL;
1995         if (tp == kVdwParType) {
1996                 up->vdw.eps14 = e;
1997                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1998                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1999         } else if (tp == kVdwPairParType) {
2000                 up->vdwp.eps14 = e;
2001                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2002                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2003         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2004         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2005         return val;
2006 }
2007
2008 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2009         UnionPar *up;
2010         Int tp, oldsrc;
2011         VALUE oldval;
2012         oldval = s_ParameterRef_GetCutoff(self);
2013         oldsrc = up->bond.src;
2014         up = s_UnionParFromValue(self, &tp, 1);
2015         val = rb_Float(val);
2016         if (tp == kVdwCutoffParType) {
2017                 up->vdwcutoff.cutoff = NUM2DBL(val);
2018         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2019         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2020         return val;
2021 }
2022
2023 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2024         UnionPar *up;
2025         Int tp, oldsrc;
2026         VALUE oldval;
2027         up = s_UnionParFromValue(self, &tp, 1);
2028         oldval = s_ParameterRef_GetRadius(self);
2029         oldsrc = up->bond.src;
2030         val = rb_Float(val);
2031         if (tp == kElementParType) {
2032                 up->atom.radius = NUM2DBL(val);
2033         } else rb_raise(rb_eMolbyError, "invalid member radius");
2034         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2035         return val;
2036 }
2037
2038 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2039         UnionPar *up;
2040         Int tp, oldsrc;
2041         VALUE *valp, oldval;
2042         up = s_UnionParFromValue(self, &tp, 1);
2043         oldval = s_ParameterRef_GetColor(self);
2044         oldsrc = up->bond.src;
2045         val = rb_ary_to_ary(val);
2046         if (RARRAY_LEN(val) != 3)
2047                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2048         valp = RARRAY_PTR(val);
2049         if (tp == kElementParType) {
2050                 up->atom.r = NUM2DBL(rb_Float(valp[0]));
2051                 up->atom.g = NUM2DBL(rb_Float(valp[1]));
2052                 up->atom.b = NUM2DBL(rb_Float(valp[2]));
2053         } else rb_raise(rb_eMolbyError, "invalid member color");
2054         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2055         return val;
2056 }
2057
2058 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2059         UnionPar *up;
2060         Int tp, oldsrc;
2061         VALUE oldval;
2062         up = s_UnionParFromValue(self, &tp, 1);
2063         oldval = s_ParameterRef_GetAtomicNumber(self);
2064         oldsrc = up->bond.src;
2065         val = rb_Integer(val);
2066         if (tp == kElementParType)
2067                 up->atom.number = NUM2INT(val);
2068         else if (tp == kVdwParType) {
2069                 up->vdw.atomicNumber = NUM2INT(val);
2070                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2071         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2072         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2073         return val;
2074 }
2075
2076 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2077         UnionPar *up;
2078         Int tp, oldsrc;
2079         VALUE oldval;
2080         up = s_UnionParFromValue(self, &tp, 1);
2081         oldval = s_ParameterRef_GetName(self);
2082         oldsrc = up->bond.src;
2083         if (tp == kElementParType) {
2084                 strncpy(up->atom.name, StringValuePtr(val), 4);
2085         } else rb_raise(rb_eMolbyError, "invalid member name");
2086         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2087         return val;
2088 }
2089
2090 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2091         UnionPar *up;
2092         Int tp, oldsrc;
2093         VALUE oldval;
2094         val = rb_Float(val);
2095         oldval = s_ParameterRef_GetWeight(self);
2096         up = s_UnionParFromValue(self, &tp, 1);
2097         oldsrc = up->bond.src;
2098         if (tp == kElementParType)
2099                 up->atom.weight = NUM2DBL(val);
2100         else if (tp == kVdwParType)
2101                 up->vdw.weight = NUM2DBL(val);
2102         else rb_raise(rb_eMolbyError, "invalid member weight");
2103         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2104         return val;
2105 }
2106
2107 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2108         UnionPar *up;
2109         Int tp, oldsrc;
2110         VALUE oldval;
2111         up = s_UnionParFromValue(self, &tp, 1);
2112         oldval = s_ParameterRef_GetFullName(self);
2113         oldsrc = up->bond.src;
2114         if (tp == kElementParType) {
2115                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2116                 up->atom.fullname[15] = 0;
2117         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2118         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2119         return val;
2120 }
2121
2122 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2123         UnionPar *up;
2124         Int tp, com, oldsrc;
2125         VALUE oldval;
2126         up = s_UnionParFromValue(self, &tp, 1);
2127         oldval = s_ParameterRef_GetComment(self);
2128         oldsrc = up->bond.src;
2129         if (val == Qnil)
2130                 up->bond.com = 0;
2131         else {
2132                 com = ParameterCommentIndex(StringValuePtr(val));
2133                 up->bond.com = com;
2134         }
2135         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2136         return val;     
2137 }
2138
2139 /*  Only false (undefined) and nil (local) can be set  */
2140 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2141         UnionPar *up;
2142         Int tp, oldsrc;
2143         VALUE oldval;
2144         up = s_UnionParFromValue(self, &tp, 1);
2145         if (val != Qfalse && val != Qnil)
2146                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2147         oldval = s_ParameterRef_GetSource(self);
2148         oldsrc = up->bond.src;
2149         if (oldsrc != 0 && oldsrc != -1)
2150                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2151         up->bond.src = (val == Qfalse ? -1 : 0);
2152         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2153         return val;     
2154 }
2155
2156 static struct s_ParameterAttrDef {
2157         char *name;
2158         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2159         ID id;                  /*  Will be set within InitMolby()  */
2160         VALUE (*getter)(VALUE);
2161         VALUE (*setter)(VALUE, VALUE);
2162 } s_ParameterAttrDefTable[] = {
2163         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2164         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2165         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2166         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2167         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2168         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2169         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2170         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2171         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2172         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2173 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2174         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2175         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2176         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2177 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2178         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2179         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2180         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2181         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2182         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2183         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2184         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2185         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2186         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2187         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2188         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2189         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2190         {NULL} /* Sentinel */
2191 };
2192
2193 static VALUE
2194 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2195 {
2196         int i;
2197         ID kid;
2198         if (TYPE(key) != T_SYMBOL) {
2199                 kid = rb_intern(StringValuePtr(key));
2200                 key = ID2SYM(kid);
2201         } else kid = SYM2ID(key);
2202         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2203                 if (s_ParameterAttrDefTable[i].id == kid) {
2204                         if (value == Qundef)
2205                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2206                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2207                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2208                         else
2209                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2210                 }
2211         }
2212         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2213         return Qnil; /* not reached */
2214 }
2215
2216 static VALUE
2217 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2218 {
2219         return s_ParameterRef_SetAttr(self, key, Qundef);
2220 }
2221
2222 /*
2223  *  call-seq:
2224  *     keys(idx)          -> array of valid parameter attributes
2225  *  
2226  *  Returns an array of valid parameter attributes (as Symbols).
2227  */
2228 static VALUE
2229 s_ParameterRef_Keys(VALUE self)
2230 {
2231         ParameterRef *pref;
2232         Data_Get_Struct(self, ParameterRef, pref);
2233         switch (pref->parType) {
2234                 case kBondParType:
2235                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2236                 case kAngleParType:
2237                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2238                 case kDihedralParType:
2239                 case kImproperParType:
2240                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2241                 case kVdwParType:
2242                         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);
2243                 case kVdwPairParType:
2244                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2245                 case kVdwCutoffParType:
2246                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2247                 case kElementParType:
2248                         return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_CommentSym, s_SourceSym);
2249                 default:
2250                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2251         }
2252         return Qnil;  /*  Not reached  */
2253 }
2254
2255 /*
2256  *  call-seq:
2257  *     to_hash(idx)          -> Hash
2258  *  
2259  *  Returns a hash containing valid parameter names and values
2260  */
2261 static VALUE
2262 s_ParameterRef_ToHash(VALUE self)
2263 {
2264         VALUE keys = s_ParameterRef_Keys(self);
2265         VALUE retval;
2266         int i;
2267         if (keys == Qnil)
2268                 return Qnil;
2269         retval = rb_hash_new();
2270         for (i = 0; i < RARRAY_LEN(keys); i++) {
2271                 VALUE key = RARRAY_PTR(keys)[i];
2272                 VALUE val = s_ParameterRef_GetAttr(self, key);
2273                 rb_hash_aset(retval, key, val);
2274         }
2275         return retval;
2276 }
2277
2278 /*
2279  *  call-seq:
2280  *     parameter.to_s(idx)          -> String
2281  *  
2282  *  Returns a string representation of the given parameter
2283  */
2284 static VALUE
2285 s_ParameterRef_ToString(VALUE self)
2286 {
2287         Int tp, i, n;
2288         char buf[1024], types[4][8];
2289         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2290         switch (tp) {
2291                 case kBondParType:
2292                         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);
2293                         break;
2294                 case kAngleParType:
2295                         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);
2296                         break;
2297                 case kDihedralParType:
2298                 case kImproperParType:
2299                         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]));
2300                         n = strlen(buf);
2301                         for (i = 0; i < up->torsion.mult; i++) {
2302                                 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);
2303                                 n = strlen(buf);
2304                         }
2305                         break;
2306                 case kVdwParType:
2307                         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);
2308                         break;
2309                 case kVdwPairParType:
2310                         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);
2311                         break;
2312                 case kVdwCutoffParType:
2313                         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);
2314                         break;
2315                 case kElementParType:
2316                         snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s", up->atom.name, up->atom.number, up->atom.radius, up->atom.r, up->atom.g, up->atom.b, up->atom.weight, up->atom.fullname);
2317                         break;
2318         }
2319         return rb_str_new2(buf);
2320 }
2321
2322 /*
2323  *  call-seq:
2324  *     self == parameterRef -> boolean
2325  *  
2326  *  True if the parameters point to the same parameter record.
2327  */
2328 static VALUE
2329 s_ParameterRef_Equal(VALUE self, VALUE val)
2330 {
2331         Int tp1, tp2;
2332         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2333                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2334         } else return Qfalse;
2335 }
2336         
2337 #pragma mark ====== Parameter Class ======
2338
2339 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2340  *  is NULL, then the global parameters are looked for.  */
2341
2342 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2343 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2344 static void
2345 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2346 {
2347         Molecule *mol;
2348         Data_Get_Struct(val, Molecule, mol);
2349         if (mol == NULL)
2350                 rb_raise(rb_eMolbyError, "the molecule is empty");
2351         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2352                 /*  Do self.md_arena.prepare  */
2353                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2354                 if (val2 != Qnil)
2355                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2356         }
2357 }
2358
2359 static VALUE
2360 s_NewParameterValueFromValue(VALUE val)
2361 {
2362         Molecule *mol;
2363         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2364                 Data_Get_Struct(val, Molecule, mol);
2365                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2366                 MoleculeRetain(mol);
2367                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2368         } else {
2369                 mol = NULL;
2370                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2371         }
2372 }
2373
2374 static Molecule *
2375 s_MoleculeFromParameterValue(VALUE val)
2376 {
2377         Molecule *mol;
2378         Data_Get_Struct(val, Molecule, mol);
2379         return mol;
2380 }
2381
2382 static Parameter *
2383 s_ParameterFromParameterValue(VALUE val)
2384 {
2385         Molecule *mol;
2386         Data_Get_Struct(val, Molecule, mol);
2387         if (mol != NULL)
2388                 return mol->par;
2389         return gBuiltinParameters;
2390 }
2391
2392 /*  Forward declarations  */
2393 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2394 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2395
2396 static Molecule *
2397 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2398 {
2399         if (val == rb_cParameter) {
2400                 return NULL;  /*  Parameter class method: builtin parameters  */
2401         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2402                 return s_MoleculeFromParameterValue(val);
2403         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2404                 return s_MoleculeFromParEnumerableValue(val);
2405         } else return NULL;
2406 }
2407
2408 /*
2409  *  call-seq:
2410  *     builtin    -> Parameter
2411  *  
2412  *  Returns a parameter value that points to the global (builtin) parameters.
2413  *  Equivalent to Parameter::Builtin (constant).
2414  */
2415 static VALUE
2416 s_Parameter_Builtin(VALUE self)
2417 {
2418         static ID s_builtin_id = 0;
2419         if (s_builtin_id == 0)
2420                 s_builtin_id = rb_intern("Builtin");
2421         return rb_const_get(rb_cParameter, s_builtin_id);
2422 }
2423
2424 /*
2425  *  call-seq:
2426  *     bond(idx)          -> ParameterRef
2427  *  
2428  *  The index-th bond parameter record is returned.
2429  */
2430 static VALUE
2431 s_Parameter_Bond(VALUE self, VALUE ival)
2432 {
2433         Molecule *mol;
2434         int idx, n;
2435         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2436         idx = NUM2INT(rb_Integer(ival));
2437         if (mol == NULL)
2438                 n = gBuiltinParameters->nbondPars;
2439         else if (mol->par != NULL)
2440                 n = mol->par->nbondPars;
2441         else n = 0;
2442         if (idx < -n || idx >= n)
2443                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2444         if (idx < 0)
2445                 idx += n;
2446         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2447 }
2448
2449 /*
2450  *  call-seq:
2451  *     angle(idx)          -> ParameterRef
2452  *  
2453  *  The index-th angle parameter record is returned.
2454  */
2455 static VALUE
2456 s_Parameter_Angle(VALUE self, VALUE ival)
2457 {
2458         Molecule *mol;
2459         int idx, n;
2460         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2461         idx = NUM2INT(rb_Integer(ival));
2462         if (mol == NULL)
2463                 n = gBuiltinParameters->nanglePars;
2464         else if (mol->par != NULL)
2465                 n = mol->par->nanglePars;
2466         else n = 0;
2467         if (idx < -n || idx >= n)
2468                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2469         if (idx < 0)
2470                 idx += n;
2471         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2472 }
2473
2474 /*
2475  *  call-seq:
2476  *     dihedral(idx)          -> ParameterRef
2477  *  
2478  *  The index-th dihedral parameter record is returned.
2479  */
2480 static VALUE
2481 s_Parameter_Dihedral(VALUE self, VALUE ival)
2482 {
2483         Molecule *mol;
2484         int idx, n;
2485         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2486         idx = NUM2INT(rb_Integer(ival));
2487         if (mol == NULL)
2488                 n = gBuiltinParameters->ndihedralPars;
2489         else if (mol->par != NULL)
2490                 n = mol->par->ndihedralPars;
2491         else n = 0;
2492         if (idx < -n || idx >= n)
2493                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2494         if (idx < 0)
2495                 idx += n;
2496         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2497 }
2498
2499 /*
2500  *  call-seq:
2501  *     improper(idx)          -> ParameterRef
2502  *  
2503  *  The index-th improper parameter record is returned.
2504  */
2505 static VALUE
2506 s_Parameter_Improper(VALUE self, VALUE ival)
2507 {
2508         Molecule *mol;
2509         int idx, n;
2510         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2511         idx = NUM2INT(rb_Integer(ival));
2512         if (mol == NULL)
2513                 n = gBuiltinParameters->nimproperPars;
2514         else if (mol->par != NULL)
2515                 n = mol->par->nimproperPars;
2516         else n = 0;
2517         if (idx < -n || idx >= n)
2518                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2519         if (idx < 0)
2520                 idx += n;
2521         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2522 }
2523
2524 /*
2525  *  call-seq:
2526  *     vdw(idx)          -> ParameterRef
2527  *  
2528  *  The index-th vdw parameter record is returned.
2529  */
2530 static VALUE
2531 s_Parameter_Vdw(VALUE self, VALUE ival)
2532 {
2533         Molecule *mol;
2534         int idx, n;
2535         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2536         idx = NUM2INT(rb_Integer(ival));
2537         if (mol == NULL)
2538                 n = gBuiltinParameters->nvdwPars;
2539         else if (mol->par != NULL)
2540                 n = mol->par->nvdwPars;
2541         else n = 0;
2542         if (idx < -n || idx >= n)
2543                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2544         if (idx < 0)
2545                 idx += n;
2546         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2547 }
2548
2549 /*
2550  *  call-seq:
2551  *     vdw_pair(idx)          -> ParameterRef
2552  *  
2553  *  The index-th vdw pair parameter record is returned.
2554  */
2555 static VALUE
2556 s_Parameter_VdwPair(VALUE self, VALUE ival)
2557 {
2558         Molecule *mol;
2559         int idx, n;
2560         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2561         idx = NUM2INT(rb_Integer(ival));
2562         if (mol == NULL)
2563                 n = gBuiltinParameters->nvdwpPars;
2564         else if (mol->par != NULL)
2565                 n = mol->par->nvdwpPars;
2566         else n = 0;
2567         if (idx < -n || idx >= n)
2568                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2569         if (idx < 0)
2570                 idx += n;
2571         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2572 }
2573
2574 /*
2575  *  call-seq:
2576  *     vdw_cutoff(idx)          -> ParameterRef
2577  *  
2578  *  The index-th vdw cutoff parameter record is returned.
2579  */
2580 static VALUE
2581 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2582 {
2583         Molecule *mol;
2584         int idx, n;
2585         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2586         idx = NUM2INT(rb_Integer(ival));
2587         if (mol == NULL)
2588                 n = gBuiltinParameters->nvdwCutoffPars;
2589         else if (mol->par != NULL)
2590                 n = mol->par->nvdwCutoffPars;
2591         else n = 0;
2592         if (idx < -n || idx >= n)
2593                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2594         if (idx < 0)
2595                 idx += n;
2596         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2597 }
2598
2599 /*
2600  *  call-seq:
2601  *     element(idx)            -> ParameterRef
2602  *     element(t1)             -> ParameterRef
2603  *  
2604  *  In the first form, the index-th element parameter record is returned. In the second
2605  *  form, the element parameter for t1 is looked up (the last index first). t1
2606  *  is the element name string (up to 4 characters).
2607  *  Unlike other Parameter methods, this is used only for the global parameter.
2608  */
2609 static VALUE
2610 s_Parameter_Element(VALUE self, VALUE ival)
2611 {
2612         Int idx1;
2613         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2614                 int n = gCountElementParameters;
2615                 idx1 = NUM2INT(rb_Integer(ival));
2616                 if (idx1 < -n || idx1 >= n)
2617                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2618                 if (idx1 < 0)
2619                         idx1 += n;
2620                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2621         } else {
2622                 ElementPar *ep;
2623                 char name[6];
2624                 int i;
2625                 strncpy(name, StringValuePtr(ival), 4);
2626                 name[4] = 0;
2627                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2628                         if (strncmp(ep->name, name, 4) == 0)
2629                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2630                 }
2631                 return Qnil;
2632         }
2633 }
2634
2635 /*
2636  *  call-seq:
2637  *     nbonds          -> Integer
2638  *  
2639  *  Returns the number of bond parameters.
2640  */
2641 static VALUE
2642 s_Parameter_Nbonds(VALUE self)
2643 {
2644         Int n;
2645         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2646         if (mol == NULL)
2647                 n = gBuiltinParameters->nbondPars;
2648         else if (mol->par != NULL)
2649                 n = mol->par->nbondPars;
2650         else n = 0;
2651         return INT2NUM(n);
2652 }
2653
2654 /*
2655  *  call-seq:
2656  *     nangles          -> Integer
2657  *  
2658  *  Returns the number of angle parameters.
2659  */
2660 static VALUE
2661 s_Parameter_Nangles(VALUE self)
2662 {
2663         Int n;
2664         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2665         if (mol == NULL)
2666                 n = gBuiltinParameters->nanglePars;
2667         else if (mol->par != NULL)
2668                 n = mol->par->nanglePars;
2669         else n = 0;
2670         return INT2NUM(n);
2671 }
2672
2673 /*
2674  *  call-seq:
2675  *     ndihedrals          -> Integer
2676  *  
2677  *  Returns the number of dihedral parameters.
2678  */
2679 static VALUE
2680 s_Parameter_Ndihedrals(VALUE self)
2681 {
2682         Int n;
2683         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2684         if (mol == NULL)
2685                 n = gBuiltinParameters->ndihedralPars;
2686         else if (mol->par != NULL)
2687                 n = mol->par->ndihedralPars;
2688         else n = 0;
2689         return INT2NUM(n);
2690 }
2691
2692 /*
2693  *  call-seq:
2694  *     nimpropers          -> Integer
2695  *  
2696  *  Returns the number of improper parameters.
2697  */
2698 static VALUE
2699 s_Parameter_Nimpropers(VALUE self)
2700 {
2701         Int n;
2702         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2703         if (mol == NULL)
2704                 n = gBuiltinParameters->nimproperPars;
2705         else if (mol->par != NULL)
2706                 n = mol->par->nimproperPars;
2707         else n = 0;
2708         return INT2NUM(n);
2709 }
2710
2711 /*
2712  *  call-seq:
2713  *     nvdws          -> Integer
2714  *  
2715  *  Returns the number of vdw parameters.
2716  */
2717 static VALUE
2718 s_Parameter_Nvdws(VALUE self)
2719 {
2720         Int n;
2721         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2722         if (mol == NULL)
2723                 n = gBuiltinParameters->nvdwPars;
2724         else if (mol->par != NULL)
2725                 n = mol->par->nvdwPars;
2726         else n = 0;
2727         return INT2NUM(n);
2728 }
2729
2730 /*
2731  *  call-seq:
2732  *     nvdw_pairs          -> Integer
2733  *  
2734  *  Returns the number of vdw pair parameters.
2735  */
2736 static VALUE
2737 s_Parameter_NvdwPairs(VALUE self)
2738 {
2739         Int n;
2740         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2741         if (mol == NULL)
2742                 n = gBuiltinParameters->nvdwpPars;
2743         else if (mol->par != NULL)
2744                 n = mol->par->nvdwpPars;
2745         else n = 0;
2746         return INT2NUM(n);
2747 }
2748
2749 /*
2750  *  call-seq:
2751  *     nvdw_cutoffs          -> Integer
2752  *  
2753  *  Returns the number of vdw cutoff parameters.
2754  */
2755 static VALUE
2756 s_Parameter_NvdwCutoffs(VALUE self)
2757 {
2758         Int n;
2759         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2760         if (mol == NULL)
2761                 n = gBuiltinParameters->nvdwCutoffPars;
2762         else if (mol->par != NULL)
2763                 n = mol->par->nvdwCutoffPars;
2764         else n = 0;
2765         return INT2NUM(n);
2766 }
2767
2768 /*
2769  *  call-seq:
2770  *     nelements          -> Integer
2771  *  
2772  *  Returns the number of element parameters.
2773  */
2774 static VALUE
2775 s_Parameter_Nelements(VALUE self)
2776 {
2777         return INT2NUM(gCountElementParameters);
2778 }
2779
2780 /*
2781  *  call-seq:
2782  *     bonds          -> ParEnumerable
2783  *  
2784  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2785  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2786  *  useful when all accessible parameters should be examined by use of 'each' method.
2787  */
2788 static VALUE
2789 s_Parameter_Bonds(VALUE self)
2790 {
2791         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2792         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2793 }
2794
2795 /*
2796  *  call-seq:
2797  *     angles          -> ParEnumerable
2798  *  
2799  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2800  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2801  *  useful when all accessible parameters should be examined by use of 'each' method.
2802  */
2803 static VALUE
2804 s_Parameter_Angles(VALUE self)
2805 {
2806         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2807         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2808 }
2809
2810 /*
2811  *  call-seq:
2812  *     dihedrals          -> ParEnumerable
2813  *  
2814  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
2815  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
2816  *  useful when all accessible parameters should be examined by use of 'each' method.
2817  */
2818 static VALUE
2819 s_Parameter_Dihedrals(VALUE self)
2820 {
2821         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2822         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
2823 }
2824
2825 /*
2826  *  call-seq:
2827  *     impropers          -> ParEnumerable
2828  *  
2829  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
2830  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
2831  *  useful when all accessible parameters should be examined by use of 'each' method.
2832  */
2833 static VALUE
2834 s_Parameter_Impropers(VALUE self)
2835 {
2836         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2837         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
2838 }
2839
2840 /*
2841  *  call-seq:
2842  *     vdws          -> ParEnumerable
2843  *  
2844  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
2845  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
2846  *  useful when all accessible parameters should be examined by use of 'each' method.
2847  */
2848 static VALUE
2849 s_Parameter_Vdws(VALUE self)
2850 {
2851         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2852         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
2853 }
2854
2855 /*
2856  *  call-seq:
2857  *     vdw_pairs          -> ParEnumerable
2858  *  
2859  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
2860  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
2861  *  useful when all accessible parameters should be examined by use of 'each' method.
2862  */
2863 static VALUE
2864 s_Parameter_VdwPairs(VALUE self)
2865 {
2866         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2867         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
2868 }
2869
2870 /*
2871  *  call-seq:
2872  *     vdw_cutoffs          -> ParEnumerable
2873  *  
2874  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
2875  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
2876  *  useful when all accessible parameters should be examined by use of 'each' method.
2877  */
2878 static VALUE
2879 s_Parameter_VdwCutoffs(VALUE self)
2880 {
2881         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2882         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
2883 }
2884
2885 /*
2886  *  call-seq:
2887  *     elements          -> ParEnumerable
2888  *  
2889  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
2890  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
2891  *  useful when all accessible parameters should be examined by use of 'each' method.
2892  */
2893 static VALUE
2894 s_Parameter_Elements(VALUE self)
2895 {
2896         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2897         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
2898 }
2899
2900 static VALUE
2901 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
2902 {
2903         VALUE atval, optval;
2904         UInt t[4];
2905         Int ii[4];
2906         int i, n, idx, flags, is_global;
2907
2908         rb_scan_args(argc, argv, "1*", &atval, &optval);
2909         
2910         /*  Get the atom types  */
2911         switch (parType) {
2912                 case kBondParType: n = 2; break;
2913                 case kAngleParType: n = 3; break;
2914                 case kDihedralParType: n = 4; break;
2915                 case kImproperParType: n = 4; break;
2916                 case kVdwParType: n = 1; break;
2917                 case kVdwPairParType: n = 2; break;
2918                 default: return Qnil;
2919         }
2920         s_ScanAtomTypes(atval, n, t);
2921         for (i = 0; i < n; i++) {
2922                 if (t[i] < kAtomTypeMinimum) {
2923                         /*  Explicit atom index  */
2924                         if (mol == NULL)
2925                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
2926                         if (t[i] >= mol->natoms)
2927                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
2928                         ii[i] = t[i];
2929                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
2930                 } else ii[i] = -1;
2931         }
2932         
2933         /*  Analyze options  */
2934         flags = 0;
2935         n = RARRAY_LEN(optval);
2936         for (i = 0; i < n; i++) {
2937                 VALUE oval = RARRAY_PTR(optval)[i];
2938                 if (oval == ID2SYM(rb_intern("global")))
2939                         flags |= kParameterLookupGlobal;
2940                 else if (oval == ID2SYM(rb_intern("local")))
2941                         flags |= kParameterLookupLocal;
2942                 else if (oval == ID2SYM(rb_intern("missing")))
2943                         flags |= kParameterLookupMissing;
2944                 else if (oval == ID2SYM(rb_intern("nowildcard")))
2945                         flags |= kParameterLookupNoWildcard;
2946                 else if (oval == ID2SYM(rb_intern("nobasetype")))
2947                         flags |= kParameterLookupNoBaseAtomType;
2948                 else if (oval == ID2SYM(rb_intern("create")))
2949                         flags |= 256;
2950         }
2951         if (flags == 0)
2952                 flags = kParameterLookupGlobal | kParameterLookupLocal;
2953         
2954         idx = -1;
2955         is_global = 0;
2956         switch (parType) {
2957                 case kBondParType: {
2958                         BondPar *bp;
2959                         if (mol != NULL) {
2960                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
2961                                 if (bp != NULL) {
2962                                         idx = bp - mol->par->bondPars;
2963                                         break;
2964                                 }
2965                         }
2966                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
2967                         if (bp != NULL) {
2968                                 idx = bp - gBuiltinParameters->bondPars;
2969                                 is_global = 1;
2970                         }
2971                         break;
2972                 }
2973                 case kAngleParType: {
2974                         AnglePar *ap;
2975                         if (mol != NULL) {
2976                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
2977                                 if (ap != NULL) {
2978                                         idx = ap - mol->par->anglePars;
2979                                         break;
2980                                 }
2981                         }
2982                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
2983                         if (ap != NULL) {
2984                                 idx = ap - gBuiltinParameters->anglePars;
2985                                 is_global = 1;
2986                         }
2987                         break;
2988                 }
2989                 case kDihedralParType: {
2990                         TorsionPar *tp;
2991                         if (mol != NULL) {
2992                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
2993                                 if (tp != NULL) {
2994                                         idx = tp - mol->par->dihedralPars;
2995                                         break;
2996                                 }
2997                         }
2998                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2999                         if (tp != NULL) {
3000                                 idx = tp - gBuiltinParameters->dihedralPars;
3001                                 is_global = 1;
3002                         }
3003                         break;
3004                 }
3005                 case kImproperParType: {
3006                         TorsionPar *tp;
3007                         if (mol != NULL) {
3008                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3009                                 if (tp != NULL) {
3010                                         idx = tp - mol->par->improperPars;
3011                                         break;
3012                                 }
3013                         }
3014                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3015                         if (tp != NULL) {
3016                                 idx = tp - gBuiltinParameters->improperPars;
3017                                 is_global = 1;
3018                         }
3019                         break;
3020                 }       
3021                 case kVdwParType: {
3022                         VdwPar *vp;
3023                         if (mol != NULL) {
3024                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3025                                 if (vp != NULL) {
3026                                         idx = vp - mol->par->vdwPars;
3027                                         break;
3028                                 }
3029                         }
3030                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3031                         if (vp != NULL) {
3032                                 idx = vp - gBuiltinParameters->vdwPars;
3033                                 is_global = 1;
3034                         }
3035                         break;
3036                 }       
3037                 case kVdwPairParType: {
3038                         VdwPairPar *vp;
3039                         if (mol != NULL) {
3040                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3041                                 if (vp != NULL) {
3042                                         idx = vp - mol->par->vdwpPars;
3043                                         break;
3044                                 }
3045                         }
3046                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3047                         if (vp != NULL) {
3048                                 idx = vp - gBuiltinParameters->vdwpPars;
3049                                 is_global = 1;
3050                         }
3051                         break;
3052                 }
3053                 default:
3054                         return Qnil;
3055         }
3056         if (idx < 0) {
3057                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3058                         return Qnil;            
3059                 else {
3060                         /*  Insert a new parameter record  */
3061                         UnionPar *up;
3062                         Int count = ParameterGetCountForType(mol->par, parType);
3063                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3064                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3065                         IntGroupRelease(ig);
3066                         is_global = 0;
3067                         idx = count;
3068                         /*  Set atom types  */
3069                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3070                         if (up == NULL)
3071                                 return Qnil;
3072                         switch (parType) {
3073                                 case kBondParType:
3074                                         up->bond.type1 = t[0];
3075                                         up->bond.type2 = t[1];
3076                                         break;
3077                                 case kAngleParType:
3078                                         up->angle.type1 = t[0];
3079                                         up->angle.type2 = t[1];
3080                                         up->angle.type3 = t[2];
3081                                         break;
3082                                 case kDihedralParType:
3083                                 case kImproperParType:
3084                                         up->torsion.type1 = t[0];
3085                                         up->torsion.type2 = t[1];
3086                                         up->torsion.type3 = t[2];
3087                                         up->torsion.type4 = t[3];
3088                                         break;
3089                                 case kVdwParType:
3090                                         up->vdw.type1 = t[0];
3091                                         break;
3092                                 case kVdwPairParType:
3093                                         up->vdwp.type1 = t[0];
3094                                         up->vdwp.type2 = t[1];
3095                                         break;
3096                                 default:
3097                                         return Qnil;
3098                         }
3099                 }
3100         }
3101         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3102 }
3103
3104 /*
3105  *  call-seq:
3106  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3107  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3108  *
3109  *  Find the parameter record that matches the given atom types. The atom types are given
3110  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3111  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3112  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3113  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3114  */
3115 static VALUE
3116 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3117 {
3118         int parType;
3119         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3120         if (argc == 0)
3121                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3122         parType = s_ParTypeFromValue(argv[0]);
3123         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3124 }
3125
3126 /*
3127  *  call-seq:
3128  *     self == parameter -> boolean
3129  *  
3130  *  True if the parameters point to the same parameter table.
3131  */
3132 static VALUE
3133 s_Parameter_Equal(VALUE self, VALUE val)
3134 {
3135         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3136                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3137         } else return Qfalse;
3138 }
3139
3140 #pragma mark ====== ParEnumerable Class ======
3141
3142 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3143  and the parameter type. If the Molecule is NULL, then it refers to the
3144  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3145  the global parameters are always accessible. */
3146
3147 typedef struct ParEnumerable {
3148         Molecule *mol;
3149         Int parType;   /*  Same as parType in ParameterRef  */
3150 } ParEnumerable;
3151
3152 static ParEnumerable *
3153 s_ParEnumerableNew(Molecule *mol, Int parType)
3154 {
3155         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3156         if (pen != NULL) {
3157                 pen->mol = mol;
3158                 if (mol != NULL)
3159                         MoleculeRetain(mol);
3160                 pen->parType = parType;
3161         }
3162         return pen;
3163 }
3164
3165 static void
3166 s_ParEnumerableRelease(ParEnumerable *pen)
3167 {
3168         if (pen != NULL) {
3169                 if (pen->mol != NULL)
3170                         MoleculeRelease(pen->mol);
3171                 free(pen);
3172         }
3173 }
3174
3175 static Molecule *
3176 s_MoleculeFromParEnumerableValue(VALUE val)
3177 {
3178         ParEnumerable *pen;
3179     Data_Get_Struct(val, ParEnumerable, pen);
3180         return pen->mol;
3181 }
3182
3183 static VALUE
3184 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3185 {
3186         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3187         if (pen == NULL)
3188                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3189         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3190 }
3191
3192 /*
3193  *  call-seq:
3194  *     par_type -> String
3195  *
3196  *  Get the parameter type, like "bond", "angle", etc.
3197  */
3198 static VALUE
3199 s_ParEnumerable_ParType(VALUE self) {
3200         ParEnumerable *pen;
3201         Int tp;
3202     Data_Get_Struct(self, ParEnumerable, pen);
3203         tp = pen->parType;
3204         if (tp == kElementParType)
3205                 return rb_str_new2("element");
3206         tp -= kFirstParType;
3207         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3208                 return rb_str_new2(s_ParameterTypeNames[tp]);
3209         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3210 }
3211
3212 /*
3213  *  call-seq:
3214  *     self[idx]          -> ParameterRef
3215  *  
3216  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3217  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3218  *  parent Parameter object of self.
3219  *
3220  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3221  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3222  */
3223 static VALUE
3224 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3225 {
3226         ParEnumerable *pen;
3227     Data_Get_Struct(self, ParEnumerable, pen);
3228         switch (pen->parType) {
3229                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3230                 case kBondParType:      return s_Parameter_Bond(self, ival);
3231                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3232                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3233                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3234                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3235                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3236                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3237                 case kElementParType:   return s_Parameter_Element(self, ival);
3238                 default:
3239                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3240         }
3241         return Qnil;  /*  Not reached  */
3242 }
3243
3244 /*
3245  *  call-seq:
3246  *     length          -> Integer
3247  *  
3248  *  Returns the number of parameters included in this enumerable.
3249  */
3250 static VALUE
3251 s_ParEnumerable_Length(VALUE self)
3252 {
3253         ParEnumerable *pen;
3254     Data_Get_Struct(self, ParEnumerable, pen);
3255         switch (pen->parType) {
3256                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3257                 case kBondParType:      return s_Parameter_Nbonds(self);
3258                 case kAngleParType:     return s_Parameter_Nangles(self); 
3259                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3260                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3261                 case kVdwParType:       return s_Parameter_Nvdws(self);
3262                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3263                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3264                 case kElementParType:   return s_Parameter_Nelements(self);
3265                 default:
3266                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3267         }
3268         return Qnil;  /*  Not reached  */
3269 }
3270
3271 /*
3272  *  call-seq:
3273  *     each {|pref| ...}
3274  *  
3275  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3276  */
3277 VALUE
3278 s_ParEnumerable_Each(VALUE self)
3279 {
3280         VALUE aval;
3281         ParEnumerable *pen;
3282         ParameterRef *pref;
3283         int i, ofs, n;
3284     Data_Get_Struct(self, ParEnumerable, pen);
3285         if (pen->parType == kElementParType)
3286                 n = gCountElementParameters;
3287         else {
3288                 switch (pen->parType) {
3289                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3290                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3291                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3292                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3293                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3294                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3295                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3296                         default:
3297                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3298                 }
3299                 if (pen->mol == NULL)
3300                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3301                 else if (pen->mol->par != NULL)
3302                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3303                 else return self;
3304         }               
3305         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3306         Data_Get_Struct(aval, ParameterRef, pref);
3307         for (i = 0; i < n; i++) {
3308                 pref->idx = i;
3309                 rb_yield(aval);
3310         }
3311     return self;
3312 }
3313
3314 /*
3315  *  call-seq:
3316  *     reverse_each {|pref| ...}
3317  *  
3318  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3319  */
3320 VALUE
3321 s_ParEnumerable_ReverseEach(VALUE self)
3322 {
3323         VALUE aval;
3324         ParEnumerable *pen;
3325         ParameterRef *pref;
3326         int i, ofs, n;
3327     Data_Get_Struct(self, ParEnumerable, pen);
3328         if (pen->parType == kElementParType)
3329                 n = gCountElementParameters;
3330         else {
3331                 switch (pen->parType) {
3332                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3333                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3334                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3335                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3336                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3337                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3338                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3339                         default:
3340                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3341                 }
3342                 if (pen->mol == NULL)
3343                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3344                 else if (pen->mol->par != NULL)
3345                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3346                 else return self;
3347         }               
3348         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3349         Data_Get_Struct(aval, ParameterRef, pref);
3350         for (i = n - 1; i >= 0; i--) {
3351                 pref->idx = i;
3352                 rb_yield(aval);
3353         }
3354     return self;
3355 }
3356
3357 /*
3358  *  call-seq:
3359  *     insert(idx = nil, pref = nil)       -> ParameterRef
3360  *  
3361  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3362  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3363  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3364  *  parameter is left undefined.
3365  *  Throws an exception if ParEnumerable points to the global parameter.
3366  */
3367 static VALUE
3368 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3369 {
3370         VALUE ival, pval;
3371         ParEnumerable *pen;
3372         int i, n;
3373         IntGroup *ig;
3374         UnionPar u;
3375         MolAction *act;
3376     Data_Get_Struct(self, ParEnumerable, pen);
3377         if (pen->mol == NULL)
3378                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3379         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3380         rb_scan_args(argc, argv, "02", &ival, &pval);
3381         if (ival != Qnil) {
3382                 i = NUM2INT(rb_Integer(ival));
3383                 if (i < 0 || i > n)
3384                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3385                 n = i;
3386         }
3387         if (pval != Qnil) {
3388                 Int type;
3389                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3390                 if (up == NULL || type != pen->parType)
3391                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3392                 ParameterCopyOneWithType(&u, up, pen->parType);
3393                 u.bond.src = 0;
3394         } else {
3395                 memset(&u, 0, sizeof(u));
3396                 u.bond.src = 0;
3397         }
3398         ig = IntGroupNewWithPoints(n, 1, -1);
3399         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3400
3401         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3402         MolActionCallback_registerUndo(pen->mol, act);
3403         MolActionRelease(act);
3404         
3405         IntGroupRelease(ig);
3406         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3407 }
3408
3409 /*
3410  *  call-seq:
3411  *     delete(Integer)
3412  *     delete(IntGroup)
3413  *  
3414  *  Delete the parameter(s) specified by the argument.
3415  */
3416 static VALUE
3417 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3418 {
3419         ParEnumerable *pen;
3420         int i, n;
3421         IntGroup *ig;
3422     Data_Get_Struct(self, ParEnumerable, pen);
3423         if (pen->mol == NULL)
3424                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3425         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3426         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3427                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3428                 i = 1;
3429         } else {
3430                 ig = IntGroupFromValue(ival);
3431                 if ((i = IntGroupGetCount(ig)) == 0) {
3432                         IntGroupRelease(ig);
3433                         return Qnil;
3434                 }
3435         }
3436         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3437                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3438         n = i;
3439
3440         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3441         IntGroupRelease(ig);
3442         return ival;
3443 }
3444
3445 /*
3446  *  call-seq:
3447  *     lookup(atom_types, options, ...) -> ParameterRef
3448  *     lookup(atom_type_string, options, ...) -> ParameterRef
3449  *
3450  *  Find the parameter record that matches the given atom types. The arguments are
3451  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3452  *  specified.
3453  */
3454 static VALUE
3455 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3456 {
3457         ParEnumerable *pen;
3458     Data_Get_Struct(self, ParEnumerable, pen);
3459         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3460 }
3461
3462 /*
3463  *  call-seq:
3464  *     self == parEnumerable -> boolean
3465  *  
3466  *  True if the arguments point to the same parameter table and type.
3467  */
3468 static VALUE
3469 s_ParEnumerable_Equal(VALUE self, VALUE val)
3470 {
3471         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3472                 ParEnumerable *pen1, *pen2;
3473                 Data_Get_Struct(self, ParEnumerable, pen1);
3474                 Data_Get_Struct(val, ParEnumerable, pen2);
3475                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3476         } else return Qfalse;
3477 }
3478
3479 #pragma mark ====== AtomRef Class ======
3480
3481 /*  Forward declaration for register undo  */
3482 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3483
3484 /*  Ruby string "set_atom_attr"  */
3485 static VALUE s_SetAtomAttrString;
3486
3487 static int
3488 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3489 {
3490         AtomRef *aref;
3491         int idx;
3492         Data_Get_Struct(self, AtomRef, aref);
3493         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3494         if (idx < 0 || idx >= aref->mol->natoms)
3495                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3496         if (app != NULL)
3497                 *app = aref->mol->atoms + idx;
3498         if (mpp != NULL)
3499                 *mpp = aref->mol;
3500         return idx;
3501 }
3502
3503 static Atom *
3504 s_AtomFromValue(VALUE self)
3505 {
3506         Atom *ap;
3507         s_AtomIndexFromValue(self, &ap, NULL);
3508         return ap;
3509 }
3510
3511 static Atom *
3512 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3513 {
3514         Atom *ap;
3515         s_AtomIndexFromValue(self, &ap, mpp);
3516         return ap;
3517 }
3518
3519 static void
3520 s_NotifyModificationForAtomRef(VALUE self)
3521 {
3522         AtomRef *aref;
3523         Data_Get_Struct(self, AtomRef, aref);
3524         MoleculeIncrementModifyCount(aref->mol);
3525 }
3526
3527 static void
3528 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3529 {
3530         AtomRef *aref;
3531         Data_Get_Struct(self, AtomRef, aref);
3532         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3533                 /*  Register undo  */
3534                 MolAction *act;
3535                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3536                 MolActionCallback_registerUndo(aref->mol, act);
3537                 MoleculeCallback_notifyModification(aref->mol, 0);
3538                 /*  Request MD rebuilt if necessary  */
3539                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3540                         aref->mol->needsMDRebuild = 1;
3541         }
3542 }
3543
3544 VALUE
3545 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3546 {
3547         AtomRef *aref;
3548         aref = AtomRefNew(mol, idx);
3549         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3550 }
3551
3552 static VALUE
3553 s_AtomRef_GetMolecule(VALUE self)
3554 {
3555         Molecule *mpp;
3556         s_AtomIndexFromValue(self, NULL, &mpp);
3557         return ValueFromMolecule(mpp);
3558 }
3559
3560 static VALUE s_AtomRef_GetIndex(VALUE self) {
3561         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3562 }
3563
3564 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3565         return INT2NUM(s_AtomFromValue(self)->segSeq);
3566 }
3567
3568 static VALUE s_AtomRef_GetSegName(VALUE self) {
3569         char *p = s_AtomFromValue(self)->segName;
3570         return rb_str_new(p, strlen_limit(p, 4));
3571 }
3572
3573 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3574         return INT2NUM(s_AtomFromValue(self)->resSeq);
3575 }
3576
3577 static VALUE s_AtomRef_GetResName(VALUE self) {
3578         char *p = s_AtomFromValue(self)->resName;
3579         return rb_str_new(p, strlen_limit(p, 4));
3580 }
3581
3582 static VALUE s_AtomRef_GetName(VALUE self) {
3583         char *p = s_AtomFromValue(self)->aname;
3584         return rb_str_new(p, strlen_limit(p, 4));
3585 }
3586
3587 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3588         int type = s_AtomFromValue(self)->type;
3589         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3590         return rb_str_new(p, strlen_limit(p, 6));
3591 }
3592
3593 static VALUE s_AtomRef_GetCharge(VALUE self) {
3594         return rb_float_new(s_AtomFromValue(self)->charge);
3595 }
3596
3597 static VALUE s_AtomRef_GetWeight(VALUE self) {
3598         return rb_float_new(s_AtomFromValue(self)->weight);
3599 }
3600
3601 static VALUE s_AtomRef_GetElement(VALUE self) {
3602         char *p = s_AtomFromValue(self)->element;
3603         return rb_str_new(p, strlen_limit(p, 4));
3604 }
3605
3606 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3607         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3608 }
3609
3610 static VALUE s_AtomRef_GetConnects(VALUE self) {
3611         VALUE retval;
3612         Int i, *cp;
3613         Atom *ap = s_AtomFromValue(self);
3614         retval = rb_ary_new();
3615         cp = AtomConnectData(&ap->connect);
3616         for (i = 0; i < ap->connect.count; i++)
3617                 rb_ary_push(retval, INT2NUM(cp[i]));
3618         return retval;
3619 }
3620
3621 static VALUE s_AtomRef_GetR(VALUE self) {
3622         return ValueFromVector(&(s_AtomFromValue(self)->r));
3623 }
3624
3625 static VALUE s_AtomRef_GetX(VALUE self) {
3626         return rb_float_new(s_AtomFromValue(self)->r.x);
3627 }
3628
3629 static VALUE s_AtomRef_GetY(VALUE self) {
3630         return rb_float_new(s_AtomFromValue(self)->r.y);
3631 }
3632
3633 static VALUE s_AtomRef_GetZ(VALUE self) {
3634         return rb_float_new(s_AtomFromValue(self)->r.z);
3635 }
3636
3637 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3638         Atom *ap;
3639         Molecule *mp;
3640         Vector r1;
3641         s_AtomIndexFromValue(self, &ap, &mp);
3642         r1 = ap->r;
3643         if (mp->cell != NULL)
3644                 TransformVec(&r1, mp->cell->rtr, &r1);
3645         return r1;
3646 }
3647
3648 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3649         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3650         return ValueFromVector(&r1);
3651 }
3652
3653 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3654         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3655 }
3656
3657 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3658         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3659 }
3660
3661 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3662         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3663 }
3664
3665 static VALUE s_AtomRef_GetSigma(VALUE self) {
3666         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3667 }
3668
3669 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3670         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3671 }
3672
3673 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3674         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3675 }
3676
3677 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3678         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3679 }
3680
3681 static VALUE s_AtomRef_GetV(VALUE self) {
3682         return ValueFromVector(&(s_AtomFromValue(self)->v));
3683 }
3684
3685 static VALUE s_AtomRef_GetF(VALUE self) {
3686         Vector v = s_AtomFromValue(self)->f;
3687         VecScaleSelf(v, INTERNAL2KCAL);
3688         return ValueFromVector(&v);
3689 }
3690
3691 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3692         return rb_float_new(s_AtomFromValue(self)->occupancy);
3693 }
3694
3695 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3696         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3697 }
3698
3699 static VALUE s_AtomRef_GetAniso(VALUE self) {
3700         VALUE retval;
3701         int i;
3702         Atom *ap = s_AtomFromValue(self);
3703         if (ap->aniso == NULL)
3704                 return Qnil;
3705         retval = rb_ary_new();
3706         for (i = 0; i < 6; i++)
3707                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3708         if (ap->aniso->has_bsig) {
3709                 rb_ary_push(retval, INT2NUM(0));
3710                 for (i = 0; i < 6; i++)
3711                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3712         }
3713         return retval;
3714 }
3715
3716 static VALUE s_AtomRef_GetSymop(VALUE self) {
3717         VALUE retval;
3718         Atom *ap = s_AtomFromValue(self);
3719         if (!ap->symop.alive)
3720                 return Qnil;
3721         retval = rb_ary_new();
3722         rb_ary_push(retval, INT2NUM(ap->symop.sym));
3723         rb_ary_push(retval, INT2NUM(ap->symop.dx));
3724         rb_ary_push(retval, INT2NUM(ap->symop.dy));
3725         rb_ary_push(retval, INT2NUM(ap->symop.dz));
3726         rb_ary_push(retval, INT2NUM(ap->symbase));
3727         return retval;
3728 }
3729
3730 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3731         return INT2NUM(s_AtomFromValue(self)->intCharge);
3732 }
3733
3734 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3735         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3736 }
3737
3738 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3739         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3740 }
3741
3742 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3743         Molecule *mol;
3744         Atom *ap;
3745         int idx, i;
3746         MDExclusion *exinfo;
3747         Int *exlist;
3748         VALUE retval, aval;
3749         idx = s_AtomIndexFromValue(self, &ap, &mol);
3750         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3751                 VALUE mval = ValueFromMolecule(mol);
3752                 s_RebuildMDParameterIfNecessary(mval, Qnil);
3753         }
3754         if (mol->arena->exinfo == NULL)
3755                 return Qnil;
3756         exinfo = mol->arena->exinfo + idx;
3757         exlist = mol->arena->exlist;
3758         retval = rb_ary_new();
3759         aval = rb_ary_new();
3760         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
3761                 rb_ary_push(aval, INT2NUM(exlist[i]));
3762         rb_ary_push(retval, aval);
3763         aval = rb_ary_new();
3764         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
3765                 rb_ary_push(aval, INT2NUM(exlist[i]));
3766         rb_ary_push(retval, aval);
3767         aval = rb_ary_new();
3768         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
3769                 rb_ary_push(aval, INT2NUM(exlist[i]));
3770         rb_ary_push(retval, aval);
3771         return retval;
3772 }
3773
3774 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3775         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3776 }
3777
3778 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3779         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3780 }
3781
3782 static VALUE s_AtomRef_GetHidden(VALUE self) {
3783         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3784 }
3785
3786 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3787         VALUE retval;
3788         Int i, count, *cp;
3789         Atom *ap = s_AtomFromValue(self);
3790         if (ap->anchor == NULL)
3791                 return Qnil;
3792         count = ap->anchor->connect.count;
3793         retval = rb_ary_new2(count * 2);
3794         cp = AtomConnectData(&ap->anchor->connect);
3795         for (i = 0; i < count; i++) {
3796                 rb_ary_store(retval, i, INT2NUM(cp[i]));
3797                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3798         }
3799         return retval;
3800 }
3801
3802 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3803         char *p = s_AtomFromValue(self)->uff_type;
3804         return rb_str_new(p, strlen_limit(p, 5));
3805 }
3806
3807 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3808         rb_raise(rb_eMolbyError, "index cannot be directly set");
3809         return Qnil;
3810 }
3811
3812 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
3813         VALUE oval = s_AtomRef_GetSegSeq(self);
3814         val = rb_Integer(val);
3815         s_AtomFromValue(self)->segSeq = NUM2INT(val);
3816         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
3817         return val;
3818 }
3819
3820 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
3821         char *p = StringValuePtr(val);
3822         VALUE oval = s_AtomRef_GetSegName(self);
3823         strncpy(s_AtomFromValue(self)->segName, p, 4);
3824         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
3825         return val;
3826 }
3827
3828 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
3829         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
3830         return val; /* Not reached */
3831 }
3832
3833 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
3834         Atom *ap = s_AtomFromValue(self);
3835         char *p = StringValuePtr(val);
3836         VALUE oval = s_AtomRef_GetName(self);
3837         if (ap->anchor != NULL && p[0] == '_')
3838                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
3839         strncpy(ap->aname, p, 4);
3840         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
3841         return val;
3842 }
3843
3844 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
3845         Molecule *mp;
3846         char *p = StringValuePtr(val);
3847         VALUE oval = s_AtomRef_GetAtomType(self);
3848         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
3849         if (type != 0 && type < kAtomTypeMinimum)
3850                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
3851         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
3852         mp->needsMDRebuild = 1;
3853         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
3854         return val;
3855 }
3856
3857 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
3858         Molecule *mp;
3859         VALUE oval = s_AtomRef_GetCharge(self);
3860         val = rb_Float(val);
3861         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
3862         mp->needsMDRebuild = 1;
3863         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
3864         return val;
3865 }
3866
3867 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
3868         Molecule *mp;
3869         VALUE oval = s_AtomRef_GetWeight(self);
3870         val = rb_Float(val);
3871         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
3872         mp->needsMDRebuild = 1;
3873         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
3874         return val;
3875 }
3876
3877 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
3878         Double w;
3879         Molecule *mp;
3880         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3881         char *p = StringValuePtr(val);
3882         VALUE oval = s_AtomRef_GetElement(self);
3883         ap->atomicNumber = ElementToInt(p);
3884         ElementToString(ap->atomicNumber, ap->element);
3885         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3886                 ap->weight = w;
3887         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
3888         mp->needsMDRebuild = 1;
3889         return val;
3890 }
3891
3892 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
3893         Double w;
3894         Molecule *mp;
3895         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3896         VALUE oval = s_AtomRef_GetAtomicNumber(self);
3897         val = rb_Integer(val);
3898         ap->atomicNumber = NUM2INT(val);
3899         ElementToString(ap->atomicNumber, ap->element);
3900         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3901                 ap->weight = w;
3902         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
3903         mp->needsMDRebuild = 1;
3904         return val;
3905 }
3906
3907 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
3908         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
3909         return val; /* Not reached */
3910 }
3911
3912 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
3913         Vector v;
3914         Molecule *mp;
3915         VALUE oval = s_AtomRef_GetR(self);
3916         VectorFromValue(val, &v);
3917         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
3918         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
3919         mp->needsMDCopyCoordinates = 1;
3920         return val;
3921 }
3922
3923 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
3924         Double f;
3925         Molecule *mp;
3926         VALUE oval = s_AtomRef_GetX(self);
3927         val = rb_Float(val);
3928         f = NUM2DBL(val);
3929         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
3930         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
3931         mp->needsMDCopyCoordinates = 1;
3932         return val;
3933 }
3934
3935 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
3936         Double f;
3937         Molecule *mp;
3938         VALUE oval = s_AtomRef_GetY(self);
3939         val = rb_Float(val);
3940         f = NUM2DBL(val);
3941         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
3942         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
3943         mp->needsMDCopyCoordinates = 1;
3944         return val;
3945 }
3946
3947 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
3948         Double f;
3949         Molecule *mp;
3950         VALUE oval = s_AtomRef_GetZ(self);
3951         val = rb_Float(val);
3952         f = NUM2DBL(val);
3953         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
3954         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
3955         mp->needsMDCopyCoordinates = 1;
3956         return val;
3957 }
3958
3959 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
3960         Vector v, ov;
3961         Atom *ap;
3962         Molecule *mp;
3963         s_AtomIndexFromValue(self, &ap, &mp);
3964         ov = ap->r;
3965         VectorFromValue(val, &v);
3966         if (mp->cell != NULL)
3967                 TransformVec(&v, mp->cell->tr, &v);
3968         ap->r = v;
3969         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3970         mp->needsMDCopyCoordinates = 1;
3971         return val;
3972 }
3973
3974 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
3975         double f;
3976         Vector v, ov;
3977         Atom *ap;
3978         Molecule *mp;
3979         s_AtomIndexFromValue(self, &ap, &mp);
3980         ov = v = ap->r;
3981         val = rb_Float(val);
3982         f = NUM2DBL(val);
3983         if (mp->cell != NULL) {
3984                 TransformVec(&v, mp->cell->rtr, &v);
3985                 v.x = f;
3986                 TransformVec(&v, mp->cell->tr, &v);
3987         } else v.x = f;
3988         ap->r = v;
3989         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3990         mp->needsMDCopyCoordinates = 1;
3991         return val;
3992 }
3993
3994 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
3995         double f;
3996         Vector v, ov;
3997         Atom *ap;
3998         Molecule *mp;
3999         s_AtomIndexFromValue(self, &ap, &mp);
4000         ov = v = ap->r;
4001         val = rb_Float(val);
4002         f = NUM2DBL(val);
4003         if (mp->cell != NULL) {
4004                 TransformVec(&v, mp->cell->rtr, &v);
4005                 v.y = f;
4006                 TransformVec(&v, mp->cell->tr, &v);
4007         } else v.y = f;
4008         ap->r = v;
4009         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4010         mp->needsMDCopyCoordinates = 1;
4011         return val;
4012 }
4013
4014 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4015         double f;
4016         Vector v, ov;
4017         Atom *ap;
4018         Molecule *mp;
4019         s_AtomIndexFromValue(self, &ap, &mp);
4020         ov = v = ap->r;
4021         val = rb_Float(val);
4022         f = NUM2DBL(val);
4023         if (mp->cell != NULL) {
4024                 TransformVec(&v, mp->cell->rtr, &v);
4025                 v.z = f;
4026                 TransformVec(&v, mp->cell->tr, &v);
4027         } else v.z = f;
4028         ap->r = v;
4029         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4030         mp->needsMDCopyCoordinates = 1;
4031         return val;
4032 }
4033
4034 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4035         Vector v;
4036         Molecule *mp;
4037         VALUE oval = s_AtomRef_GetSigma(self);
4038         VectorFromValue(val, &v);
4039         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4040         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4041         mp->needsMDCopyCoordinates = 1;
4042         return val;
4043 }
4044
4045 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4046         Double f;
4047         Molecule *mp;
4048         VALUE oval = s_AtomRef_GetSigmaX(self);
4049         val = rb_Float(val);
4050         f = NUM2DBL(val);
4051         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4052         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4053         mp->needsMDCopyCoordinates = 1;
4054         return val;
4055 }
4056
4057 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4058         Double f;
4059         Molecule *mp;
4060         VALUE oval = s_AtomRef_GetSigmaY(self);
4061         val = rb_Float(val);
4062         f = NUM2DBL(val);
4063         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4064         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4065         mp->needsMDCopyCoordinates = 1;
4066         return val;
4067 }
4068
4069 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4070         Double f;
4071         Molecule *mp;
4072         VALUE oval = s_AtomRef_GetSigmaZ(self);
4073         val = rb_Float(val);
4074         f = NUM2DBL(val);
4075         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4076         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4077         mp->needsMDCopyCoordinates = 1;
4078         return val;
4079 }
4080
4081 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4082         Vector v;
4083         Atom *ap;
4084         Molecule *mp;
4085         VALUE oval = s_AtomRef_GetV(self);
4086         VectorFromValue(val, &v);
4087         s_AtomIndexFromValue(self, &ap, &mp);
4088         ap->v = v;
4089         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4090         mp->needsMDCopyCoordinates = 1;
4091         return val;
4092 }
4093
4094 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4095         Vector v;
4096         Molecule *mp;
4097         VALUE oval = s_AtomRef_GetF(self);
4098         VectorFromValue(val, &v);
4099         VecScaleSelf(v, KCAL2INTERNAL);
4100         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4101         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4102         mp->needsMDCopyCoordinates = 1;
4103         return val;
4104 }
4105
4106 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4107         VALUE oval = s_AtomRef_GetOccupancy(self);
4108         Molecule *mp;
4109         val = rb_Float(val);
4110         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4111         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4112         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4113         return val;
4114 }
4115
4116 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4117         VALUE oval = s_AtomRef_GetTempFactor(self);
4118         val = rb_Float(val);
4119         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4120         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4121         return val;
4122 }
4123
4124 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4125         AtomRef *aref;
4126         int i, n, type;
4127         VALUE *valp;
4128         Double f[12];
4129         VALUE oval = s_AtomRef_GetAniso(self);
4130         Data_Get_Struct(self, AtomRef, aref);
4131         val = rb_funcall(val, rb_intern("to_a"), 0);
4132         n = RARRAY_LEN(val);
4133         valp = RARRAY_PTR(val);
4134         for (i = 0; i < 6; i++) {
4135                 if (i < n)
4136                         f[i] = NUM2DBL(rb_Float(valp[i]));
4137                 else f[i] = 0.0;
4138         }
4139         if (n >= 7)
4140                 type = NUM2INT(rb_Integer(valp[6]));
4141         else type = 0;
4142         if (n >= 13) {
4143                 for (i = 0; i < 6; i++)
4144                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4145         } else {
4146                 for (i = 0; i < 6; i++)
4147                         f[i + 6] = 0.0;
4148         }
4149         i = s_AtomIndexFromValue(self, NULL, NULL);
4150         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4151         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4152         return val;
4153 }
4154
4155 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4156         Molecule *mol;
4157         Atom *ap;
4158         int i, n;
4159         VALUE *valp;
4160         Int ival[5];
4161         VALUE oval = s_AtomRef_GetSymop(self);
4162         i = s_AtomIndexFromValue(self, &ap, &mol);
4163         if (val == Qnil) {
4164                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4165         } else {
4166                 val = rb_funcall(val, rb_intern("to_a"), 0);
4167                 n = RARRAY_LEN(val);
4168                 valp = RARRAY_PTR(val);
4169                 for (i = 0; i < 5; i++) {
4170                         if (i < n) {
4171                                 if (valp[i] == Qnil)
4172                                         ival[i] = -100000;
4173                                 else 
4174                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4175                         } else ival[i] = -100000;
4176                 }
4177         }
4178         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4179                 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));
4180         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4181                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4182         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4183                 return val;  /*  No need to change  */
4184         if (ival[0] != -100000)
4185                 ap->symop.sym = ival[0];
4186         if (ival[1] != -100000)
4187                 ap->symop.dx = ival[1];
4188         if (ival[2] != -100000)
4189                 ap->symop.dy = ival[2];
4190         if (ival[3] != -100000)
4191                 ap->symop.dz = ival[3];
4192         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4193         if (ival[4] != -100000)
4194                 ap->symbase = ival[4];
4195         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4196                 /*  The anisotropic parameters should be recalculated  */
4197                 VALUE oaval = s_AtomRef_GetAniso(self);
4198                 MoleculeSetAnisoBySymop(mol, i);
4199                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4200         }
4201         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4202         return val;
4203 }
4204
4205 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4206         VALUE oval = s_AtomRef_GetIntCharge(self);
4207         val = rb_Integer(val);
4208         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4209         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4210         return val;
4211 }
4212
4213 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4214         Molecule *mp;
4215         VALUE oval = s_AtomRef_GetFixForce(self);
4216         val = rb_Float(val);
4217         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4218         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4219         mp->needsMDRebuild = 1;
4220         return val;
4221 }
4222
4223 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4224         Vector v;
4225         Molecule *mp;
4226         VALUE oval = s_AtomRef_GetFixPos(self);
4227         VectorFromValue(val, &v);
4228         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4229         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4230         mp->needsMDRebuild = 1;
4231         return val;
4232 }
4233
4234 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4235         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4236         return val; /* Not reached */
4237 }
4238
4239 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4240         VALUE oval = s_AtomRef_GetIntCharge(self);
4241         val = rb_Integer(val);
4242         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4243         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4244         return val;
4245 }
4246
4247 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4248         VALUE oval = s_AtomRef_GetIntCharge(self);
4249         val = rb_Integer(val);
4250         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4251         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4252         return val;
4253 }
4254
4255 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4256         Atom *ap = s_AtomFromValue(self);
4257         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4258         if (RTEST(val)) {
4259                 ap->exflags |= kAtomHiddenFlag;
4260         } else {
4261                 ap->exflags &= ~kAtomHiddenFlag;
4262         }
4263         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4264         return val;
4265 }
4266
4267 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4268         Int idx, i, j, k, n, *ip;
4269         Double *dp;
4270         Atom *ap;
4271         Molecule *mol;
4272         VALUE oval, v;
4273         AtomConnect ac;
4274         Int nUndoActions;
4275         MolAction **undoActions;
4276         memset(&ac, 0, sizeof(ac));
4277         idx = s_AtomIndexFromValue(self, &ap, &mol);
4278         oval = s_AtomRef_GetAnchorList(self);
4279         if (val != Qnil) {
4280                 val = rb_ary_to_ary(val);
4281                 n = RARRAY_LEN(val);
4282         } else n = 0;
4283         if (n == 0) {
4284                 if (ap->anchor != NULL) {
4285                         AtomConnectResize(&ap->anchor->connect, 0);
4286                         free(ap->anchor->coeffs);
4287                         free(ap->anchor);
4288                         ap->anchor = NULL;
4289                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4290                 }
4291                 return val;
4292         }
4293         if (n < 2)
4294                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4295         if (ap->aname[0] == '_')
4296                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4297         ip = (Int *)malloc(sizeof(Int) * n);
4298         dp = NULL;
4299         for (i = 0; i < n; i++) {
4300                 v = RARRAY_PTR(val)[i];
4301                 if (rb_obj_is_kind_of(v, rb_cFloat))
4302                         break;
4303                 j = NUM2INT(rb_Integer(v));
4304                 if (j < 0 || j >= mol->natoms)
4305                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4306                 for (k = 0; k < i; k++) {
4307                         if (ip[k] == j)
4308                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4309                 }
4310                 ip[i] = j;
4311         }
4312         if (i < n) {
4313                 if (i < 2)
4314                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4315                 else if (i * 2 != n)
4316                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4317                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4318                 for (i = 0; i < n / 2; i++) {
4319                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4320                         if (dp[i] <= 0.0)
4321                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4322                 }
4323                 n /= 2;
4324         }
4325         nUndoActions = 0;
4326         undoActions = NULL;
4327         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4328         free(dp);
4329         free(ip);
4330         if (i != 0)
4331                 rb_raise(rb_eMolbyError, "invalid argument");
4332         if (nUndoActions > 0) {
4333                 for (i = 0; i < nUndoActions; i++) {
4334                         MolActionCallback_registerUndo(mol, undoActions[i]);
4335                         MolActionRelease(undoActions[i]);
4336                 }
4337                 free(undoActions);
4338         }
4339         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4340         return val;
4341 }
4342
4343 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4344         Atom *ap = s_AtomFromValue(self);
4345         char *p = StringValuePtr(val);
4346         VALUE oval = s_AtomRef_GetUFFType(self);
4347         strncpy(ap->uff_type, p, 5);
4348         ap->uff_type[5] = 0;
4349         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4350         return val;
4351 }
4352
4353 static struct s_AtomAttrDef {
4354         char *name;
4355         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4356         ID id;                  /*  Will be set within InitMolby()  */
4357         VALUE (*getter)(VALUE);
4358         VALUE (*setter)(VALUE, VALUE);
4359 } s_AtomAttrDefTable[] = {
4360         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4361         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4362         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4363         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4364         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4365         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4366         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4367         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4368         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4369         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4370         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4371         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4372         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4373         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4374         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4375     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4376         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4377         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4378         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4379         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4380         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4381         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4382         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4383         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4384         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4385         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4386         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4387         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4388         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4389         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4390         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4391         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4392         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4393         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4394         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4395         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4396         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4397         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4398         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4399         {NULL} /* Sentinel */
4400 };
4401
4402 static VALUE
4403 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4404 {
4405         int i;
4406         ID kid;
4407         if (TYPE(key) != T_SYMBOL) {
4408                 kid = rb_intern(StringValuePtr(key));
4409                 key = ID2SYM(kid);
4410         } else kid = SYM2ID(key);
4411         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4412                 if (s_AtomAttrDefTable[i].id == kid) {
4413                         if (value == Qundef)
4414                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4415                         else
4416                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4417                 }
4418         }
4419         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4420         return Qnil; /* not reached */
4421 }
4422
4423 static VALUE
4424 s_AtomRef_GetAttr(VALUE self, VALUE key)
4425 {
4426         return s_AtomRef_SetAttr(self, key, Qundef);
4427 }
4428
4429 /*
4430  *  call-seq:
4431  *     self == atomRef -> boolean
4432  *
4433  *  True if the two references point to the same atom.
4434  */
4435 static VALUE
4436 s_AtomRef_Equal(VALUE self, VALUE val)
4437 {
4438         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4439                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4440         } else return Qfalse;
4441 }
4442
4443 #pragma mark ====== MolEnumerable Class ======
4444
4445 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4446
4447 /*
4448  *  call-seq:
4449  *     self[idx] -> AtomRef or Array of Integers
4450  *  
4451  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4452  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4453  *  value is a String. Otherwise, the return value is an Array of Integers.
4454  */
4455 static VALUE
4456 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4457 {
4458         MolEnumerable *mseq;
4459         Molecule *mol;
4460         int idx1, idx2;
4461     Data_Get_Struct(self, MolEnumerable, mseq);
4462         mol = mseq->mol;
4463         if (mseq->kind == kAtomKind) {
4464                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4465         }
4466         idx1 = NUM2INT(arg1);
4467         switch (mseq->kind) {
4468                 case kBondKind: {
4469                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4470                         if (idx2 < 0 || idx2 >= mol->nbonds)
4471                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4472                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4473                 }
4474                 case kAngleKind: {
4475                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4476                         if (idx2 < 0 || idx2 >= mol->nangles)
4477                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4478                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4479                 }
4480                 case kDihedralKind: {
4481                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4482                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4483                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4484                         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]));
4485                 }
4486                 case kImproperKind: {
4487                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4488                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4489                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4490                         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]));
4491                 }
4492                 case kResidueKind: {
4493                         char *p;
4494                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4495                         if (idx2 < 0 || idx2 >= mol->nresidues)
4496                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4497                         p = mol->residues[idx2];
4498                         return rb_str_new(p, strlen_limit(p, 4));
4499                 }
4500         }
4501         return Qnil;
4502 }
4503
4504 /*
4505  *  call-seq:
4506  *     length          -> Integer
4507  *  
4508  *  Returns the number of objects included in this enumerable.
4509  */
4510 static VALUE
4511 s_MolEnumerable_Length(VALUE self)
4512 {
4513         MolEnumerable *mseq;
4514     Data_Get_Struct(self, MolEnumerable, mseq);
4515         switch (mseq->kind) {
4516                 case kAtomKind:
4517                         return INT2NUM(mseq->mol->natoms);
4518                 case kBondKind:
4519                         return INT2NUM(mseq->mol->nbonds);
4520                 case kAngleKind:
4521                         return INT2NUM(mseq->mol->nangles);
4522                 case kDihedralKind:
4523                         return INT2NUM(mseq->mol->ndihedrals);
4524                 case kImproperKind:
4525                         return INT2NUM(mseq->mol->nimpropers);
4526                 case kResidueKind:
4527                         return INT2NUM(mseq->mol->nresidues);
4528         }
4529         return INT2NUM(-1);
4530 }
4531
4532 /*
4533  *  call-seq:
4534  *     each {|obj| ...}
4535  *  
4536  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4537  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4538  *  For the atoms, a same AtomRef object is passed (with different internal information)
4539  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4540  *  for each iteration.
4541  */
4542 VALUE
4543 s_MolEnumerable_Each(VALUE self)
4544 {
4545         MolEnumerable *mseq;
4546         int i;
4547         int len = NUM2INT(s_MolEnumerable_Length(self));
4548     Data_Get_Struct(self, MolEnumerable, mseq);
4549         if (mseq->kind == kAtomKind) {
4550                 /*  The same AtomRef object will be used during the loop  */
4551                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4552                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4553                 for (i = 0; i < len; i++) {
4554                         aref->idx = i;
4555                         rb_yield(arval);
4556                 }
4557     } else {
4558                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4559                 for (i = 0; i < len; i++) {
4560                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4561                 }
4562         }
4563     return self;
4564 }
4565
4566 /*
4567  *  call-seq:
4568  *     self == molEnumerable -> boolean
4569  *
4570  *  True if the two arguments point to the same molecule and enumerable type.
4571  */
4572 static VALUE
4573 s_MolEnumerable_Equal(VALUE self, VALUE val)
4574 {
4575         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4576                 MolEnumerable *mseq1, *mseq2;
4577                 Data_Get_Struct(self, MolEnumerable, mseq1);
4578                 Data_Get_Struct(val, MolEnumerable, mseq2);
4579                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4580         } else return Qfalse;
4581 }
4582
4583
4584 #pragma mark ====== Molecule Class ======
4585
4586 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method.  */
4587 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4588 char *gLoadSaveErrorMessage = NULL;
4589
4590 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4591
4592 Molecule *
4593 MoleculeFromValue(VALUE val)
4594 {
4595         Molecule *mol;
4596         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4597                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4598     Data_Get_Struct(val, Molecule, mol);
4599         return mol;
4600 }
4601
4602 static VALUE sMoleculeRetainArray = Qnil;
4603
4604 /*  The function is called from MoleculeRelease()  */
4605 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4606 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4607 /*  object is always returned for the same Molecule.  */
4608 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4609 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4610 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4611 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4612 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4613
4614 /*  Register/unregister the exmolobj Ruby object  */
4615 void
4616 MoleculeReleaseExternalObj(Molecule *mol)
4617 {
4618         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4619                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4620                 mol->exmolobjProtected = 0;
4621         }
4622 }
4623
4624 void
4625 MoleculeRetainExternalObj(Molecule *mol)
4626 {
4627         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4628                 if (sMoleculeRetainArray == Qnil) {
4629                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4630                         sMoleculeRetainArray = rb_ary_new();
4631                 }
4632                 
4633                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4634                 mol->exmolobjProtected = 1;
4635         }
4636 }
4637
4638 /*  Release hook function for Ruby  */
4639 void
4640 MoleculeReleaseHook(Molecule *mol)
4641 {
4642         if (mol->exmolobj != NULL) {
4643                 /*  No need to remove from sMoleculeRetainArray  */
4644                 mol->exmolobj = NULL;
4645                 mol->exmolobjProtected = 0;
4646         }
4647         MoleculeRelease(mol);
4648 }
4649
4650 VALUE
4651 ValueFromMolecule(Molecule *mol)
4652 {
4653         if (mol == NULL)
4654                 return Qnil;
4655         if (mol->exmolobj != NULL)
4656                 return (VALUE)mol->exmolobj;
4657         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4658         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4659         return (VALUE)mol->exmolobj;
4660 }
4661
4662 /*  Allocator  */
4663 static VALUE
4664 s_Molecule_Alloc(VALUE klass)
4665 {
4666         VALUE val;
4667         Molecule *mol = MoleculeNew();
4668         val = ValueFromMolecule(mol);
4669         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4670         return val;
4671 }
4672
4673 static int
4674 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4675 {
4676         int n;
4677         char *p = "";
4678         if (FIXNUM_P(val)) {
4679                 n = FIX2INT(val);
4680                 if (n >= 0 && n < mol->natoms)
4681                         return n;
4682                 n = -1; /*  No such atom  */
4683                 val = rb_inspect(val);
4684         } else {
4685                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4686         }
4687         if (n >= 0 && n < mol->natoms)
4688                 return n;
4689         p = StringValuePtr(val);
4690         if (n == -1)
4691                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4692         else if (n == -2)
4693                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4694         else
4695                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4696         return 0; /* Not reached */
4697 }
4698
4699 static IntGroup *
4700 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4701 {
4702         IntGroup *ig;
4703         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4704         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4705                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4706         Data_Get_Struct(val, IntGroup, ig);
4707         IntGroupRetain(ig);
4708         return ig;
4709 }
4710
4711 static void
4712 s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
4713 {
4714         if (gLoadSaveErrorMessage != NULL) {
4715                 MyAppCallback_setConsoleColor(1);
4716                 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
4717                 MyAppCallback_setConsoleColor(0);
4718         }
4719         if (status != 0)
4720                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4721 }
4722
4723 /*
4724  *  call-seq:
4725  *     dup          -> Molecule
4726  *
4727  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
4728  *  created object does not affect the old object in any sense.
4729  */
4730 static VALUE
4731 s_Molecule_InitCopy(VALUE self, VALUE arg)
4732 {
4733         Molecule *mp1, *mp2;
4734         Data_Get_Struct(self, Molecule, mp1);
4735         mp2 = MoleculeFromValue(arg);
4736         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4737                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4738         return self;
4739 }
4740
4741 /*
4742  *  call-seq:
4743  *     loadmbsf(file)       -> bool
4744  *
4745  *  Read a structure from a mbsf file.
4746  *  Return true if successful.
4747  */
4748 static VALUE
4749 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4750 {
4751         VALUE fname;
4752         char *fstr;
4753         Molecule *mol;
4754         int retval;
4755         MoleculeClearLoadSaveErrorMessage();
4756         Data_Get_Struct(self, Molecule, mol);
4757         rb_scan_args(argc, argv, "1", &fname);
4758         fstr = FileStringValuePtr(fname);
4759         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4760         s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
4761         return Qtrue;   
4762 }
4763
4764 /*
4765  *  call-seq:
4766  *     loadpsf(file, pdbfile = nil)       -> bool
4767  *
4768  *  Read a structure from a psf file. molecule must be empty. The psf may be
4769  *  an "extended" version, which also contains coordinates. If pdbfile 
4770  *  is given, then atomic coordinates are read from that file.
4771  *  Return true if successful.
4772  */
4773 static VALUE
4774 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4775 {
4776         VALUE fname, pdbname;
4777         char *fstr, *pdbstr;
4778         Molecule *mol;
4779         int retval;
4780         Data_Get_Struct(self, Molecule, mol);
4781         if (mol->natoms > 0)
4782                 return Qnil;  /*  Must be a new molecule  */
4783         MoleculeClearLoadSaveErrorMessage();
4784         rb_scan_args(argc, argv, "11", &fname, &pdbname);
4785         fstr = FileStringValuePtr(fname);
4786         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4787         s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
4788         pdbstr = NULL;
4789         if (!NIL_P(pdbname)) {
4790                 pdbstr = strdup(FileStringValuePtr(pdbname));
4791                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
4792                 free(pdbstr);
4793                 s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
4794         }
4795         return Qtrue;
4796 }
4797
4798 /*
4799  *  call-seq:
4800  *     loadpdb(file)       -> bool
4801  *
4802  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
4803  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
4804  *  Return true if successful.
4805  */
4806 static VALUE
4807 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4808 {
4809         VALUE fname;
4810         char *fstr;
4811         Molecule *mol;
4812         int retval;
4813         Data_Get_Struct(self, Molecule, mol);
4814         rb_scan_args(argc, argv, "1", &fname);
4815         MoleculeClearLoadSaveErrorMessage();
4816         fstr = FileStringValuePtr(fname);
4817         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4818         s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
4819         return Qtrue;   
4820 }
4821
4822 /*
4823  *  call-seq:
4824  *     loaddcd(file)       -> bool
4825  *
4826  *  Read coordinates from a dcd file. The molecule should not empty.
4827  *  Return true if successful.
4828  */
4829 static VALUE
4830 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
4831 {
4832         VALUE fname;
4833         char *fstr;
4834         Molecule *mol;
4835         int retval;
4836         Data_Get_Struct(self, Molecule, mol);
4837         rb_scan_args(argc, argv, "1", &fname);
4838         MoleculeClearLoadSaveErrorMessage();
4839         fstr = FileStringValuePtr(fname);
4840         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
4841         s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
4842         return Qtrue;   
4843 }
4844
4845 /*
4846  *  call-seq:
4847  *     loadtep(file)       -> bool
4848  *
4849  *  Read coordinates from an ortep .tep file.
4850  *  Return true if successful.
4851  */
4852 static VALUE
4853 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
4854 {
4855         VALUE fname;
4856         char *fstr;
4857         Molecule *mol;
4858         int retval;
4859         Data_Get_Struct(self, Molecule, mol);
4860         rb_scan_args(argc, argv, "1", &fname);
4861         MoleculeClearLoadSaveErrorMessage();
4862         fstr = FileStringValuePtr(fname);
4863         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
4864         s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
4865         return Qtrue;   
4866 }
4867
4868 /*
4869  *  call-seq:
4870  *     loadres(file)       -> bool
4871  *
4872  *  Read coordinates from a shelx .res file.
4873  *  Return true if successful.
4874  */
4875 static VALUE
4876 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
4877 {
4878         VALUE fname;
4879         char *fstr;
4880         Molecule *mol;
4881         int retval;
4882         Data_Get_Struct(self, Molecule, mol);
4883         rb_scan_args(argc, argv, "1", &fname);
4884         MoleculeClearLoadSaveErrorMessage();
4885         fstr = FileStringValuePtr(fname);
4886         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
4887         s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
4888         return Qtrue;   
4889 }
4890
4891 /*
4892  *  call-seq:
4893  *     loadfchk(file)       -> bool
4894  *
4895  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
4896  *  Return true if successful.
4897  */
4898 static VALUE
4899 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
4900 {
4901         VALUE fname;
4902         char *fstr;
4903         Molecule *mol;
4904         int retval;
4905         Data_Get_Struct(self, Molecule, mol);
4906         rb_scan_args(argc, argv, "1", &fname);
4907         MoleculeClearLoadSaveErrorMessage();
4908         fstr = FileStringValuePtr(fname);
4909         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
4910         s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
4911         return Qtrue;   
4912 }
4913
4914 /*
4915  *  call-seq:
4916  *     loaddat(file)       -> bool
4917  *
4918  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
4919  *  Return true if successful.
4920  */
4921 static VALUE
4922 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
4923 {
4924         VALUE fname;
4925         char *fstr;
4926         Molecule *mol;
4927         int retval;
4928         Data_Get_Struct(self, Molecule, mol);
4929         rb_scan_args(argc, argv, "1", &fname);
4930         MoleculeClearLoadSaveErrorMessage();
4931         fstr = FileStringValuePtr(fname);
4932         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
4933         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
4934         MyAppCallback_hideProgressPanel();
4935         s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
4936         return Qtrue;   
4937 }
4938
4939 /*
4940  *  call-seq:
4941  *     savembsf(file)       -> bool
4942  *
4943  *  Write structure as a mbsf file. Returns true if successful.
4944  */
4945 static VALUE
4946 s_Molecule_Savembsf(VALUE self, VALUE fname)
4947 {
4948         char *fstr;
4949     Molecule *mol;
4950         int retval;
4951     Data_Get_Struct(self, Molecule, mol);
4952         MoleculeClearLoadSaveErrorMessage();
4953         fstr = FileStringValuePtr(fname);
4954         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4955         s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
4956         return Qtrue;
4957 }
4958
4959 /*
4960  *  call-seq:
4961  *     savepsf(file)       -> bool
4962  *
4963  *  Write structure as a psf file. Returns true if successful.
4964  */
4965 static VALUE
4966 s_Molecule_Savepsf(VALUE self, VALUE fname)
4967 {
4968         char *fstr;
4969     Molecule *mol;
4970         int retval;
4971     Data_Get_Struct(self, Molecule, mol);
4972         MoleculeClearLoadSaveErrorMessage();
4973         fstr = FileStringValuePtr(fname);
4974         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4975         s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
4976         return Qtrue;
4977 }
4978
4979 /*
4980  *  call-seq:
4981  *     savepdb(file)       -> bool
4982  *
4983  *  Write coordinates as a pdb file. Returns true if successful.
4984  */
4985 static VALUE
4986 s_Molecule_Savepdb(VALUE self, VALUE fname)
4987 {
4988         char *fstr;
4989     Molecule *mol;
4990         int retval;
4991     Data_Get_Struct(self, Molecule, mol);
4992         MoleculeClearLoadSaveErrorMessage();
4993         fstr = FileStringValuePtr(fname);
4994         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4995         s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
4996         return Qtrue;
4997 }
4998
4999 /*
5000  *  call-seq:
5001  *     savedcd(file)       -> bool
5002  *
5003  *  Write coordinates as a dcd file. Returns true if successful.
5004  */
5005 static VALUE
5006 s_Molecule_Savedcd(VALUE self, VALUE fname)
5007 {
5008         char *fstr;
5009     Molecule *mol;
5010         int retval;
5011     Data_Get_Struct(self, Molecule, mol);
5012         MoleculeClearLoadSaveErrorMessage();
5013         fstr = FileStringValuePtr(fname);
5014         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5015         s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
5016         return Qtrue;
5017 }
5018
5019 /*
5020  *  call-seq:
5021  *     savetep(file)       -> bool
5022  *
5023  *  Write coordinates as an ORTEP file. Returns true if successful.
5024  */
5025 /*
5026 static VALUE
5027 s_Molecule_Savetep(VALUE self, VALUE fname)
5028 {
5029         char *fstr;
5030     Molecule *mol;
5031         char errbuf[128];
5032     Data_Get_Struct(self, Molecule, mol);
5033         fstr = FileStringValuePtr(fname);
5034         if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
5035                 rb_raise(rb_eMolbyError, errbuf);
5036         return Qtrue;
5037 }
5038 */
5039
5040 /*  load([ftype, ] fname, ...)  */
5041 static VALUE
5042 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5043 {
5044         VALUE rval;
5045         char *argstr, *methname, *p, *type = "";
5046         ID mid = 0;
5047         int i;
5048         const char *ls = (loadFlag ? "load" : "save");
5049         int lslen = strlen(ls);
5050
5051         if (argc == 0)
5052                 return Qnil;
5053
5054         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5055                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5056         if (argstr[0] == ':') {
5057                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5058                 methname = ALLOC_N(char, lslen + strlen(argstr));
5059                 strcpy(methname, ls);
5060                 strcat(methname, argstr + 1);
5061                 type = argstr + 1;
5062                 for (i = lslen; methname[i] != 0; i++)
5063                         methname[i] = tolower(methname[i]);
5064                 mid = rb_intern(methname);
5065                 xfree(methname);
5066                 argc--;
5067                 argv++;
5068                 rval = rb_funcall2(self, mid, argc, argv);
5069                 if (rval == Qnil)
5070                         goto failure;
5071                 else
5072                         goto success;
5073         }
5074         /*  Guess file type from extension  */
5075         p = strrchr(argstr, '.');
5076         if (p != NULL) {
5077                 p++;
5078                 type = p;
5079                 for (methname = p; *methname != 0; methname++) {
5080                         if (!isalpha(*methname))
5081                                 break;
5082                 }
5083                 if (*methname == 0) {
5084                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5085                         if (methname == NULL)
5086                                 rb_raise(rb_eMolbyError, "Low memory");
5087                         strcpy(methname, ls);
5088                         strcat(methname, p);
5089                         for (i = lslen; methname[i] != 0; i++)
5090                                 methname[i] = tolower(methname[i]);
5091                         mid = rb_intern(methname);
5092                         xfree(methname);
5093                         if (loadFlag) {
5094                                 if (rb_respond_to(self, mid)) {
5095                                         /*  Load: try to call the load procedure only if it is available  */
5096                                         rval = rb_funcall2(self, mid, argc, argv);
5097                                         if (rval != Qnil)
5098                                                 goto success;
5099                                 }
5100                         } else {
5101                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5102                                 rval = rb_funcall2(self, mid, argc, argv);
5103                                 if (rval != Qnil)
5104                                         goto success;
5105                         }
5106                 }
5107         }
5108 failure:
5109         rval = rb_str_to_str(argv[0]);
5110         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5111         s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
5112         return Qnil;  /*  Does not reach here  */
5113
5114 success:
5115         {
5116                 /*  Register the path  */
5117                 Molecule *mol;
5118         /*      Atom *ap; */
5119                 Data_Get_Struct(self, Molecule, mol);
5120                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5121                 
5122                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5123         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5124                         if (ap->occupancy != 0.0)
5125                                 break;
5126                 }
5127                 if (i == mol->natoms) {
5128                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5129                                 ap->occupancy = 1.0;
5130                         }
5131                 } */
5132         }
5133         return rval;
5134 }
5135
5136 /*
5137  *  call-seq:
5138  *     molload(file, *args)       -> bool
5139  *
5140  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5141  *  file type given by the extension). If this method fails, then all defined (public)
5142  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5143  */
5144 static VALUE
5145 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5146 {
5147         return s_Molecule_LoadSave(argc, argv, self, 1);
5148 }
5149
5150 /*
5151  *  call-seq:
5152  *     molsave(file, *args)       -> bool
5153  *
5154  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5155  *  (XXX is the file type given by the extension).
5156  */
5157 static VALUE
5158 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5159 {
5160         return s_Molecule_LoadSave(argc, argv, self, 0);
5161 }
5162
5163 /*
5164  *  call-seq:
5165  *     name       -> String
5166  *
5167  *  Returns the display name of the molecule. If the molecule has no associated
5168  *  document, then returns nil.
5169  */
5170 static VALUE
5171 s_Molecule_Name(VALUE self)
5172 {
5173     Molecule *mol;
5174         char buf[1024];
5175     Data_Get_Struct(self, Molecule, mol);
5176         MoleculeCallback_displayName(mol, buf, sizeof buf);
5177         if (buf[0] == 0)
5178                 return Qnil;
5179         else
5180                 return rb_str_new2(buf);
5181 }
5182
5183 /*
5184  *  call-seq:
5185  *     set_name(string) -> self
5186  *
5187  *  Set the name of an untitled molecule. If the molecule is not associated with window
5188  *  or it already has an associated file, then exception is thrown.
5189  */
5190 static VALUE
5191 s_Molecule_SetName(VALUE self, VALUE nval)
5192 {
5193     Molecule *mol;
5194     Data_Get_Struct(self, Molecule, mol);
5195         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5196                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5197         return self;
5198 }
5199
5200
5201 /*
5202  *  call-seq:
5203  *     path       -> String
5204  *
5205  *  Returns the full path name of the molecule, if it is associated with a file.
5206  *  If the molecule has no associated file, then returns nil.
5207  */
5208 static VALUE
5209 s_Molecule_Path(VALUE self)
5210 {
5211     Molecule *mol;
5212         char buf[1024];
5213     Data_Get_Struct(self, Molecule, mol);
5214         MoleculeCallback_pathName(mol, buf, sizeof buf);
5215         if (buf[0] == 0)
5216                 return Qnil;
5217         else
5218                 return Ruby_NewFileStringValue(buf);
5219 }
5220
5221 /*
5222  *  call-seq:
5223  *     dir       -> String
5224  *
5225  *  Returns the full path name of the directory in which the file associated with the
5226  *  molecule is located. If the molecule has no associated file, then returns nil.
5227  */
5228 static VALUE
5229 s_Molecule_Dir(VALUE self)
5230 {
5231     Molecule *mol;
5232         char buf[1024], *p;
5233     Data_Get_Struct(self, Molecule, mol);
5234         MoleculeCallback_pathName(mol, buf, sizeof buf);
5235 #if __WXMSW__
5236         translate_char(buf, '\\', '/');
5237 #endif
5238         if (buf[0] == 0)
5239                 return Qnil;
5240         else {
5241                 p = strrchr(buf, '/');
5242                 if (p != NULL)
5243                         *p = 0;
5244                 return rb_str_new2(buf);
5245         }
5246 }
5247
5248 /*
5249  *  call-seq:
5250  *     inspect       -> String
5251  *
5252  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5253  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5254  *  the Molecule structure) is returned.
5255  */
5256 static VALUE
5257 s_Molecule_Inspect(VALUE self)
5258 {
5259     Molecule *mol;
5260         char buf[256];
5261     Data_Get_Struct(self, Molecule, mol);
5262         MoleculeCallback_displayName(mol, buf, sizeof buf);
5263         if (buf[0] == 0) {
5264                 /*  No associated document  */
5265                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5266                 return rb_str_new2(buf);
5267         } else {
5268                 /*  Check whether the document name is duplicate  */
5269                 char buf2[256];
5270                 int idx, k, k2;
5271                 Molecule *mol2;
5272                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5273                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5274                         if (strcmp(buf, buf2) == 0) {
5275                                 k++;
5276                                 if (mol == mol2)
5277                                         k2 = k;
5278                         }
5279                 }
5280                 if (k > 1) {
5281                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5282                 } else {
5283                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5284                 }
5285                 return rb_str_new2(buf2);
5286         }
5287 }
5288
5289 /*
5290  *  call-seq:
5291  *     open        -> Molecule
5292  *     open(file)  -> Molecule
5293  *
5294  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5295  */
5296 static VALUE
5297 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5298 {
5299         VALUE fname;
5300         const char *p;
5301         Molecule *mp;
5302         VALUE iflag;
5303         rb_scan_args(argc, argv, "01", &fname);
5304         if (NIL_P(fname))
5305                 p = NULL;
5306         else
5307                 p = FileStringValuePtr(fname);
5308         iflag = Ruby_SetInterruptFlag(Qfalse);
5309         mp = MoleculeCallback_openNewMolecule(p);
5310         Ruby_SetInterruptFlag(iflag);
5311         if (mp == NULL) {
5312                 if (p == NULL)
5313                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5314                 else
5315                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5316         }
5317         return ValueFromMolecule(mp);
5318 }
5319
5320 /*
5321  *  call-seq:
5322  *     new  -> Molecule
5323  *     new(file, *args)  -> Molecule
5324  *
5325  *  Create a new molecule and call "load" method with the same arguments.
5326  */
5327 static VALUE
5328 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5329 {
5330         if (argc > 0)
5331                 return s_Molecule_Load(argc, argv, self);
5332         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5333 }
5334
5335 static VALUE
5336 s_Molecule_MolEnumerable(VALUE self, int kind)
5337 {
5338     Molecule *mol;
5339         MolEnumerable *mseq;
5340     Data_Get_Struct(self, Molecule, mol);
5341         mseq = MolEnumerableNew(mol, kind);
5342         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5343 }
5344
5345 /*
5346  *  call-seq:
5347  *     atoms       -> MolEnumerable
5348  *
5349  *  Returns a MolEnumerable object representing the array of atoms.
5350  */
5351 static VALUE
5352 s_Molecule_Atoms(VALUE self)
5353 {
5354         return s_Molecule_MolEnumerable(self, kAtomKind);
5355 }
5356
5357 /*
5358  *  call-seq:
5359  *     bonds       -> MolEnumerable
5360  *
5361  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5362  *  by an array of two atom indices.
5363  */
5364 static VALUE
5365 s_Molecule_Bonds(VALUE self)
5366 {
5367         return s_Molecule_MolEnumerable(self, kBondKind);
5368 }
5369
5370 /*
5371  *  call-seq:
5372  *     angles       -> MolEnumerable
5373  *
5374  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5375  *  by an array of three atom indices.
5376  */
5377 static VALUE
5378 s_Molecule_Angles(VALUE self)
5379 {
5380         return s_Molecule_MolEnumerable(self, kAngleKind);
5381 }
5382
5383 /*
5384  *  call-seq:
5385  *     dihedrals       -> MolEnumerable
5386  *
5387  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5388  *  by an array of four atom indices.
5389  */
5390 static VALUE
5391 s_Molecule_Dihedrals(VALUE self)
5392 {
5393         return s_Molecule_MolEnumerable(self, kDihedralKind);
5394 }
5395
5396 /*
5397  *  call-seq:
5398  *     impropers       -> MolEnumerable
5399  *
5400  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5401  *  by an array of four atom indices.
5402  */
5403 static VALUE
5404 s_Molecule_Impropers(VALUE self)
5405 {
5406         return s_Molecule_MolEnumerable(self, kImproperKind);
5407 }
5408
5409 /*
5410  *  call-seq:
5411  *     residues       -> MolEnumerable
5412  *
5413  *  Returns a MolEnumerable object representing the array of residue names.
5414  */
5415 static VALUE
5416 s_Molecule_Residues(VALUE self)
5417 {
5418         return s_Molecule_MolEnumerable(self, kResidueKind);
5419 }
5420
5421 /*
5422  *  call-seq:
5423  *     natoms       -> Integer
5424  *
5425  *  Returns the number of atoms.
5426  */
5427 static VALUE
5428 s_Molecule_Natoms(VALUE self)
5429 {
5430     Molecule *mol;
5431     Data_Get_Struct(self, Molecule, mol);
5432         return INT2NUM(mol->natoms);
5433 }
5434
5435 /*
5436  *  call-seq:
5437  *     nbonds       -> Integer
5438  *
5439  *  Returns the number of bonds.
5440  */
5441 static VALUE
5442 s_Molecule_Nbonds(VALUE self)
5443 {
5444     Molecule *mol;
5445     Data_Get_Struct(self, Molecule, mol);
5446         return INT2NUM(mol->nbonds);
5447 }
5448
5449 /*
5450  *  call-seq:
5451  *     nangles       -> Integer
5452  *
5453  *  Returns the number of angles.
5454  */
5455 static VALUE
5456 s_Molecule_Nangles(VALUE self)
5457 {
5458     Molecule *mol;
5459     Data_Get_Struct(self, Molecule, mol);
5460         return INT2NUM(mol->nangles);
5461 }
5462
5463 /*
5464  *  call-seq:
5465  *     ndihedrals       -> Integer
5466  *
5467  *  Returns the number of dihedrals.
5468  */
5469 static VALUE
5470 s_Molecule_Ndihedrals(VALUE self)
5471 {
5472     Molecule *mol;
5473     Data_Get_Struct(self, Molecule, mol);
5474         return INT2NUM(mol->ndihedrals);
5475 }
5476
5477 /*
5478  *  call-seq:
5479  *     nimpropers       -> Integer
5480  *
5481  *  Returns the number of impropers.
5482  */
5483 static VALUE
5484 s_Molecule_Nimpropers(VALUE self)
5485 {
5486     Molecule *mol;
5487     Data_Get_Struct(self, Molecule, mol);
5488         return INT2NUM(mol->nimpropers);
5489 }
5490
5491 /*
5492  *  call-seq:
5493  *     nresidues       -> Integer
5494  *
5495  *  Returns the number of residues.
5496  */
5497 static VALUE
5498 s_Molecule_Nresidues(VALUE self)
5499 {
5500     Molecule *mol;
5501     Data_Get_Struct(self, Molecule, mol);
5502         return INT2NUM(mol->nresidues);
5503 }
5504
5505 static VALUE
5506 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5507 {
5508         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.");
5509 }
5510
5511 /*
5512  *  call-seq:
5513  *     bond_par(idx)    -> ParameterRef
5514  *
5515  *  Returns the MD parameter for the idx-th bond.
5516  */
5517 /*
5518 static VALUE
5519 s_Molecule_BondPar(VALUE self, VALUE val)
5520 {
5521     Molecule *mol;
5522         BondPar *bp;
5523         UInt t1, t2;
5524         Int i1, i2;
5525         Int ival;
5526     Data_Get_Struct(self, Molecule, mol);
5527         ival = NUM2INT(rb_Integer(val));
5528         if (ival < -mol->nbonds || ival >= mol->nbonds)
5529                 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5530         if (ival < 0)
5531                 ival += mol->nbonds;
5532         s_RebuildMDParameterIfNecessary(self, Qtrue);
5533         i1 = mol->bonds[ival * 2];
5534         i2 = mol->bonds[ival * 2 + 1];
5535         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5536         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5537         bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5538         if (bp == NULL)
5539                 return Qnil;
5540         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5541 }
5542 */
5543
5544 /*
5545  *  call-seq:
5546  *     angle_par(idx)    -> ParameterRef
5547  *
5548  *  Returns the MD parameter for the idx-th angle.
5549  */
5550 /*
5551 static VALUE
5552 s_Molecule_AnglePar(VALUE self, VALUE val)
5553 {
5554     Molecule *mol;
5555         AnglePar *ap;
5556         UInt t1, t2, t3;
5557         Int i1, i2, i3;
5558         Int ival;
5559     Data_Get_Struct(self, Molecule, mol);
5560         ival = NUM2INT(rb_Integer(val));
5561         if (ival < -mol->nangles || ival >= mol->nangles)
5562                 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5563         if (ival < 0)
5564                 ival += mol->nangles;
5565         s_RebuildMDParameterIfNecessary(self, Qtrue);
5566         i1 = mol->angles[ival * 3];
5567         i2 = mol->angles[ival * 3 + 1];
5568         i3 = mol->angles[ival * 3 + 2];
5569         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5570         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5571         t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5572         ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5573         if (ap == NULL)
5574                 return Qnil;
5575         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5576 }
5577 */
5578 /*
5579  *  call-seq:
5580  *     dihedral_par(idx)    -> ParameterRef
5581  *
5582  *  Returns the MD parameter for the idx-th dihedral.
5583  */
5584 /*
5585 static VALUE
5586 s_Molecule_DihedralPar(VALUE self, VALUE val)
5587 {
5588     Molecule *mol;
5589         Int ival;
5590         TorsionPar *tp;
5591         UInt t1, t2, t3, t4;
5592         Int i1, i2, i3, i4;
5593     Data_Get_Struct(self, Molecule, mol);
5594         ival = NUM2INT(rb_Integer(val));
5595         if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5596                 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5597         if (ival < 0)
5598                 ival += mol->ndihedrals;
5599         s_RebuildMDParameterIfNecessary(self, Qtrue);
5600         i1 = mol->dihedrals[ival * 4];
5601         i2 = mol->dihedrals[ival * 4 + 1];
5602         i3 = mol->dihedrals[ival * 4 + 2];
5603         i4 = mol->dihedrals[ival * 4 + 3];
5604         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5605         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5606         t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5607         t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5608         tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5609         if (tp == NULL)
5610                 return Qnil;
5611         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5612 }
5613 */
5614 /*
5615  *  call-seq:
5616  *     improper_par(idx)    -> ParameterRef
5617  *
5618  *  Returns the MD parameter for the idx-th improper.
5619  */
5620 /*
5621 static VALUE
5622 s_Molecule_ImproperPar(VALUE self, VALUE val)
5623 {
5624     Molecule *mol;
5625         Int ival;
5626         TorsionPar *tp;
5627         UInt t1, t2, t3, t4;
5628         Int i1, i2, i3, i4;
5629     Data_Get_Struct(self, Molecule, mol);
5630         ival = NUM2INT(rb_Integer(val));
5631         if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5632                 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5633         if (ival < 0)
5634                 ival += mol->nimpropers;
5635         s_RebuildMDParameterIfNecessary(self, Qtrue);
5636         i1 = mol->impropers[ival * 4];
5637         i2 = mol->impropers[ival * 4 + 1];
5638         i3 = mol->impropers[ival * 4 + 2];
5639         i4 = mol->impropers[ival * 4 + 3];
5640         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5641         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5642         t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5643         t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5644         tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5645         if (tp == NULL)
5646                 return Qnil;
5647         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5648 }
5649 */
5650
5651 /*
5652  *  call-seq:
5653  *     start_step       -> Integer
5654  *
5655  *  Returns the start step (defined by dcd format).
5656  */
5657 static VALUE
5658 s_Molecule_StartStep(VALUE self)
5659 {
5660     Molecule *mol;
5661     Data_Get_Struct(self, Molecule, mol);
5662         return INT2NUM(mol->startStep);
5663 }
5664
5665 /*
5666  *  call-seq:
5667  *     start_step = Integer
5668  *
5669  *  Set the start step (defined by dcd format).
5670  */
5671 static VALUE
5672 s_Molecule_SetStartStep(VALUE self, VALUE val)
5673 {
5674     Molecule *mol;
5675     Data_Get_Struct(self, Molecule, mol);
5676         mol->startStep = NUM2INT(rb_Integer(val));
5677         return val;
5678 }
5679
5680 /*
5681  *  call-seq:
5682  *     steps_per_frame       -> Integer
5683  *
5684  *  Returns the number of steps between frames (defined by dcd format).
5685  */
5686 static VALUE
5687 s_Molecule_StepsPerFrame(VALUE self)
5688 {
5689     Molecule *mol;
5690     Data_Get_Struct(self, Molecule, mol);
5691         return INT2NUM(mol->stepsPerFrame);
5692 }
5693
5694 /*
5695  *  call-seq:
5696  *     steps_per_frame = Integer
5697  *
5698  *  Set the number of steps between frames (defined by dcd format).
5699  */
5700 static VALUE
5701 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5702 {
5703     Molecule *mol;
5704     Data_Get_Struct(self, Molecule, mol);
5705         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5706         return val;
5707 }
5708
5709 /*
5710  *  call-seq:
5711  *     ps_per_step       -> Float
5712  *
5713  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
5714  */
5715 static VALUE
5716 s_Molecule_PsPerStep(VALUE self)
5717 {
5718     Molecule *mol;
5719     Data_Get_Struct(self, Molecule, mol);
5720         return rb_float_new(mol->psPerStep);
5721 }
5722
5723 /*
5724  *  call-seq:
5725  *     ps_per_step = Float
5726  *
5727  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
5728  */
5729 static VALUE
5730 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5731 {
5732     Molecule *mol;
5733     Data_Get_Struct(self, Molecule, mol);
5734         mol->psPerStep = NUM2DBL(rb_Float(val));
5735         return val;
5736 }
5737
5738 /*
5739  *  call-seq:
5740  *     find_angles     -> Integer
5741  *
5742  *  Find the angles from the bonds. Returns the number of angles newly created.
5743  */
5744 /*
5745 static VALUE
5746 s_Molecule_FindAngles(VALUE self)
5747 {
5748     Molecule *mol;
5749         Atom *ap;
5750         int n1, i, j, nc;
5751         Int *ip, nip, n[3];
5752     Data_Get_Struct(self, Molecule, mol);
5753         if (mol == NULL || mol->natoms == 0)
5754                 return INT2NUM(0);
5755         ip = NULL;
5756         nip = 0;
5757         for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5758                 nc = ap->connect.count;
5759                 n[1] = n1;
5760                 for (i = 0; i < nc; i++) {
5761                         n[0] = ap->connects[i];
5762                         for (j = i + 1; j < nc; j++) {
5763                                 n[2] = ap->connects[j];
5764                                 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5765                                         AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5766                         }
5767                 }
5768         }
5769         if (nip > 0) {
5770                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);         
5771                 free(ip);
5772         }
5773         return INT2NUM(nip);
5774 }
5775 */
5776 /*
5777  *  call-seq:
5778  *     find_dihedrals     -> Integer
5779  *
5780  *  Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5781  */
5782 /*
5783 static VALUE
5784 s_Molecule_FindDihedrals(VALUE self)
5785 {
5786     Molecule *mol;
5787         Atom *ap1, *ap2;
5788         int n1, i, j, k, nc1, nc2;
5789         Int *ip, nip, n[4];
5790     Data_Get_Struct(self, Molecule, mol);
5791         if (mol == NULL || mol->natoms == 0)
5792                 return INT2NUM(0);
5793         ip = NULL;
5794         nip = 0;
5795         for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5796                 nc1 = ap1->connect.count;
5797                 n[1] = n1;
5798                 for (i = 0; i < nc1; i++) {
5799                         n[2] = ap1->connects[i];
5800                         if (n[1] > n[2])
5801                                 continue;
5802                         ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5803                         nc2 = ap2->connect.count;
5804                         for (j = 0; j < nc1; j++) {
5805                                 n[0] = ap1->connects[j];
5806                                 if (n[0] == n[2])
5807                                         continue;
5808                                 for (k = 0; k < nc2; k++) {
5809                                         n[3] = ap2->connects[k];
5810                                         if (n[3] == n1 || n[3] == n[0])
5811                                                 continue;
5812                                         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
5813                                                 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
5814                                 }
5815                         }
5816                 }
5817         }
5818         if (nip > 0) {
5819                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
5820                 free(ip);
5821         }
5822         return INT2NUM(nip);
5823 }
5824 */
5825
5826 /*
5827  *  call-seq:
5828  *     nresidues = Integer
5829  *
5830  *  Change the number of residues.
5831  */
5832 static VALUE
5833 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5834 {
5835     Molecule *mol;
5836         int ival = NUM2INT(val);
5837     Data_Get_Struct(self, Molecule, mol);
5838         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5839         if (ival != mol->nresidues)
5840                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5841         return val;
5842 }
5843
5844 /*
5845  *  call-seq:
5846  *     max_residue_number(atom_group = nil)     -> Integer
5847  *
5848  *  Returns the maximum residue number actually used. If an atom group is given, only
5849  *  these atoms are examined. If no atom is present, nil is returned.
5850  */
5851 static VALUE
5852 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5853 {
5854     Molecule *mol;
5855         VALUE gval;
5856         int maxSeq;
5857         IntGroup *ig;
5858     Data_Get_Struct(self, Molecule, mol);
5859         rb_scan_args(argc, argv, "01", &gval);
5860         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5861         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5862         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5863 }
5864
5865 /*
5866  *  call-seq:
5867  *     min_residue_number(atom_group = nil)     -> Integer
5868  *
5869  *  Returns the minimum residue number actually used. If an atom group is given, only
5870  *  these atoms are examined. If no atom is present, nil is returned.
5871  */
5872 static VALUE
5873 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5874 {
5875     Molecule *mol;
5876         VALUE gval;
5877         int minSeq;
5878         IntGroup *ig;
5879     Data_Get_Struct(self, Molecule, mol);
5880         rb_scan_args(argc, argv, "01", &gval);
5881         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5882         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5883         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5884 }
5885
5886 /*
5887  *  call-seq:
5888  *     each_atom(atom_group = nil) {|aref| ...}
5889  *
5890  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5891  *  group is given, only these atoms are processed.
5892  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5893  *  is self (a Molecule object).
5894  */
5895 static VALUE
5896 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5897 {
5898         int i;
5899     Molecule *mol;
5900         AtomRef *aref;
5901         VALUE arval;
5902         VALUE gval;
5903         IntGroup *ig;
5904     Data_Get_Struct(self, Molecule, mol);
5905         rb_scan_args(argc, argv, "01", &gval);
5906         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5907         arval = ValueFromMoleculeAndIndex(mol, 0);
5908         Data_Get_Struct(arval, AtomRef, aref);
5909         for (i = 0; i < mol->natoms; i++) {
5910                 aref->idx = i;
5911                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5912                         rb_yield(arval);
5913         }
5914         if (ig != NULL)
5915                 IntGroupRelease(ig);
5916     return self;
5917 }
5918
5919 /*
5920  *  call-seq:
5921  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5922  *
5923  *  Returns the unit cell parameters. If cell is not set, returns nil.
5924  */
5925 static VALUE
5926 s_Molecule_Cell(VALUE self)
5927 {
5928     Molecule *mol;
5929         int i;
5930         VALUE val;
5931     Data_Get_Struct(self, Molecule, mol);
5932         if (mol->cell == NULL)
5933                 return Qnil;
5934         val = rb_ary_new2(6);
5935         for (i = 0; i < 6; i++)
5936                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
5937         if (mol->cell->has_sigma) {
5938                 for (i = 0; i < 6; i++) {
5939                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
5940                 }
5941         }
5942         return val;
5943 }
5944
5945 /*
5946  *  call-seq:
5947  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5948  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
5949  *
5950  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
5951     If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
5952     This operation is undoable.
5953     Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
5954  */
5955 static VALUE
5956 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
5957 {
5958     Molecule *mol;
5959         VALUE val, cval;
5960         int i, convert_coord, n;
5961         double d[12];
5962     Data_Get_Struct(self, Molecule, mol);
5963         rb_scan_args(argc, argv, "11", &val, &cval);
5964         if (val == Qnil) {
5965                 n = 0;
5966         } else {
5967                 int len;
5968                 val = rb_ary_to_ary(val);
5969                 len = RARRAY_LEN(val);
5970                 if (len >= 12) {
5971                         n = 12;
5972                 } else if (len >= 6) {
5973                         n = 6;
5974                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
5975                 for (i = 0; i < n; i++)
5976                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
5977         }
5978         convert_coord = (RTEST(cval) ? 1 : 0);
5979         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
5980         return val;
5981 }
5982
5983 /*
5984  *  call-seq:
5985  *     box -> [avec, bvec, cvec, origin, flags]
5986  *
5987  *  Get the unit cell information in the form of a periodic bounding box.
5988  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
5989  *  Integers which define whether the system is periodic along the axis.
5990  *  If no unit cell is defined, nil is returned.
5991  */
5992 static VALUE
5993 s_Molecule_Box(VALUE self)
5994 {
5995     Molecule *mol;
5996         VALUE v[5], val;
5997     Data_Get_Struct(self, Molecule, mol);
5998         if (mol == NULL || mol->cell == NULL)
5999                 return Qnil;
6000         v[0] = ValueFromVector(&(mol->cell->axes[0]));
6001         v[1] = ValueFromVector(&(mol->cell->axes[1]));
6002         v[2] = ValueFromVector(&(mol->cell->axes[2]));
6003         v[3] = ValueFromVector(&(mol->cell->origin));
6004         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
6005         val = rb_ary_new4(5, v);
6006         return val;
6007 }
6008
6009 /*
6010  *  call-seq:
6011  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
6012  *     set_box(d, origin = [0, 0, 0])
6013  *     set_box
6014  *
6015  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
6016  If it is a number, the x/y/z axis vector is multiplied with the given number and used
6017  as the box vector.
6018  Flags, if present, is a 3-member array of Integers defining whether the system is
6019  periodic along the axis.
6020  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
6021  In the second form, an isotropic box with cell-length d is set.
6022  In the third form, the existing box is cleared.
6023  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
6024  */
6025 static VALUE
6026 s_Molecule_SetBox(VALUE self, VALUE aval)
6027 {
6028     Molecule *mol;
6029         VALUE v[6];
6030         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
6031         Vector vv[3];
6032         Vector origin = {0, 0, 0};
6033         char flags[3];
6034         Double d;
6035         int i, convertCoordinates = 0;
6036     Data_Get_Struct(self, Molecule, mol);
6037         if (aval == Qnil) {
6038                 MolActionCreateAndPerform(mol, gMolActionClearBox);
6039                 return self;
6040         }
6041         aval = rb_ary_to_ary(aval);
6042         for (i = 0; i < 6; i++) {
6043                 if (i < RARRAY_LEN(aval))
6044                         v[i] = (RARRAY_PTR(aval))[i];
6045                 else v[i] = Qnil;
6046         }
6047         if (v[0] == Qnil) {
6048                 MolActionCreateAndPerform(mol, gMolActionClearBox);
6049                 return self;
6050         }
6051         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
6052                 d = NUM2DBL(rb_Float(v[0]));
6053                 for (i = 0; i < 3; i++)
6054                         VecScale(vv[i], ax[i], d);
6055                 if (v[1] != Qnil)
6056                         VectorFromValue(v[1], &origin);
6057                 flags[0] = flags[1] = flags[2] = 1;
6058         } else {
6059                 for (i = 0; i < 3; i++) {
6060                         if (v[i] == Qnil) {
6061                                 VecZero(vv[i]);
6062                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
6063                                 d = NUM2DBL(rb_Float(v[i]));
6064                                 VecScale(vv[i], ax[i], d);
6065                         } else {
6066                                 VectorFromValue(v[i], &vv[i]);
6067                         }
6068                         flags[i] = (VecLength2(vv[i]) > 0.0);
6069                 }
6070                 if (v[3] != Qnil)
6071                         VectorFromValue(v[3], &origin);
6072                 if (v[4] != Qnil) {
6073                         for (i = 0; i < 3; i++) {
6074                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
6075                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
6076                         }
6077                 }
6078                 if (RTEST(v[5]))
6079                         convertCoordinates = 1;
6080         }
6081         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
6082         return self;
6083 }
6084
6085 /*
6086  *  call-seq:
6087  *     cell_periodicity -> [n1, n2, n3]
6088  *
6089  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
6090  *  nil is returned.
6091  */
6092 static VALUE
6093 s_Molecule_CellPeriodicity(VALUE self)
6094 {
6095     Molecule *mol;
6096     Data_Get_Struct(self, Molecule, mol);
6097         if (mol->cell == NULL)
6098                 return Qnil;
6099         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
6100 }
6101
6102 /*
6103  *  call-seq:
6104  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
6105  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
6106  *
6107  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
6108  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
6109  *  If cell is not defined, exception is raised.
6110  *  This operation is undoable.
6111  */
6112 static VALUE
6113 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
6114 {
6115     Molecule *mol;
6116         Int flag;
6117     Data_Get_Struct(self, Molecule, mol);
6118         if (mol->cell == NULL)
6119                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
6120         if (arg == Qnil)
6121                 flag = 0;
6122         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
6123                 flag = NUM2INT(rb_Integer(arg));
6124         else {
6125                 Int i;
6126                 VALUE arg0;
6127                 arg = rb_ary_to_ary(arg);
6128                 flag = 0;
6129                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
6130                         arg0 = RARRAY_PTR(arg)[i];
6131                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
6132                                 flag |= (1 << (2 - i));
6133                 }
6134         }
6135         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
6136         return arg;
6137 }
6138
6139 /*
6140  *  call-seq:
6141  *     cell_flexibility -> bool
6142  *
6143  *  Returns the unit cell is flexible or not
6144  */
6145 static VALUE
6146 s_Molecule_CellFlexibility(VALUE self)
6147 {
6148         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
6149         return Qtrue;
6150 /*    Molecule *mol;
6151     Data_Get_Struct(self, Molecule, mol);
6152         if (mol->cell == NULL)
6153                 return Qfalse;
6154         if (mol->useFlexibleCell)
6155                 return Qtrue;
6156         else return Qfalse; */
6157 }
6158
6159 /*
6160  *  call-seq:
6161  *     self.cell_flexibility = bool
6162  *     set_cell_flexibility(bool)
6163  *
6164  *  Change the unit cell is flexible or not
6165  */
6166 static VALUE
6167 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
6168 {
6169         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
6170         return self;
6171 /*    Molecule *mol;
6172     Data_Get_Struct(self, Molecule, mol);
6173         MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
6174         return self; */
6175 }
6176
6177 /*
6178  *  call-seq:
6179  *     cell_transform -> Transform
6180  *
6181  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
6182  *  If cell is not defined, nil is returned.
6183  */
6184 static VALUE
6185 s_Molecule_CellTransform(VALUE self)
6186 {
6187     Molecule *mol;
6188     Data_Get_Struct(self, Molecule, mol);
6189         if (mol == NULL || mol->cell == NULL)
6190                 return Qnil;
6191         return ValueFromTransform(&(mol->cell->tr));
6192 }
6193
6194 /*
6195  *  call-seq:
6196  *     symmetry -> Array of Transforms
6197  *     symmetries -> Array of Transforms
6198  *
6199  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
6200  *  returns an empty array.
6201  */
6202 static VALUE
6203 s_Molecule_Symmetry(VALUE self)
6204 {
6205     Molecule *mol;
6206         VALUE val;
6207         int i;
6208     Data_Get_Struct(self, Molecule, mol);
6209         if (mol->nsyms <= 0)
6210                 return rb_ary_new();
6211         val = rb_ary_new2(mol->nsyms);
6212         for (i = 0; i < mol->nsyms; i++) {
6213                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
6214         }
6215         return val;
6216 }
6217
6218 /*
6219  *  call-seq:
6220  *     nsymmetries -> Integer
6221  *
6222  *  Get the number of currently defined symmetry operations.
6223  */
6224 static VALUE
6225 s_Molecule_Nsymmetries(VALUE self)
6226 {
6227     Molecule *mol;
6228     Data_Get_Struct(self, Molecule, mol);
6229         return INT2NUM(mol->nsyms);
6230 }
6231
6232 /*
6233  *  call-seq:
6234  *     add_symmetry(Transform) -> Integer
6235  *
6236  *  Add a new symmetry operation. If no symmetry operation is defined and the
6237  *  given argument is not an identity transform, then also add an identity
6238  *  transform at the index 0.
6239  *  Returns the total number of symmetries after operation.
6240  */
6241 static VALUE
6242 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
6243 {
6244     Molecule *mol;
6245         Transform tr;
6246     Data_Get_Struct(self, Molecule, mol);
6247         TransformFromValue(trans, &tr);
6248         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
6249         return INT2NUM(mol->nsyms);
6250 }
6251
6252 /*
6253  *  call-seq:
6254  *     remove_symmetry(count = nil) -> Integer
6255  *     remove_symmetries(count = nil) -> Integer
6256  *
6257  *  Remove the specified number of symmetry operations. The last added ones are removed
6258  *  first. If count is nil, then all symmetry operations are removed. Returns the
6259  *  number of leftover symmetries.
6260  */
6261 static VALUE
6262 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
6263 {
6264     Molecule *mol;
6265         VALUE cval;
6266         int i, n;
6267     Data_Get_Struct(self, Molecule, mol);
6268         rb_scan_args(argc, argv, "01", &cval);
6269         if (cval == Qnil)
6270                 n = mol->nsyms - 1;
6271         else {
6272                 n = NUM2INT(rb_Integer(cval));
6273                 if (n < 0 || n > mol->nsyms)
6274                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
6275                 if (n == mol->nsyms)
6276                         n = mol->nsyms - 1;
6277         }
6278         for (i = 0; i < n; i++)
6279                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
6280         return INT2NUM(mol->nsyms);
6281 }
6282
6283 static VALUE
6284 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6285 {
6286         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6287         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6288         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6289         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6290         return Qnil;
6291 }
6292
6293 /*
6294  *  call-seq:
6295  *     atom_group
6296  *     atom_group {|aref| ...}
6297  *     atom_group(arg1, arg2, ...)
6298  *     atom_group(arg1, arg2, ...) {|aref| ...}
6299  *
6300  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6301  *  If arguments are given, then the atoms reprensented by the arguments are added to the
6302  *  group. For a conversion of a string to an atom index, see the description
6303  *  of Molecule#atom_index.
6304  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
6305  *  representing each atom, and the atoms are removed from the result if the block returns false.
6306  *
6307  */
6308 static VALUE
6309 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6310 {
6311         IntGroup *ig1, *ig2;
6312     Molecule *mol;
6313         Int i, startPt, interval;
6314         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6315         Data_Get_Struct(retval, IntGroup, ig1);
6316     Data_Get_Struct(self, Molecule, mol);
6317         if (argc == 0) {
6318                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6319         } else {
6320                 while (argc > 0) {
6321                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6322                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6323                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6324                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6325                                 ig2 = IntGroupFromValue(*argv);
6326                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6327                                         interval = IntGroupGetInterval(ig2, i);
6328                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6329                                 }
6330                                 IntGroupRelease(ig2);
6331                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
6332                                 VALUE values[2];
6333                                 values[0] = (VALUE)mol;
6334                                 values[1] = (VALUE)ig1;
6335                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6336                         } else
6337                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6338                         argc--;
6339                         argv++;
6340                 }
6341         }
6342         if (rb_block_given_p()) {
6343                 /*  Evaluate the given block with an AtomRef as the argument, and delete
6344                         the index if the block returns false  */
6345                 AtomRef *aref = AtomRefNew(mol, 0);
6346                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6347                 ig2 = IntGroupNew();
6348                 IntGroupCopy(ig2, ig1);
6349                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6350                         VALUE resval;
6351                         if (startPt >= mol->natoms)
6352                                 break;
6353                         aref->idx = startPt;
6354                         resval = rb_yield(arval);
6355                         if (!RTEST(resval))
6356                                 IntGroupRemove(ig1, startPt, 1);
6357                 }
6358                 IntGroupRelease(ig2);
6359         }
6360         
6361         /*  Remove points that are out of bounds */
6362         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6363
6364         return retval;                  
6365 }
6366
6367 /*
6368  *  call-seq:
6369  *     atom_index(val)       -> Integer
6370  *
6371  *  Returns the atom index represented by val. val can be either a non-negative integer
6372  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
6373  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
6374  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
6375  *  If val is a string and multiple atoms match the description, the atom with the lowest index
6376  *  is returned.
6377  */
6378 static VALUE
6379 s_Molecule_AtomIndex(VALUE self, VALUE val)
6380 {
6381     Molecule *mol;
6382     Data_Get_Struct(self, Molecule, mol);
6383         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
6384 }
6385
6386 /*
6387  *  call-seq:
6388  *     extract(group, dummy_flag = nil)       -> Molecule
6389  *
6390  *  Extract the atoms given by group and return as a new molecule object.
6391  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6392  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6393  *  names beginning with an underscore) and included in the new molecule object.
6394  */
6395 static VALUE
6396 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6397 {
6398     Molecule *mol1, *mol2;
6399         IntGroup *ig;
6400         VALUE group, dummy_flag, retval;
6401     Data_Get_Struct(self, Molecule, mol1);
6402         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6403         ig = s_Molecule_AtomGroupFromValue(self, group);
6404         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6405                 retval = Qnil;
6406         } else {
6407                 retval = ValueFromMolecule(mol2);
6408         }
6409         IntGroupRelease(ig);
6410         return retval;
6411 }
6412
6413 /*
6414  *  call-seq:
6415  *     add(molecule2)       -> self
6416  *
6417  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6418     conflicts.
6419     This operation is undoable.
6420  */
6421 static VALUE
6422 s_Molecule_Add(VALUE self, VALUE val)
6423 {
6424     Molecule *mol1, *mol2;
6425     Data_Get_Struct(self, Molecule, mol1);
6426         mol2 = MoleculeFromValue(val);
6427         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6428         return self; 
6429 }
6430
6431 /*
6432  *  call-seq:
6433  *     remove(group)       -> Molecule
6434  *
6435  *  The atoms designated by the given group are removed from the molecule.
6436  *  This operation is undoable.
6437  */
6438 static VALUE
6439 s_Molecule_Remove(VALUE self, VALUE group)
6440 {
6441     Molecule *mol1;
6442         IntGroup *ig, *bg;
6443         Int i;
6444         IntGroupIterator iter;
6445
6446     Data_Get_Struct(self, Molecule, mol1);
6447         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6448         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6449                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6450         Data_Get_Struct(group, IntGroup, ig);
6451
6452         /*  Remove the bonds between the two fragments  */
6453         /*  (This is necessary for undo to work correctly)  */
6454         IntGroupIteratorInit(ig, &iter);
6455         bg = NULL;
6456         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6457                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6458                 Int j, *cp;
6459                 cp = AtomConnectData(&ap->connect);
6460                 for (j = 0; j < ap->connect.count; j++) {
6461                         int n = cp[j];
6462                         if (!IntGroupLookup(ig, n, NULL)) {
6463                                 /*  bond i-n, i is in ig and n is not  */
6464                                 int k = MoleculeLookupBond(mol1, i, n);
6465                                 if (k >= 0) {
6466                                         if (bg == NULL)
6467                                                 bg = IntGroupNew();
6468                                         IntGroupAdd(bg, k, 1);
6469                                 }
6470                         }
6471                 }
6472         }
6473         IntGroupIteratorRelease(&iter);
6474         if (bg != NULL) {
6475                 /*  Remove bonds  */
6476                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6477                 IntGroupRelease(bg);
6478         }
6479         /*  Remove atoms  */
6480         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6481                 return Qnil;
6482         return self;
6483 }
6484
6485 /*
6486  *  call-seq:
6487  *     create_atom(name, pos = -1)  -> AtomRef
6488  *
6489  *  Create a new atom with the specified name (may contain residue 
6490  *  information) and position (if position is out of range, the atom is appended at
6491  *  the end). Returns the reference to the new atom.
6492  *  This operation is undoable.
6493  */
6494 static VALUE
6495 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6496 {
6497     Molecule *mol;
6498     Int i, pos;
6499         VALUE name, ival;
6500     Atom arec;
6501     AtomRef *aref;
6502         char *p, resName[6], atomName[6];
6503         int resSeq;
6504     Data_Get_Struct(self, Molecule, mol);
6505         rb_scan_args(argc, argv, "02", &name, &ival);
6506         if (ival != Qnil)
6507                 pos = NUM2INT(rb_Integer(ival));
6508         else pos = -1;
6509         if (name != Qnil) {
6510                 p = StringValuePtr(name);
6511                 if (p[0] != 0) {
6512                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6513                         if (atomName[0] == 0)
6514                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6515                 }
6516         } else p = NULL;
6517         if (p == NULL || p[0] == 0) {
6518                 memset(atomName, 0, 4);
6519                 resSeq = -1;
6520         }
6521     memset(&arec, 0, sizeof(arec));
6522     strncpy(arec.aname, atomName, 4);
6523     if (resSeq >= 0) {
6524       strncpy(arec.resName, resName, 4);
6525       arec.resSeq = resSeq;
6526     }
6527         arec.occupancy = 1.0;
6528 //    i = MoleculeCreateAnAtom(mol, &arec);
6529         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6530                 return Qnil;
6531     aref = AtomRefNew(mol, pos);
6532     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6533 }
6534
6535 /*
6536  *  call-seq:
6537  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6538  *
6539  *  Create a new atom with the same attributes (but no bonding information)
6540  *  with the specified atom. Returns the reference to the new atom.
6541  *  This operation is undoable.
6542  */
6543 static VALUE
6544 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6545 {
6546     Molecule *mol;
6547         const Atom *apsrc;
6548     Atom arec;
6549         AtomRef *aref;
6550         VALUE retval, aval, ival;
6551         Int pos;
6552     Data_Get_Struct(self, Molecule, mol);
6553         rb_scan_args(argc, argv, "11", &aval, &ival);
6554         if (FIXNUM_P(aval)) {
6555                 int idx = NUM2INT(aval);
6556                 if (idx < 0 || idx >= mol->natoms)
6557                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6558                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6559         } else {
6560                 apsrc = s_AtomFromValue(aval);
6561         }
6562         if (apsrc == NULL)
6563                 rb_raise(rb_eMolbyError, "bad atom specification");
6564         if (ival != Qnil)
6565                 pos = NUM2INT(rb_Integer(ival));
6566         else pos = -1;
6567         AtomDuplicate(&arec, apsrc);
6568         arec.connect.count = 0;
6569         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6570                 retval = Qnil;
6571         else {
6572                 aref = AtomRefNew(mol, pos);
6573                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6574         }
6575         AtomClean(&arec);
6576         return retval;
6577 }
6578
6579 /*
6580  *  call-seq:
6581  *     create_bond(n1, n2, ...)       -> Integer
6582  *
6583  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6584  *  do nothing for that pair. Returns the number of bonds actually created.
6585  *  This operation is undoable.
6586  */
6587 static VALUE
6588 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6589 {
6590     Molecule *mol;
6591         Int i, j, k, *ip, old_nbonds;
6592         if (argc == 0)
6593                 rb_raise(rb_eMolbyError, "missing arguments");
6594         if (argc % 2 != 0)
6595                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6596     Data_Get_Struct(self, Molecule, mol);
6597         ip = ALLOC_N(Int, argc + 1);
6598         for (i = j = 0; i < argc; i++, j++) {
6599                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6600                 if (i % 2 == 1) {
6601                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6602                                 j -= 2;  /*  This bond is already present: skip it  */
6603                         else {
6604                                 for (k = 0; k < j - 1; k += 2) {
6605                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6606                                                 j -= 2;   /*  The same entry is already in the argument  */
6607                                                 break;
6608                                         }
6609                                 }
6610                         }
6611                 }
6612         }
6613         old_nbonds = mol->nbonds;
6614         if (j > 0) {
6615                 ip[j] = kInvalidIndex;
6616                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6617         } else i = 0;
6618         xfree(ip);
6619         if (i == -1)
6620                 rb_raise(rb_eMolbyError, "atom index out of range");
6621         else if (i == -2)
6622                 rb_raise(rb_eMolbyError, "too many bonds");
6623         else if (i == -3)
6624                 rb_raise(rb_eMolbyError, "duplicate bonds");
6625         else if (i == -5)
6626                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6627         else if (i != 0)
6628                 rb_raise(rb_eMolbyError, "error in creating bonds");
6629         return INT2NUM(mol->nbonds - old_nbonds);
6630 }
6631
6632 /*
6633  *  call-seq:
6634  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6635  *
6636  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6637  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6638  *  This operation is undoable.
6639  */
6640 static VALUE
6641 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6642 {
6643     Molecule *mol;
6644         Int i, j, n[2];
6645         IntGroup *bg;
6646         if (argc == 0)
6647                 rb_raise(rb_eMolbyError, "missing arguments");
6648         if (argc % 2 != 0)
6649                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6650     Data_Get_Struct(self, Molecule, mol);
6651         bg = NULL;
6652         for (i = j = 0; i < argc; i++, j = 1 - j) {
6653                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6654                 if (j == 1) {
6655                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6656                         if (k >= 0) {
6657                                 if (bg == NULL)
6658                                         bg = IntGroupNew();
6659                                 IntGroupAdd(bg, k, 1);
6660                         }
6661                 }
6662         }
6663         if (bg != NULL) {
6664                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6665                 i = IntGroupGetCount(bg);
6666                 IntGroupRelease(bg);
6667         } else i = 0;
6668         return INT2NUM(i);
6669 }
6670
6671 /*
6672  *  call-seq:
6673  *     assign_bond_order(idx, d1)
6674  *     assign_bond_orders(group, [d1, d2, ...])
6675  *
6676  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6677  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6678  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6679  *  (This may change in the future)
6680  *  This operation is undoable.
6681  */
6682 static VALUE
6683 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6684 {
6685     Molecule *mol;
6686         IntGroup *ig;
6687     Data_Get_Struct(self, Molecule, mol);
6688         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6689                 /*  The first form  */
6690                 Int idx = NUM2INT(rb_Integer(idxval));
6691                 Double d1 = NUM2DBL(rb_Float(dval));
6692                 if (idx < 0 || idx >= mol->nbonds)
6693                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6694                 ig = IntGroupNewWithPoints(idx, 1, -1);
6695                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6696                 IntGroupRelease(ig);
6697         } else {
6698                 Int i, n;
6699                 Double *dp;
6700                 ig = IntGroupFromValue(idxval);
6701                 n = IntGroupGetCount(ig);
6702                 if (n == 0)
6703                         rb_raise(rb_eMolbyError, "the bond index is empty");
6704                 dval = rb_ary_to_ary(dval);
6705                 dp = (Double *)calloc(sizeof(Double), n);
6706                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6707                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6708                 }
6709                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6710                 free(dp);
6711                 IntGroupRelease(ig);
6712         }
6713         return self;
6714 }
6715
6716 /*
6717  *  call-seq:
6718  *     get_bond_order(idx) -> Float
6719  *     get_bond_orders(group) -> Array
6720  *
6721  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6722  *  In the second form, the bond orders at the indices in the group are returned as an array.
6723  *  If no bond order information have been assigned, returns nil (the first form)
6724  *  or an empty array (the second form).
6725  */
6726 static VALUE
6727 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6728 {
6729     Molecule *mol;
6730         IntGroup *ig;
6731         Double *dp;
6732         VALUE retval;
6733         Int i, n, numericArg;
6734     Data_Get_Struct(self, Molecule, mol);
6735         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6736                 /*  The first form  */
6737                 Int idx = NUM2INT(rb_Integer(idxval));
6738                 if (idx < 0 || idx >= mol->nbonds)
6739                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6740                 if (mol->bondOrders == NULL)
6741                         return Qnil;
6742                 ig = IntGroupNewWithPoints(idx, 1, -1);
6743                 n = 1;
6744                 numericArg = 1;
6745         } else {
6746                 if (mol->bondOrders == NULL)
6747                         return rb_ary_new();
6748                 ig = IntGroupFromValue(idxval);
6749                 n = IntGroupGetCount(ig);
6750                 if (n == 0)
6751                         rb_raise(rb_eMolbyError, "the bond index is empty");
6752                 numericArg = 0;
6753         }
6754         dp = (Double *)calloc(sizeof(Double), n);
6755         MoleculeGetBondOrders(mol, dp, ig);
6756         if (numericArg)
6757                 retval = rb_float_new(dp[0]);
6758         else {
6759                 retval = rb_ary_new();
6760                 for (i = 0; i < n; i++)
6761                         rb_ary_push(retval, rb_float_new(dp[i]));
6762         }
6763         free(dp);
6764         IntGroupRelease(ig);
6765         return retval;
6766 }
6767
6768 /*
6769  *  call-seq:
6770  *     bond_exist?(idx1, idx2) -> bool
6771  *
6772  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6773  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6774  */
6775 static VALUE
6776 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6777 {
6778         Molecule *mol;
6779         Int idx1, idx2, i;
6780         Atom *ap;
6781         Int *cp;
6782     Data_Get_Struct(self, Molecule, mol);
6783         idx1 = NUM2INT(rb_Integer(ival1));
6784         idx2 = NUM2INT(rb_Integer(ival2));
6785         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6786                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6787         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6788         cp = AtomConnectData(&ap->connect);
6789         for (i = 0; i < ap->connect.count; i++) {
6790                 if (cp[i] == idx2)
6791                         return Qtrue;
6792         }
6793         return Qfalse;
6794 }
6795
6796 /*
6797  *  call-seq:
6798  *     add_angle(n1, n2, n3)       -> Molecule
6799  *
6800  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6801  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6802  *  This operation is undoable.
6803  */
6804 static VALUE
6805 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6806 {
6807         Int n[4];
6808     Molecule *mol;
6809     Data_Get_Struct(self, Molecule, mol);
6810         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6811         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6812         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6813         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6814                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6815         n[3] = kInvalidIndex;
6816         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6817         return self;
6818 }
6819
6820 /*
6821  *  call-seq:
6822  *     remove_angle(n1, n2, n3)       -> Molecule
6823  *
6824  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6825  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6826  *  This operation is undoable.
6827  */
6828 static VALUE
6829 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6830 {
6831         Int n[4];
6832     Molecule *mol;
6833         IntGroup *ig;
6834     Data_Get_Struct(self, Molecule, mol);
6835         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6836         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6837         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6838         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6839                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6840         ig = IntGroupNewWithPoints(n[3], 1, -1);
6841         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6842         IntGroupRelease(ig);
6843         return self;
6844 }
6845
6846 /*
6847  *  call-seq:
6848  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6849  *
6850  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6851  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6852  *  This operation is undoable.
6853  */
6854 static VALUE
6855 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6856 {
6857         Int n[5];
6858     Molecule *mol;
6859     Data_Get_Struct(self, Molecule, mol);
6860         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6861         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6862         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6863         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6864         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6865                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6866         n[4] = kInvalidIndex;
6867         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6868         return self;
6869 }
6870
6871 /*
6872  *  call-seq:
6873  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6874  *
6875  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6876  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6877  *  This operation is undoable.
6878  */
6879 static VALUE
6880 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6881 {
6882         Int n[5];
6883     Molecule *mol;
6884         IntGroup *ig;
6885     Data_Get_Struct(self, Molecule, mol);
6886         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6887         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6888         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6889         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6890         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6891                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6892         ig = IntGroupNewWithPoints(n[4], 1, -1);
6893         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6894         IntGroupRelease(ig);
6895         return self;
6896 }
6897
6898 /*
6899  *  call-seq:
6900  *     add_improper(n1, n2, n3, n4)       -> Molecule
6901  *
6902  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6903  *  not automatically added when a new bond is created, so this method is more useful than
6904  *  the angle/dihedral counterpart.
6905  *  This operation is undoable.
6906  */
6907 static VALUE
6908 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6909 {
6910         Int n[5];
6911     Molecule *mol;
6912     Data_Get_Struct(self, Molecule, mol);
6913         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6914         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6915         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6916         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6917         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6918                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6919         n[4] = kInvalidIndex;
6920         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6921         return self;
6922 }
6923
6924 /*
6925  *  call-seq:
6926  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6927  *     remove_improper(intgroup)             -> Molecule
6928  *
6929  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6930  *  Returns self. Unlike angles and dihedrals, impropers are
6931  *  not automatically added when a new bond is created, so this method is more useful than
6932  *  the angle/dihedral counterpart.
6933  *  This operation is undoable.
6934  */
6935 static VALUE
6936 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6937 {
6938         Int n[5];
6939         VALUE v1, v2, v3, v4;
6940     Molecule *mol;
6941         IntGroup *ig;
6942     Data_Get_Struct(self, Molecule, mol);
6943         if (argc == 1) {
6944                 ig = IntGroupFromValue(argv[0]);
6945         } else {
6946                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6947                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6948                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6949                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6950                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6951                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6952                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6953                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6954         }
6955         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6956         IntGroupRelease(ig);
6957         return self;
6958 }
6959
6960 /*
6961  *  call-seq:
6962  *     assign_residue(group, res)       -> Molecule
6963  *
6964  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6965  *  or "resname.resno". When the residue number is not specified, the residue number of
6966  *  the first atom in the group is used.
6967  *  This operation is undoable.
6968  */
6969 static VALUE
6970 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6971 {
6972     Molecule *mol;
6973         IntGroup *ig;
6974         char *p, *pp, buf[16];
6975         Int resid, n;
6976         Atom *ap;
6977     Data_Get_Struct(self, Molecule, mol);
6978         
6979         /*  Parse the argument res  */
6980         if (FIXNUM_P(res)) {
6981                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6982                 resid = NUM2INT(res);
6983                 buf[0] = 0;
6984         } else {
6985                 p = StringValuePtr(res);
6986                 pp = strchr(p, '.');
6987                 if (pp != NULL) {
6988                         resid = atoi(pp + 1);
6989                         n = pp - p;
6990                 } else {
6991                         resid = -1;
6992                         n = strlen(p);
6993                 }
6994                 if (n > sizeof buf - 1)
6995                         n = sizeof buf - 1;
6996                 strncpy(buf, p, n);
6997                 buf[n] = 0;
6998         }
6999         ig = s_Molecule_AtomGroupFromValue(self, range);
7000         if (ig == NULL || IntGroupGetCount(ig) == 0)
7001                 return Qnil;
7002
7003         if (resid < 0) {
7004                 /*  Use the residue number of the first specified atom  */
7005                 n = IntGroupGetNthPoint(ig, 0);
7006                 if (n >= mol->natoms)
7007                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
7008                 ap = ATOM_AT_INDEX(mol->atoms, n);
7009                 resid = ap->resSeq;
7010         }
7011         /*  Change the residue number  */
7012         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
7013         /*  Change the residue name if necessary  */
7014         if (buf[0] != 0) {
7015         /*      Int seqs[2];
7016                 seqs[0] = resid;
7017                 seqs[1] = kInvalidIndex; */
7018                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
7019         }
7020         IntGroupRelease(ig);
7021         return self;
7022 }
7023
7024 /*
7025  *  call-seq:
7026  *     offset_residue(group, offset)       -> Molecule
7027  *
7028  *  Offset the residue number of the specified atoms. If any of the residue number gets
7029  *  negative, then exception is thrown.
7030  *  This operation is undoable.
7031  */
7032 static VALUE
7033 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
7034 {
7035     Molecule *mol;
7036         IntGroup *ig;
7037         int ofs, result;
7038     Data_Get_Struct(self, Molecule, mol);
7039         ig = s_Molecule_AtomGroupFromValue(self, range);
7040         ofs = NUM2INT(offset);
7041         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
7042         if (result > 0)
7043                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
7044         IntGroupRelease(ig);
7045         return self;
7046 }
7047
7048 /*
7049  *  call-seq:
7050  *     renumber_atoms(array)       -> IntGroup
7051  *
7052  *  Change the order of atoms so that the atoms specified in the array argument appear
7053  *  in this order from the top of the molecule. The atoms that are not included in array
7054  *  are placed after these atoms, and these atoms are returned as an intGroup.
7055  *  This operation is undoable.
7056  */
7057 static VALUE
7058 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
7059 {
7060     Molecule *mol;
7061         Int *new2old;
7062         IntGroup *ig;
7063         int i, n;
7064         VALUE *valp, retval;
7065     Data_Get_Struct(self, Molecule, mol);
7066         if (TYPE(array) != T_ARRAY)
7067                 array = rb_funcall(array, rb_intern("to_a"), 0);
7068         n = RARRAY_LEN(array);
7069         valp = RARRAY_PTR(array);
7070         new2old = ALLOC_N(Int, n + 1);
7071         for (i = 0; i < n; i++)
7072                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
7073         new2old[i] = kInvalidIndex;
7074         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
7075         if (i == 1)
7076                 rb_raise(rb_eMolbyError, "Atom index out of range");
7077         else if (i == 2)
7078                 rb_raise(rb_eMolbyError, "Duplicate entry");
7079         else if (i == 3)
7080                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
7081         retval = IntGroup_Alloc(rb_cIntGroup);
7082         Data_Get_Struct(retval, IntGroup, ig);
7083         if (mol->natoms > n)
7084                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
7085         xfree(new2old);
7086         return retval;
7087 }
7088
7089 /*
7090  *  call-seq:
7091  *     find_close_atoms(atom, limit = 1.2)       -> array of Integers (atom indices)
7092  *
7093  *  Find atoms that are within the threshold distance from the given atom.
7094  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7095  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7096  *  If limit is not given, a default value of 1.2 is used.
7097  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7098  */
7099 static VALUE
7100 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7101 {
7102     Molecule *mol;
7103         VALUE aval, limval;
7104         double limit;
7105         Int n1, nbonds, *bonds;
7106     Data_Get_Struct(self, Molecule, mol);
7107         rb_scan_args(argc, argv, "11", &aval, &limval);
7108         n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7109         if (limval == Qnil)
7110                 limit = 1.2;
7111         else
7112                 limit = NUM2DBL(rb_Float(limval));
7113         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7114         bonds = NULL;
7115         MoleculeFindCloseAtoms(mol, n1, limit, &nbonds, &bonds, 0);
7116         aval = rb_ary_new();
7117         if (nbonds > 0) {
7118                 for (n1 = 0; n1 < nbonds; n1++)
7119                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7120                 free(bonds);
7121         }
7122         return aval;
7123 }
7124
7125 /*
7126  *  call-seq:
7127  *     guess_bonds(limit = 1.2)       -> Integer
7128  *
7129  *  Create bonds between atoms that are within the threshold distance.
7130  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7131  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7132  *  If limit is not given, a default value of 1.2 is used.
7133  *  The number of the newly created bonds is returned.
7134  *  This operation is undoable.
7135  */
7136 static VALUE
7137 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7138 {
7139     Molecule *mol;
7140         VALUE limval;
7141         double limit;
7142         Int nbonds, *bonds;
7143     Data_Get_Struct(self, Molecule, mol);
7144         rb_scan_args(argc, argv, "01", &limval);
7145         if (limval == Qnil)
7146                 limit = 1.2;
7147         else
7148                 limit = NUM2DBL(rb_Float(limval));
7149         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7150         if (nbonds > 0) {
7151                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7152                 free(bonds);
7153         }
7154         return INT2NUM(nbonds);
7155 }
7156         
7157 /*
7158  *  call-seq:
7159  *     register_undo(script, *args)
7160  *
7161  *  Register an undo operation with the current molecule.
7162  */
7163 static VALUE
7164 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
7165 {
7166         Molecule *mol;
7167         VALUE script, args;
7168         MolAction *act;
7169     Data_Get_Struct(self, Molecule, mol);
7170         rb_scan_args(argc, argv, "1*", &script, &args);
7171         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
7172         MolActionCallback_registerUndo(mol, act);
7173         return script;
7174 }
7175
7176 /*
7177  *  call-seq:
7178  *     undo_enabled? -> bool
7179  *
7180  *  Returns true if undo is enabled for this molecule; otherwise no.
7181  */
7182 static VALUE
7183 s_Molecule_UndoEnabled(VALUE self)
7184 {
7185     Molecule *mol;
7186     Data_Get_Struct(self, Molecule, mol);
7187         if (MolActionCallback_isUndoRegistrationEnabled(mol))
7188                 return Qtrue;
7189         else return Qfalse;
7190 }
7191
7192 /*
7193  *  call-seq:
7194  *     undo_enabled = bool
7195  *
7196  *  Enable or disable undo.
7197  */
7198 static VALUE
7199 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
7200 {
7201     Molecule *mol;
7202     Data_Get_Struct(self, Molecule, mol);
7203         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
7204         return val;
7205 }
7206
7207 /*
7208  *  call-seq:
7209  *     selection       -> IntGroup
7210  *
7211  *  Returns the current selection.
7212  */
7213 static VALUE
7214 s_Molecule_Selection(VALUE self)
7215 {
7216     Molecule *mol;
7217         IntGroup *ig;
7218         VALUE val;
7219     Data_Get_Struct(self, Molecule, mol);
7220         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
7221                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
7222                 val = ValueFromIntGroup(ig);
7223                 IntGroupRelease(ig);
7224         } else {
7225                 val = IntGroup_Alloc(rb_cIntGroup);
7226         }
7227         return val;
7228 }
7229
7230 static VALUE
7231 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
7232 {
7233     Molecule *mol;
7234         IntGroup *ig;
7235     Data_Get_Struct(self, Molecule, mol);
7236         if (val == Qnil)
7237                 ig = NULL;
7238         else
7239                 ig = s_Molecule_AtomGroupFromValue(self, val);
7240         if (undoable)
7241                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
7242         else
7243                 MoleculeSetSelection(mol, ig);
7244         if (ig != NULL)
7245                 IntGroupRelease(ig);
7246         return val;
7247 }
7248
7249 /*
7250  *  call-seq:
7251  *     selection = IntGroup
7252  *
7253  *  Set the current selection. The right-hand operand may be nil.
7254  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
7255  */
7256 static VALUE
7257 s_Molecule_SetSelection(VALUE self, VALUE val)
7258 {
7259         return s_Molecule_SetSelectionSub(self, val, 0);
7260 }
7261
7262 /*
7263  *  call-seq:
7264  *     set_undoable_selection(IntGroup)
7265  *
7266  *  Set the current selection with undo registration. The right-hand operand may be nil.
7267  *  This operation is undoable.
7268  */
7269 static VALUE
7270 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
7271 {
7272         return s_Molecule_SetSelectionSub(self, val, 1);
7273 }
7274
7275 /*
7276  *  call-seq:
7277  *     hidden_atoms       -> IntGroup
7278  *
7279  *  Returns the currently hidden atoms.
7280  */
7281 static VALUE
7282 s_Molecule_HiddenAtoms(VALUE self)
7283 {
7284         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7285         return Qnil;  /*  Not reached  */
7286 /*    Molecule *mol;
7287         IntGroup *ig;
7288         VALUE val;
7289     Data_Get_Struct(self, Molecule, mol);
7290         if (mol != NULL) {
7291                 Atom *ap;
7292                 int i;
7293                 ig = IntGroupNew();
7294                 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7295                         if (ap->exflags & kAtomHiddenFlag)
7296                                 IntGroupAdd(ig, i, 1);
7297                 }
7298                 val = ValueFromIntGroup(ig);
7299                 IntGroupRelease(ig);
7300                 rb_obj_freeze(val);
7301                 return val;
7302         } else return Qnil; */
7303 }
7304
7305 /*
7306  *  call-seq:
7307  *     set_hidden_atoms(IntGroup)
7308  *     self.hidden_atoms = IntGroup
7309  *
7310  *  Hide the specified atoms. This operation is _not_ undoable.
7311  */
7312 static VALUE
7313 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
7314 {
7315         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7316         return Qnil;  /*  Not reached  */
7317 /*
7318         Molecule *mol;
7319     Data_Get_Struct(self, Molecule, mol);
7320         if (mol != NULL) {
7321                 Atom *ap;
7322                 int i;
7323                 IntGroup *ig;
7324                 if (val == Qnil)
7325                         ig = NULL;
7326                 else
7327                         ig = s_Molecule_AtomGroupFromValue(self, val);
7328                 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7329                         if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
7330                                 ap->exflags |= kAtomHiddenFlag;
7331                         } else {
7332                                 ap->exflags &= kAtomHiddenFlag;
7333                         }
7334                 }
7335                 if (ig != NULL)
7336                         IntGroupRelease(ig);
7337                 MoleculeCallback_notifyModification(mol, 0);
7338         }
7339         return val; */
7340 }
7341
7342 /*
7343  *  call-seq:
7344  *     select_frame(index)
7345  *     frame = index
7346  *
7347  *  Select the specified frame. If successful, returns true, otherwise returns false.
7348  */
7349 static VALUE
7350 s_Molecule_SelectFrame(VALUE self, VALUE val)
7351 {
7352     Molecule *mol;
7353         int ival = NUM2INT(val);
7354     Data_Get_Struct(self, Molecule, mol);
7355         ival = MoleculeSelectFrame(mol, ival, 1);
7356         if (ival >= 0)
7357                 return Qtrue;
7358         else return Qfalse;
7359 }
7360
7361 /*
7362  *  call-seq:
7363  *     frame -> Integer
7364  *
7365  *  Get the current frame.
7366  */
7367 static VALUE
7368 s_Molecule_Frame(VALUE self)
7369 {
7370     Molecule *mol;
7371     Data_Get_Struct(self, Molecule, mol);
7372         return INT2NUM(mol->cframe);
7373 }
7374
7375 /*
7376  *  call-seq:
7377  *     nframes -> Integer
7378  *
7379  *  Get the number of frames.
7380  */
7381 static VALUE
7382 s_Molecule_Nframes(VALUE self)
7383 {
7384     Molecule *mol;
7385     Data_Get_Struct(self, Molecule, mol);
7386         return INT2NUM(MoleculeGetNumberOfFrames(mol));
7387 }
7388
7389 /*
7390  *  call-seq:
7391  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7392  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7393  *
7394  *  Insert new frames at the indices specified by the intGroup. If the first argument is
7395  *  an integer, a single new frame is inserted at that index. If the first argument is 
7396  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7397  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
7398  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
7399  *  to the new frame.
7400  *  Returns an intGroup representing the inserted frames if successful, nil if not.
7401  */
7402 static VALUE
7403 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7404 {
7405         VALUE val, coords, cells;
7406     Molecule *mol;
7407         IntGroup *ig;
7408         int count, ival, i, j, len, len_c, len2, nframes;
7409         VALUE *ptr, *ptr2;
7410         Vector *vp, *vp2;
7411     Data_Get_Struct(self, Molecule, mol);
7412         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7413         if (coords != Qnil) {
7414                 if (TYPE(coords) != T_ARRAY)
7415                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7416                 len = RARRAY_LEN(coords);
7417         } else len = 0;
7418         if (cells != Qnil) {
7419                 if (mol->cell == NULL)
7420                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7421                 if (TYPE(cells) != T_ARRAY)
7422                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7423                 len_c = RARRAY_LEN(cells);
7424         } else len_c = 0;
7425         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
7426         nframes = MoleculeGetNumberOfFrames(mol);
7427         if (val == Qnil) {
7428                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7429                 val = ValueFromIntGroup(ig);
7430         } else {
7431                 ig = IntGroupFromValue(val);
7432         }
7433         count = IntGroupGetCount(ig);  /*  Count is updated here  */
7434         vp = ALLOC_N(Vector, mol->natoms * count);
7435         if (cells != Qnil)
7436                 vp2 = ALLOC_N(Vector, 4 * count);
7437         else vp2 = NULL;
7438         if (len > 0) {
7439                 if (len < count)
7440                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7441                 ptr = RARRAY_PTR(coords);
7442                 for (i = 0; i < count; i++) {
7443                         if (TYPE(ptr[i]) != T_ARRAY)
7444                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7445                         len2 = RARRAY_LEN(ptr[i]);
7446                         if (len2 < mol->natoms)
7447                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7448                         ptr2 = RARRAY_PTR(ptr[i]);
7449                         for (j = 0; j < mol->natoms; j++)
7450                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7451                 }
7452         } else {
7453                 Atom *ap;
7454                 for (i = 0; i < count; i++) {
7455                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7456                                 vp[i * mol->natoms + j] = ap->r;
7457                         }
7458                 }
7459         }
7460         if (len_c > 0) {
7461                 if (len_c < count)
7462                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7463                 ptr = RARRAY_PTR(cells);
7464                 for (i = 0; i < count; i++) {
7465                         if (TYPE(ptr[i]) != T_ARRAY)
7466                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7467                         len2 = RARRAY_LEN(ptr[i]);
7468                         if (len2 < 4)
7469                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7470                         ptr2 = RARRAY_PTR(ptr[i]);
7471                         for (j = 0; j < 4; j++)
7472                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7473                 }
7474         }
7475         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7476         IntGroupRelease(ig);
7477         xfree(vp);
7478         if (vp2 != NULL)
7479                 xfree(vp2);
7480         return (ival >= 0 ? val : Qnil);
7481 }
7482
7483 /*
7484  *  call-seq:
7485  *     create_frame(coordinates = nil) -> Integer
7486  *     create_frames(coordinates = nil) -> Integer
7487  *
7488  *  Same as molecule.insert_frames(nil, coordinates).
7489  */
7490 static VALUE
7491 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
7492 {
7493         VALUE vals[3];
7494         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
7495         vals[0] = Qnil;
7496         return s_Molecule_InsertFrames(3, vals, self);
7497 }
7498
7499 /*
7500  *  call-seq:
7501  *     remove_frames(IntGroup, wantCoordinates = false)
7502  *
7503  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
7504  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
7505  *  removed frames is returned if operation is successful.
7506  */
7507 static VALUE
7508 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
7509 {
7510         VALUE val, flag;
7511         VALUE retval;
7512     Molecule *mol;
7513         IntGroup *ig;
7514         int count;
7515     Data_Get_Struct(self, Molecule, mol);
7516         rb_scan_args(argc, argv, "11", &val, &flag);
7517         ig = IntGroupFromValue(val);
7518         count = IntGroupGetCount(ig);
7519         if (RTEST(flag)) {
7520                 /*  Create return value before removing frames  */
7521                 VALUE coords;
7522                 int i, j, n;
7523                 Atom *ap;
7524                 Vector v;
7525                 retval = rb_ary_new2(count);
7526                 for (i = 0; i < count; i++) {
7527                         n = IntGroupGetNthPoint(ig, i);
7528                         coords = rb_ary_new2(mol->natoms);
7529                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7530                                 if (n < ap->nframes && n != mol->cframe)
7531                                         v = ap->frames[n];
7532                                 else v = ap->r;
7533                                 rb_ary_push(coords, ValueFromVector(&v));
7534                         }
7535                         rb_ary_push(retval, coords);
7536                 }
7537         } else retval = Qtrue;
7538         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
7539                 return retval;
7540         else return Qnil;
7541 }
7542
7543 /*
7544  *  call-seq:
7545  *     each_frame {|n| ...}
7546  *
7547  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
7548  *  the frame number. After completion, the original frame number is restored.
7549  */
7550 static VALUE
7551 s_Molecule_EachFrame(VALUE self)
7552 {
7553         int i, cframe, nframes;
7554     Molecule *mol;
7555     Data_Get_Struct(self, Molecule, mol);
7556         cframe = mol->cframe;
7557         nframes = MoleculeGetNumberOfFrames(mol);
7558         if (nframes > 0) {
7559                 for (i = 0; i < nframes; i++) {
7560                         MoleculeSelectFrame(mol, i, 1);
7561                         rb_yield(INT2NUM(i));
7562                 }
7563                 MoleculeSelectFrame(mol, cframe, 1);
7564         }
7565     return self;
7566 }
7567
7568 /*
7569  *  call-seq:
7570  *     set_atom_attr(index, key, value)
7571  *
7572  *  Set the atom attribute for the specified atom.
7573  *  This operation is undoable.
7574  */
7575 static VALUE
7576 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7577 {
7578         Molecule *mol;
7579         VALUE aref, oldval;
7580     Data_Get_Struct(self, Molecule, mol);
7581         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7582         oldval = s_AtomRef_GetAttr(aref, key);
7583         if (val == Qundef)
7584                 return oldval;
7585         s_AtomRef_SetAttr(aref, key, val);
7586         return val;
7587 }
7588
7589 /*
7590  *  call-seq:
7591  *     get_atom_attr(index, key)
7592  *
7593  *  Get the atom attribute for the specified atom.
7594  */
7595 static VALUE
7596 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7597 {
7598         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7599 }
7600
7601 /*
7602  *  call-seq:
7603  *     get_coord_from_frame(index, group = nil)
7604  *
7605  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7606  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
7607  *  copied; now they are always copied)
7608  */
7609 static VALUE
7610 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7611 {
7612         Molecule *mol;
7613         VALUE ival, gval, cval;
7614         Int index, i, j, n, nn;
7615         IntGroup *ig;
7616         IntGroupIterator iter;
7617         Atom *ap;
7618         Vector *vp;
7619     Data_Get_Struct(self, Molecule, mol);
7620         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7621         if (argc == 3)
7622                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
7623         index = NUM2INT(rb_Integer(ival));
7624         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7625                 if (n == 0)
7626                         rb_raise(rb_eMolbyError, "No frame is present");
7627                 else
7628                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7629         }
7630         if (gval == Qnil) {
7631                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7632         } else {
7633                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7634         }
7635         n = IntGroupGetCount(ig);
7636         if (n > 0) {
7637                 vp = (Vector *)calloc(sizeof(Vector), n);
7638                 IntGroupIteratorInit(ig, &iter);
7639                 j = 0;
7640                 nn = 0;
7641                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7642                         ap = ATOM_AT_INDEX(mol->atoms, i);
7643                         if (index < ap->nframes) {
7644                                 vp[j] = ap->frames[index];
7645                                 nn++;
7646                         } else {
7647                                 vp[j] = ap->r;
7648                         }
7649                         j++;
7650                 }
7651                 if (nn > 0)
7652                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7653                 free(vp);
7654                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7655                         vp = mol->frame_cells + index * 4;
7656                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7657                 }
7658                 IntGroupIteratorRelease(&iter);
7659         }
7660         IntGroupRelease(ig);
7661         return self;
7662 }
7663
7664 /*
7665  *  call-seq:
7666  *     fragment(n1, *exatoms)  -> IntGroup
7667  *     fragment(group, *exatoms)  -> IntGroup
7668  *
7669  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7670  *  those atoms will not be counted during the search.
7671  */
7672 static VALUE
7673 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7674 {
7675     Molecule *mol;
7676         IntGroup *baseg, *ig, *exatoms;
7677         int n;
7678         volatile VALUE nval, exval;
7679     Data_Get_Struct(self, Molecule, mol);
7680         rb_scan_args(argc, argv, "1*", &nval, &exval);
7681         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7682                 baseg = NULL;
7683                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7684         } else {
7685                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7686         }
7687         if (RARRAY_LEN(exval) == 0) {
7688                 exatoms = NULL;
7689         } else {
7690                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7691                 Data_Get_Struct(exval, IntGroup, exatoms);
7692         }
7693         if (baseg == NULL) {
7694                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7695         } else {
7696                 IntGroupIterator iter;
7697                 IntGroupIteratorInit(baseg, &iter);
7698                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7699                         ig = IntGroupNew();
7700                 } else {
7701                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7702                         if (ig != NULL) {
7703                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7704                                         IntGroup *subg;
7705                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7706                                         if (subg != NULL) {
7707                                                 IntGroupAddIntGroup(ig, subg);
7708                                                 IntGroupRelease(subg);
7709                                         }
7710                                 }
7711                         }
7712                 }
7713                 IntGroupIteratorRelease(&iter);
7714                 IntGroupRelease(baseg);
7715         }
7716         if (ig == NULL)
7717                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7718         nval = ValueFromIntGroup(ig);
7719         IntGroupRelease(ig);
7720         return nval;
7721 }
7722
7723 /*
7724  *  call-seq:
7725  *     fragments(exclude = nil)
7726  *
7727  *  Returns the fragments as an array of IntGroups. 
7728  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
7729  *  in defining the fragment.
7730  */
7731 static VALUE
7732 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
7733 {
7734     Molecule *mol;
7735         IntGroup *ag, *fg, *eg;
7736         VALUE gval, exval, retval;
7737     Data_Get_Struct(self, Molecule, mol);
7738         if (mol == NULL)
7739                 return Qnil;
7740         if (mol->natoms == 0)
7741                 return rb_ary_new();
7742         rb_scan_args(argc, argv, "01", &exval);
7743         if (exval == Qnil)
7744                 eg = NULL;
7745         else
7746                 eg = IntGroupFromValue(exval);
7747         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7748         if (eg != NULL)
7749                 IntGroupRemoveIntGroup(ag, eg);
7750         retval = rb_ary_new();
7751         while (IntGroupGetCount(ag) > 0) {
7752                 int n = IntGroupGetNthPoint(ag, 0);
7753                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7754                 if (fg == NULL)
7755                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
7756                 gval = ValueFromIntGroup(fg);
7757                 rb_ary_push(retval, gval);
7758                 IntGroupRemoveIntGroup(ag, fg);
7759                 IntGroupRelease(fg);
7760         }
7761         IntGroupRelease(ag);
7762         if (eg != NULL)
7763                 IntGroupRelease(eg);
7764         return retval;
7765 }
7766
7767 /*
7768  *  call-seq:
7769  *     each_fragment(exclude = nil) {|group| ...}
7770  *
7771  *  Execute the block, with the IntGroup object for each fragment as the argument.
7772  *  Atoms or bonds should not be added or removed during the execution of the block.
7773  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
7774  *  in defining the fragment.
7775  */
7776 static VALUE
7777 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
7778 {
7779     Molecule *mol;
7780         IntGroup *ag, *fg, *eg;
7781         VALUE gval, exval;
7782     Data_Get_Struct(self, Molecule, mol);
7783         if (mol == NULL || mol->natoms == 0)
7784                 return self;
7785         rb_scan_args(argc, argv, "01", &exval);
7786         if (exval == Qnil)
7787                 eg = NULL;
7788         else
7789                 eg = IntGroupFromValue(exval);
7790         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7791         if (eg != NULL)
7792                 IntGroupRemoveIntGroup(ag, eg);
7793         while (IntGroupGetCount(ag) > 0) {
7794                 int n = IntGroupGetNthPoint(ag, 0);
7795                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7796                 if (fg == NULL)
7797                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
7798                 gval = ValueFromIntGroup(fg);
7799                 rb_yield(gval);
7800                 IntGroupRemoveIntGroup(ag, fg);
7801                 IntGroupRelease(fg);
7802         }
7803         IntGroupRelease(ag);
7804         if (eg != NULL)
7805                 IntGroupRelease(eg);
7806         return self;
7807 }
7808
7809 /*
7810  *  call-seq:
7811  *     detachable?(group)  -> [n1, n2]
7812  *
7813  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
7814  *  of the molecule via only one bond. If it is, then the indices of the atoms
7815  *  belonging to the bond is returned, the first element being the atom included
7816  *  in the fragment. Otherwise, Qnil is returned.
7817  */
7818 static VALUE
7819 s_Molecule_Detachable_P(VALUE self, VALUE gval)
7820 {
7821         Molecule *mol;
7822         IntGroup *ig;
7823         int n1, n2;
7824         VALUE retval;
7825     Data_Get_Struct(self, Molecule, mol);
7826         ig = s_Molecule_AtomGroupFromValue(self, gval);
7827         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
7828                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
7829         } else retval = Qnil;
7830         IntGroupRelease(ig);
7831         return retval;
7832 }
7833
7834 /*
7835  *  call-seq:
7836  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
7837  *
7838  *  Returns an array of bonds that connect an atom in the group and an atom out
7839  *  of the group. The first atom in the bond always belongs to the group. If no
7840  *  such bonds are present, an empty array is returned.
7841  */
7842 static VALUE
7843 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
7844 {
7845         Molecule *mol;
7846         IntGroup *ig, *bg;
7847         VALUE gval, retval;
7848     Data_Get_Struct(self, Molecule, mol);
7849         rb_scan_args(argc, argv, "01", &gval);
7850         if (gval == Qnil) {
7851                 ig = MoleculeGetSelection(mol);
7852                 if (ig != NULL)
7853                         IntGroupRetain(ig);
7854         } else {
7855                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7856         }
7857         retval = rb_ary_new();
7858         if (ig == NULL)
7859                 return retval;
7860         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
7861         if (bg != NULL) {
7862                 IntGroupIterator iter;
7863                 Int i;
7864                 IntGroupIteratorInit(bg, &iter);
7865                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7866                         /*  The atoms at the border  */
7867                         Int n1, n2;
7868                         n1 = mol->bonds[i * 2];
7869                         n2 = mol->bonds[i * 2 + 1];
7870                         if (IntGroupLookupPoint(ig, n1) < 0) {
7871                                 int w = n1;
7872                                 n1 = n2;
7873                                 n2 = w;
7874                                 if (IntGroupLookupPoint(ig, n1) < 0)
7875                                         continue;  /*  Actually this is an internal error  */
7876                         }
7877                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
7878                 }
7879                 IntGroupIteratorRelease(&iter);
7880         }
7881         IntGroupRelease(bg);
7882         IntGroupRelease(ig);
7883         return retval;
7884 }
7885
7886 /*
7887  *  call-seq:
7888  *     translate(vec, group = nil)       -> Molecule
7889  *
7890  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7891  *  This operation is undoable.
7892  */
7893 static VALUE
7894 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7895 {
7896     Molecule *mol;
7897         VALUE vec, group;
7898         Vector v;
7899         IntGroup *ig;
7900     Data_Get_Struct(self, Molecule, mol);
7901         rb_scan_args(argc, argv, "11", &vec, &group);
7902         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7903         VectorFromValue(vec, &v);
7904 //      MoleculeTranslate(mol, &v, ig);
7905         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7906         if (ig != NULL)
7907                 IntGroupRelease(ig);
7908         return self;
7909 }
7910
7911 /*
7912  *  call-seq:
7913  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7914  *
7915  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7916  *  If group is given, only atoms in the group are moved.
7917  *  This operation is undoable.
7918  */
7919 static VALUE
7920 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7921 {
7922     Molecule *mol;
7923         volatile VALUE aval, anval, cval, gval;
7924         Double angle;
7925         Vector av, cv;
7926         Transform tr;
7927         IntGroup *ig;
7928     Data_Get_Struct(self, Molecule, mol);
7929         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7930         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7931         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7932         VectorFromValue(aval, &av);
7933         if (NIL_P(cval))
7934                 cv.x = cv.y = cv.z = 0.0;
7935         else
7936                 VectorFromValue(cval, &cv);
7937         if (TransformForRotation(tr, &av, angle, &cv))
7938                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7939         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7940         if (ig != NULL)
7941                 IntGroupRelease(ig);
7942         return self;
7943 }
7944
7945 /*
7946  *  call-seq:
7947  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7948  *
7949  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7950  *  axis must not be a zero vector.
7951  *  If group is given, only atoms in the group are moved.
7952  *  This operation is undoable.
7953  */
7954 static VALUE
7955 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7956 {
7957     Molecule *mol;
7958         volatile VALUE aval, cval, gval;
7959         Vector av, cv;
7960         Transform tr;
7961         IntGroup *ig;
7962     Data_Get_Struct(self, Molecule, mol);
7963         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7964         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7965         VectorFromValue(aval, &av);
7966         if (NIL_P(cval))
7967                 cv.x = cv.y = cv.z = 0.0;
7968         else
7969                 VectorFromValue(cval, &cv);
7970         if (TransformForReflection(tr, &av, &cv))
7971                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7972         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7973         if (ig != NULL)
7974                 IntGroupRelease(ig);
7975         return self;
7976 }
7977
7978 /*
7979  *  call-seq:
7980  *     invert(center = [0,0,0], group = nil)       -> Molecule
7981  *
7982  *  Invert the molecule with the given center.
7983  *  If group is given, only atoms in the group are moved.
7984  *  This operation is undoable.
7985  */
7986 static VALUE
7987 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7988 {
7989         Molecule *mol;
7990         volatile VALUE cval, gval;
7991         Vector cv;
7992         Transform tr;
7993         IntGroup *ig;
7994     Data_Get_Struct(self, Molecule, mol);
7995         rb_scan_args(argc, argv, "02", &cval, &gval);
7996         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7997         if (NIL_P(cval))
7998                 cv.x = cv.y = cv.z = 0.0;
7999         else
8000                 VectorFromValue(cval, &cv);
8001         TransformForInversion(tr, &cv);
8002         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8003         if (ig != NULL)
8004                 IntGroupRelease(ig);
8005         return self;
8006 }
8007
8008 /*
8009  *  call-seq:
8010  *     transform(transform, group = nil)       -> Molecule
8011  *
8012  *  Transform the molecule by the given Transform object.
8013  *  If group is given, only atoms in the group are moved.
8014  *  This operation is undoable.
8015  */
8016 static VALUE
8017 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
8018 {
8019     Molecule *mol;
8020         VALUE trans, group;
8021         Transform tr;
8022         IntGroup *ig;
8023     Data_Get_Struct(self, Molecule, mol);
8024         rb_scan_args(argc, argv, "11", &trans, &group);
8025         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8026         TransformFromValue(trans, &tr);
8027 /*      MoleculeTransform(mol, tr, ig); */
8028         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8029         if (ig != NULL)
8030                 IntGroupRelease(ig);
8031         return self;
8032 }
8033
8034 static void
8035 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
8036 {
8037         switch (MoleculeCenterOfMass(mol, outv, ig)) {
8038                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
8039                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
8040                 case 0: break;
8041                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
8042         }
8043 }
8044
8045 /*
8046  *  call-seq:
8047  *     center_of_mass(group = nil)       -> Vector3D
8048  *
8049  *  Calculate the center of mass for the given set of atoms. The argument
8050  *  group is null, then all atoms are considered.
8051  */
8052 static VALUE
8053 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
8054 {
8055     Molecule *mol;
8056         VALUE group;
8057         IntGroup *ig;
8058         Vector v;
8059     Data_Get_Struct(self, Molecule, mol);
8060         rb_scan_args(argc, argv, "01", &group);
8061         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8062         s_Molecule_DoCenterOfMass(mol, &v, ig);
8063         if (ig != NULL)
8064                 IntGroupRelease(ig);
8065         return ValueFromVector(&v);
8066 }
8067
8068 /*
8069  *  call-seq:
8070  *     centralize(group = nil)       -> self
8071  *
8072  *  Translate the molecule so that the center of mass of the given group is located
8073  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
8074  */
8075 static VALUE
8076 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
8077 {
8078     Molecule *mol;
8079         VALUE group;
8080         IntGroup *ig;
8081         Vector v;
8082     Data_Get_Struct(self, Molecule, mol);
8083         rb_scan_args(argc, argv, "01", &group);
8084         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8085         s_Molecule_DoCenterOfMass(mol, &v, ig);
8086         if (ig != NULL)
8087                 IntGroupRelease(ig);
8088         v.x = -v.x;
8089         v.y = -v.y;
8090         v.z = -v.z;
8091         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
8092         return self;
8093 }
8094
8095 /*
8096  *  call-seq:
8097  *     bounds(group = nil)       -> [min, max]
8098  *
8099  *  Calculate the boundary. The return value is an array of two Vector3D objects.
8100  */
8101 static VALUE
8102 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
8103 {
8104     Molecule *mol;
8105         VALUE group;
8106         IntGroup *ig;
8107         Vector vmin, vmax;
8108         int n;
8109         Atom *ap;
8110     Data_Get_Struct(self, Molecule, mol);
8111         rb_scan_args(argc, argv, "01", &group);
8112         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8113         if (ig != NULL && IntGroupGetCount(ig) == 0)
8114                 rb_raise(rb_eMolbyError, "atom group is empty");
8115         vmin.x = vmin.y = vmin.z = 1e30;
8116         vmax.x = vmax.y = vmax.z = -1e30;
8117         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
8118                 Vector r;
8119                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
8120                         continue;
8121                 r = ap->r;
8122                 if (r.x < vmin.x)
8123                         vmin.x = r.x;
8124                 if (r.y < vmin.y)
8125                         vmin.y = r.y;
8126                 if (r.z < vmin.z)
8127                         vmin.z = r.z;
8128                 if (r.x > vmax.x)
8129                         vmax.x = r.x;
8130                 if (r.y > vmax.y)
8131                         vmax.y = r.y;
8132                 if (r.z > vmax.z)
8133                         vmax.z = r.z;
8134         }
8135         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
8136 }
8137
8138 /*  Get atom position or a vector  */
8139 static void
8140 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
8141 {
8142         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
8143                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
8144                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
8145         } else {
8146                 VectorFromValue(val, vp);
8147         }
8148 }
8149
8150 /*
8151  *  call-seq:
8152  *     measure_bond(n1, n2)       -> Float
8153  *
8154  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
8155  *  or Vector3D values.
8156  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8157  */
8158 static VALUE
8159 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
8160 {
8161     Molecule *mol;
8162         Vector v1, v2;
8163     Data_Get_Struct(self, Molecule, mol);
8164         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8165         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8166         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
8167 }
8168
8169 /*
8170  *  call-seq:
8171  *     measure_angle(n1, n2, n3)       -> Float
8172  *
8173  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
8174  *  or Vector3D values. The return value is in degree.
8175  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8176  */
8177 static VALUE
8178 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
8179 {
8180     Molecule *mol;
8181         Vector v1, v2, v3;
8182         Double d;
8183     Data_Get_Struct(self, Molecule, mol);
8184         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8185         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8186         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
8187         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
8188         if (isnan(d))
8189                 return Qnil;  /*  Cannot define  */
8190         else return rb_float_new(d);
8191 }
8192
8193 /*
8194  *  call-seq:
8195  *     measure_dihedral(n1, n2, n3, n4)       -> Float
8196  *
8197  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
8198  *  or Vector3D values. The return value is in degree.
8199  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8200  */
8201 static VALUE
8202 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
8203 {
8204     Molecule *mol;
8205         Vector v1, v2, v3, v4;
8206         Double d;
8207     Data_Get_Struct(self, Molecule, mol);
8208         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8209         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8210         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
8211         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
8212         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
8213         if (isnan(d))
8214                 return Qnil;  /*  Cannot define  */
8215         else return rb_float_new(d);
8216 }
8217
8218 /*
8219  *  call-seq:
8220  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> Array
8221  *
8222  *  Expand the specified part of the molecule by the given symmetry operation.
8223  *  Returns the array of atom indices corresponding to the expanded atoms.
8224  */
8225 static VALUE
8226 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
8227 {
8228     Molecule *mol;
8229         VALUE gval, sval, xval, yval, zval, rval;
8230         IntGroup *ig;
8231         Int n[4];
8232         Int natoms;
8233         Int nidx, *idx;
8234
8235     Data_Get_Struct(self, Molecule, mol);
8236         rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
8237         n[0] = NUM2INT(rb_Integer(sval));
8238         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
8239         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
8240         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
8241         ig = s_Molecule_AtomGroupFromValue(self, gval);
8242         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
8243                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
8244         natoms = mol->natoms;
8245         
8246         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], &nidx, &idx);
8247
8248         rval = rb_ary_new2(nidx);
8249         while (--nidx >= 0) {
8250                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
8251         }
8252 /*      if (natoms == mol->natoms)
8253                 rval = Qnil;
8254         else {
8255                 rval = IntGroup_Alloc(rb_cIntGroup);
8256                 Data_Get_Struct(rval, IntGroup, ig);
8257                 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
8258         } */
8259         return rval;
8260 }
8261
8262 /*
8263  *  call-seq:
8264  *     amend_by_symmetry(group = nil) -> IntGroup
8265  *
8266  *  Expand the specified part of the molecule by the given symmetry operation.
8267  *  Returns an IntGroup containing the added atoms.
8268  */
8269 static VALUE
8270 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
8271 {
8272     Molecule *mol;
8273         IntGroup *ig, *ig2;
8274         VALUE rval, gval;
8275     Data_Get_Struct(self, Molecule, mol);
8276         rb_scan_args(argc, argv, "01", &gval);
8277         if (gval != Qnil)
8278                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8279         else ig = NULL;
8280         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
8281         rval = ValueFromIntGroup(ig2);
8282         IntGroupRelease(ig2);
8283         return rval;
8284 }
8285
8286 /*
8287  *  call-seq:
8288  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
8289  *
8290  *  Get the transform corresponding to the symmetry operation. The symop can either be
8291  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
8292  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
8293  *  Otherwise, the returned transform is for fractional coordinates.
8294  *  Raises exception when no cell or no transform are defined.
8295  */
8296 static VALUE
8297 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
8298 {
8299     Molecule *mol;
8300         VALUE sval, fval;
8301         Symop symop;
8302         Transform tr;
8303     Data_Get_Struct(self, Molecule, mol);
8304         if (mol->cell == NULL)
8305                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8306         if (mol->nsyms == 0)
8307                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8308         rb_scan_args(argc, argv, "11", &sval, &fval);
8309         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
8310                 symop.sym = NUM2INT(rb_Integer(sval));
8311                 symop.dx = symop.dy = symop.dz = 0;
8312         } else {
8313                 sval = rb_ary_to_ary(sval);
8314                 if (RARRAY_LEN(sval) < 4)
8315                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
8316                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
8317                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
8318                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
8319                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
8320         }
8321         if (symop.sym >= mol->nsyms)
8322                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
8323         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
8324         return ValueFromTransform(&tr);
8325 }
8326         
8327 /*
8328  *  call-seq:
8329  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8330  *
8331  *  Get the symmetry operation corresponding to the given transform.
8332  *  If is_cartesian is true, the given transform is for cartesian coordinates.
8333  *  Otherwise, the given transform is for fractional coordinates.
8334  *  Raises exception when no cell or no transform are defined.
8335  */
8336 static VALUE
8337 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8338 {
8339     Molecule *mol;
8340         VALUE tval, fval;
8341         Symop symop;
8342         Transform tr;
8343         int n;
8344     Data_Get_Struct(self, Molecule, mol);
8345         if (mol->cell == NULL)
8346                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8347         if (mol->nsyms == 0)
8348                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8349         rb_scan_args(argc, argv, "11", &tval, &fval);
8350         TransformFromValue(tval, &tr);
8351         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8352         if (n == 0) {
8353                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8354         } else {
8355                 return Qnil;  /*  Not found  */
8356         }
8357 }
8358
8359 /*
8360  *  call-seq:
8361  *     wrap_unit_cell(group) -> Vector3D
8362  *
8363  *  Move the specified group so that the center of mass of the group is within the
8364  *  unit cell. The offset vector is returned. If no periodic box is defined, 
8365  *  exception is raised.
8366  */
8367 static VALUE
8368 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
8369 {
8370     Molecule *mol;
8371         IntGroup *ig;
8372         Vector v, cv, dv;
8373     Data_Get_Struct(self, Molecule, mol);
8374         if (mol->cell == NULL)
8375                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8376         ig = s_Molecule_AtomGroupFromValue(self, gval);
8377         s_Molecule_DoCenterOfMass(mol, &cv, ig);
8378         TransformVec(&v, mol->cell->rtr, &cv);
8379         if (mol->cell->flags[0])
8380                 v.x -= floor(v.x);
8381         if (mol->cell->flags[1])
8382                 v.y -= floor(v.y);
8383         if (mol->cell->flags[2])
8384                 v.z -= floor(v.z);
8385         TransformVec(&dv, mol->cell->tr, &v);
8386         VecDec(dv, cv);
8387         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
8388         IntGroupRelease(ig);
8389         return ValueFromVector(&dv);
8390 }
8391
8392 /*
8393  *  call-seq:
8394  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
8395  *
8396  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
8397  *  first and second atom in the pair should belong to group1 and group2, respectively.
8398  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
8399  */
8400 static VALUE
8401 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
8402 {
8403     Molecule *mol;
8404         VALUE limval, gval1, gval2, rval, igval;
8405         IntGroup *ig1, *ig2;
8406         IntGroupIterator iter1, iter2;
8407         Int npairs, *pairs;
8408         Int n[2], i;
8409         Double lim;
8410         Vector r1;
8411         Atom *ap1, *ap2;
8412         MDExclusion *exinfo;
8413         Int *exlist;
8414
8415     Data_Get_Struct(self, Molecule, mol);
8416         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
8417         lim = NUM2DBL(rb_Float(limval));
8418         if (lim <= 0.0)
8419                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
8420         if (gval1 != Qnil)
8421                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
8422         else
8423                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
8424         if (gval2 != Qnil)
8425                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
8426         else
8427                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
8428         
8429         if (!RTEST(igval)) {
8430                 /*  Use the exclusion table in MDArena  */
8431                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
8432                         VALUE mval = ValueFromMolecule(mol);
8433                         s_RebuildMDParameterIfNecessary(mval, Qnil);
8434                 }
8435                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
8436                 exlist = mol->arena->exlist;    
8437         } else {
8438                 exinfo = NULL;
8439                 exlist = NULL;
8440         }
8441         IntGroupIteratorInit(ig1, &iter1);
8442         IntGroupIteratorInit(ig2, &iter2);
8443         npairs = 0;
8444         pairs = NULL;
8445         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
8446                 Int exn1, exn2;
8447                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
8448                 r1 = ap1->r;
8449                 if (exinfo != NULL) {
8450                         exn1 = exinfo[n[0]].index1;
8451                         exn2 = exinfo[n[0] + 1].index1;
8452                 } else exn1 = exn2 = -1;
8453                 IntGroupIteratorReset(&iter2);
8454                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
8455                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
8456                         if (n[0] == n[1])
8457                                 continue;  /*  Same atom  */
8458                         if (exinfo != NULL) {
8459                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
8460                                 for (i = exn1; i < exn2; i++) {
8461                                         if (exlist[i] == n[1])
8462                                                 break;
8463                                 }
8464                                 if (i < exn2)
8465                                         continue;  /*  Should be excluded  */
8466                         }
8467                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
8468                                 /*  Is this pair already registered?  */
8469                                 Int *ip;
8470                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
8471                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
8472                                                 break;
8473                                 }
8474                                 if (i >= npairs) {
8475                                         /*  Not registered yet  */
8476                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
8477                                 }
8478                         }
8479                 }
8480         }
8481         IntGroupIteratorRelease(&iter2);
8482         IntGroupIteratorRelease(&iter1);
8483         IntGroupRelease(ig2);
8484         IntGroupRelease(ig1);
8485         rval = rb_ary_new2(npairs);
8486         if (pairs != NULL) {
8487                 for (i = 0; i < npairs; i++) {
8488                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
8489                 }
8490                 free(pairs);
8491         }
8492         return rval;
8493 }
8494
8495 /*  Calculate the transform that moves the current coordinates to the reference
8496  coordinates with least displacements.   */
8497 static Double
8498 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8499 {
8500         Atom *ap, *ap1;
8501         Int natoms, nn;
8502         Vector org1, org2;
8503         Int i, in, j, k;
8504         Double w, w1;
8505         Mat33 r, q, u;
8506         Double eigen_val[3];
8507         Vector eigen_vec[3];
8508         Vector s[3];
8509         IntGroupIterator iter;
8510
8511         natoms = mol->natoms;
8512         ap = mol->atoms;
8513         IntGroupIteratorInit(ig, &iter);
8514         
8515         /*  Calculate the weighted center  */
8516         VecZero(org1);
8517         VecZero(org2);
8518         w = 0.0;
8519         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8520                 ap1 = ATOM_AT_INDEX(ap, in);
8521                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8522                 VecScaleInc(org1, ap1->r, w1);
8523                 VecScaleInc(org2, ref[i], w1);
8524                 w += w1;
8525         }
8526         w = 1.0 / w;
8527         VecScaleSelf(org1, w);
8528         VecScaleSelf(org2, w);
8529
8530     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8531     /*  Matrix to diagonalize = R * tR    */
8532         memset(r, 0, sizeof(Mat33));
8533         memset(q, 0, sizeof(Mat33));
8534         memset(u, 0, sizeof(Mat33));
8535         nn = 0;
8536         IntGroupIteratorReset(&iter);
8537         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8538                 Vector v1, v2;
8539                 ap1 = ATOM_AT_INDEX(ap, in);
8540                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8541                 w1 *= w1;
8542                 VecSub(v1, ap1->r, org1);
8543                 VecSub(v2, ref[i], org2);
8544                 r[0] += w1 * v1.x * v2.x;
8545                 r[1] += w1 * v1.y * v2.x;
8546                 r[2] += w1 * v1.z * v2.x;
8547                 r[3] += w1 * v1.x * v2.y;
8548                 r[4] += w1 * v1.y * v2.y;
8549                 r[5] += w1 * v1.z * v2.y;
8550                 r[6] += w1 * v1.x * v2.z;
8551                 r[7] += w1 * v1.y * v2.z;
8552                 r[8] += w1 * v1.z * v2.z;
8553                 nn++;
8554         }
8555         for (i = 0; i < 9; i++)
8556                 r[i] /= (nn * nn);
8557         for (i = 0; i < 3; i++) {
8558                 for (j = 0; j < 3; j++) {
8559                         for (k = 0; k < 3; k++) {
8560                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8561                         }
8562                 }
8563         }
8564         
8565         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8566                 IntGroupIteratorRelease(&iter);
8567                 return -1.0;  /*  Cannot determine the eigenvector  */
8568         }
8569
8570     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8571     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8572         MatrixTranspose(r, r);
8573         for (i = 0; i < 3; i++) {
8574                 MatrixVec(&s[i], r, &eigen_vec[i]);
8575                 w1 = 1.0 / sqrt(eigen_val[i]);
8576                 VecScaleSelf(s[i], w1);
8577         }
8578         for (k = 0; k < 3; k++) {
8579                 u[0] += s[k].x * eigen_vec[k].x;
8580                 u[1] += s[k].y * eigen_vec[k].x;
8581                 u[2] += s[k].z * eigen_vec[k].x;
8582                 u[3] += s[k].x * eigen_vec[k].y;
8583                 u[4] += s[k].y * eigen_vec[k].y;
8584                 u[5] += s[k].z * eigen_vec[k].y;
8585                 u[6] += s[k].x * eigen_vec[k].z;
8586                 u[7] += s[k].y * eigen_vec[k].z;
8587                 u[8] += s[k].z * eigen_vec[k].z;
8588         }
8589         
8590         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8591         MatrixVec(&org1, u, &org1);
8592         VecDec(org2, org1);
8593         for (i = 0; i < 9; i++)
8594                 trans[i] = u[i];
8595         trans[9] = org2.x;
8596         trans[10] = org2.y;
8597         trans[11] = org2.z;
8598         
8599         /*  Calculate rmsd  */
8600         IntGroupIteratorReset(&iter);
8601         w = 0.0;
8602         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8603                 Vector tv;
8604                 ap1 = ATOM_AT_INDEX(ap, in);
8605                 TransformVec(&tv, trans, &ap1->r);
8606                 VecDec(tv, ref[i]);
8607                 w += VecLength2(tv);
8608         }
8609         w = sqrt(w / nn);
8610         IntGroupIteratorRelease(&iter);
8611         return w;
8612 }
8613
8614 /*
8615  *  call-seq:
8616  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8617  *
8618  *  Calculate the transform to fit the given group to the set of reference coordinates.
8619  *  The reference coordinates ref is given as either a frame number, an array of
8620  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8621  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8622  *  Return values are the transform (that converts the present coordinates to the
8623  *  target coordinates) and root mean square deviation (without weight).
8624  */
8625 static VALUE
8626 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8627 {
8628         Molecule *mol;
8629         Atom *ap;
8630         VALUE gval, rval, wval;
8631         IntGroup *ig;
8632         IntGroupIterator iter;
8633         int nn, errno, i, j, in, status;
8634         Vector *ref;
8635         Double *weights, dval[3];
8636         Transform tr;
8637
8638         Data_Get_Struct(self, Molecule, mol);
8639         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8640         if (gval == Qnil)
8641                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8642         else
8643                 ig = IntGroupFromValue(gval);
8644         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8645                 IntGroupRelease(ig);
8646                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8647         }
8648         ref = (Vector *)calloc(sizeof(Vector), nn);
8649         weights = (Double *)calloc(sizeof(Double), nn);
8650         IntGroupIteratorInit(ig, &iter);
8651         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8652                 int fn = NUM2INT(rb_Integer(rval));
8653                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8654                         errno = 1;
8655                         status = fn;
8656                         goto err;
8657                 }
8658                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8659                         ap = ATOM_AT_INDEX(mol->atoms, in);
8660                         if (fn < ap->nframes)
8661                                 ref[i] = ap->frames[fn];
8662                         else ref[i] = ap->r;
8663                 }
8664         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8665                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8666                 if (m->row * m->column < nn * 3) {
8667                         errno = 2;
8668                         goto err;
8669                 }
8670                 for (i = 0; i < nn; i++) {
8671                         ref[i].x = m->data[i * 3];
8672                         ref[i].y = m->data[i * 3 + 1];
8673                         ref[i].z = m->data[i * 3 + 2];
8674                 }
8675         } else {
8676                 VALUE aval;
8677                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8678                 if (status != 0) {
8679                         errno = 3;
8680                         goto err;
8681                 }
8682                 if (RARRAY_LEN(rval) < nn) {
8683                         errno = 2;
8684                         goto err;
8685                 }
8686                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8687                         /*  Array of 3*nn numbers  */
8688                         if (RARRAY_LEN(rval) < nn * 3) {
8689                                 errno = 2;
8690                                 goto err;
8691                         }
8692                         for (i = 0; i < nn; i++) {
8693                                 for (j = 0; j < 3; j++) {
8694                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8695                                         if (status != 0) {
8696                                                 errno = 3;
8697                                                 goto err;
8698                                         }
8699                                         dval[j] = NUM2DBL(aval);
8700                                 }
8701                                 ref[i].x = dval[0];
8702                                 ref[i].y = dval[1];
8703                                 ref[i].z = dval[2];
8704                         }
8705                 } else {
8706                         /*  Array of nn Vector3Ds or Arrays  */
8707                         for (i = 0; i < nn; i++) {
8708                                 aval = (RARRAY_PTR(rval))[i];
8709                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8710                                         VectorFromValue(aval, &ref[i]);
8711                                 } else {
8712                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8713                                         if (status != 0) {
8714                                                 errno = 3;
8715                                                 goto err;
8716                                         }
8717                                         if (RARRAY_LEN(aval) < 3) {
8718                                                 errno = 4;
8719                                                 status = i;
8720                                                 goto err;
8721                                         }
8722                                         for (j = 0; j < 3; j++) {
8723                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8724                                                 if (status != 0) {
8725                                                         errno = 3;
8726                                                         goto err;
8727                                                 }
8728                                                 dval[j] = NUM2DBL(aaval);
8729                                         }
8730                                         ref[i].x = dval[0];
8731                                         ref[i].y = dval[1];
8732                                         ref[i].z = dval[2];
8733                                 }
8734                         }
8735                 }
8736         }
8737         if (wval == Qnil) {
8738                 /*  Use atomic weights  */
8739                 IntGroupIteratorReset(&iter);
8740                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8741                         ap = ATOM_AT_INDEX(mol->atoms, in);
8742                         weights[i] = ap->weight;
8743                 }
8744         } else {
8745                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8746                 if (status != 0) {
8747                         errno = 3;
8748                         goto err;
8749                 }
8750                 if (RARRAY_LEN(wval) < nn) {
8751                         errno = 5;
8752                         goto err;
8753                 }
8754                 for (i = 0; i < nn; i++) {
8755                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8756                         if (status != 0) {
8757                                 errno = 3;
8758                                 goto err;
8759                         }
8760                         weights[i] = NUM2DBL(wwval);
8761                 }
8762         }
8763         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8764         if (dval[0] < 0) {
8765                 errno = 6;
8766                 goto err;
8767         }
8768         errno = 0;
8769 err:
8770         IntGroupIteratorRelease(&iter);
8771         free(ref);
8772         free(weights);
8773         if (errno == 0) {
8774                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8775         } else if (errno == 1) {
8776                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8777         } else if (errno == 2) {
8778                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8779         } else if (errno == 3) {
8780                 rb_jump_tag(status);
8781         } else if (errno == 4) {
8782                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8783         } else if (errno == 5) {
8784                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8785         } else if (errno == 6) {
8786                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8787         }
8788         return Qnil;  /*  Not reached  */
8789 }
8790
8791 /*
8792  *  call-seq:
8793  *     display
8794  *
8795  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8796  */
8797 static VALUE
8798 s_Molecule_Display(VALUE self)
8799 {
8800     Molecule *mol;
8801     Data_Get_Struct(self, Molecule, mol);
8802         if (mol->mview != NULL)
8803                 MainViewCallback_display(mol->mview);
8804         return Qnil;
8805 }
8806
8807 /*
8808  *  call-seq:
8809  *     make_front
8810  *
8811  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8812  */
8813 static VALUE
8814 s_Molecule_MakeFront(VALUE self)
8815 {
8816     Molecule *mol;
8817     Data_Get_Struct(self, Molecule, mol);
8818         if (mol->mview != NULL)
8819                 MainViewCallback_makeFront(mol->mview);
8820         return Qnil;
8821 }
8822
8823 /*
8824  *  call-seq:
8825  *     update_enabled? -> bool
8826  *
8827  *  Returns true if screen update is enabled; otherwise no.
8828  */
8829 static VALUE
8830 s_Molecule_UpdateEnabled(VALUE self)
8831 {
8832     Molecule *mol;
8833     Data_Get_Struct(self, Molecule, mol);
8834         if (mol->mview != NULL && !mol->mview->freezeScreen)
8835                 return Qtrue;
8836         else return Qfalse;
8837 }
8838
8839 /*
8840  *  call-seq:
8841  *     update_enabled = bool
8842  *
8843  *  Enable or disable screen update. This is effective for automatic update on modification.
8844  *  Explicit call to molecule.display() always updates the screen.
8845  */
8846 static VALUE
8847 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8848 {
8849     Molecule *mol;
8850     Data_Get_Struct(self, Molecule, mol);
8851         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8852         if (mol->mview != NULL)
8853                 mol->mview->freezeScreen = (val == Qfalse);
8854         else val = Qfalse;
8855         return val;
8856 }
8857
8858 /*
8859  *  call-seq:
8860  *     show_unitcell
8861  *     show_unitcell(bool)
8862  *     show_unitcell = bool
8863  *
8864  *  Set the flag whether to show the unit cell. If no argument is given, the
8865  *  current flag is returned.
8866  */
8867 static VALUE
8868 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8869 {
8870     Molecule *mol;
8871     Data_Get_Struct(self, Molecule, mol);
8872         if (mol->mview == NULL)
8873                 return Qnil;
8874         if (argc > 0) {
8875                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8876                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8877         }
8878         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8879 }
8880
8881 /*
8882  *  call-seq:
8883  *     show_hydrogens
8884  *     show_hydrogens(bool)
8885  *     show_hydrogens = bool
8886  *
8887  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8888  *  current flag is returned.
8889  */
8890 static VALUE
8891 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8892 {
8893     Molecule *mol;
8894     Data_Get_Struct(self, Molecule, mol);
8895         if (mol->mview == NULL)
8896                 return Qnil;
8897         if (argc > 0) {
8898                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8899                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8900         }
8901         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8902 }
8903
8904 /*
8905  *  call-seq:
8906  *     show_dummy_atoms
8907  *     show_dummy_atoms(bool)
8908  *     show_dummy_atoms = bool
8909  *
8910  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8911  *  current flag is returned.
8912  */
8913 static VALUE
8914 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8915 {
8916     Molecule *mol;
8917     Data_Get_Struct(self, Molecule, mol);
8918         if (mol->mview == NULL)
8919                 return Qnil;
8920         if (argc > 0) {
8921                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8922                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8923         }
8924         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8925 }
8926
8927 /*
8928  *  call-seq:
8929  *     show_expanded
8930  *     show_expanded(bool)
8931  *     show_expanded = bool
8932  *
8933  *  Set the flag whether to show the expanded atoms. If no argument is given, the
8934  *  current flag is returned.
8935  */
8936 static VALUE
8937 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8938 {
8939     Molecule *mol;
8940     Data_Get_Struct(self, Molecule, mol);
8941         if (mol->mview == NULL)
8942                 return Qnil;
8943         if (argc > 0) {
8944                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8945                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8946         }
8947         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8948 }
8949
8950 /*
8951  *  call-seq:
8952  *     show_ellipsoids
8953  *     show_ellipsoids(bool)
8954  *     show_ellipsoids = bool
8955  *
8956  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8957  *  current flag is returned.
8958  */
8959 static VALUE
8960 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8961 {
8962     Molecule *mol;
8963     Data_Get_Struct(self, Molecule, mol);
8964         if (mol->mview == NULL)
8965                 return Qnil;
8966         if (argc > 0) {
8967                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8968                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8969         }
8970         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8971 }
8972
8973 /*
8974  *  call-seq:
8975  *     is_atom_visible(index)  -> Boolean
8976  *
8977  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8978  *  as well as the molecule attributes (showHydrogens, etc.)
8979  */
8980 static VALUE
8981 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8982 {
8983         Molecule *mol;
8984         Int idx;
8985         Atom *ap;
8986     Data_Get_Struct(self, Molecule, mol);
8987         idx = s_Molecule_AtomIndexFromValue(mol, ival);
8988         if (idx < 0 || idx >= mol->natoms)
8989                 return Qnil;
8990         ap = ATOM_AT_INDEX(mol->atoms, idx);
8991         if (mol->mview != NULL) {
8992                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8993                         return Qfalse;
8994                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8995                         return Qfalse;
8996                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8997                         return Qfalse;
8998         }
8999         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9000 }
9001
9002 /*
9003  *  call-seq:
9004  *     show_graphite -> Integer
9005  *     show_graphite = Integer
9006  *     show_graphite = boolean
9007  *
9008  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9009  *  number of rings to display for each direction.
9010  *  If the argument is boolean, only the show/hide flag is set.
9011  */
9012 static VALUE
9013 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9014 {
9015     Molecule *mol;
9016     Data_Get_Struct(self, Molecule, mol);
9017         if (mol->mview == NULL)
9018                 return Qnil;
9019         if (argc > 0) {
9020                 if (argv[0] == Qnil || argv[0] == Qfalse)
9021                         mol->mview->showGraphiteFlag = 0;
9022                 else if (argv[0] == Qtrue)
9023                         mol->mview->showGraphiteFlag = 1;
9024                 else {
9025                         int n = NUM2INT(rb_Integer(argv[0]));
9026                         if (n < 0)
9027                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9028                         mol->mview->showGraphite = n;
9029                 }
9030                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9031         }
9032         return INT2NUM(mol->mview->showGraphite);
9033 }
9034
9035 /*
9036  *  call-seq:
9037  *     show_graphite? -> boolean
9038  *
9039  *  Return whether the graphite is set visible or not.
9040 */
9041 static VALUE
9042 s_Molecule_ShowGraphiteFlag(VALUE self)
9043 {
9044     Molecule *mol;
9045     Data_Get_Struct(self, Molecule, mol);
9046         if (mol->mview == NULL)
9047                 return Qnil;
9048         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9049 }
9050         
9051 /*
9052  *  call-seq:
9053  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9054  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9055  *     show_periodic_image = boolean
9056  *
9057  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9058  *  set but no visual effects are observed.
9059  *  If the argument is boolean, only the show/hide flag is modified.
9060  */
9061 static VALUE
9062 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9063 {
9064     Molecule *mol;
9065         VALUE val;
9066         int ival[6];
9067         int i;
9068     Data_Get_Struct(self, Molecule, mol);
9069         if (mol->mview == NULL)
9070                 return Qnil;
9071         rb_scan_args(argc, argv, "01", &val);
9072         if (argc > 0) {
9073                 /*  Change current settings  */
9074                 if (val == Qnil || val == Qfalse)
9075                         mol->mview->showPeriodicImageFlag = 0;
9076                 else if (val == Qtrue)
9077                         mol->mview->showPeriodicImageFlag = 1;
9078                 else {
9079                         val = rb_ary_to_ary(val);
9080                         for (i = 0; i < 6; i++) {
9081                                 if (i < RARRAY_LEN(val))
9082                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9083                         }
9084                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9085                                 rb_raise(rb_eMolbyError, "bad arguments");
9086                         for (i = 0; i < 6; i++)
9087                                 mol->mview->showPeriodicImage[i] = ival[i];
9088                 }
9089                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9090         }
9091         val = rb_ary_new();
9092         for (i = 0; i < 6; i++)
9093                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9094         return val;
9095 }
9096
9097 /*
9098  *  call-seq:
9099  *     show_periodic_image? -> boolean
9100  *
9101  *  Return whether the periodic images are set to visible or not. This flag is
9102  *  independent from the show_periodic_image settings.
9103  */
9104 static VALUE
9105 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9106 {
9107     Molecule *mol;
9108     Data_Get_Struct(self, Molecule, mol);
9109         if (mol->mview == NULL)
9110                 return Qnil;
9111         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9112 }
9113
9114 /*
9115  *  call-seq:
9116  *     line_mode
9117  *     line_mode(bool)
9118  *     line_mode = bool
9119  *
9120  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9121  *  current flag is returned.
9122  */
9123 static VALUE
9124 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9125 {
9126     Molecule *mol;
9127     Data_Get_Struct(self, Molecule, mol);
9128         if (mol->mview == NULL)
9129                 return Qnil;
9130         if (argc > 0) {
9131                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9132                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9133         }
9134         return (mol->mview->lineMode ? Qtrue : Qfalse);
9135 }
9136
9137 /*
9138  *  call-seq:
9139  *     resize_to_fit
9140  *
9141  *  Resize the model drawing to fit in the window.
9142  */
9143 static VALUE
9144 s_Molecule_ResizeToFit(VALUE self)
9145 {
9146     Molecule *mol;
9147     Data_Get_Struct(self, Molecule, mol);
9148         if (mol->mview != NULL)
9149                 MainView_resizeToFit(mol->mview);
9150         return self;    
9151 }
9152
9153 /*
9154  *  call-seq:
9155  *     get_view_rotation -> [[ax, ay, az], angle]
9156  *
9157  *  Get the current rotation for the view. Angle is in degree, not radian.
9158  */
9159 static VALUE
9160 s_Molecule_GetViewRotation(VALUE self)
9161 {
9162     Molecule *mol;
9163         float f[4];
9164         Vector v;
9165     Data_Get_Struct(self, Molecule, mol);
9166         if (mol->mview == NULL)
9167                 return Qnil;
9168         TrackballGetRotate(mol->mview->track, f);
9169         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9170         v.x = f[1];
9171         v.y = f[2];
9172         v.z = f[3];
9173         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9174 }
9175
9176 /*
9177  *  call-seq:
9178  *     get_view_scale -> float
9179  *
9180  *  Get the current scale for the view.
9181  */
9182 static VALUE
9183 s_Molecule_GetViewScale(VALUE self)
9184 {
9185     Molecule *mol;
9186     Data_Get_Struct(self, Molecule, mol);
9187         if (mol->mview == NULL)
9188                 return Qnil;
9189         return rb_float_new(TrackballGetScale(mol->mview->track));
9190 }
9191
9192 /*
9193  *  call-seq:
9194  *     get_view_center -> Vector
9195  *
9196  *  Get the current center point of the view.
9197  */
9198 static VALUE
9199 s_Molecule_GetViewCenter(VALUE self)
9200 {
9201     Molecule *mol;
9202         float f[4];
9203         Vector v;
9204     Data_Get_Struct(self, Molecule, mol);
9205         if (mol->mview == NULL)
9206                 return Qnil;
9207         TrackballGetTranslate(mol->mview->track, f);
9208         v.x = -f[0] * mol->mview->dimension;
9209         v.y = -f[1] * mol->mview->dimension;
9210         v.z = -f[2] * mol->mview->dimension;
9211         return ValueFromVector(&v);
9212 }
9213
9214 /*
9215  *  call-seq:
9216  *     set_view_rotation([ax, ay, az], angle) -> self
9217  *
9218  *  Set the current rotation for the view. Angle is in degree, not radian.
9219  */
9220 static VALUE
9221 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9222 {
9223     Molecule *mol;
9224         float f[4];
9225         Vector v;
9226     Data_Get_Struct(self, Molecule, mol);
9227         if (mol->mview == NULL)
9228                 return Qnil;
9229         VectorFromValue(aval, &v);
9230         if (NormalizeVec(&v, &v))
9231                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9232         f[1] = v.x;
9233         f[2] = v.y;
9234         f[3] = v.z;
9235         f[0] = -NUM2DBL(rb_Float(angval));
9236         TrackballSetRotate(mol->mview->track, f);
9237         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9238         return self;
9239 }
9240
9241 /*
9242  *  call-seq:
9243  *     set_view_scale(scale) -> self
9244  *
9245  *  Set the current scale for the view.
9246  */
9247 static VALUE
9248 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9249 {
9250     Molecule *mol;
9251     Data_Get_Struct(self, Molecule, mol);
9252         if (mol->mview == NULL)
9253                 return Qnil;
9254         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9255         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9256         return self;
9257 }
9258
9259 /*
9260  *  call-seq:
9261  *     set_view_center(vec) -> self
9262  *
9263  *  Set the current center point of the view.
9264  */
9265 static VALUE
9266 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9267 {
9268     Molecule *mol;
9269         Vector v;
9270         float f[4];
9271     Data_Get_Struct(self, Molecule, mol);
9272         if (mol->mview == NULL)
9273                 return Qnil;
9274         VectorFromValue(aval, &v);
9275         f[0] = -v.x / mol->mview->dimension;
9276         f[1] = -v.y / mol->mview->dimension;
9277         f[2] = -v.z / mol->mview->dimension;
9278         TrackballSetTranslate(mol->mview->track, f);
9279         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9280         return self;
9281 }
9282
9283 /*
9284  *  call-seq:
9285  *     set_background_color(red, green, blue)
9286  *
9287  *  Set the background color of the model window.
9288  */
9289 static VALUE
9290 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9291 {
9292     Molecule *mol;
9293     Data_Get_Struct(self, Molecule, mol);
9294         if (mol->mview != NULL) {
9295                 VALUE rval, gval, bval;
9296                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9297                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9298         }
9299         return self;    
9300 }
9301
9302 /*
9303  *  call-seq:
9304  *     create_graphic(kind, color, points, fill = nil) -> integer
9305  *
9306  *  Create a new graphic object.
9307  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9308  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9309  *   points: an array of Vectors
9310  *   
9311  */
9312 static VALUE
9313 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9314 {
9315     Molecule *mol;
9316         MainViewGraphic g;
9317         int i, n, ni;
9318         const char *p;
9319         VALUE kval, cval, pval, fval;
9320     Data_Get_Struct(self, Molecule, mol);
9321         if (mol->mview == NULL)
9322                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9323         rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
9324         kval = rb_obj_as_string(kval);
9325         memset(&g, 0, sizeof(g));
9326         g.visible = 1;
9327         p = RSTRING_PTR(kval);
9328         if (strcmp(p, "line") == 0)
9329                 g.kind = kMainViewGraphicLine;
9330         else if (strcmp(p, "poly") == 0)
9331                 g.kind = kMainViewGraphicPoly;
9332         else if (strcmp(p, "cylinder") == 0)
9333                 g.kind = kMainViewGraphicCylinder;
9334         else if (strcmp(p, "cone") == 0)
9335                 g.kind = kMainViewGraphicCone;
9336         else if (strcmp(p, "ellipsoid") == 0)
9337                 g.kind = kMainViewGraphicEllipsoid;
9338         else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9339         g.closed = (RTEST(fval) ? 1 : 0);
9340         cval = rb_ary_to_ary(cval);
9341         n = RARRAY_LEN(cval);
9342         if (n < 3 || n >= 5)
9343                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9344         if (n == 3)
9345                 g.rgba[3] = 1.0;
9346         for (i = 0; i < n; i++)
9347                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9348         pval = rb_ary_to_ary(pval);
9349         n = RARRAY_LEN(pval);
9350         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9351         if (n <= 0)
9352                 rb_raise(rb_eArgError, "no control points are given");
9353         switch (g.kind) {
9354                 case kMainViewGraphicLine:
9355                         if (n < 2)
9356                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9357                         break;
9358                 case kMainViewGraphicPoly:
9359                         if (n < 3)
9360                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9361                         break;
9362                 case kMainViewGraphicCylinder:
9363                 case kMainViewGraphicCone:
9364                         if (n != 3)
9365                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9366                         ni = 2;
9367                         break;
9368                 case kMainViewGraphicEllipsoid:
9369                         if (n == 2) {
9370                                 ni = 1;
9371                         } else if (n != 4)
9372                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9373                         break;
9374         }
9375         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9376         for (i = 0; i < n; i++) {
9377                 Vector v;
9378                 if (i == ni) {
9379                         v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
9380                         v.y = v.z = 0;
9381                 } else {
9382                         VectorFromValue(RARRAY_PTR(pval)[i], &v);
9383                 }
9384                 g.points[i * 3] = v.x;
9385                 g.points[i * 3 + 1] = v.y;
9386                 g.points[i * 3 + 2] = v.z;
9387         }
9388         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9389                 /*  Sphere  */
9390                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9391                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9392                 g.points[7] = g.points[11] = g.points[3];
9393         }
9394         MainView_insertGraphic(mol->mview, -1, &g);
9395         return INT2NUM(mol->mview->ngraphics - 1);      
9396 }
9397
9398 /*
9399  *  call-seq:
9400  *     remove_graphic(index) -> integer
9401  *
9402  *  Remove a graphic object.
9403  */
9404 static VALUE
9405 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9406 {
9407     Molecule *mol;
9408         int i;
9409     Data_Get_Struct(self, Molecule, mol);
9410         if (mol->mview == NULL)
9411                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9412         i = NUM2INT(rb_Integer(ival));
9413         if (i < 0 || i >= mol->mview->ngraphics)
9414                 rb_raise(rb_eArgError, "graphic index is out of range");
9415         MainView_removeGraphic(mol->mview, i);
9416         return ival;
9417 }
9418
9419 /*
9420  *  call-seq:
9421  *     ngraphics -> integer
9422  *
9423  *  Get the number of graphic objects.
9424  */
9425 static VALUE
9426 s_Molecule_NGraphics(VALUE self)
9427 {
9428     Molecule *mol;
9429     Data_Get_Struct(self, Molecule, mol);
9430         if (mol->mview == NULL)
9431                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9432         return INT2NUM(mol->mview->ngraphics);
9433 }
9434         
9435 /*
9436  *  call-seq:
9437  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9438  *
9439  *  Change the point_index-th control point of graphic_index-th graphic object
9440  *   
9441  */
9442 static VALUE
9443 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
9444 {
9445         MainViewGraphic *gp;
9446     Molecule *mol;
9447         int index;
9448         Vector v;
9449     Data_Get_Struct(self, Molecule, mol);
9450         if (mol->mview == NULL)
9451                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9452         index = NUM2INT(rb_Integer(gval));
9453         if (index < 0 || index >= mol->mview->ngraphics)
9454                 rb_raise(rb_eArgError, "the graphic index is out of range");
9455         gp = mol->mview->graphics + index;
9456         index = NUM2INT(rb_Integer(pval));
9457         if (index < 0 || index >= gp->npoints)
9458                 rb_raise(rb_eArgError, "the point index is out of range");
9459         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9460                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
9461                         v.x = NUM2DBL(rb_Float(nval));
9462                         v.y = v.z = 0;
9463                 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
9464                         gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
9465                         gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9466                         return nval;
9467                 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9468         } else {
9469                 if (nval == Qnil) {
9470                         v.x = kInvalidFloat;
9471                         v.y = v.z = 0.0;
9472                 } else VectorFromValue(nval, &v);
9473         }
9474         gp->points[index * 3] = v.x;
9475         gp->points[index * 3 + 1] = v.y;
9476         gp->points[index * 3 + 2] = v.z;
9477         MoleculeCallback_notifyModification(mol, 0);
9478         return nval;
9479 }
9480
9481 /*
9482  *  call-seq:
9483  *     set_graphic_color(graphic_index, new_value) -> new_value
9484  *
9485  *  Change the color of graphic_index-th graphic object
9486  *   
9487  */
9488 static VALUE
9489 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9490 {
9491         MainViewGraphic *gp;
9492     Molecule *mol;
9493         int index, n;
9494     Data_Get_Struct(self, Molecule, mol);
9495         if (mol->mview == NULL)
9496                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9497         index = NUM2INT(rb_Integer(gval));
9498         if (index < 0 || index >= mol->mview->ngraphics)
9499                 rb_raise(rb_eArgError, "the graphic index is out of range");
9500         gp = mol->mview->graphics + index;
9501         cval = rb_ary_to_ary(cval);
9502         n = RARRAY_LEN(cval);
9503         if (n != 3 && n != 4)
9504                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9505         for (index = 0; index < n; index++) {
9506                 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
9507         }
9508         if (n == 3)
9509                 gp->rgba[3] = 1.0;
9510         MoleculeCallback_notifyModification(mol, 0);
9511         return cval;
9512 }
9513
9514 /*
9515  *  call-seq:
9516  *     show_graphic(graphic_index) -> self
9517  *
9518  *  Enable the visible flag of the graphic_index-th graphic object
9519  *   
9520  */
9521 static VALUE
9522 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9523 {
9524         MainViewGraphic *gp;
9525     Molecule *mol;
9526         int index;
9527     Data_Get_Struct(self, Molecule, mol);
9528         if (mol->mview == NULL)
9529                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9530         index = NUM2INT(rb_Integer(gval));
9531         if (index < 0 || index >= mol->mview->ngraphics)
9532                 rb_raise(rb_eArgError, "the graphic index is out of range");
9533         gp = mol->mview->graphics + index;
9534         gp->visible = 1;
9535         MoleculeCallback_notifyModification(mol, 0);
9536         return self;
9537 }
9538
9539 /*
9540  *  call-seq:
9541  *     hide_graphic(graphic_index) -> self
9542  *
9543  *  Disable the visible flag of the graphic_index-th graphic object
9544  *   
9545  */
9546 static VALUE
9547 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9548 {
9549         MainViewGraphic *gp;
9550     Molecule *mol;
9551         int index;
9552     Data_Get_Struct(self, Molecule, mol);
9553         if (mol->mview == NULL)
9554                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9555         index = NUM2INT(rb_Integer(gval));
9556         if (index < 0 || index >= mol->mview->ngraphics)
9557                 rb_raise(rb_eArgError, "the graphic index is out of range");
9558         gp = mol->mview->graphics + index;
9559         gp->visible = 0;
9560         MoleculeCallback_notifyModification(mol, 0);
9561         return self;
9562 }
9563
9564 /*
9565  *  call-seq:
9566  *     show_text(string)
9567  *
9568  *  Show the string in the info text box.
9569  */
9570 static VALUE
9571 s_Molecule_ShowText(VALUE self, VALUE arg)
9572 {
9573     Molecule *mol;
9574     Data_Get_Struct(self, Molecule, mol);
9575         if (mol->mview != NULL)
9576                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9577         return Qnil;
9578 }
9579
9580 /*
9581  *  call-seq:
9582  *     md_arena -> MDArena
9583  *
9584  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
9585  *  this molecule, a new arena is created.
9586  */
9587 static VALUE
9588 s_Molecule_MDArena(VALUE self)
9589 {
9590     Molecule *mol;
9591         VALUE retval;
9592     Data_Get_Struct(self, Molecule, mol);
9593         if (mol->arena == NULL)
9594                 md_arena_new(mol);
9595         retval = ValueFromMDArena(mol->arena);
9596         return retval;
9597 }
9598
9599 /*
9600  *  call-seq:
9601  *     set_parameter_attr(type, index, key, value, src) -> value
9602  *
9603  *  This method is used only internally.
9604  */
9605 static VALUE
9606 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9607 {
9608         /*  This method is called from MolAction to change a MM parameter attribute.  */
9609     Molecule *mol;
9610         VALUE pval;
9611         ParameterRef *pref;
9612         UnionPar *up;
9613     Data_Get_Struct(self, Molecule, mol);
9614         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
9615         vval = s_ParameterRef_SetAttr(pval, kval, vval);
9616         
9617         /*  This is the special part of this method; it allows modification of the src field. */
9618         /*  (ParameterRef#set_attr sets 0 to the src field)  */
9619         Data_Get_Struct(pval, ParameterRef, pref);
9620         up = ParameterRefGetPar(pref);
9621         up->bond.src = FIX2INT(sval);
9622         
9623         return vval;
9624 }
9625
9626 /*
9627  *  call-seq:
9628  *     parameter -> Parameter
9629  *
9630  *  Get the local parameter of this molecule. If not defined, returns nil.
9631  */
9632 static VALUE
9633 s_Molecule_Parameter(VALUE self)
9634 {
9635     Molecule *mol;
9636     Data_Get_Struct(self, Molecule, mol);
9637 /*      if (mol->par == NULL)
9638                 return Qnil; */
9639         return s_NewParameterValueFromValue(self);
9640 }
9641
9642 /*
9643  *  call-seq:
9644  *     selectedMO -> IntGroup
9645  *
9646  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
9647  *  is not selected, returns nil. If the MO info table is selected but no MOs 
9648  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
9649  */
9650 static VALUE
9651 s_Molecule_SelectedMO(VALUE self)
9652 {
9653     Molecule *mol;
9654         IntGroup *ig;
9655         VALUE val;
9656     Data_Get_Struct(self, Molecule, mol);
9657         if (mol->mview == NULL)
9658                 return Qnil;
9659         ig = MainView_selectedMO(mol->mview);
9660         if (ig == NULL)
9661                 return Qnil;
9662         IntGroupOffset(ig, 1);
9663         val = ValueFromIntGroup(ig);
9664         IntGroupRelease(ig);
9665         return val;
9666 }
9667
9668 /*
9669  *  call-seq:
9670  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
9671  *
9672  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
9673  *  If the molecule does not contain a basis set information, then returns nil.
9674  */
9675 static VALUE
9676 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
9677 {
9678     Molecule *mol;
9679         Vector o, dx, dy, dz;
9680         Int nx, ny, nz;
9681         VALUE nval;
9682         Int npoints = 80 * 80 * 80;
9683     Data_Get_Struct(self, Molecule, mol);
9684         if (mol->bset == NULL)
9685                 return Qnil;
9686         rb_scan_args(argc, argv, "01", &nval);
9687         if (nval != Qnil)
9688                 npoints = NUM2INT(rb_Integer(nval));
9689         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9690                 return Qnil;
9691         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));
9692 }
9693
9694 static int
9695 s_Cubegen_callback(double progress, void *ref)
9696 {
9697         MyAppCallback_setProgressValue(progress);
9698         if (MyAppCallback_checkInterrupt())
9699                 return 1;
9700         else return 0;
9701 }
9702
9703 /*
9704  *  call-seq:
9705  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
9706  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
9707  *
9708  *  Calculate the molecular orbital with number mo and create a 'cube' file.
9709  *  In the first form, the cube size is estimated from the atomic coordinates. In the
9710  *  second form, the cube dimension is explicitly given.
9711  *  Returns fname when successful, nil otherwise.
9712  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
9713  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
9714  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
9715  */
9716 static VALUE
9717 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
9718 {
9719         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
9720     Molecule *mol;
9721         Int mono, nx, ny, nz, npoints;
9722         Vector o, dx, dy, dz;
9723         int index, n;
9724         char buf[1024];
9725     Data_Get_Struct(self, Molecule, mol);
9726         if (mol->bset == NULL)
9727                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
9728         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
9729         
9730         /*  Set up parameters  */
9731         mono = NUM2INT(rb_Integer(mval));
9732         if (mono <= 0 || mono > mol->bset->ncomps)
9733                 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
9734         if (RTEST(bval)) {
9735                 if (mol->bset->rflag != 0)
9736                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
9737                 mono += mol->bset->ncomps;
9738         }
9739                 
9740         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
9741                 /*  Automatic grid formation  */
9742                 if (oval != Qnil)
9743                         npoints = NUM2INT(rb_Integer(oval));
9744                 else npoints = 0;
9745                 if (npoints == 0)
9746                         npoints = 1000000;
9747                 else if (npoints < 8)
9748                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
9749                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9750                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
9751                 ival = dxval;
9752                 bval = dyval;
9753         } else {
9754                 VectorFromValue(oval, &o);
9755                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
9756                         VectorFromValue(dxval, &dx);
9757                 else {
9758                         dx.x = NUM2DBL(rb_Float(dxval));
9759                         dx.y = dx.z = 0.0;
9760                 }
9761                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
9762                         VectorFromValue(dyval, &dy);
9763                 else {
9764                         dy.y = NUM2DBL(rb_Float(dyval));
9765                         dy.x = dy.z = 0.0;
9766                 }
9767                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
9768                         VectorFromValue(dzval, &dz);
9769                 else {
9770                         dz.z = NUM2DBL(rb_Float(dzval));
9771                         dz.x = dz.y = 0.0;
9772                 }
9773                 nx = NUM2INT(rb_Integer(nxval));
9774                 ny = NUM2INT(rb_Integer(nyval));
9775                 nz = NUM2INT(rb_Integer(nzval));
9776                 if (nx <= 0 || ny <= 0 || nz <= 0)
9777                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
9778                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
9779                         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);
9780         }
9781         
9782         /*  Calc MO  */
9783         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
9784         if (index == -2)
9785                 rb_interrupt();
9786         else if (index < 0)
9787                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
9788         
9789         /*  Output to file  */
9790         MoleculeCallback_displayName(mol, buf, sizeof buf);
9791         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
9792         if (n != 0)
9793                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
9794         
9795         /*  Discard the cube  */
9796         MoleculeClearCubeAtIndex(mol, index);
9797         return fval;
9798 }
9799
9800 /*
9801  *  call-seq:
9802  *     nelpots
9803  *
9804  *  Get the number of electrostatic potential info.
9805  */
9806 static VALUE
9807 s_Molecule_NElpots(VALUE self)
9808 {
9809         Molecule *mol;
9810     Data_Get_Struct(self, Molecule, mol);
9811         return INT2NUM(mol->nelpots);
9812 }
9813
9814 /*
9815  *  call-seq:
9816  *     elpot(idx)
9817  *
9818  *  Get the electrostatic potential info at the given index. If present, then the
9819  *  return value is [Vector, Float] (position and potential). If not present, then
9820  *  returns nil.
9821  */
9822 static VALUE
9823 s_Molecule_Elpot(VALUE self, VALUE ival)
9824 {
9825         Molecule *mol;
9826         int idx;
9827     Data_Get_Struct(self, Molecule, mol);
9828         idx = NUM2INT(rb_Integer(ival));
9829         if (idx < 0 || idx >= mol->nelpots)
9830                 return Qnil;
9831         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
9832 }
9833
9834 /*
9835  *  call-seq:
9836  *     add_gaussian_orbital_shell(sym, nprims, atom_index)
9837  *
9838  *  To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
9839  *  and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
9840  *  -2, D5-type.
9841  */
9842 static VALUE
9843 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
9844 {
9845         Molecule *mol;
9846         int sym, nprims, a_idx, n;
9847     Data_Get_Struct(self, Molecule, mol);
9848         sym = NUM2INT(rb_Integer(symval));
9849         nprims = NUM2INT(rb_Integer(npval));
9850         a_idx = NUM2INT(rb_Integer(aval));
9851         n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
9852         if (n == -1)
9853                 rb_raise(rb_eMolbyError, "Molecule is emptry");
9854         else if (n == -2)
9855                 rb_raise(rb_eMolbyError, "Low memory");
9856         else if (n == -3)
9857                 rb_raise(rb_eMolbyError, "Unknown orbital type");
9858         else if (n != 0)
9859                 rb_raise(rb_eMolbyError, "Unknown error");
9860         return self;
9861 }
9862
9863 /*
9864  *  call-seq:
9865  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
9866  *
9867  *  To be used internally. Add a gaussian primitive coefficients.
9868  */
9869 static VALUE
9870 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
9871 {
9872         Molecule *mol;
9873         Int n;
9874         Double exponent, contraction, contraction_sp;
9875     Data_Get_Struct(self, Molecule, mol);
9876         exponent = NUM2DBL(rb_Float(expval));
9877         contraction = NUM2DBL(rb_Float(cval));
9878         contraction_sp = NUM2DBL(rb_Float(cspval));
9879         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
9880         if (n == -1)
9881                 rb_raise(rb_eMolbyError, "Molecule is emptry");
9882         else if (n == -2)
9883                 rb_raise(rb_eMolbyError, "Low memory");
9884         else if (n != 0)
9885                 rb_raise(rb_eMolbyError, "Unknown error");
9886         return self;
9887 }
9888
9889 /*
9890  *  call-seq:
9891  *     mo_type
9892  *
9893  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
9894  */
9895 static VALUE
9896 s_Molecule_MOType(VALUE self)
9897 {
9898         Molecule *mol;
9899     Data_Get_Struct(self, Molecule, mol);
9900         if (mol != NULL && mol->bset != NULL) {
9901                 const char *s;
9902                 int rflag = mol->bset->rflag;
9903                 if (rflag == 0)
9904                         s = "UHF";
9905                 else if (rflag == 2)
9906                         s = "ROHF";
9907                 else s = "RHF";
9908                 return rb_str_new2(s);
9909         } else return Qnil;
9910 }
9911
9912 /*
9913  *  call-seq:
9914  *     set_mo_coefficients(idx, energy, coefficients)
9915  *
9916  *  To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system, 
9917  *  beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
9918  *  of MO coefficients.
9919  */
9920 static VALUE
9921 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
9922 {
9923         Molecule *mol;
9924         Int idx, ncomps, i;
9925         Double energy;
9926         Double *coeffs;
9927     Data_Get_Struct(self, Molecule, mol);
9928         idx = NUM2INT(rb_Integer(ival));
9929         energy = NUM2DBL(rb_Float(eval));
9930         aval = rb_ary_to_ary(aval);
9931         ncomps = RARRAY_LEN(aval);
9932         coeffs = (Double *)calloc(sizeof(Double), ncomps);
9933         if (coeffs == NULL) {
9934                 i = -2;
9935                 goto end;
9936         }
9937         for (i = 0; i < ncomps; i++)
9938                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
9939         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
9940 end:
9941         if (i == -1)
9942                 rb_raise(rb_eMolbyError, "Molecule is emptry");
9943         else if (i == -2)
9944                 rb_raise(rb_eMolbyError, "Low memory");
9945         else if (i == -3)
9946                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
9947         else if (i == -4)
9948                 rb_raise(rb_eMolbyError, "Bad MO index");
9949         else if (i == -5)
9950                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
9951         else if (i != 0)
9952                 rb_raise(rb_eMolbyError, "Unknown error");
9953         return self;
9954 }
9955
9956 /*
9957  *  call-seq:
9958  *     allocate_basis_set_record(rflag, ne_alpha, ne_beta)
9959  *
9960  *  To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
9961  *  ne_alpha, ne_beta: number of alpha/beta electrons.
9962  */
9963 static VALUE
9964 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
9965 {
9966         Molecule *mol;
9967         Int rflag, na, nb, n;
9968     Data_Get_Struct(self, Molecule, mol);
9969         rflag = NUM2INT(rb_Integer(rval));
9970         na = NUM2INT(rb_Integer(naval));
9971         nb = NUM2INT(rb_Integer(nbval));
9972         n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
9973         if (n == -1)
9974                 rb_raise(rb_eMolbyError, "Molecule is emptry");
9975         else if (n == -2)
9976                 rb_raise(rb_eMolbyError, "Low memory");
9977         else if (n != 0)
9978                 rb_raise(rb_eMolbyError, "Unknown error");
9979         return self;
9980 }
9981
9982 /*
9983  *  call-seq:
9984  *     search_equivalent_atoms(ig = nil)
9985  *
9986  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
9987  */
9988 static VALUE
9989 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
9990 {
9991         Molecule *mol;
9992         Int *result, i;
9993         VALUE val;
9994         IntGroup *ig;
9995     Data_Get_Struct(self, Molecule, mol);
9996         if (mol->natoms == 0)
9997                 return Qnil;
9998         rb_scan_args(argc, argv, "01", &val);
9999         if (val != Qnil)
10000                 ig = IntGroupFromValue(val);
10001         else ig = NULL;
10002         result = MoleculeSearchEquivalentAtoms(mol, ig);
10003         if (result == NULL)
10004                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10005         if (ig != NULL)
10006                 IntGroupRelease(ig);
10007         val = rb_ary_new2(mol->natoms);
10008         for (i = 0; i < mol->natoms; i++)
10009                 rb_ary_push(val, INT2NUM(result[i]));
10010         free(result);
10011         return val;
10012 }
10013
10014 /*
10015  *  call-seq:
10016  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10017  *
10018  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10019  *  Name is the name of the new pi anchor, and group is the atoms that define
10020  *  the pi system. Type (a String) is an atom type for MM implementation.
10021  *  Weights represent the relative significance of the component atoms; if omitted, then
10022  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
10023  *  The weight values will be normalized so that the sum of the weights is 1.0.
10024  *  The weight values must be positive.
10025  *  Index is the atom index where the created pi-anchor is inserted in the 
10026  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
10027  *  having the largest index.
10028  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
10029  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10030  */
10031 static VALUE
10032 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10033 {
10034         Molecule *mol;
10035         VALUE nval, gval;
10036         IntGroup *ig;
10037         Int i, n, idx, last_component;
10038         Atom a, *ap;
10039         PiAnchor an;
10040         AtomRef *aref;
10041         if (argc < 2 || argc >= 6)
10042                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10043         nval = *argv++;
10044         gval = *argv++;
10045         argc -= 2;
10046     Data_Get_Struct(self, Molecule, mol);
10047         ig = IntGroupFromValue(gval);
10048         memset(&a, 0, sizeof(a));
10049         memset(&an, 0, sizeof(an));
10050         strncpy(a.aname, StringValuePtr(nval), 4);
10051         if (a.aname[0] == '_')
10052                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10053         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
10054         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10055                 if (n >= mol->natoms) {
10056                         AtomConnectResize(&an.connect, 0);
10057                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10058                 }
10059                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10060                 last_component = n;
10061         }
10062         if (an.connect.count == 0)
10063                 rb_raise(rb_eMolbyError, "no atoms are specified");
10064         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10065         for (i = 0; i < an.connect.count; i++) {
10066                 an.coeffs[i] = 1.0 / an.connect.count;
10067         }
10068         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10069                 /*  Atom type  */
10070                 if (argv[0] != Qnil)
10071                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10072                 argc--;
10073                 argv++;
10074         }
10075         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10076                 if (argv[0] != Qnil) {
10077                         VALUE aval = rb_ary_to_ary(argv[0]);
10078                         Double d, sum;
10079                         if (RARRAY_LEN(aval) != an.connect.count)
10080                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10081                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10082                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10083                                 if (d <= 0.0)
10084                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
10085                                 sum += d;
10086                                 an.coeffs[i] = d;
10087                         }
10088                         for (i = 0; i < an.connect.count; i++)
10089                                 an.coeffs[i] /= sum;
10090                 }
10091                 argc--;
10092                 argv++;
10093         }
10094         if (argc > 0 && argv[0] != Qnil) {
10095                 /*  Index  */
10096                 idx = NUM2INT(rb_Integer(argv[0]));
10097         } else idx = -1;
10098         if (idx < 0 || idx > mol->natoms) {
10099                 /*  Immediately after the last specified atom  */
10100                 idx = last_component + 1;
10101         }
10102         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10103         memmove(a.anchor, &an, sizeof(PiAnchor));
10104         /*  Use residue information of the last specified atom  */
10105         ap = ATOM_AT_INDEX(mol->atoms, last_component);
10106         a.resSeq = ap->resSeq;
10107         strncpy(a.resName, ap->resName, 4);
10108         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10109                 return Qnil;
10110         MoleculeCalculatePiAnchorPosition(mol, idx);
10111     aref = AtomRefNew(mol, idx);
10112     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10113 }
10114
10115 /*
10116  *  call-seq:
10117  *     current       -> Molecule
10118  *
10119  *  Get the currently "active" molecule.
10120  */
10121 static VALUE
10122 s_Molecule_Current(VALUE klass)
10123 {
10124         return ValueFromMolecule(MoleculeCallback_currentMolecule());
10125 }
10126
10127 /*
10128  *  call-seq:
10129  *     Molecule[]          -> Molecule
10130  *     Molecule[n]         -> Molecule
10131  *     Molecule[name]      -> Molecule
10132  *     Molecule[name, k]   -> Molecule
10133  *     Molecule[regex]     -> Molecule
10134  *     Molecule[regex, k]  -> Molecule
10135  *
10136  *  Molecule[] is equivalent to Molecule.current.
10137  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
10138  *  Molecule[name] gives the first document (in the order of creation time) that has
10139  *  the given name. If a second argument (k) is given, the k-th document that has the
10140  *  given name is returned.
10141  *  Molecule[regex] gives the first document (in the order of creation time) that
10142  *  has a name matching the regular expression. If a second argument (k) is given, 
10143  *  the k-th document that has a name matching the re is returned.
10144  */
10145 static VALUE
10146 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
10147 {
10148         VALUE val, kval;
10149         int idx, k;
10150         Molecule *mol;
10151         char buf[1024];
10152         rb_scan_args(argc, argv, "02", &val, &kval);
10153         if (val == Qnil)
10154                 return s_Molecule_Current(klass);
10155         if (rb_obj_is_kind_of(val, rb_cInteger)) {
10156                 idx = NUM2INT(val);
10157                 mol = MoleculeCallback_moleculeAtIndex(idx);
10158         } else if (rb_obj_is_kind_of(val, rb_cString)) {
10159                 char *p = StringValuePtr(val);
10160                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10161                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10162                         MoleculeCallback_displayName(mol, buf, sizeof buf);
10163                         if (strcmp(buf, p) == 0 && --k == 0)
10164                                 break;
10165                 }
10166         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
10167                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10168                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10169                         VALUE name;
10170                         MoleculeCallback_displayName(mol, buf, sizeof buf);
10171                         name = rb_str_new2(buf);
10172                         if (rb_reg_match(val, name) != Qnil && --k == 0)
10173                                 break;
10174                 }       
10175         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
10176         
10177         if (mol == NULL)
10178                 return Qnil;
10179         else return ValueFromMolecule(mol);
10180 }
10181
10182 /*
10183  *  call-seq:
10184  *     list         -> array of Molecules
10185  *
10186  *  Get the list of molecules associated to the documents, in the order of creation
10187  *  time of the document. If no document is open, returns an empry array.
10188  */
10189 static VALUE
10190 s_Molecule_List(VALUE klass)
10191 {
10192         Molecule *mol;
10193         int i;
10194         VALUE ary;
10195         i = 0;
10196         ary = rb_ary_new();
10197         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
10198                 rb_ary_push(ary, ValueFromMolecule(mol));
10199                 i++;
10200         }
10201         return ary;
10202 }
10203
10204 /*
10205  *  call-seq:
10206  *     ordered_list         -> array of Molecules
10207  *
10208  *  Get the list of molecules associated to the documents, in the order of front-to-back
10209  *  ordering of the associated window. If no document is open, returns an empry array.
10210  */
10211 static VALUE
10212 s_Molecule_OrderedList(VALUE klass)
10213 {
10214         Molecule *mol;
10215         int i;
10216         VALUE ary;
10217         i = 0;
10218         ary = rb_ary_new();
10219         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
10220                 rb_ary_push(ary, ValueFromMolecule(mol));
10221                 i++;
10222         }
10223         return ary;
10224 }
10225
10226 /*
10227  *  call-seq:
10228  *     error_message       -> String
10229  *
10230  *  Get the error_message from the last load/save method. If no error, returns nil.
10231  */
10232 static VALUE
10233 s_Molecule_ErrorMessage(VALUE klass)
10234 {
10235         if (gLoadSaveErrorMessage == NULL)
10236                 return Qnil;
10237         else return rb_str_new2(gLoadSaveErrorMessage);
10238 }
10239
10240 /*
10241  *  call-seq:
10242  *     set_error_message(String)
10243  *     Molecule.error_message = String
10244  *
10245  *  Get the error_message from the last load/save method. If no error, returns nil.
10246  */
10247 static VALUE
10248 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
10249 {
10250         if (gLoadSaveErrorMessage != NULL) {
10251                 free(gLoadSaveErrorMessage);
10252                 gLoadSaveErrorMessage = NULL;
10253         }
10254         if (sval != Qnil) {
10255                 sval = rb_str_to_str(sval);
10256                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
10257         }
10258         return sval;
10259 }
10260
10261 /*
10262  *  call-seq:
10263  *     self == Molecule -> boolean
10264  *
10265  *  True if the two arguments point to the same molecule.
10266  */
10267 static VALUE
10268 s_Molecule_Equal(VALUE self, VALUE val)
10269 {
10270         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
10271                 Molecule *mol1, *mol2;
10272                 Data_Get_Struct(self, Molecule, mol1);
10273                 Data_Get_Struct(val, Molecule, mol2);
10274                 return (mol1 == mol2 ? Qtrue : Qfalse);
10275         } else return Qfalse;
10276 }
10277
10278 /*  The callback functions for call_subprocess_async  */
10279 static int
10280 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
10281 {
10282         int ruby_status;
10283         VALUE procval, retval, args[2];
10284         args[0] = ValueFromMolecule(mol);
10285         args[1] = INT2NUM(status);
10286         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
10287         if (procval != Qnil) {
10288                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10289                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10290                         return 1;
10291         }
10292         return 0;
10293 }
10294
10295 static int
10296 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
10297 {
10298         int ruby_status;
10299         VALUE procval, retval, args[2];
10300         args[0] = ValueFromMolecule(mol);
10301         args[1] = INT2NUM(tcount);
10302         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
10303         if (procval != Qnil) {
10304                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10305                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10306                         return 1;
10307         }
10308         return 0;
10309 }
10310
10311 /*
10312  *  call-seq:
10313  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
10314  *
10315  *  Call subprocess asynchronically.
10316  *  If end_callback is given, it will be called (with two arguments self and termination status)
10317  *  when the subprocess terminated.
10318  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
10319  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
10320  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
10321  *  filename begins with ">>", then the message will be appended to the file.
10322  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
10323  *  If the argument is nil, then the message will be sent to the Ruby console.
10324  *  Returns the process ID as an integer.
10325  */
10326 static VALUE
10327 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
10328 {
10329         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
10330         Molecule *mol;
10331         char *sout, *serr;
10332         int n;
10333         FILE *fpout, *fperr;
10334         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
10335         Data_Get_Struct(self, Molecule, mol);
10336
10337         if (stdout_val == Qnil) {
10338                 fpout = (FILE *)1;
10339         } else {
10340                 sout = StringValuePtr(stdout_val);
10341                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
10342                         fpout = NULL;
10343                 else {
10344                         if (strncmp(sout, ">>", 2) == 0) {
10345                                 sout += 2;
10346                                 fpout = fopen(sout, "a");
10347                         } else {
10348                                 if (*sout == '>')
10349                                         sout++;
10350                                 fpout = fopen(sout, "w");
10351                         }
10352                         if (fpout == NULL)
10353                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
10354                 }
10355         }
10356         if (stderr_val == Qnil) {
10357                 fperr = (FILE *)1;
10358         } else {
10359                 serr = StringValuePtr(stderr_val);
10360                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
10361                         fperr = NULL;
10362                 else {
10363                         if (strncmp(serr, ">>", 2) == 0) {
10364                                 serr += 2;
10365                                 fpout = fopen(serr, "a");
10366                         } else {
10367                                 if (*serr == '>')
10368                                         serr++;
10369                                 fperr = fopen(serr, "w");
10370                         }
10371                         if (fperr == NULL)
10372                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
10373                 }
10374         }
10375         
10376         /*  Register procs as instance variables  */
10377         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
10378         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
10379         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
10380         if (fpout != NULL && fpout != (FILE *)1)
10381                 fclose(fpout);
10382         if (fperr != NULL && fperr != (FILE *)1)
10383                 fclose(fperr);
10384         return INT2NUM(n);
10385 }
10386
10387 void
10388 Init_Molby(void)
10389 {
10390         int i;
10391         
10392         /*  Define module Molby  */
10393         rb_mMolby = rb_define_module("Molby");
10394         
10395         /*  Define Vector3D, Transform, IntGroup  */
10396         Init_MolbyTypes();
10397         
10398         /*  Define MDArena  */
10399         Init_MolbyMDTypes();
10400
10401         /*  class Molecule  */
10402         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
10403         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
10404     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
10405     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
10406     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
10407     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
10408     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
10409     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
10410     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
10411     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
10412     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
10413     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
10414     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
10415     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
10416     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
10417     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
10418         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
10419     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
10420     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
10421     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
10422     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
10423 /*    rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
10424     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
10425         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
10426     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
10427     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
10428     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
10429     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
10430     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
10431     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
10432     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
10433     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
10434     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
10435         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
10436         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
10437         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
10438         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
10439         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
10440
10441         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
10442         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
10443         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
10444         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
10445         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
10446
10447         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
10448         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
10449         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
10450         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
10451         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
10452         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
10453         
10454         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
10455         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
10456         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
10457         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
10458         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
10459         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
10460         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
10461         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
10462         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
10463         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
10464         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
10465         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
10466         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
10467         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
10468         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
10469         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
10470         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
10471         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
10472         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
10473         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
10474         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
10475         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
10476         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
10477         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
10478         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
10479     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
10480         rb_define_alias(rb_cMolecule, "+", "add");
10481     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
10482         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
10483         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
10484         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
10485         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
10486         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
10487         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
10488         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
10489         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
10490         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
10491         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
10492         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
10493         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
10494         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
10495         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
10496         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
10497         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
10498         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
10499         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
10500         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
10501         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
10502         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
10503         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
10504         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
10505         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
10506         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
10507         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
10508         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
10509         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
10510         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
10511         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
10512         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
10513         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
10514         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
10515         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
10516         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
10517         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
10518         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
10519         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
10520         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
10521         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
10522         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
10523         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
10524         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
10525         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
10526         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
10527         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
10528         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
10529         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
10530         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
10531         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
10532         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
10533         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
10534         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
10535         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
10536         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
10537         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
10538         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
10539         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
10540         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
10541         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
10542         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
10543         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
10544         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
10545         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
10546         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
10547         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
10548         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
10549         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
10550         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
10551         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
10552         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
10553         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
10554         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
10555         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
10556         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
10557         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
10558         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
10559         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
10560         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
10561         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
10562         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
10563         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
10564         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
10565         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
10566         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
10567         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
10568         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
10569         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
10570         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
10571         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
10572         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
10573         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
10574         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
10575         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
10576         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
10577         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
10578         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
10579         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
10580         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
10581         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
10582         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
10583         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
10584 #if 1 || !defined(__CMDMAC__)
10585         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
10586         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
10587         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
10588         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
10589         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
10590         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
10591         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
10592         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
10593         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
10594         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
10595         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
10596         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
10597         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
10598         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
10599 #endif
10600         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
10601         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
10602         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
10603         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
10604         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
10605         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
10606         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
10607         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
10608         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
10609         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
10610         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
10611         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
10612         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
10613         rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
10614         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
10615         
10616         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
10617         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
10618         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
10619
10620         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
10621         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
10622         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
10623         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
10624         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
10625         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
10626         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
10627         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
10628         
10629         /*  class MolEnumerable  */
10630         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
10631     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
10632         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
10633         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
10634     rb_define_alias(rb_cMolEnumerable, "size", "length");
10635         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
10636         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
10637
10638         /*  class AtomRef  */
10639         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
10640         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
10641                 char buf[64];
10642                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
10643                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
10644                 s_AtomAttrDefTable[i].id = rb_intern(buf);
10645                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
10646                 strcat(buf, "=");
10647                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
10648         }
10649         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
10650         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
10651         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
10652         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
10653         s_SetAtomAttrString = rb_str_new2("set_atom_attr");
10654         rb_global_variable(&s_SetAtomAttrString);
10655         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
10656         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
10657
10658         /*  class Parameter  */
10659         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
10660         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10661         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10662         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10663         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10664         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10665         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10666         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10667         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
10668         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10669         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10670         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10671         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10672         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10673         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10674         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10675         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10676         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10677         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10678         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10679         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10680         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10681         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10682         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10683         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10684         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10685         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
10686         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
10687         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10688         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10689         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10690         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10691         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10692         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10693         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10694         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
10695         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10696         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10697         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10698         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10699         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10700         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10701         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10702         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10703         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10704         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10705         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10706         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10707         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10708         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10709         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10710         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10711         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10712         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
10713
10714         /*  class ParEnumerable  */
10715         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
10716     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
10717         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
10718         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
10719         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
10720         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
10721         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
10722         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
10723         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
10724         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
10725         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
10726         
10727         /*  class ParameterRef  */
10728         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
10729         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
10730                 char buf[64];
10731                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
10732                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
10733                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
10734                 if (s_ParameterAttrDefTable[i].symref != NULL)
10735                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
10736                 if (s_ParameterAttrDefTable[i].setter != NULL) {
10737                         strcat(buf, "=");
10738                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
10739                 }
10740         }
10741         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
10742         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
10743         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
10744         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
10745         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
10746         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
10747         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
10748         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
10749
10750         /*  class MolbyError  */
10751         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
10752
10753         /*  module Kernel  */
10754         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
10755         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
10756         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
10757         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
10758         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
10759         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
10760         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
10761         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
10762         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
10763         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
10764         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
10765         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
10766         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
10767         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
10768         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
10769         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
10770         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
10771         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
10772         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
10773         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
10774         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
10775         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
10776         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
10777         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
10778
10779         s_ID_equal = rb_intern("==");
10780         g_RubyID_call = rb_intern("call");
10781 }
10782
10783 #pragma mark ====== External functions ======
10784
10785 static VALUE s_ruby_top_self = Qfalse;
10786
10787 static VALUE
10788 s_evalRubyScriptOnMoleculeSub(VALUE val)
10789 {
10790         void **ptr = (void **)val;
10791         Molecule *mol = (Molecule *)ptr[1];
10792         VALUE sval = rb_str_new2((char *)ptr[0]);
10793         VALUE fnval;
10794         if (s_ruby_top_self == Qfalse) {
10795                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
10796         }
10797         if (ptr[2] == NULL) {
10798                 fnval = Qnil;
10799         } else {
10800                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
10801         }
10802         if (mol == NULL) {
10803                 if (fnval == Qnil)
10804                         return rb_funcall(s_ruby_top_self, rb_intern("eval"), 1, sval);
10805                 else
10806                         return rb_funcall(s_ruby_top_self, rb_intern("eval"), 4, sval, Qnil, fnval, INT2FIX(1));
10807         } else {
10808                 VALUE mval = ValueFromMolecule(mol);
10809                 if (fnval == Qnil)
10810                         return rb_funcall(mval, rb_intern("instance_eval"), 1, sval);
10811                 else return rb_funcall(mval, rb_intern("instance_eval"), 3, sval, fnval, INT2FIX(1));
10812         }
10813 }
10814
10815 RubyValue
10816 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
10817 {
10818         RubyValue retval;
10819         void *args[3];
10820         VALUE save_interrupt_flag;
10821         char *save_ruby_sourcefile;
10822         int save_ruby_sourceline;
10823         if (gMolbyIsCheckingInterrupt) {
10824                 MolActionAlertRubyIsRunning();
10825                 *status = -1;
10826                 return (RubyValue)Qnil;
10827         }
10828         gMolbyRunLevel++;
10829         args[0] = (void *)script;
10830         args[1] = (void *)mol;
10831         args[2] = (void *)fname;
10832         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
10833         save_ruby_sourcefile = ruby_sourcefile;
10834         save_ruby_sourceline = ruby_sourceline;
10835         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
10836         if (*status != 0) {
10837                 /*  Is this 'exit' exception?  */
10838                 VALUE last_exception = rb_gv_get("$!");
10839                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
10840                         /*  Capture exit and return the status value  */
10841                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
10842                         *status = 0;
10843                 }
10844         }
10845         s_SetInterruptFlag(Qnil, save_interrupt_flag);
10846         ruby_sourcefile = save_ruby_sourcefile;
10847         ruby_sourceline = save_ruby_sourceline;
10848         gMolbyRunLevel--;
10849         return retval;
10850 }
10851
10852 void
10853 Molby_showRubyValue(RubyValue value, char **outValueString)
10854 {
10855         VALUE val = (VALUE)value;
10856         if (gMolbyIsCheckingInterrupt) {
10857                 MolActionAlertRubyIsRunning();
10858                 return;
10859         }
10860         if (val != Qnil) {
10861                 int status;
10862                 char *str;
10863                 gMolbyRunLevel++;
10864                 val = rb_protect(rb_inspect, val, &status);
10865                 gMolbyRunLevel--;
10866                 str = StringValuePtr(val);
10867                 if (outValueString != NULL)
10868                         *outValueString = strdup(str);
10869                 MyAppCallback_showScriptMessage("%s", str);
10870         }
10871 }
10872
10873 void
10874 Molby_showError(int status)
10875 {
10876         static const int tag_raise = 6;
10877         char *msg = NULL, *msg2;
10878         VALUE val, backtrace;
10879         int interrupted = 0;
10880         if (status == tag_raise) {
10881                 VALUE eclass = CLASS_OF(ruby_errinfo);
10882                 if (eclass == rb_eInterrupt) {
10883                         msg = "Interrupt";
10884                         interrupted = 1;
10885                 }
10886         }
10887         gMolbyRunLevel++;
10888         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
10889         if (msg == NULL) {
10890                 val = rb_eval_string_protect("$!.to_s", &status);
10891                 if (status == 0)
10892                         msg = RSTRING_PTR(val);
10893                 else msg = "(message not available)";
10894         }
10895         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
10896         MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
10897         free(msg2);
10898         gMolbyRunLevel--;
10899 }
10900
10901 char *
10902 Molby_getDescription(void)
10903 {
10904         extern const char *gVersionString, *gCopyrightString;
10905         extern int gRevisionNumber;
10906         extern char *gLastBuildString;
10907         char *s;
10908         char *revisionString;
10909         if (gRevisionNumber > 0) {
10910                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
10911         } else revisionString = "";
10912         asprintf(&s, 
10913                          "Molby %s%s\n%s\nLast compile: %s\n"
10914 #if !defined(__CMDMAC__)
10915                          "\nIncluding:\n"
10916                          "%s"
10917 #else
10918                          "Including "
10919 #endif
10920                          "ruby %s, http://www.ruby-lang.org/\n"
10921                          "%s\n"
10922                          "FFTW 3.3.2, http://www.fftw.org/\n"
10923                          "  Copyright (C) 2003, 2007-11 Matteo Frigo\n"
10924                          "  and Massachusetts Institute of Technology",
10925                          gVersionString, revisionString, gCopyrightString, gLastBuildString,
10926 #if !defined(__CMDMAC__)
10927                          MyAppCallback_getGUIDescriptionString(),
10928 #endif
10929                          gRubyVersion, gRubyCopyright);
10930         if (revisionString[0] != 0)
10931                 free(revisionString);
10932         return s;
10933 }
10934
10935 void
10936 Molby_startup(const char *script, const char *dir)
10937 {
10938         VALUE val;
10939         int status;
10940         char *libpath;
10941         char *respath, *p, *wbuf;
10942
10943         /*  Get version/copyright string from Ruby interpreter  */
10944         {
10945                 gRubyVersion = strdup(ruby_version);
10946                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
10947 #if defined(__CMDMAC__)
10948                                  "",
10949 #else
10950                                  "  ",  /*  Indent for displaying in About dialog  */
10951 #endif
10952                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
10953         }
10954         
10955         /*  Read build and revision information for Molby  */
10956 /*      {
10957                 char buf[200];
10958                 extern int gRevisionNumber;
10959                 extern char *gLastBuildString;
10960                 FILE *fp = fopen("../buildInfo.txt", "r");
10961                 gLastBuildString = "";
10962                 if (fp != NULL) {
10963                         if (fgets(buf, sizeof(buf), fp) != NULL) {
10964                                 char *p1 = strchr(buf, '\"');
10965                                 char *p2 = strrchr(buf, '\"');
10966                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
10967                                         memmove(buf, p1 + 1, p2 - p1 - 1);
10968                                         buf[p2 - p1 - 1] = 0;
10969                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
10970                                 }
10971                         }
10972                         fclose(fp);
10973                 }
10974                 fp = fopen("../revisionInfo.txt", "r");
10975                 gRevisionNumber = 0;
10976                 if (fp != NULL) {
10977                         if (fgets(buf, sizeof(buf), fp) != NULL) {
10978                                 gRevisionNumber = strtol(buf, NULL, 0);
10979                         }
10980                         fclose(fp);
10981                 }
10982     } */
10983
10984 #if defined(__CMDMAC__)
10985         wbuf = Molby_getDescription();
10986         printf("%s\n", wbuf);
10987         free(wbuf);
10988 #endif
10989         
10990         /*  Read atom display parameters  */
10991         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
10992 #if defined(__CMDMAC__)
10993                 fprintf(stderr, "%s\n", wbuf);
10994 #else
10995                 MyAppCallback_setConsoleColor(1);
10996                 MyAppCallback_showScriptMessage("%s", wbuf);
10997                 MyAppCallback_setConsoleColor(0);
10998 #endif
10999                 free(wbuf);
11000         }
11001         
11002         /*  Read default parameters  */
11003         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
11004         if (wbuf != NULL) {
11005 #if defined(__CMDMAC__)
11006                 fprintf(stderr, "%s\n", wbuf);
11007 #else
11008                 MyAppCallback_setConsoleColor(1);
11009                 MyAppCallback_showScriptMessage("%s", wbuf);
11010                 MyAppCallback_setConsoleColor(0);
11011 #endif
11012                 free(wbuf);
11013         }
11014                 
11015         /*  Initialize Ruby interpreter  */
11016         ruby_init();
11017         
11018         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
11019         ruby_incpush(".");
11020         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
11021         ruby_incpush(libpath);
11022         free(libpath);
11023         ruby_incpush(dir);
11024
11025         ruby_script("Molby");
11026         
11027         /*  Find the resource path (the parent directory of the given directory)  */
11028         respath = strdup(dir);
11029         p = strrchr(respath, '/');
11030         if (p == NULL && PATH_SEPARATOR != '/')
11031                 p = strrchr(respath, PATH_SEPARATOR);
11032         if (p != NULL)
11033                 *p = 0;
11034         val = Ruby_NewFileStringValue(respath);
11035         rb_define_global_const("MolbyResourcePath", val);
11036         free(respath);
11037
11038         /*  Define Molby classes  */
11039         Init_Molby();
11040         RubyDialogInitClass();
11041
11042         rb_define_const(rb_mMolby, "ResourcePath", val);
11043         val = Ruby_NewFileStringValue(dir);
11044         rb_define_const(rb_mMolby, "ScriptPath", val);
11045         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
11046         val = Ruby_NewFileStringValue(p);
11047         rb_define_const(rb_mMolby, "MbsfPath", val);    
11048         free(p);
11049         
11050 #if defined(__CMDMAC__)
11051         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
11052 #else
11053         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
11054 #endif
11055
11056 #if !__CMDMAC__
11057         
11058         /*  Create objects for stdout and stderr  */
11059         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11060         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
11061         rb_gv_set("$stdout", val);
11062         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11063         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
11064         rb_gv_set("$stderr", val);
11065
11066         /*  Create objects for stdin  */
11067         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11068         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
11069         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
11070         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
11071         rb_gv_set("$stdin", val);
11072         
11073 #endif
11074         
11075         /*  Global variable to hold backtrace  */
11076         rb_define_variable("$backtrace", &gMolbyBacktrace);
11077
11078 #if !__CMDMAC__
11079         /*  Register interrupt check code  */
11080         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL);
11081 #endif
11082         
11083 #if !__CMDMAC__
11084         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
11085         s_SetIntervalTimer(0, 50);
11086 #endif
11087         
11088         /*  Read the startup script  */
11089         if (script != NULL && script[0] != 0) {
11090                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
11091                 gMolbyRunLevel++;
11092                 rb_load_protect(rb_str_new2(script), 0, &status);
11093                 gMolbyRunLevel--;
11094                 if (status != 0)
11095                         Molby_showError(status);
11096                 else
11097                         MyAppCallback_showScriptMessage("Done.\n");
11098         }
11099 }
11100
11101 void
11102 Molby_buildARGV(int argc, const char **argv)
11103 {
11104         int i;
11105     rb_ary_clear(rb_argv);
11106     for (i = 0; i < argc; i++) {
11107                 VALUE arg = rb_tainted_str_new2(argv[i]);
11108                 OBJ_FREEZE(arg);
11109                 rb_ary_push(rb_argv, arg);
11110     }
11111 }