OSDN Git Service

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