OSDN Git Service

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