OSDN Git Service

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