OSDN Git Service

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