OSDN Git Service

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