OSDN Git Service

Ruby#listen is now obsolete, and the message mechanism of Ruby dialog is reworked.
[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 <stdio.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <limits.h>
27
28 #include "version.h"       /*  for Ruby version  */
29 #include "ruby/version.h"  /*  for RUBY_BIRTH_YEAR etc.  */
30 #include "ruby/encoding.h" /*  for rb_str_encode() etc. */
31 /*#include <node.h>     *//*  for rb_add_event_hook()  */
32
33 #if defined(__WXMAC__) || defined(__CMDMAC__)
34 #define USE_PTHREAD_FOR_TIMER 1
35 #endif
36
37 #if !__WXMSW__
38 #if USE_PTHREAD_FOR_TIMER
39 #include <unistd.h>   /*  for usleep()  */
40 #include <pthread.h>  /*  for pthread  */
41 #else
42 #include <signal.h>   /*  for sigaction()  */
43 #endif
44 #endif
45
46 #include "../Missing.h"
47
48 #pragma mark ====== Global Values ======
49
50 VALUE rb_eMolbyError;
51 VALUE rb_mMolby;
52 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
53 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
54
55 VALUE gMolbyBacktrace;
56 VALUE gMolbyErrorHistory;
57 VALUE gScriptMenuCommands;
58 VALUE gScriptMenuEnablers;
59
60 int gMolbyRunLevel = 0;
61 int gMolbyIsCheckingInterrupt = 0;
62
63 char *gRubyVersion, *gRubyCopyright;
64
65 /*  For convenience  */
66 static ID s_ID_equal;  /*  rb_intern("==")  */
67
68 int g_RubyID_call;
69
70 /*  Symbols for atom attributes  */
71 static VALUE
72         s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
73         s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
74         s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
75         s_RSym, s_XSym, s_YSym, s_ZSym,
76         s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
77         s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
78         s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
79         s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
80         s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
81         s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
82
83 /*  Symbols for parameter attributes  */
84 static VALUE
85         s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
86         s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
87         /* s_ASym, s_BSym, */
88         s_ReqSym, s_EpsSym,
89         /* s_A14Sym, s_B14Sym, */
90         s_Req14Sym, s_Eps14Sym,
91         s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym, s_VdwRadiusSym,
92         s_CommentSym, s_SourceSym;
93
94 /*  Symbols for graphics  */
95 static VALUE
96         s_LineSym, s_PolySym, s_CylinderSym, s_ConeSym, s_EllipsoidSym;
97
98 /*
99  *  Utility function
100  *  Get ary[i] by calling "[]" method
101  */
102 VALUE
103 Ruby_ObjectAtIndex(VALUE ary, int idx)
104 {
105         static ID index_method = 0;
106         if (TYPE(ary) == T_ARRAY) {
107                 int len = RARRAY_LEN(ary);
108                 if (idx >= 0 && idx < len)
109                         return (RARRAY_PTR(ary))[idx];
110                 else return Qnil;
111         }
112         if (index_method == 0)
113                 index_method = rb_intern("[]");
114         return rb_funcall(ary, index_method, 1, INT2NUM(idx));
115 }
116
117 char *
118 Ruby_FileStringValuePtr(VALUE *valp)
119 {
120 #if __WXMSW__
121         char *p = strdup(StringValuePtr(*valp));
122         translate_char(p, '/', '\\');
123         *valp = rb_str_new2(p);
124         free(p);
125         return StringValuePtr(*valp);
126 #else
127         return StringValuePtr(*valp);
128 #endif
129 }
130
131 VALUE
132 Ruby_NewFileStringValue(const char *fstr)
133 {
134 #if __WXMSW__
135         VALUE retval;
136         char *p = strdup(fstr);
137         translate_char(p, '\\', '/');
138         retval = rb_enc_str_new(p, strlen(p), rb_default_external_encoding());
139         free(p);
140         return retval;
141 #else
142         return rb_str_new2(fstr);
143 #endif
144 }
145
146 char *
147 Ruby_EncodedStringValuePtr(VALUE *valp)
148 {
149         rb_string_value(valp);
150         *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
151         return RSTRING_PTR(*valp);
152 }
153
154 VALUE
155 Ruby_NewEncodedStringValue(const char *str, int len)
156 {
157         if (len <= 0)
158                 len = strlen(str);
159         return rb_enc_str_new(str, len, rb_default_external_encoding());
160 }
161
162 VALUE
163 Ruby_ObjToStringObj(VALUE val)
164 {
165         switch (TYPE(val)) {
166                 case T_STRING:
167                         return val;
168                 case T_SYMBOL:
169                         return rb_str_new2(rb_id2name(SYM2ID(val)));
170                 default:
171                         return rb_str_to_str(val);
172         }
173 }
174
175 #pragma mark ====== Message input/output ======
176
177 /*
178  *  call-seq:
179  *     message_box(str, title, button = nil, icon = :info)
180  *
181  *  Show a message box.
182  *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
183  *  Icon: :info, :warning, :error
184  */
185 static VALUE
186 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
187 {
188         char *str, *title, *s;
189         int buttons, icon;
190         VALUE sval, tval, bval, ival;
191         rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
192         str = StringValuePtr(sval);
193         title = StringValuePtr(tval);
194         if (bval != Qnil) {
195                 bval = Ruby_ObjToStringObj(bval);
196                 s = RSTRING_PTR(bval);
197                 if (strncmp(s, "ok", 2) == 0)
198                         buttons = 1;
199                 else if (strncmp(s, "cancel", 6) == 0)
200                         buttons = 2;
201                 else
202                         rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
203         } else buttons = 3;
204         if (ival != Qnil) {
205                 ival = Ruby_ObjToStringObj(ival);
206                 s = RSTRING_PTR(ival);
207                 if (strncmp(s, "info", 4) == 0)
208                         icon = 1;
209                 else if (strncmp(s, "warn", 4) == 0)
210                         icon = 2;
211                 else if (strncmp(s, "err", 3) == 0)
212                         icon = 3;
213                 else
214                         rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
215         } else icon = 1;
216         MyAppCallback_messageBox(str, title, buttons, icon);
217         return Qnil;
218 }
219
220 /*
221  *  call-seq:
222  *     error_message_box(str)
223  *
224  *  Show an error message box.
225  */
226 static VALUE
227 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
228 {
229         char *str = StringValuePtr(sval);
230         MyAppCallback_errorMessageBox("%s", str);
231         return Qnil;
232 }
233
234 /*
235  *  call-seq:
236  *     ask(prompt, default = nil) -> string
237  *
238  *  Open a modal dialog and get a line of text.
239  */
240 static VALUE
241 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
242 {
243         volatile VALUE prompt, message;
244         char buf[1024];
245         int retval;
246         rb_scan_args(argc, argv, "11", &prompt, &message);
247         if (message != Qnil) {
248                 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
249                 buf[sizeof buf - 1] = 0;
250         } else buf[0] = 0;
251         retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
252         if (retval)
253                 return rb_str_new2(buf);
254         else
255                 return Qnil;    
256 }
257
258 /*
259  *  call-seq:
260  *     show_console_window
261  *
262  *  Show the console window and bring to the front.
263  */
264 static VALUE
265 s_Kernel_ShowConsoleWindow(VALUE self)
266 {
267         MyAppCallback_showConsoleWindow();
268         return Qnil;
269 }
270
271 /*
272  *  call-seq:
273  *     hide_console_window
274  *
275  *  Hide the console window.
276  */
277 static VALUE
278 s_Kernel_HideConsoleWindow(VALUE self)
279 {
280         MyAppCallback_hideConsoleWindow();
281         return Qnil;
282 }
283
284 /*
285  *  call-seq:
286  *     bell
287  *
288  *  Ring the system bell.
289  */
290 static VALUE
291 s_Kernel_Bell(VALUE self)
292 {
293         MyAppCallback_bell();
294         return Qnil;
295 }
296
297 /*
298  *  call-seq:
299  *     play_sound(filename, flag = 0)
300  *
301  *  Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
302  *  1, play the sound asynchronously; 3, play the sound with loop asynchronously
303  */
304 static VALUE
305 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
306 {
307         VALUE fnval, flval;
308         int flag, retval;
309         char *fname;
310         rb_scan_args(argc, argv, "11", &fnval, &flval);
311         if (flval == Qnil)
312                 flag = 0;
313         else flag = NUM2INT(rb_Integer(flval));
314         fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
315         fname = StringValuePtr(fnval);
316         retval = MyAppCallback_playSound(fname, flag);
317         return (retval ? Qtrue : Qnil);
318 }
319
320 /*
321  *  call-seq:
322  *     stop_sound
323  *
324  *  Stop the sound if playing.
325  */
326 static VALUE
327 s_Kernel_StopSound(VALUE self)
328 {
329         MyAppCallback_stopSound();
330         return Qnil;
331 }
332
333 /*
334  *  call-seq:
335  *     export_to_clipboard(str)
336  *
337  *  Export the given string to clipboard.
338  */
339 static VALUE
340 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
341 {
342 #if !defined(__CMDMAC__)
343         const char *s = StringValuePtr(sval);
344         char *ns;
345 #if __WXMSW__
346         /*  Convert the end-of-line characters  */
347         {       const char *p; int nc; char *np;
348                 nc = 0;
349                 for (p = s; *p != 0; p++) {
350                         if (*p == '\n')
351                                 nc++;
352                 }       
353                 ns = (char *)malloc(strlen(s) + nc + 1);
354                 for (np = ns, p = s; *p != 0; p++, np++) {
355                         if (*p == '\n')
356                                 *np++ = '\r';
357                         *np = *p;
358                 }
359                 *np = 0;
360         }
361 #else
362         ns = (char *)malloc(strlen(s) + 1);
363         strcpy(ns, s);
364 #if __WXMAC__
365         {       char *np;
366                 /*  wxMac still has Carbon code. Oops.  */
367                 for (np = ns; *np != 0; np++) {
368                         if (*np == '\n')
369                                 *np = '\r';
370                 }
371         }
372 #endif
373 #endif
374         if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
375                 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
376 #endif
377         return Qnil;
378 }
379
380 /*
381  *  call-seq:
382  *     stdout.write(str)
383  *
384  *  Put the message in the main text view in black color.
385  */
386 static VALUE
387 s_StandardOutput(VALUE self, VALUE str)
388 {
389         int n;
390         MyAppCallback_setConsoleColor(0);
391         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
392         return INT2NUM(n);
393 }
394
395 /*
396  *  call-seq:
397  *     stderr.write(str)
398  *
399  *  Put the message in the main text view in red color.
400  */
401 static VALUE
402 s_StandardErrorOutput(VALUE self, VALUE str)
403 {
404         int n;
405         MyAppCallback_setConsoleColor(1);
406         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
407         MyAppCallback_setConsoleColor(0);
408         return INT2NUM(n);
409 }
410
411 /*
412  *  call-seq:
413  *     stdout.flush
414  *     stderr.flush
415  *
416  *  Flush the standard (error) output. Actually do nothing.
417  */
418 static VALUE
419 s_FlushConsoleOutput(VALUE self)
420 {
421         return self;
422 }
423
424 /*
425  *  call-seq:
426  *     stdin.gets(rs = $/)
427  *
428  *  Read one line message via dialog box.
429  */
430 static VALUE
431 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
432 {
433         VALUE pval, rval;
434         pval = rb_str_new2("Enter a line:");
435         rval = s_Kernel_Ask(1, &pval, self);
436         if (rval == Qnil)
437                 rb_interrupt();
438         rb_str_cat2(rval, "\n");
439         return rval;
440 }
441
442 /*
443  *  call-seq:
444  *     stdin.method_missing(name, args, ...)
445  *
446  *  Throw an exception, noting only gets and readline are defined.
447  */
448 static VALUE
449 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
450 {
451         VALUE nval;
452         rb_scan_args(argc, argv, "10", &nval);
453         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
454         return Qnil;  /*  Not reached  */
455 }
456
457 #pragma mark ====== Track key events ======
458
459 /*  User interrupt handling
460  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
461  *  key events. This polling should only be enabled during "normal" execution
462  *  of scripts and must be disabled when the rest of the application (or Ruby
463  *  script itself) is handling GUI. This is ensured by appropriate calls to
464  *  enable_interrupt and disable_interrupt.  */
465
466 static VALUE s_interrupt_flag = Qfalse;
467
468 static VALUE
469 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
470 {
471         volatile VALUE message;
472         const char *p;
473         if (Ruby_GetInterruptFlag() == Qtrue) {
474                 rb_scan_args(argc, argv, "01", &message);
475                 if (message != Qnil)
476                         p = StringValuePtr(message);
477                 else
478                         p = NULL;
479                 MyAppCallback_showProgressPanel(p);
480         }
481         return Qnil;
482 }
483
484 static VALUE
485 s_HideProgressPanel(VALUE self)
486 {
487         MyAppCallback_hideProgressPanel();
488         return Qnil;
489 }
490
491 static VALUE
492 s_SetProgressValue(VALUE self, VALUE val)
493 {
494         double dval = NUM2DBL(rb_Float(val));
495         MyAppCallback_setProgressValue(dval);
496         return Qnil;
497 }
498
499 static VALUE
500 s_SetProgressMessage(VALUE self, VALUE msg)
501 {
502         const char *p;
503         if (msg == Qnil)
504                 p = NULL;
505         else p = StringValuePtr(msg);
506         MyAppCallback_setProgressMessage(p);
507         return Qnil;
508 }
509
510 static VALUE
511 s_SetInterruptFlag(VALUE self, VALUE val)
512 {
513         VALUE oldval;
514         if (val != Qundef) {
515                 if (val == Qfalse || val == Qnil)
516                         val = Qfalse;
517                 else val = Qtrue;
518         }
519         oldval = s_interrupt_flag;
520         if (val != Qundef) {
521                 s_interrupt_flag = val;
522                 if (val == Qfalse) {
523                         s_HideProgressPanel(self);
524                 }
525         }
526         return oldval;
527 }
528
529 static VALUE
530 s_GetInterruptFlag(VALUE self)
531 {
532         return s_SetInterruptFlag(self, Qundef);
533 }
534
535 VALUE
536 Ruby_SetInterruptFlag(VALUE val)
537 {
538         return s_SetInterruptFlag(Qnil, val);
539 }
540
541 VALUE
542 Ruby_GetInterruptFlag(void)
543 {
544         return s_SetInterruptFlag(Qnil, Qundef);
545 }
546
547 /*
548  *  call-seq:
549  *     check_interrupt -> integer
550  *
551  *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
552  */
553 static VALUE
554 s_Kernel_CheckInterrupt(VALUE self)
555 {
556         if (Ruby_GetInterruptFlag() == Qfalse)
557                 return INT2NUM(-1);
558         else if (MyAppCallback_checkInterrupt())
559                 return INT2NUM(1);
560         else return INT2NUM(0);
561 }
562
563 static volatile unsigned long sITimerCount = 0;
564
565 #if __WXMSW__
566 static HANDLE sITimerEvent;
567 static HANDLE sITimerThread;
568 static int sITimerInterval;
569
570 static __stdcall unsigned
571 s_ITimerThreadFunc(void *p)
572 {
573         while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
574                 sITimerCount++;
575         }
576         return 0;
577 }
578
579 #elif USE_PTHREAD_FOR_TIMER
580
581 /*  Timer thread  */
582 static pthread_t sTimerThread;
583
584 /*  -1: uninitiated; 0: active, 1: inactive, -2: request to terminate  */
585 static volatile signed char sTimerFlag = -1;
586 static volatile int sTimerIntervalMicrosec = 0;
587
588 static void *
589 s_TimerThreadEntry(void *param)
590 {
591         while (1) {
592                 usleep(sTimerIntervalMicrosec);
593                 if (sTimerFlag == 0)
594                         sITimerCount++;
595                 else if (sTimerFlag == -2)
596                         break;
597         }
598         return NULL;    
599 }
600
601 #endif
602
603 static void
604 s_SignalAction(int n)
605 {
606         sITimerCount++;
607 }
608
609 static void
610 s_SetIntervalTimer(int n, int msec)
611 {
612 #if __WXMSW__
613         if (n == 0) {
614                 /*  Start interval timer  */
615                 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
616                 sITimerInterval = msec;
617                 if (sITimerEvent) {
618                         sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
619                 }
620         } else {
621                 /*  Stop interval timer  */
622                 if (sITimerEvent)
623                         SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
624                 if (sITimerThread) {
625                         WaitForSingleObject(sITimerThread, 1000);
626                         CloseHandle(sITimerThread);
627                 }
628                 if (sITimerEvent)
629                         CloseHandle(sITimerEvent);
630                 sITimerEvent = NULL;
631                 sITimerThread = NULL;
632         }
633 #elif USE_PTHREAD_FOR_TIMER
634         if (n == 0) {
635                 if (sTimerFlag == -1) {
636                         int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
637                         if (status != 0) {
638                                 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
639                         }
640                 }
641                 sTimerFlag = 0;  /*  Active  */
642                 sTimerIntervalMicrosec = msec * 1000;
643         } else if (sTimerFlag != -1)
644                 sTimerFlag = 1;  /*  Inactive  */       
645 #else
646         static struct itimerval sOldValue;
647         static struct sigaction sOldAction;
648         struct itimerval val;
649         struct sigaction act;
650         if (n == 0) {
651                 sITimerCount = 0;
652                 act.sa_handler = s_SignalAction;
653                 act.sa_mask = 0;
654                 act.sa_flags = 0;
655                 sigaction(SIGALRM, &act, &sOldAction);
656                 val.it_value.tv_sec = 0;
657                 val.it_value.tv_usec = msec * 1000;
658                 val.it_interval.tv_sec = 0;
659                 val.it_interval.tv_usec = msec * 1000;
660                 setitimer(ITIMER_REAL, &val, &sOldValue);
661         } else {
662                 setitimer(ITIMER_REAL, &sOldValue, &val);
663                 sigaction(SIGALRM, &sOldAction, &act);
664         }
665 #endif
666 }
667
668 static unsigned long
669 s_GetTimerCount(void)
670 {
671         return sITimerCount;
672 }
673
674 static void
675 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
676 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
677 {
678         if (s_interrupt_flag != Qfalse) {
679                 static unsigned long sLastTime = 0;
680                 unsigned long currentTime;
681                 int flag;
682                 currentTime = s_GetTimerCount();
683                 if (currentTime != sLastTime) {
684                         sLastTime = currentTime;
685                         gMolbyIsCheckingInterrupt = 1;
686                         flag = MyAppCallback_checkInterrupt();
687                         gMolbyIsCheckingInterrupt = 0;
688                         if (flag) {
689                                 s_SetInterruptFlag(Qnil, Qfalse);
690                                 rb_interrupt();
691                         }
692                 }
693         }
694 }
695
696 #pragma mark ====== Menu handling ======
697
698 /*
699  *  call-seq:
700  *     register_menu(title, method, enable_proc = nil)
701  *
702  *  Register the method (specified as a symbol) in the script menu.
703  *  The method must be either an instance method of Molecule with no argument,
704  *  or a class method of Molecule with one argument (the current molecule),
705  *  or a proc object with one argument (the current molecule).
706  *  The menu associated with the class method can be invoked even when no document
707  *  is open (the argument is set to Qnil in this case). On the other hand, the
708  *  menu associated with the instance method can only be invoked when at least one 
709  *  document is active.
710  *  If enable_proc is non-nil, then it is called whenever the availability of
711  *  the menu command is tested. It is usually a proc object with one argument
712  *  (the current molecule or nil). As a special case, the following symbols can
713  *  be given; :mol (enabled when any molecule is open), :non_empty (enabled when
714  *  the top-level molecule has at least one atom), :selection (enabled when
715  *  the top-level molecule has one or more selected atoms).
716  */
717 static VALUE
718 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
719 {
720         int n, mtype = 0;
721         VALUE tval, mval, pval;
722         static VALUE sMolSym, sNonEmptySym, sSelectionSym;
723         static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
724         rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
725         tval = rb_str_to_str(tval);
726         n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
727         if (n < 0)
728                 return Qnil;
729         if (TYPE(mval) == T_SYMBOL) {
730                 /*  Create an appropriate proc object  */
731                 const char *name = rb_id2name(SYM2ID(mval));
732                 char *s;
733                 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
734                         /*  Defined as a Molecule method  */
735                         asprintf(&s, "lambda { |m| m.%s }", name);
736                         mtype = 1;
737                 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
738                         /*  Defined as a Molecule class method  */
739                         asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
740                         mtype = 2;
741                 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
742                 mval = rb_eval_string(s);
743                 free(s);
744         }
745         if (sMolSym == Qfalse) {
746                 sMolSym = ID2SYM(rb_intern("mol"));
747                 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
748                 sSelectionSym = ID2SYM(rb_intern("selection"));
749                 sMolProc = rb_eval_string("lambda { |m| m != nil }");
750                 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
751                 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
752                 sTrueProc = rb_eval_string("lambda { |m| true }");
753                 rb_global_variable(&sMolProc);
754                 rb_global_variable(&sNonEmptyProc);
755                 rb_global_variable(&sSelectionProc);
756                 rb_global_variable(&sTrueProc);
757         }
758         
759         if (pval == Qnil) {
760                 if (mtype == 1)
761                         pval = sMolProc;
762                 else
763                         pval = sTrueProc;
764         } else if (pval == sMolSym)
765                 pval = sMolProc;
766         else if (pval == sNonEmptySym)
767                 pval = sNonEmptyProc;
768         else if (pval == sSelectionSym)
769                 pval = sSelectionProc;
770         rb_ary_store(gScriptMenuCommands, n, mval);
771         rb_ary_store(gScriptMenuEnablers, n, pval);
772         return INT2NUM(n);
773 }
774
775 static VALUE
776 s_Kernel_LookupMenu(VALUE self, VALUE title)
777 {
778         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
779         return INT2NUM(n);
780 }
781
782 static VALUE
783 s_Ruby_UpdateUI_handler(VALUE data)
784 {
785         void **p = (void **)data;
786         int index = (int)p[0];
787         Molecule *mol = (Molecule *)p[1];
788         int *outChecked = (int *)p[2];
789         char **outTitle = (char **)p[3];
790         VALUE mval = ValueFromMolecule(mol);
791         VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
792         static ID call_id = 0;
793         if (call_id == 0)
794                 call_id = rb_intern("call");
795         if (pval == Qnil)
796                 return Qnil;
797         pval = rb_funcall(pval, call_id, 1, mval);
798         if (rb_obj_is_kind_of(pval, rb_cArray)) {
799                 VALUE val;
800                 if (outChecked != NULL) {
801                         val = rb_ary_entry(pval, 1);  /*  Checked or not  */
802                         *outChecked = (RTEST(val) ? 1 : 0);
803                 }
804                 if (outTitle != NULL) {
805                         val = rb_ary_entry(pval, 2);  /*  Text  */
806                         if (TYPE(val) == T_STRING) {
807                                 *outTitle = strdup(StringValuePtr(val));
808                         }
809                 }
810                 pval = rb_ary_entry(pval, 0);
811         }
812         return pval;
813 }
814
815 int
816 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
817 {
818         int status;
819         void *p[4];
820         VALUE retval;
821         p[0] = (void *)index;
822         p[1] = mol;
823         p[2] = outChecked;
824         p[3] = outTitle;
825         retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
826         return (RTEST(retval) ? 1 : 0);
827 }
828
829 /*
830 static VALUE
831 s_Ruby_methodType_sub(VALUE data)
832 {
833         const char **p = (const char **)data;
834         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
835         ID mid = rb_intern(p[1]);
836         int ival;
837         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
838                 ival = 1;
839         else if (rb_respond_to(klass, mid))
840                 ival = 2;
841         else ival = 0;
842         return INT2FIX(ival);
843 }
844 */      
845 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
846     has the singleton method (class method) with the given name, 0 otherwise.  */
847 /*int
848 Ruby_methodType(const char *className, const char *methodName)
849 {
850         int status;
851         VALUE retval;
852         const char *p[2];
853         p[0] = className;
854         p[1] = methodName;
855         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
856         if (status == 0)
857                 return FIX2INT(retval);
858         else return 0;
859 }
860 */
861
862 /*
863  *  call-seq:
864  *     execute_script_file(fname)
865  *
866  *  Execute the script in the given file. If a molecule is active, then
867  *  the script is evaluated as Molecule.current.instance_eval(script).
868  *  Before entering the script, the current directory is set to the parent
869  *  directory of the script.
870  */
871 static VALUE
872 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
873 {
874         int status;
875         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
876         if (retval == (VALUE)6 && status == -1)
877                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
878         if (status != 0)
879                 rb_jump_tag(status);
880         return retval;
881 }
882
883 /*
884  *  call-seq:
885  *     document_home
886  *
887  *  Get the directory suitable for storing user documents. On Windows
888  *  it is the home directory + "My Documents". On other platforms
889  *  it is the home directory.
890  */
891 static VALUE
892 s_Kernel_DocumentHome(VALUE self)
893 {
894         char *s = MyAppCallback_getDocumentHomeDir();
895         VALUE retval = Ruby_NewFileStringValue(s);
896         free(s);
897         return retval;
898 }
899
900 /*  The callback function for call_subprocess  */
901 static int
902 s_Kernel_CallSubProcess_Callback(void *data)
903 {
904         int status;
905         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
906         if (status != 0 || retval == Qnil || retval == Qfalse)
907                 return 1;
908         else return 0;
909 }
910
911 /*
912  *  call-seq:
913  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
914  *
915  *  Call subprocess. A progress dialog window is displayed, with a message
916  *  "Running #{process_name}...".
917  *  A callback proc can be given, which is called periodically during execution. If the proc returns
918  *  nil or false, then the execution will be interrupted.
919  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
920  *  filename begins with ">>", then the message will be appended to the file.
921  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
922  *  If the argument is nil, then the message will be sent to the Ruby console.
923  */
924 static VALUE
925 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
926 {
927         VALUE cmd, procname, cproc, stdout_val, stderr_val;
928         int n;
929         char *sout, *serr;
930         FILE *fpout, *fperr;
931
932         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
933
934         if (stdout_val == Qnil) {
935                 fpout = (FILE *)1;
936         } else {
937                 sout = StringValuePtr(stdout_val);
938                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
939                         fpout = NULL;
940                 else {
941                         if (strncmp(sout, ">>", 2) == 0) {
942                                 sout += 2;
943                                 fpout = fopen(sout, "a");
944                         } else {
945                                 if (*sout == '>')
946                                         sout++;
947                                 fpout = fopen(sout, "w");
948                         }
949                         if (fpout == NULL)
950                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
951                 }
952         }
953         if (stderr_val == Qnil) {
954                 fperr = (FILE *)1;
955         } else {
956                 serr = StringValuePtr(stderr_val);
957                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
958                         fperr = NULL;
959                 else {
960                         if (strncmp(serr, ">>", 2) == 0) {
961                                 serr += 2;
962                                 fpout = fopen(serr, "a");
963                         } else {
964                                 if (*serr == '>')
965                                         serr++;
966                                 fperr = fopen(serr, "w");
967                         }
968                         if (fperr == NULL)
969                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
970                 }
971         }
972
973         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
974         
975         if (fpout != NULL && fpout != (FILE *)1)
976                 fclose(fpout);
977         if (fperr != NULL && fperr != (FILE *)1)
978                 fclose(fperr);
979
980         return INT2NUM(n);
981
982         
983 }
984
985 /*
986  *  call-seq:
987  *     backquote(cmd)
988  *
989  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
990  */
991 static VALUE
992 s_Kernel_Backquote(VALUE self, VALUE cmd)
993 {
994         char *buf;
995         int n;
996         VALUE val;
997         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
998         if (n != 0)
999                 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
1000         if (buf != NULL) {
1001                 val = Ruby_NewEncodedStringValue(buf, 0);
1002                 free(buf);
1003         } else {
1004                 val = Ruby_NewEncodedStringValue("", 0);
1005         }
1006         return val;
1007 }
1008
1009 #pragma mark ====== User defaults ======
1010
1011 /*
1012  *  call-seq:
1013  *     get_global_settings(key)
1014  *
1015  *  Get a setting data for key from the application preferences.
1016  */
1017 static VALUE
1018 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1019 {
1020         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1021         if (p != NULL) {
1022                 VALUE retval = rb_eval_string(p);
1023                 free(p);
1024                 return retval;
1025         } else return Qnil;
1026 }
1027
1028 /*
1029  *  call-seq:
1030  *     set_global_settings(key, value)
1031  *
1032  *  Set a setting data for key to the application preferences.
1033  */
1034 static VALUE
1035 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1036 {
1037         VALUE sval = rb_inspect(value);
1038         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1039         return value;
1040 }
1041
1042 #pragma mark ====== IO extension ======
1043
1044 /*
1045  *  call-seq:
1046  *     gets_any_eol
1047  *
1048  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1049  */
1050 static VALUE
1051 s_IO_gets_any_eol(VALUE self)
1052 {
1053         VALUE val, cval;
1054         char buf[1024];
1055         int i, c;
1056         static ID id_getbyte = 0, id_ungetbyte;
1057         if (id_getbyte == 0) {
1058                 id_getbyte = rb_intern("getbyte");
1059                 id_ungetbyte = rb_intern("ungetbyte");
1060         }
1061         i = 0;
1062         val = Qnil;
1063         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1064                 c = NUM2INT(rb_Integer(cval));
1065                 if (c == 0x0d) {
1066                         cval = rb_funcall(self, id_getbyte, 0);
1067                         if (cval != Qnil) {
1068                                 c = NUM2INT(rb_Integer(cval));
1069                                 if (c != 0x0a)
1070                                         rb_funcall(self, id_ungetbyte, 1, cval);
1071                         }
1072                         break;
1073                 } else if (c != 0x0a) {
1074                         buf[i++] = c;
1075                         if (i >= 1020) {
1076                                 buf[i] = 0;
1077                                 if (val == Qnil)
1078                                         val = rb_str_new(buf, i);
1079                                 else
1080                                         rb_str_append(val, rb_str_new(buf, i));
1081                                 i = 0;
1082                         }
1083                 } else break;
1084         }
1085         if (cval == Qnil && i == 0 && val == Qnil)
1086                 return Qnil;  /*  End of file  */
1087         buf[i] = 0;
1088         if (val == Qnil)
1089                 val = rb_str_new(buf, i);
1090         else if (i > 0)
1091                 rb_str_append(val, rb_str_new(buf, i));
1092         val = rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
1093         if (cval != Qnil) {
1094                 /*  Needs a end-of-line mark  */
1095                 cval = rb_gv_get("$/");
1096                 rb_str_append(val, cval);
1097         }
1098         rb_gv_set("$_", val);
1099         return val;
1100 }
1101
1102 #pragma mark ====== Utility functions (protected funcall) ======
1103
1104 struct Ruby_funcall2_record {
1105         VALUE recv;
1106         ID mid;
1107         int argc;
1108         VALUE *argv;
1109 };
1110
1111 static VALUE
1112 s_Ruby_funcall2_sub(VALUE data)
1113 {
1114         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1115         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1116 }
1117
1118 VALUE
1119 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1120 {
1121         struct Ruby_funcall2_record rec;
1122         rec.recv = recv;
1123         rec.mid = mid;
1124         rec.argc = argc;
1125         rec.argv = argv;
1126         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1127 }
1128
1129 RubyValue
1130 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1131 {
1132         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1133 }
1134
1135 #pragma mark ====== ParameterRef Class ======
1136
1137 static UnionPar *
1138 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1139 {
1140         UnionPar *up;
1141         ParameterRef *pref;
1142         Data_Get_Struct(self, ParameterRef, pref);
1143         if (typep != NULL)
1144                 *typep = pref->parType;
1145         if (pref->parType == kElementParType) {
1146                 up = (UnionPar *)&gElementParameters[pref->idx];
1147         } else {
1148                 up = ParameterRefGetPar(pref);
1149                 if (checkEditable) {
1150                         if (pref->idx < 0)
1151                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1152                         if (up->bond.src != 0 && up->bond.src != -1)
1153                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1154                 }
1155         }
1156         return up;
1157 }
1158
1159 static void
1160 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1161 {
1162         UnionPar *up;
1163         ParameterRef *pref;
1164         Data_Get_Struct(self, ParameterRef, pref);
1165         if (pref->mol == NULL)
1166                 return;
1167         up = ParameterRefGetPar(pref);
1168         if (key != s_SourceSym)
1169                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1170         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1171                 /*  Register undo  */
1172                 MolAction *act;
1173                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1174                 MolActionCallback_registerUndo(pref->mol, act);
1175                 MoleculeCallback_notifyModification(pref->mol, 0);
1176                 pref->mol->needsMDRebuild = 1;
1177         }
1178 }
1179
1180 VALUE
1181 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1182 {
1183         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1184         if (pref != NULL)
1185                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1186         else
1187                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1188 }
1189
1190 static int
1191 s_AtomTypeIndexFromValue(VALUE val)
1192 {
1193         if (rb_obj_is_kind_of(val, rb_cNumeric))
1194                 return NUM2INT(val);
1195         else
1196                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1197 }
1198
1199 static const char *s_ParameterTypeNames[] = {
1200         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1201 };
1202 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1203
1204 static int
1205 s_ParTypeFromValue(VALUE val)
1206 {
1207         int i, n;
1208         ID valid;
1209         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1210         if (s_ParameterTypeIDs[0] == 0) {
1211                 for (i = 0; i < n; i++)
1212                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1213         }
1214         valid = rb_to_id(val);
1215         for (i = 0; i < n; i++) {
1216                 if (valid == s_ParameterTypeIDs[i]) {
1217                         if (i == 7)
1218                                 return kElementParType;
1219                         else return kFirstParType + i;
1220                 }
1221         }
1222         return kInvalidParType;
1223 }
1224
1225 /*
1226  *  call-seq:
1227  *     index -> Integer
1228  *
1229  *  Get the index in the parameter list.
1230  */
1231 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1232         ParameterRef *pref;
1233         Data_Get_Struct(self, ParameterRef, pref);
1234         return INT2NUM(pref->idx);
1235 }
1236
1237 /*
1238  *  call-seq:
1239  *     par_type -> String
1240  *
1241  *  Get the parameter type, like "bond", "angle", etc.
1242  */
1243 static VALUE s_ParameterRef_GetParType(VALUE self) {
1244         Int tp;
1245         s_UnionParFromValue(self, &tp, 0);
1246         if (tp == kElementParType)
1247                 return rb_str_new2("element");
1248         tp -= kFirstParType;
1249         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1250                 return rb_str_new2(s_ParameterTypeNames[tp]);
1251         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1252 }
1253
1254 /*
1255  *  call-seq:
1256  *     atom_type -> String or Array of String
1257  *     atom_types -> String or Array of String
1258  *
1259  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1260  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1261  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1262  *  The atom type may be "X", which is a wildcard that matches any atom type.
1263  */
1264 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1265         UnionPar *up;
1266         Int tp, i, n;
1267         UInt types[4];
1268         VALUE vals[4];
1269         up = s_UnionParFromValue(self, &tp, 0);
1270         n = ParameterGetAtomTypes(tp, up, types);
1271         if (n == 0)
1272                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1273         for (i = 0; i < n; i++) {
1274                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1275                         vals[i] = INT2NUM(types[i]);
1276                 else
1277                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1278         }
1279         if (n == 1)
1280                 return vals[0];
1281         else
1282                 return rb_ary_new4(n, vals);
1283 }
1284
1285 /*
1286  *  call-seq:
1287  *     k -> Float
1288  *
1289  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1290  */
1291 static VALUE s_ParameterRef_GetK(VALUE self) {
1292         UnionPar *up;
1293         Int tp, i, n;
1294         VALUE vals[3];
1295         up = s_UnionParFromValue(self, &tp, 0);
1296         switch (tp) {
1297                 case kBondParType:
1298                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1299                 case kAngleParType:
1300                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1301                 case kDihedralParType:
1302                 case kImproperParType:
1303                         if (up->torsion.mult == 1)
1304                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1305                         n = up->torsion.mult;
1306                         if (n > 3)
1307                                 n = 3;
1308                         for (i = 0; i < n; i++)
1309                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1310                         return rb_ary_new4(n, vals);
1311                 default:
1312                         rb_raise(rb_eMolbyError, "invalid member k");
1313         }
1314 }
1315
1316 /*
1317  *  call-seq:
1318  *     r0 -> Float
1319  *
1320  *  Get the equilibrium bond length. Only available for bond parameters.
1321  */
1322 static VALUE s_ParameterRef_GetR0(VALUE self) {
1323         UnionPar *up;
1324         Int tp;
1325         up = s_UnionParFromValue(self, &tp, 0);
1326         if (tp == kBondParType)
1327                 return rb_float_new(up->bond.r0);
1328         else rb_raise(rb_eMolbyError, "invalid member r0");
1329 }
1330
1331 /*
1332  *  call-seq:
1333  *     a0 -> Float
1334  *
1335  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1336  */
1337 static VALUE s_ParameterRef_GetA0(VALUE self) {
1338         UnionPar *up;
1339         Int tp;
1340         up = s_UnionParFromValue(self, &tp, 0);
1341         if (tp == kAngleParType)
1342                 return rb_float_new(up->angle.a0 * kRad2Deg);
1343         else rb_raise(rb_eMolbyError, "invalid member a0");
1344 }
1345
1346 /*
1347  *  call-seq:
1348  *     mult -> Float
1349  *
1350  *  Get the multiplicity. Only available for dihedral and improper parameters.
1351  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1352  */
1353 static VALUE s_ParameterRef_GetMult(VALUE self) {
1354         UnionPar *up;
1355         Int tp;
1356         up = s_UnionParFromValue(self, &tp, 0);
1357         if (tp == kDihedralParType || tp == kImproperParType)
1358                 return rb_float_new(up->torsion.mult);
1359         else rb_raise(rb_eMolbyError, "invalid member mult");
1360 }
1361
1362 /*
1363  *  call-seq:
1364  *     period -> Integer or Array of Integers
1365  *
1366  *  Get the periodicity. Only available for dihedral and improper parameters.
1367  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1368  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1369  */
1370 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1371         UnionPar *up;
1372         Int tp, i, n;
1373         VALUE vals[3];
1374         up = s_UnionParFromValue(self, &tp, 0);
1375         if (tp == kDihedralParType || tp == kImproperParType) {
1376                 if (up->torsion.mult == 1)
1377                         return INT2NUM(up->torsion.period[0]);
1378                 n = up->torsion.mult;
1379                 if (n > 3)
1380                         n = 3;
1381                 for (i = 0; i < n; i++)
1382                         vals[i] = INT2NUM(up->torsion.period[i]);
1383                 return rb_ary_new4(n, vals);
1384         } else rb_raise(rb_eMolbyError, "invalid member period");
1385 }
1386
1387 /*
1388  *  call-seq:
1389  *     phi0 -> Float or Array of Floats
1390  *
1391  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1392  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1393  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1394  */
1395 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1396         UnionPar *up;
1397         Int tp, i, n;
1398         VALUE vals[3];
1399         up = s_UnionParFromValue(self, &tp, 0);
1400         if (tp == kDihedralParType || tp == kImproperParType) {
1401                 if (up->torsion.mult == 1)
1402                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1403                 n = up->torsion.mult;
1404                 if (n > 3)
1405                         n = 3;
1406                 for (i = 0; i < n; i++)
1407                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1408                 return rb_ary_new4(n, vals);
1409         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1410 }
1411
1412 /*
1413  *  call-seq:
1414  *     A -> Float
1415  *
1416  *  Get the "A" value for the van der Waals parameter.
1417  */
1418 /*
1419  static VALUE s_ParameterRef_GetA(VALUE self) {
1420         UnionPar *up;
1421         Int tp;
1422         up = s_UnionParFromValue(self, &tp, 0);
1423         if (tp == kVdwParType)
1424                 return rb_float_new(up->vdw.A);
1425         else if (tp == kVdwPairParType)
1426                 return rb_float_new(up->vdwp.A);
1427         else rb_raise(rb_eMolbyError, "invalid member A");
1428 }
1429 */
1430
1431 /*
1432  *  call-seq:
1433  *     B -> Float
1434  *
1435  *  Get the "B" value for the van der Waals parameter.
1436  */
1437 /*
1438 static VALUE s_ParameterRef_GetB(VALUE self) {
1439         UnionPar *up;
1440         Int tp;
1441         up = s_UnionParFromValue(self, &tp, 0);
1442         if (tp == kVdwParType)
1443                 return rb_float_new(up->vdw.B);
1444         else if (tp == kVdwPairParType)
1445                 return rb_float_new(up->vdwp.B);
1446         else rb_raise(rb_eMolbyError, "invalid member B");
1447 }
1448 */
1449
1450 /*
1451  *  call-seq:
1452  *     r_eq -> Float
1453  *
1454  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1455  */
1456 static VALUE s_ParameterRef_GetReq(VALUE self) {
1457         UnionPar *up;
1458         Int tp;
1459 /*      Double a, b, r; */
1460         Double r;
1461         up = s_UnionParFromValue(self, &tp, 0);
1462         if (tp == kVdwParType) {
1463         /*      a = up->vdw.A;
1464                 b = up->vdw.B;  */
1465                 r = up->vdw.r_eq;
1466         } else if (tp == kVdwPairParType) {
1467         /*      a = up->vdwp.A;
1468                 b = up->vdwp.B;  */
1469                 r = up->vdwp.r_eq;
1470         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1471 /*      if (a == 0.0 || b == 0.0) */
1472         return rb_float_new(r);
1473 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1474 }
1475
1476 /*
1477  *  call-seq:
1478  *     eps -> Float
1479  *
1480  *  Get the minimum energy for the van der Waals parameter.
1481  */
1482 static VALUE s_ParameterRef_GetEps(VALUE self) {
1483         UnionPar *up;
1484         Int tp;
1485 /*      Double a, b; */
1486         Double eps;
1487         up = s_UnionParFromValue(self, &tp, 0);
1488         if (tp == kVdwParType) {
1489         /*      a = up->vdw.A;
1490                 b = up->vdw.B;  */
1491                 eps = up->vdw.eps;
1492         } else if (tp == kVdwPairParType) {
1493         /*      a = up->vdwp.A;
1494                 b = up->vdwp.B; */
1495                 eps = up->vdwp.eps;
1496         } else rb_raise(rb_eMolbyError, "invalid member eps");
1497 /*      if (a == 0.0 || b == 0.0)  */
1498                 return rb_float_new(eps * INTERNAL2KCAL);
1499 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1500 }
1501
1502 /*
1503  *  call-seq:
1504  *     A14 -> Float
1505  *
1506  *  Get the "A" value for the 1-4 van der Waals parameter.
1507  */
1508 /*
1509 static VALUE s_ParameterRef_GetA14(VALUE self) {
1510         UnionPar *up;
1511         Int tp;
1512         up = s_UnionParFromValue(self, &tp, 0);
1513         if (tp == kVdwParType)
1514                 return rb_float_new(up->vdw.A14);
1515         else if (tp == kVdwPairParType)
1516                 return rb_float_new(up->vdwp.A14);
1517         else rb_raise(rb_eMolbyError, "invalid member A14");
1518 }
1519 */
1520
1521 /*
1522  *  call-seq:
1523  *     B14 -> Float
1524  *
1525  *  Get the "B" value for the 1-4 van der Waals parameter.
1526  */
1527 /*
1528 static VALUE s_ParameterRef_GetB14(VALUE self) {
1529         UnionPar *up;
1530         Int tp;
1531         up = s_UnionParFromValue(self, &tp, 0);
1532         if (tp == kVdwParType)
1533                 return rb_float_new(up->vdw.B14);
1534         else if (tp == kVdwPairParType)
1535                 return rb_float_new(up->vdwp.B14);
1536         else rb_raise(rb_eMolbyError, "invalid member B14");
1537 }
1538 */
1539
1540 /*
1541  *  call-seq:
1542  *     r_eq14 -> Float
1543  *
1544  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1545  */
1546 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1547         UnionPar *up;
1548         Int tp;
1549 /*      Double a, b, r; */
1550         Double r;
1551         up = s_UnionParFromValue(self, &tp, 0);
1552         if (tp == kVdwParType) {
1553         /*      a = up->vdw.A14;
1554                 b = up->vdw.B14; */
1555                 r = up->vdw.r_eq14;
1556         } else if (tp == kVdwPairParType) {
1557         /*      a = up->vdwp.A14;
1558                 b = up->vdwp.B14;  */
1559                 r = up->vdwp.r_eq14;
1560         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1561 /*      if (a == 0.0 || b == 0.0)  */
1562         return rb_float_new(r);
1563 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1564 }
1565
1566 /*
1567  *  call-seq:
1568  *     eps14 -> Float
1569  *
1570  *  Get the minimum energy for the 1-4 van der Waals parameter.
1571  */
1572 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1573         UnionPar *up;
1574         Int tp;
1575 /*      Double a, b;  */
1576         Double eps;
1577         up = s_UnionParFromValue(self, &tp, 0);
1578         if (tp == kVdwParType) {
1579         /*      a = up->vdw.A14;
1580                 b = up->vdw.B14;  */
1581                 eps = up->vdw.eps14;
1582         } else if (tp == kVdwPairParType) {
1583         /*      a = up->vdwp.A14;
1584                 b = up->vdwp.B14; */
1585                 eps = up->vdwp.eps14;
1586         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1587 /*      if (a == 0.0 || b == 0.0) */
1588         return rb_float_new(eps * INTERNAL2KCAL);
1589 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1590 }
1591
1592 /*
1593  *  call-seq:
1594  *     cutoff -> Float
1595  *
1596  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1597  */
1598 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1599         UnionPar *up;
1600         Int tp;
1601         up = s_UnionParFromValue(self, &tp, 0);
1602         if (tp == kVdwCutoffParType)
1603                 return rb_float_new(up->vdwcutoff.cutoff);
1604         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1605 }
1606
1607 /*
1608  *  call-seq:
1609  *     radius -> Float
1610  *
1611  *  Get the atomic (covalent) radius for the element parameter.
1612  */
1613 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1614         UnionPar *up;
1615         Int tp;
1616         up = s_UnionParFromValue(self, &tp, 0);
1617         if (tp == kElementParType)
1618                 return rb_float_new(up->atom.radius);
1619         else rb_raise(rb_eMolbyError, "invalid member radius");
1620 }
1621
1622 /*
1623  *  call-seq:
1624  *     vdw_radius -> Float
1625  *
1626  *  Get the van der Waals radius for the element parameter. (0 if not given)
1627  */
1628 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1629         UnionPar *up;
1630         Int tp;
1631         up = s_UnionParFromValue(self, &tp, 0);
1632         if (tp == kElementParType)
1633                 return rb_float_new(up->atom.vdw_radius);
1634         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1635 }
1636
1637 /*
1638  *  call-seq:
1639  *     color -> [Float, Float, Float]
1640  *
1641  *  Get the rgb color for the element parameter.
1642  */
1643 static VALUE s_ParameterRef_GetColor(VALUE self) {
1644         UnionPar *up;
1645         Int tp;
1646         up = s_UnionParFromValue(self, &tp, 0);
1647         if (tp == kElementParType)
1648                 return rb_ary_new3(3, rb_float_new(up->atom.red / 65535.0), rb_float_new(up->atom.green / 65535.0), rb_float_new(up->atom.blue / 65535.0));
1649         else rb_raise(rb_eMolbyError, "invalid member color");
1650 }
1651
1652 /*
1653  *  call-seq:
1654  *     atomic_number -> Integer
1655  *
1656  *  Get the atomic number for the vdw or element parameter.
1657  */
1658 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1659         UnionPar *up;
1660         Int tp;
1661         up = s_UnionParFromValue(self, &tp, 0);
1662         if (tp == kElementParType)
1663                 return INT2NUM(up->atom.number);
1664         else if (tp == kVdwParType)
1665                 return INT2NUM(up->vdw.atomicNumber);
1666         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1667 }
1668
1669 /*
1670  *  call-seq:
1671  *     name -> String
1672  *
1673  *  Get the name for the element parameter.
1674  */
1675 static VALUE s_ParameterRef_GetName(VALUE self) {
1676         UnionPar *up;
1677         Int tp;
1678         up = s_UnionParFromValue(self, &tp, 0);
1679         if (tp == kElementParType) {
1680                 char name[5];
1681                 strncpy(name, up->atom.name, 4);
1682                 name[4] = 0;
1683                 return rb_str_new2(name);
1684         } else rb_raise(rb_eMolbyError, "invalid member name");
1685 }
1686
1687 /*
1688  *  call-seq:
1689  *     weight -> Float
1690  *
1691  *  Get the atomic weight for the element parameter.
1692  */
1693 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1694         UnionPar *up;
1695         Int tp;
1696         up = s_UnionParFromValue(self, &tp, 0);
1697         if (tp == kElementParType)
1698                 return rb_float_new(up->atom.weight);
1699         else if (tp == kVdwParType)
1700                 return rb_float_new(up->vdw.weight);
1701         else rb_raise(rb_eMolbyError, "invalid member weight");
1702 }
1703
1704 /*
1705  *  call-seq:
1706  *     fullname -> String
1707  *
1708  *  Get the full name for the element parameter.
1709  */
1710 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1711         UnionPar *up;
1712         Int tp;
1713         up = s_UnionParFromValue(self, &tp, 0);
1714         if (tp == kElementParType) {
1715                 char fullname[16];
1716                 strncpy(fullname, up->atom.fullname, 15);
1717                 fullname[15] = 0;
1718                 return rb_str_new2(fullname);
1719         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1720 }
1721
1722 /*
1723  *  call-seq:
1724  *     comment -> String
1725  *
1726  *  Get the comment for the parameter.
1727  */
1728 static VALUE s_ParameterRef_GetComment(VALUE self) {
1729         UnionPar *up;
1730         Int tp, com;
1731         up = s_UnionParFromValue(self, &tp, 0);
1732         com = up->bond.com;
1733         if (com == 0)
1734                 return Qnil;
1735         else return rb_str_new2(ParameterGetComment(com));
1736 }
1737
1738 /*
1739  *  call-seq:
1740  *     source -> String
1741  *
1742  *  Get the source string for the parameter. Returns false for undefined parameter,
1743  *  and nil for "local" parameter that is specific for the molecule.
1744  */
1745 static VALUE s_ParameterRef_GetSource(VALUE self) {
1746         UnionPar *up;
1747         Int tp, src;
1748         up = s_UnionParFromValue(self, &tp, 0);
1749         src = up->bond.src;
1750         if (src < 0)
1751                 return Qfalse;  /* undefined */
1752         else if (src == 0)
1753                 return Qnil;  /*  local  */
1754         else return rb_str_new2(ParameterGetComment(src));
1755 }
1756
1757 static void
1758 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1759 {
1760         VALUE *valp;
1761         int i;
1762         if (n == 1)
1763                 valp = &val;
1764         else {
1765                 if (rb_obj_is_kind_of(val, rb_cString)) {
1766                         char *s = StringValuePtr(val);
1767                         char *p;
1768                         for (i = 0; i < n; i++) {
1769                                 char buf[40];
1770                                 int len;
1771                                 /*  Skip leading separaters  */
1772                                 while (*s == '-' || *s == ' ' || *s == '\t')
1773                                         s++;
1774                                 for (p = s; *p != 0; p++) {
1775                                         if (*p == '-' || *p == ' ' || *p == '\t')
1776                                                 break;
1777                                 }
1778                                 len = p - s;
1779                                 if (len >= sizeof(buf))
1780                                         len = sizeof(buf) - 1;
1781                                 strncpy(buf, s, len);
1782                                 buf[len] = 0;
1783                                 /*  Skip trailing blanks  */
1784                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1785                                         buf[len] = 0;
1786                                 if (buf[0] == 0)
1787                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1788                                 if (buf[0] >= '0' && buf[0] <= '9')
1789                                         types[i] = atoi(buf);
1790                                 else
1791                                         types[i] = AtomTypeEncodeToUInt(buf);
1792                                 if (p == NULL || *p == 0) {
1793                                         i++;
1794                                         break;
1795                                 } else s = p + 1;
1796                         }
1797                         if (i < n)
1798                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1799                         return;
1800                 }
1801                 val = rb_ary_to_ary(val);
1802                 if (RARRAY_LEN(val) != n)
1803                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1804                 valp = RARRAY_PTR(val);
1805         }
1806         for (i = 0; i < n; i++) {
1807                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1808                         types[i] = NUM2INT(rb_Integer(valp[i]));
1809                 else {
1810                         VALUE sval = valp[i];
1811                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1812                 }
1813         }
1814 }
1815
1816 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1817         UnionPar *up;
1818         VALUE oldval;
1819         Int oldsrc, tp;
1820         UInt types[4];
1821         up = s_UnionParFromValue(self, &tp, 1);
1822         oldval = s_ParameterRef_GetAtomTypes(self);
1823         oldsrc = up->bond.src;
1824         switch (tp) {
1825                 case kBondParType:
1826                         s_ScanAtomTypes(val, 2, types);
1827                         up->bond.type1 = types[0];
1828                         up->bond.type2 = types[1];
1829                         break;
1830                 case kAngleParType:
1831                         s_ScanAtomTypes(val, 3, types);
1832                         up->angle.type1 = types[0];
1833                         up->angle.type2 = types[1];
1834                         up->angle.type3 = types[2];
1835                         break;
1836                 case kDihedralParType:
1837                 case kImproperParType:
1838                         s_ScanAtomTypes(val, 4, types);
1839                         up->torsion.type1 = types[0];
1840                         up->torsion.type2 = types[1];
1841                         up->torsion.type3 = types[2];
1842                         up->torsion.type4 = types[3];
1843                         break;
1844                 case kVdwParType:
1845                         s_ScanAtomTypes(val, 1, types);
1846                         up->vdw.type1 = types[0];
1847                         break;
1848                 case kVdwPairParType:
1849                         s_ScanAtomTypes(val, 2, types);
1850                         up->vdwp.type1 = types[0];
1851                         up->vdwp.type2 = types[1];
1852                         break;
1853                 case kVdwCutoffParType:
1854                         s_ScanAtomTypes(val, 2, types);
1855                         up->vdwcutoff.type1 = types[0];
1856                         up->vdwcutoff.type2 = types[1];
1857                         break;
1858                 default:
1859                         return Qnil;
1860         }
1861         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1862         return val;
1863 }
1864
1865 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1866         UnionPar *up;
1867         Int tp, i, n, oldsrc;
1868         VALUE *valp, oldval;
1869         up = s_UnionParFromValue(self, &tp, 1);
1870         oldval = s_ParameterRef_GetK(self);
1871         oldsrc = up->bond.src;
1872         switch (tp) {
1873                 case kBondParType:
1874                         val = rb_Float(val);
1875                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1876                         break;
1877                 case kAngleParType:
1878                         val = rb_Float(val);
1879                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1880                         break;
1881                 case kDihedralParType:
1882                 case kImproperParType:
1883                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1884                                 up->torsion.mult = 1;
1885                                 val = rb_Float(val);
1886                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1887                                 break;
1888                         }
1889                         n = up->torsion.mult;
1890                         if (n > 3)
1891                                 n = 3;
1892                         val = rb_ary_to_ary(val);
1893                         if (RARRAY_LEN(val) != n)
1894                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1895                         valp = RARRAY_PTR(val);
1896                         for (i = 0; i < n; i++) {
1897                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1898                         }
1899                         break;
1900                 default:
1901                         rb_raise(rb_eMolbyError, "invalid member k");
1902         }
1903         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1904         return val;
1905 }
1906
1907 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1908         UnionPar *up;
1909         Int tp, oldsrc;
1910         VALUE oldval;
1911         up = s_UnionParFromValue(self, &tp, 1);
1912         oldval = s_ParameterRef_GetR0(self);
1913         oldsrc = up->bond.src;
1914         if (tp == kBondParType) {
1915                 val = rb_Float(val);
1916                 up->bond.r0 = NUM2DBL(val);
1917         } else rb_raise(rb_eMolbyError, "invalid member r0");
1918         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1919         return val;
1920 }
1921
1922 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1923         UnionPar *up;
1924         Int tp, oldsrc;
1925         VALUE oldval;
1926         up = s_UnionParFromValue(self, &tp, 1);
1927         oldval = s_ParameterRef_GetA0(self);
1928         oldsrc = up->bond.src;
1929         if (tp == kAngleParType) {
1930                 val = rb_Float(val);
1931                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1932         } else rb_raise(rb_eMolbyError, "invalid member a0");
1933         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1934         return val;
1935 }
1936
1937 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1938         UnionPar *up;
1939         Int tp, oldsrc;
1940         VALUE oldval;
1941         up = s_UnionParFromValue(self, &tp, 1);
1942         oldval = s_ParameterRef_GetMult(self);
1943         oldsrc = up->bond.src;
1944         if (tp == kDihedralParType || tp == kImproperParType) {
1945                 int i;
1946                 val = rb_Integer(val);
1947                 i = NUM2INT(val);
1948                 if (i < 0 || i > 3)
1949                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1950                 up->torsion.mult = i;
1951         } else rb_raise(rb_eMolbyError, "invalid member mult");
1952         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1953         return val;
1954 }
1955
1956 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1957         UnionPar *up;
1958         Int tp, i, n, oldsrc;
1959         VALUE *valp, oldval;
1960         up = s_UnionParFromValue(self, &tp, 1);
1961         oldval = s_ParameterRef_GetPeriod(self);
1962         oldsrc = up->bond.src;
1963         if (tp == kDihedralParType || tp == kImproperParType) {
1964                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1965                         up->torsion.mult = 1;
1966                         val = rb_Integer(val);
1967                         up->torsion.period[0] = NUM2INT(val);
1968                 } else {
1969                         n = up->torsion.mult;
1970                         if (n > 3)
1971                                 n = 3;
1972                         val = rb_ary_to_ary(val);
1973                         if (RARRAY_LEN(val) != n)
1974                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1975                         valp = RARRAY_PTR(val);
1976                         for (i = 0; i < n; i++) {
1977                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1978                         }
1979                 }
1980         } else rb_raise(rb_eMolbyError, "invalid member period");
1981         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1982         return val;
1983 }
1984
1985 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1986         UnionPar *up;
1987         Int tp, i, n, oldsrc;
1988         VALUE *valp, oldval;
1989         up = s_UnionParFromValue(self, &tp, 1);
1990         oldval = s_ParameterRef_GetPhi0(self);
1991         oldsrc = up->bond.src;
1992         if (tp == kDihedralParType || tp == kImproperParType) {
1993                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1994                         up->torsion.mult = 1;
1995                         val = rb_Float(val);
1996                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1997                 } else {
1998                         n = up->torsion.mult;
1999                         if (n > 3)
2000                                 n = 3;
2001                         val = rb_ary_to_ary(val);
2002                         if (RARRAY_LEN(val) != n)
2003                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2004                         valp = RARRAY_PTR(val);
2005                         for (i = 0; i < n; i++)
2006                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2007                 }
2008         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2009         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2010         return val;
2011 }
2012
2013 /*
2014 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2015         UnionPar *up;
2016         Int tp, oldsrc;
2017         double d;
2018         VALUE oldval;
2019         up = s_UnionParFromValue(self, &tp, 1);
2020         oldval = s_ParameterRef_GetA(self);
2021         oldsrc = up->bond.src;
2022         val = rb_Float(val);
2023         d = NUM2DBL(val);
2024         if (tp == kVdwParType)
2025                 up->vdw.A = d;
2026         else if (tp == kVdwPairParType)
2027                 up->vdwp.A = d;
2028         else rb_raise(rb_eMolbyError, "invalid member A");
2029         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2030         return val;
2031 }
2032
2033 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2034         UnionPar *up;
2035         Int tp, oldsrc;
2036         double d;
2037         VALUE oldval;
2038         up = s_UnionParFromValue(self, &tp, 1);
2039         oldval = s_ParameterRef_GetB(self);
2040         oldsrc = up->bond.src;
2041         val = rb_Float(val);
2042         d = NUM2DBL(val);
2043         if (tp == kVdwParType)
2044                 up->vdw.B = d;
2045         else if (tp == kVdwPairParType)
2046                 up->vdwp.B = d;
2047         else rb_raise(rb_eMolbyError, "invalid member B");
2048         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2049         return val;
2050 }
2051 */
2052
2053 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2054         UnionPar *up;
2055         Int tp, oldsrc;
2056         Double r;
2057         VALUE oldval;
2058         up = s_UnionParFromValue(self, &tp, 1);
2059         oldval = s_ParameterRef_GetReq(self);
2060         oldsrc = up->bond.src;
2061         val = rb_Float(val);
2062         r = NUM2DBL(val);
2063         if (tp == kVdwParType) {
2064                 up->vdw.r_eq = r;
2065                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2066                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2067         } else if (tp == kVdwPairParType) {
2068                 up->vdwp.r_eq = r;
2069                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2070                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2071         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2072         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2073         return val;
2074 }
2075
2076 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2077         UnionPar *up;
2078         Int tp, oldsrc;
2079         Double e;
2080         VALUE oldval;
2081         up = s_UnionParFromValue(self, &tp, 1);
2082         oldval = s_ParameterRef_GetEps(self);
2083         oldsrc = up->bond.src;
2084         val = rb_Float(val);
2085         e = NUM2DBL(val) * KCAL2INTERNAL;
2086         if (tp == kVdwParType) {
2087                 up->vdw.eps = e;
2088                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2089                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2090         } else if (tp == kVdwPairParType) {
2091                 up->vdwp.eps = e;
2092                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2093                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2094         } else rb_raise(rb_eMolbyError, "invalid member eps");
2095         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2096         return val;
2097 }
2098
2099 /*
2100 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2101         UnionPar *up;
2102         Int tp, oldsrc;
2103         double d;
2104         VALUE oldval;
2105         up = s_UnionParFromValue(self, &tp, 1);
2106         oldval = s_ParameterRef_GetA14(self);
2107         oldsrc = up->bond.src;
2108         val = rb_Float(val);
2109         d = NUM2DBL(val);
2110         if (tp == kVdwParType)
2111                 up->vdw.A14 = d;
2112         else if (tp == kVdwPairParType)
2113                 up->vdwp.A14 = d;
2114         else rb_raise(rb_eMolbyError, "invalid member A14");
2115         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2116         return val;
2117 }
2118
2119 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2120         UnionPar *up;
2121         Int tp, oldsrc;
2122         double d;
2123         VALUE oldval;
2124         up = s_UnionParFromValue(self, &tp, 1);
2125         oldval = s_ParameterRef_GetB14(self);
2126         oldsrc = up->bond.src;
2127         val = rb_Float(val);
2128         d = NUM2DBL(val);
2129         if (tp == kVdwParType)
2130                 up->vdw.B14 = d;
2131         else if (tp == kVdwPairParType)
2132                 up->vdwp.B14 = d;
2133         else rb_raise(rb_eMolbyError, "invalid member B14");
2134         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2135         return val;
2136 }
2137 */
2138
2139 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2140         UnionPar *up;
2141         Int tp, oldsrc;
2142         Double r;
2143         VALUE oldval;
2144         up = s_UnionParFromValue(self, &tp, 1);
2145         oldval = s_ParameterRef_GetReq14(self);
2146         oldsrc = up->bond.src;
2147         val = rb_Float(val);
2148         r = NUM2DBL(val);
2149         if (tp == kVdwParType) {
2150                 up->vdw.r_eq14 = r;
2151                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2152                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2153         } else if (tp == kVdwPairParType) {
2154                 up->vdwp.r_eq14 = r;
2155                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2156                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2157         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2158         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2159         return val;
2160 }
2161
2162 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2163         UnionPar *up;
2164         Int tp, oldsrc;
2165         Double e;
2166         VALUE oldval;
2167         up = s_UnionParFromValue(self, &tp, 1);
2168         oldval = s_ParameterRef_GetEps14(self);
2169         oldsrc = up->bond.src;
2170         val = rb_Float(val);
2171         e = NUM2DBL(val) * KCAL2INTERNAL;
2172         if (tp == kVdwParType) {
2173                 up->vdw.eps14 = e;
2174                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2175                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2176         } else if (tp == kVdwPairParType) {
2177                 up->vdwp.eps14 = e;
2178                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2179                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2180         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2181         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2182         return val;
2183 }
2184
2185 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2186         UnionPar *up;
2187         Int tp, oldsrc;
2188         VALUE oldval;
2189         oldval = s_ParameterRef_GetCutoff(self);
2190         oldsrc = up->bond.src;
2191         up = s_UnionParFromValue(self, &tp, 1);
2192         val = rb_Float(val);
2193         if (tp == kVdwCutoffParType) {
2194                 up->vdwcutoff.cutoff = NUM2DBL(val);
2195         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2196         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2197         return val;
2198 }
2199
2200 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2201         UnionPar *up;
2202         Int tp, oldsrc;
2203         VALUE oldval;
2204         up = s_UnionParFromValue(self, &tp, 1);
2205         oldval = s_ParameterRef_GetRadius(self);
2206         oldsrc = up->bond.src;
2207         val = rb_Float(val);
2208         if (tp == kElementParType) {
2209                 up->atom.radius = NUM2DBL(val);
2210         } else rb_raise(rb_eMolbyError, "invalid member radius");
2211         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2212         return val;
2213 }
2214
2215 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2216         UnionPar *up;
2217         Int tp, oldsrc;
2218         VALUE oldval;
2219         up = s_UnionParFromValue(self, &tp, 1);
2220         oldval = s_ParameterRef_GetVdwRadius(self);
2221         oldsrc = up->bond.src;
2222         val = rb_Float(val);
2223         if (tp == kElementParType) {
2224                 up->atom.vdw_radius = NUM2DBL(val);
2225         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2226         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2227         return val;
2228 }
2229
2230 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2231         UnionPar *up;
2232         Int tp, oldsrc;
2233         VALUE *valp, oldval;
2234         up = s_UnionParFromValue(self, &tp, 1);
2235         oldval = s_ParameterRef_GetColor(self);
2236         oldsrc = up->bond.src;
2237         val = rb_ary_to_ary(val);
2238         if (RARRAY_LEN(val) != 3)
2239                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2240         valp = RARRAY_PTR(val);
2241         if (tp == kElementParType) {
2242                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2243                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2244                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2245         } else rb_raise(rb_eMolbyError, "invalid member color");
2246         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2247         return val;
2248 }
2249
2250 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2251         UnionPar *up;
2252         Int tp, oldsrc;
2253         VALUE oldval;
2254         up = s_UnionParFromValue(self, &tp, 1);
2255         oldval = s_ParameterRef_GetAtomicNumber(self);
2256         oldsrc = up->bond.src;
2257         val = rb_Integer(val);
2258         if (tp == kElementParType)
2259                 up->atom.number = NUM2INT(val);
2260         else if (tp == kVdwParType) {
2261                 up->vdw.atomicNumber = NUM2INT(val);
2262                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2263         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2264         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2265         return val;
2266 }
2267
2268 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2269         UnionPar *up;
2270         Int tp, oldsrc;
2271         VALUE oldval;
2272         up = s_UnionParFromValue(self, &tp, 1);
2273         oldval = s_ParameterRef_GetName(self);
2274         oldsrc = up->bond.src;
2275         if (tp == kElementParType) {
2276                 strncpy(up->atom.name, StringValuePtr(val), 4);
2277         } else rb_raise(rb_eMolbyError, "invalid member name");
2278         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2279         return val;
2280 }
2281
2282 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2283         UnionPar *up;
2284         Int tp, oldsrc;
2285         VALUE oldval;
2286         val = rb_Float(val);
2287         oldval = s_ParameterRef_GetWeight(self);
2288         up = s_UnionParFromValue(self, &tp, 1);
2289         oldsrc = up->bond.src;
2290         if (tp == kElementParType)
2291                 up->atom.weight = NUM2DBL(val);
2292         else if (tp == kVdwParType)
2293                 up->vdw.weight = NUM2DBL(val);
2294         else rb_raise(rb_eMolbyError, "invalid member weight");
2295         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2296         return val;
2297 }
2298
2299 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2300         UnionPar *up;
2301         Int tp, oldsrc;
2302         VALUE oldval;
2303         up = s_UnionParFromValue(self, &tp, 1);
2304         oldval = s_ParameterRef_GetFullName(self);
2305         oldsrc = up->bond.src;
2306         if (tp == kElementParType) {
2307                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2308                 up->atom.fullname[15] = 0;
2309         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2310         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2311         return val;
2312 }
2313
2314 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2315         UnionPar *up;
2316         Int tp, com, oldsrc;
2317         VALUE oldval;
2318         up = s_UnionParFromValue(self, &tp, 1);
2319         oldval = s_ParameterRef_GetComment(self);
2320         oldsrc = up->bond.src;
2321         if (val == Qnil)
2322                 up->bond.com = 0;
2323         else {
2324                 com = ParameterCommentIndex(StringValuePtr(val));
2325                 up->bond.com = com;
2326         }
2327         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2328         return val;     
2329 }
2330
2331 /*  Only false (undefined) and nil (local) can be set  */
2332 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2333         UnionPar *up;
2334         Int tp, oldsrc;
2335         VALUE oldval;
2336         up = s_UnionParFromValue(self, &tp, 1);
2337         if (val != Qfalse && val != Qnil)
2338                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2339         oldval = s_ParameterRef_GetSource(self);
2340         oldsrc = up->bond.src;
2341         if (oldsrc != 0 && oldsrc != -1)
2342                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2343         up->bond.src = (val == Qfalse ? -1 : 0);
2344         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2345         return val;     
2346 }
2347
2348 static struct s_ParameterAttrDef {
2349         char *name;
2350         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2351         ID id;                  /*  Will be set within InitMolby()  */
2352         VALUE (*getter)(VALUE);
2353         VALUE (*setter)(VALUE, VALUE);
2354 } s_ParameterAttrDefTable[] = {
2355         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2356         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2357         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2358         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2359         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2360         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2361         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2362         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2363         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2364         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2365 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2366         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2367         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2368         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2369 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2370         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2371         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2372         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2373         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2374         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2375         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2376         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2377         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2378         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2379         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2380         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2381         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2382         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2383         {NULL} /* Sentinel */
2384 };
2385
2386 static VALUE
2387 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2388 {
2389         int i;
2390         ID kid;
2391         if (TYPE(key) != T_SYMBOL) {
2392                 kid = rb_intern(StringValuePtr(key));
2393                 key = ID2SYM(kid);
2394         } else kid = SYM2ID(key);
2395         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2396                 if (s_ParameterAttrDefTable[i].id == kid) {
2397                         if (value == Qundef)
2398                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2399                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2400                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2401                         else
2402                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2403                 }
2404         }
2405         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2406         return Qnil; /* not reached */
2407 }
2408
2409 static VALUE
2410 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2411 {
2412         return s_ParameterRef_SetAttr(self, key, Qundef);
2413 }
2414
2415 /*
2416  *  call-seq:
2417  *     keys(idx)          -> array of valid parameter attributes
2418  *  
2419  *  Returns an array of valid parameter attributes (as Symbols).
2420  */
2421 static VALUE
2422 s_ParameterRef_Keys(VALUE self)
2423 {
2424         ParameterRef *pref;
2425         Data_Get_Struct(self, ParameterRef, pref);
2426         switch (pref->parType) {
2427                 case kBondParType:
2428                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2429                 case kAngleParType:
2430                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2431                 case kDihedralParType:
2432                 case kImproperParType:
2433                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2434                 case kVdwParType:
2435                         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);
2436                 case kVdwPairParType:
2437                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2438                 case kVdwCutoffParType:
2439                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2440                 case kElementParType:
2441                         return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_VdwRadiusSym, s_CommentSym, s_SourceSym);
2442                 default:
2443                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2444         }
2445         return Qnil;  /*  Not reached  */
2446 }
2447
2448 /*
2449  *  call-seq:
2450  *     to_hash(idx)          -> Hash
2451  *  
2452  *  Returns a hash containing valid parameter names and values
2453  */
2454 static VALUE
2455 s_ParameterRef_ToHash(VALUE self)
2456 {
2457         VALUE keys = s_ParameterRef_Keys(self);
2458         VALUE retval;
2459         int i;
2460         if (keys == Qnil)
2461                 return Qnil;
2462         retval = rb_hash_new();
2463         for (i = 0; i < RARRAY_LEN(keys); i++) {
2464                 VALUE key = RARRAY_PTR(keys)[i];
2465                 VALUE val = s_ParameterRef_GetAttr(self, key);
2466                 rb_hash_aset(retval, key, val);
2467         }
2468         return retval;
2469 }
2470
2471 /*
2472  *  call-seq:
2473  *     parameter.to_s(idx)          -> String
2474  *  
2475  *  Returns a string representation of the given parameter
2476  */
2477 static VALUE
2478 s_ParameterRef_ToString(VALUE self)
2479 {
2480         Int tp, i, n;
2481         char buf[1024], types[4][8];
2482         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2483         switch (tp) {
2484                 case kBondParType:
2485                         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);
2486                         break;
2487                 case kAngleParType:
2488                         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);
2489                         break;
2490                 case kDihedralParType:
2491                 case kImproperParType:
2492                         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]));
2493                         n = strlen(buf);
2494                         for (i = 0; i < up->torsion.mult; i++) {
2495                                 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);
2496                                 n = strlen(buf);
2497                         }
2498                         break;
2499                 case kVdwParType:
2500                         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);
2501                         break;
2502                 case kVdwPairParType:
2503                         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);
2504                         break;
2505                 case kVdwCutoffParType:
2506                         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);
2507                         break;
2508                 case kElementParType:
2509                         snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s %6.3f", up->atom.name, up->atom.number, up->atom.radius, up->atom.red / 65535.0, up->atom.green / 65535.0, up->atom.blue / 65535.0, up->atom.weight, up->atom.fullname, up->atom.vdw_radius);
2510                         break;
2511         }
2512         return rb_str_new2(buf);
2513 }
2514
2515 /*
2516  *  call-seq:
2517  *     self == parameterRef -> boolean
2518  *  
2519  *  True if the parameters point to the same parameter record.
2520  */
2521 static VALUE
2522 s_ParameterRef_Equal(VALUE self, VALUE val)
2523 {
2524         Int tp1, tp2;
2525         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2526                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2527         } else return Qfalse;
2528 }
2529         
2530 #pragma mark ====== Parameter Class ======
2531
2532 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2533  *  is NULL, then the global parameters are looked for.  */
2534
2535 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2536 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2537 static void
2538 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2539 {
2540         Molecule *mol;
2541         Data_Get_Struct(val, Molecule, mol);
2542         if (mol == NULL)
2543                 rb_raise(rb_eMolbyError, "the molecule is empty");
2544         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2545                 /*  Do self.md_arena.prepare  */
2546                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2547                 if (val2 != Qnil)
2548                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2549         }
2550 }
2551
2552 static VALUE
2553 s_NewParameterValueFromValue(VALUE val)
2554 {
2555         Molecule *mol;
2556         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2557                 Data_Get_Struct(val, Molecule, mol);
2558                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2559                 MoleculeRetain(mol);
2560                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2561         } else {
2562                 mol = NULL;
2563                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2564         }
2565 }
2566
2567 static Molecule *
2568 s_MoleculeFromParameterValue(VALUE val)
2569 {
2570         Molecule *mol;
2571         Data_Get_Struct(val, Molecule, mol);
2572         return mol;
2573 }
2574
2575 static Parameter *
2576 s_ParameterFromParameterValue(VALUE val)
2577 {
2578         Molecule *mol;
2579         Data_Get_Struct(val, Molecule, mol);
2580         if (mol != NULL)
2581                 return mol->par;
2582         return gBuiltinParameters;
2583 }
2584
2585 /*  Forward declarations  */
2586 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2587 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2588
2589 static Molecule *
2590 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2591 {
2592         if (val == rb_cParameter) {
2593                 return NULL;  /*  Parameter class method: builtin parameters  */
2594         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2595                 return s_MoleculeFromParameterValue(val);
2596         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2597                 return s_MoleculeFromParEnumerableValue(val);
2598         } else return NULL;
2599 }
2600
2601 /*
2602  *  call-seq:
2603  *     builtin    -> Parameter
2604  *  
2605  *  Returns a parameter value that points to the global (builtin) parameters.
2606  *  Equivalent to Parameter::Builtin (constant).
2607  */
2608 static VALUE
2609 s_Parameter_Builtin(VALUE self)
2610 {
2611         static ID s_builtin_id = 0;
2612         if (s_builtin_id == 0)
2613                 s_builtin_id = rb_intern("Builtin");
2614         return rb_const_get(rb_cParameter, s_builtin_id);
2615 }
2616
2617 /*
2618  *  call-seq:
2619  *     bond(idx)          -> ParameterRef
2620  *  
2621  *  The index-th bond parameter record is returned.
2622  */
2623 static VALUE
2624 s_Parameter_Bond(VALUE self, VALUE ival)
2625 {
2626         Molecule *mol;
2627         int idx, n;
2628         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2629         idx = NUM2INT(rb_Integer(ival));
2630         if (mol == NULL)
2631                 n = gBuiltinParameters->nbondPars;
2632         else if (mol->par != NULL)
2633                 n = mol->par->nbondPars;
2634         else n = 0;
2635         if (idx < -n || idx >= n)
2636                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2637         if (idx < 0)
2638                 idx += n;
2639         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2640 }
2641
2642 /*
2643  *  call-seq:
2644  *     angle(idx)          -> ParameterRef
2645  *  
2646  *  The index-th angle parameter record is returned.
2647  */
2648 static VALUE
2649 s_Parameter_Angle(VALUE self, VALUE ival)
2650 {
2651         Molecule *mol;
2652         int idx, n;
2653         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2654         idx = NUM2INT(rb_Integer(ival));
2655         if (mol == NULL)
2656                 n = gBuiltinParameters->nanglePars;
2657         else if (mol->par != NULL)
2658                 n = mol->par->nanglePars;
2659         else n = 0;
2660         if (idx < -n || idx >= n)
2661                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2662         if (idx < 0)
2663                 idx += n;
2664         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2665 }
2666
2667 /*
2668  *  call-seq:
2669  *     dihedral(idx)          -> ParameterRef
2670  *  
2671  *  The index-th dihedral parameter record is returned.
2672  */
2673 static VALUE
2674 s_Parameter_Dihedral(VALUE self, VALUE ival)
2675 {
2676         Molecule *mol;
2677         int idx, n;
2678         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2679         idx = NUM2INT(rb_Integer(ival));
2680         if (mol == NULL)
2681                 n = gBuiltinParameters->ndihedralPars;
2682         else if (mol->par != NULL)
2683                 n = mol->par->ndihedralPars;
2684         else n = 0;
2685         if (idx < -n || idx >= n)
2686                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2687         if (idx < 0)
2688                 idx += n;
2689         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2690 }
2691
2692 /*
2693  *  call-seq:
2694  *     improper(idx)          -> ParameterRef
2695  *  
2696  *  The index-th improper parameter record is returned.
2697  */
2698 static VALUE
2699 s_Parameter_Improper(VALUE self, VALUE ival)
2700 {
2701         Molecule *mol;
2702         int idx, n;
2703         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2704         idx = NUM2INT(rb_Integer(ival));
2705         if (mol == NULL)
2706                 n = gBuiltinParameters->nimproperPars;
2707         else if (mol->par != NULL)
2708                 n = mol->par->nimproperPars;
2709         else n = 0;
2710         if (idx < -n || idx >= n)
2711                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2712         if (idx < 0)
2713                 idx += n;
2714         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2715 }
2716
2717 /*
2718  *  call-seq:
2719  *     vdw(idx)          -> ParameterRef
2720  *  
2721  *  The index-th vdw parameter record is returned.
2722  */
2723 static VALUE
2724 s_Parameter_Vdw(VALUE self, VALUE ival)
2725 {
2726         Molecule *mol;
2727         int idx, n;
2728         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2729         idx = NUM2INT(rb_Integer(ival));
2730         if (mol == NULL)
2731                 n = gBuiltinParameters->nvdwPars;
2732         else if (mol->par != NULL)
2733                 n = mol->par->nvdwPars;
2734         else n = 0;
2735         if (idx < -n || idx >= n)
2736                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2737         if (idx < 0)
2738                 idx += n;
2739         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2740 }
2741
2742 /*
2743  *  call-seq:
2744  *     vdw_pair(idx)          -> ParameterRef
2745  *  
2746  *  The index-th vdw pair parameter record is returned.
2747  */
2748 static VALUE
2749 s_Parameter_VdwPair(VALUE self, VALUE ival)
2750 {
2751         Molecule *mol;
2752         int idx, n;
2753         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2754         idx = NUM2INT(rb_Integer(ival));
2755         if (mol == NULL)
2756                 n = gBuiltinParameters->nvdwpPars;
2757         else if (mol->par != NULL)
2758                 n = mol->par->nvdwpPars;
2759         else n = 0;
2760         if (idx < -n || idx >= n)
2761                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2762         if (idx < 0)
2763                 idx += n;
2764         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2765 }
2766
2767 /*
2768  *  call-seq:
2769  *     vdw_cutoff(idx)          -> ParameterRef
2770  *  
2771  *  The index-th vdw cutoff parameter record is returned.
2772  */
2773 static VALUE
2774 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2775 {
2776         Molecule *mol;
2777         int idx, n;
2778         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2779         idx = NUM2INT(rb_Integer(ival));
2780         if (mol == NULL)
2781                 n = gBuiltinParameters->nvdwCutoffPars;
2782         else if (mol->par != NULL)
2783                 n = mol->par->nvdwCutoffPars;
2784         else n = 0;
2785         if (idx < -n || idx >= n)
2786                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2787         if (idx < 0)
2788                 idx += n;
2789         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2790 }
2791
2792 /*
2793  *  call-seq:
2794  *     element(idx)            -> ParameterRef
2795  *     element(t1)             -> ParameterRef
2796  *  
2797  *  In the first form, the index-th element parameter record is returned. In the second
2798  *  form, the element parameter for t1 is looked up (the last index first). t1
2799  *  is the element name string (up to 4 characters).
2800  *  Unlike other Parameter methods, this is used only for the global parameter.
2801  */
2802 static VALUE
2803 s_Parameter_Element(VALUE self, VALUE ival)
2804 {
2805         Int idx1;
2806         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2807                 int n = gCountElementParameters;
2808                 idx1 = NUM2INT(rb_Integer(ival));
2809                 if (idx1 < -n || idx1 >= n)
2810                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2811                 if (idx1 < 0)
2812                         idx1 += n;
2813                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2814         } else {
2815                 ElementPar *ep;
2816                 char name[6];
2817                 int i;
2818                 strncpy(name, StringValuePtr(ival), 4);
2819                 name[4] = 0;
2820                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2821                         if (strncmp(ep->name, name, 4) == 0)
2822                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2823                 }
2824                 return Qnil;
2825         }
2826 }
2827
2828 /*
2829  *  call-seq:
2830  *     nbonds          -> Integer
2831  *  
2832  *  Returns the number of bond parameters.
2833  */
2834 static VALUE
2835 s_Parameter_Nbonds(VALUE self)
2836 {
2837         Int n;
2838         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2839         if (mol == NULL)
2840                 n = gBuiltinParameters->nbondPars;
2841         else if (mol->par != NULL)
2842                 n = mol->par->nbondPars;
2843         else n = 0;
2844         return INT2NUM(n);
2845 }
2846
2847 /*
2848  *  call-seq:
2849  *     nangles          -> Integer
2850  *  
2851  *  Returns the number of angle parameters.
2852  */
2853 static VALUE
2854 s_Parameter_Nangles(VALUE self)
2855 {
2856         Int n;
2857         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2858         if (mol == NULL)
2859                 n = gBuiltinParameters->nanglePars;
2860         else if (mol->par != NULL)
2861                 n = mol->par->nanglePars;
2862         else n = 0;
2863         return INT2NUM(n);
2864 }
2865
2866 /*
2867  *  call-seq:
2868  *     ndihedrals          -> Integer
2869  *  
2870  *  Returns the number of dihedral parameters.
2871  */
2872 static VALUE
2873 s_Parameter_Ndihedrals(VALUE self)
2874 {
2875         Int n;
2876         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2877         if (mol == NULL)
2878                 n = gBuiltinParameters->ndihedralPars;
2879         else if (mol->par != NULL)
2880                 n = mol->par->ndihedralPars;
2881         else n = 0;
2882         return INT2NUM(n);
2883 }
2884
2885 /*
2886  *  call-seq:
2887  *     nimpropers          -> Integer
2888  *  
2889  *  Returns the number of improper parameters.
2890  */
2891 static VALUE
2892 s_Parameter_Nimpropers(VALUE self)
2893 {
2894         Int n;
2895         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2896         if (mol == NULL)
2897                 n = gBuiltinParameters->nimproperPars;
2898         else if (mol->par != NULL)
2899                 n = mol->par->nimproperPars;
2900         else n = 0;
2901         return INT2NUM(n);
2902 }
2903
2904 /*
2905  *  call-seq:
2906  *     nvdws          -> Integer
2907  *  
2908  *  Returns the number of vdw parameters.
2909  */
2910 static VALUE
2911 s_Parameter_Nvdws(VALUE self)
2912 {
2913         Int n;
2914         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2915         if (mol == NULL)
2916                 n = gBuiltinParameters->nvdwPars;
2917         else if (mol->par != NULL)
2918                 n = mol->par->nvdwPars;
2919         else n = 0;
2920         return INT2NUM(n);
2921 }
2922
2923 /*
2924  *  call-seq:
2925  *     nvdw_pairs          -> Integer
2926  *  
2927  *  Returns the number of vdw pair parameters.
2928  */
2929 static VALUE
2930 s_Parameter_NvdwPairs(VALUE self)
2931 {
2932         Int n;
2933         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2934         if (mol == NULL)
2935                 n = gBuiltinParameters->nvdwpPars;
2936         else if (mol->par != NULL)
2937                 n = mol->par->nvdwpPars;
2938         else n = 0;
2939         return INT2NUM(n);
2940 }
2941
2942 /*
2943  *  call-seq:
2944  *     nvdw_cutoffs          -> Integer
2945  *  
2946  *  Returns the number of vdw cutoff parameters.
2947  */
2948 static VALUE
2949 s_Parameter_NvdwCutoffs(VALUE self)
2950 {
2951         Int n;
2952         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2953         if (mol == NULL)
2954                 n = gBuiltinParameters->nvdwCutoffPars;
2955         else if (mol->par != NULL)
2956                 n = mol->par->nvdwCutoffPars;
2957         else n = 0;
2958         return INT2NUM(n);
2959 }
2960
2961 /*
2962  *  call-seq:
2963  *     nelements          -> Integer
2964  *  
2965  *  Returns the number of element parameters.
2966  */
2967 static VALUE
2968 s_Parameter_Nelements(VALUE self)
2969 {
2970         return INT2NUM(gCountElementParameters);
2971 }
2972
2973 /*
2974  *  call-seq:
2975  *     bonds          -> ParEnumerable
2976  *  
2977  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2978  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2979  *  useful when all accessible parameters should be examined by use of 'each' method.
2980  */
2981 static VALUE
2982 s_Parameter_Bonds(VALUE self)
2983 {
2984         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2985         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2986 }
2987
2988 /*
2989  *  call-seq:
2990  *     angles          -> ParEnumerable
2991  *  
2992  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2993  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2994  *  useful when all accessible parameters should be examined by use of 'each' method.
2995  */
2996 static VALUE
2997 s_Parameter_Angles(VALUE self)
2998 {
2999         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3000         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3001 }
3002
3003 /*
3004  *  call-seq:
3005  *     dihedrals          -> ParEnumerable
3006  *  
3007  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3008  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3009  *  useful when all accessible parameters should be examined by use of 'each' method.
3010  */
3011 static VALUE
3012 s_Parameter_Dihedrals(VALUE self)
3013 {
3014         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3015         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3016 }
3017
3018 /*
3019  *  call-seq:
3020  *     impropers          -> ParEnumerable
3021  *  
3022  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3023  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3024  *  useful when all accessible parameters should be examined by use of 'each' method.
3025  */
3026 static VALUE
3027 s_Parameter_Impropers(VALUE self)
3028 {
3029         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3030         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3031 }
3032
3033 /*
3034  *  call-seq:
3035  *     vdws          -> ParEnumerable
3036  *  
3037  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3038  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3039  *  useful when all accessible parameters should be examined by use of 'each' method.
3040  */
3041 static VALUE
3042 s_Parameter_Vdws(VALUE self)
3043 {
3044         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3045         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3046 }
3047
3048 /*
3049  *  call-seq:
3050  *     vdw_pairs          -> ParEnumerable
3051  *  
3052  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3053  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3054  *  useful when all accessible parameters should be examined by use of 'each' method.
3055  */
3056 static VALUE
3057 s_Parameter_VdwPairs(VALUE self)
3058 {
3059         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3060         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3061 }
3062
3063 /*
3064  *  call-seq:
3065  *     vdw_cutoffs          -> ParEnumerable
3066  *  
3067  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3068  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3069  *  useful when all accessible parameters should be examined by use of 'each' method.
3070  */
3071 static VALUE
3072 s_Parameter_VdwCutoffs(VALUE self)
3073 {
3074         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3075         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3076 }
3077
3078 /*
3079  *  call-seq:
3080  *     elements          -> ParEnumerable
3081  *  
3082  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3083  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3084  *  useful when all accessible parameters should be examined by use of 'each' method.
3085  */
3086 static VALUE
3087 s_Parameter_Elements(VALUE self)
3088 {
3089         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3090         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3091 }
3092
3093 static VALUE
3094 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3095 {
3096         VALUE atval, optval;
3097         UInt t[4];
3098         Int ii[4];
3099         int i, n, idx, flags, is_global;
3100
3101         rb_scan_args(argc, argv, "1*", &atval, &optval);
3102         
3103         /*  Get the atom types  */
3104         switch (parType) {
3105                 case kBondParType: n = 2; break;
3106                 case kAngleParType: n = 3; break;
3107                 case kDihedralParType: n = 4; break;
3108                 case kImproperParType: n = 4; break;
3109                 case kVdwParType: n = 1; break;
3110                 case kVdwPairParType: n = 2; break;
3111                 default: return Qnil;
3112         }
3113         s_ScanAtomTypes(atval, n, t);
3114         for (i = 0; i < n; i++) {
3115                 if (t[i] < kAtomTypeMinimum) {
3116                         /*  Explicit atom index  */
3117                         if (mol == NULL)
3118                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3119                         if (t[i] >= mol->natoms)
3120                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3121                         ii[i] = t[i];
3122                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3123                 } else ii[i] = -1;
3124         }
3125         
3126         /*  Analyze options  */
3127         flags = 0;
3128         n = RARRAY_LEN(optval);
3129         for (i = 0; i < n; i++) {
3130                 VALUE oval = RARRAY_PTR(optval)[i];
3131                 if (oval == ID2SYM(rb_intern("global")))
3132                         flags |= kParameterLookupGlobal;
3133                 else if (oval == ID2SYM(rb_intern("local")))
3134                         flags |= kParameterLookupLocal;
3135                 else if (oval == ID2SYM(rb_intern("missing")))
3136                         flags |= kParameterLookupMissing;
3137                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3138                         flags |= kParameterLookupNoWildcard;
3139                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3140                         flags |= kParameterLookupNoBaseAtomType;
3141                 else if (oval == ID2SYM(rb_intern("create")))
3142                         flags |= 256;
3143         }
3144         if (flags == 0)
3145                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3146         
3147         idx = -1;
3148         is_global = 0;
3149         switch (parType) {
3150                 case kBondParType: {
3151                         BondPar *bp;
3152                         if (mol != NULL) {
3153                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3154                                 if (bp != NULL) {
3155                                         idx = bp - mol->par->bondPars;
3156                                         break;
3157                                 }
3158                         }
3159                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3160                         if (bp != NULL) {
3161                                 idx = bp - gBuiltinParameters->bondPars;
3162                                 is_global = 1;
3163                         }
3164                         break;
3165                 }
3166                 case kAngleParType: {
3167                         AnglePar *ap;
3168                         if (mol != NULL) {
3169                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3170                                 if (ap != NULL) {
3171                                         idx = ap - mol->par->anglePars;
3172                                         break;
3173                                 }
3174                         }
3175                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3176                         if (ap != NULL) {
3177                                 idx = ap - gBuiltinParameters->anglePars;
3178                                 is_global = 1;
3179                         }
3180                         break;
3181                 }
3182                 case kDihedralParType: {
3183                         TorsionPar *tp;
3184                         if (mol != NULL) {
3185                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3186                                 if (tp != NULL) {
3187                                         idx = tp - mol->par->dihedralPars;
3188                                         break;
3189                                 }
3190                         }
3191                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3192                         if (tp != NULL) {
3193                                 idx = tp - gBuiltinParameters->dihedralPars;
3194                                 is_global = 1;
3195                         }
3196                         break;
3197                 }
3198                 case kImproperParType: {
3199                         TorsionPar *tp;
3200                         if (mol != NULL) {
3201                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3202                                 if (tp != NULL) {
3203                                         idx = tp - mol->par->improperPars;
3204                                         break;
3205                                 }
3206                         }
3207                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3208                         if (tp != NULL) {
3209                                 idx = tp - gBuiltinParameters->improperPars;
3210                                 is_global = 1;
3211                         }
3212                         break;
3213                 }       
3214                 case kVdwParType: {
3215                         VdwPar *vp;
3216                         if (mol != NULL) {
3217                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3218                                 if (vp != NULL) {
3219                                         idx = vp - mol->par->vdwPars;
3220                                         break;
3221                                 }
3222                         }
3223                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3224                         if (vp != NULL) {
3225                                 idx = vp - gBuiltinParameters->vdwPars;
3226                                 is_global = 1;
3227                         }
3228                         break;
3229                 }       
3230                 case kVdwPairParType: {
3231                         VdwPairPar *vp;
3232                         if (mol != NULL) {
3233                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3234                                 if (vp != NULL) {
3235                                         idx = vp - mol->par->vdwpPars;
3236                                         break;
3237                                 }
3238                         }
3239                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3240                         if (vp != NULL) {
3241                                 idx = vp - gBuiltinParameters->vdwpPars;
3242                                 is_global = 1;
3243                         }
3244                         break;
3245                 }
3246                 default:
3247                         return Qnil;
3248         }
3249         if (idx < 0) {
3250                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3251                         return Qnil;            
3252                 else {
3253                         /*  Insert a new parameter record  */
3254                         UnionPar *up;
3255                         Int count = ParameterGetCountForType(mol->par, parType);
3256                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3257                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3258                         IntGroupRelease(ig);
3259                         is_global = 0;
3260                         idx = count;
3261                         /*  Set atom types  */
3262                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3263                         if (up == NULL)
3264                                 return Qnil;
3265                         switch (parType) {
3266                                 case kBondParType:
3267                                         up->bond.type1 = t[0];
3268                                         up->bond.type2 = t[1];
3269                                         break;
3270                                 case kAngleParType:
3271                                         up->angle.type1 = t[0];
3272                                         up->angle.type2 = t[1];
3273                                         up->angle.type3 = t[2];
3274                                         break;
3275                                 case kDihedralParType:
3276                                 case kImproperParType:
3277                                         up->torsion.type1 = t[0];
3278                                         up->torsion.type2 = t[1];
3279                                         up->torsion.type3 = t[2];
3280                                         up->torsion.type4 = t[3];
3281                                         break;
3282                                 case kVdwParType:
3283                                         up->vdw.type1 = t[0];
3284                                         break;
3285                                 case kVdwPairParType:
3286                                         up->vdwp.type1 = t[0];
3287                                         up->vdwp.type2 = t[1];
3288                                         break;
3289                                 default:
3290                                         return Qnil;
3291                         }
3292                 }
3293         }
3294         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3295 }
3296
3297 /*
3298  *  call-seq:
3299  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3300  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3301  *
3302  *  Find the parameter record that matches the given atom types. The atom types are given
3303  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3304  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3305  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3306  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3307  */
3308 static VALUE
3309 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3310 {
3311         int parType;
3312         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3313         if (argc == 0)
3314                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3315         parType = s_ParTypeFromValue(argv[0]);
3316         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3317 }
3318
3319 /*
3320  *  call-seq:
3321  *     self == parameter -> boolean
3322  *  
3323  *  True if the parameters point to the same parameter table.
3324  */
3325 static VALUE
3326 s_Parameter_Equal(VALUE self, VALUE val)
3327 {
3328         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3329                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3330         } else return Qfalse;
3331 }
3332
3333 #pragma mark ====== ParEnumerable Class ======
3334
3335 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3336  and the parameter type. If the Molecule is NULL, then it refers to the
3337  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3338  the global parameters are always accessible. */
3339
3340 typedef struct ParEnumerable {
3341         Molecule *mol;
3342         Int parType;   /*  Same as parType in ParameterRef  */
3343 } ParEnumerable;
3344
3345 static ParEnumerable *
3346 s_ParEnumerableNew(Molecule *mol, Int parType)
3347 {
3348         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3349         if (pen != NULL) {
3350                 pen->mol = mol;
3351                 if (mol != NULL)
3352                         MoleculeRetain(mol);
3353                 pen->parType = parType;
3354         }
3355         return pen;
3356 }
3357
3358 static void
3359 s_ParEnumerableRelease(ParEnumerable *pen)
3360 {
3361         if (pen != NULL) {
3362                 if (pen->mol != NULL)
3363                         MoleculeRelease(pen->mol);
3364                 free(pen);
3365         }
3366 }
3367
3368 static Molecule *
3369 s_MoleculeFromParEnumerableValue(VALUE val)
3370 {
3371         ParEnumerable *pen;
3372     Data_Get_Struct(val, ParEnumerable, pen);
3373         return pen->mol;
3374 }
3375
3376 static VALUE
3377 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3378 {
3379         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3380         if (pen == NULL)
3381                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3382         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3383 }
3384
3385 /*
3386  *  call-seq:
3387  *     par_type -> String
3388  *
3389  *  Get the parameter type, like "bond", "angle", etc.
3390  */
3391 static VALUE
3392 s_ParEnumerable_ParType(VALUE self) {
3393         ParEnumerable *pen;
3394         Int tp;
3395     Data_Get_Struct(self, ParEnumerable, pen);
3396         tp = pen->parType;
3397         if (tp == kElementParType)
3398                 return rb_str_new2("element");
3399         tp -= kFirstParType;
3400         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3401                 return rb_str_new2(s_ParameterTypeNames[tp]);
3402         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3403 }
3404
3405 /*
3406  *  call-seq:
3407  *     self[idx]          -> ParameterRef
3408  *  
3409  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3410  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3411  *  parent Parameter object of self.
3412  *
3413  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3414  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3415  */
3416 static VALUE
3417 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3418 {
3419         ParEnumerable *pen;
3420     Data_Get_Struct(self, ParEnumerable, pen);
3421         switch (pen->parType) {
3422                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3423                 case kBondParType:      return s_Parameter_Bond(self, ival);
3424                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3425                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3426                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3427                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3428                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3429                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3430                 case kElementParType:   return s_Parameter_Element(self, ival);
3431                 default:
3432                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3433         }
3434         return Qnil;  /*  Not reached  */
3435 }
3436
3437 /*
3438  *  call-seq:
3439  *     length          -> Integer
3440  *  
3441  *  Returns the number of parameters included in this enumerable.
3442  */
3443 static VALUE
3444 s_ParEnumerable_Length(VALUE self)
3445 {
3446         ParEnumerable *pen;
3447     Data_Get_Struct(self, ParEnumerable, pen);
3448         switch (pen->parType) {
3449                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3450                 case kBondParType:      return s_Parameter_Nbonds(self);
3451                 case kAngleParType:     return s_Parameter_Nangles(self); 
3452                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3453                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3454                 case kVdwParType:       return s_Parameter_Nvdws(self);
3455                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3456                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3457                 case kElementParType:   return s_Parameter_Nelements(self);
3458                 default:
3459                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3460         }
3461         return Qnil;  /*  Not reached  */
3462 }
3463
3464 /*
3465  *  call-seq:
3466  *     each {|pref| ...}
3467  *  
3468  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3469  */
3470 VALUE
3471 s_ParEnumerable_Each(VALUE self)
3472 {
3473         VALUE aval;
3474         ParEnumerable *pen;
3475         ParameterRef *pref;
3476         int i, ofs, n;
3477     Data_Get_Struct(self, ParEnumerable, pen);
3478         if (pen->parType == kElementParType)
3479                 n = gCountElementParameters;
3480         else {
3481                 switch (pen->parType) {
3482                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3483                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3484                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3485                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3486                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3487                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3488                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3489                         default:
3490                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3491                 }
3492                 if (pen->mol == NULL)
3493                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3494                 else if (pen->mol->par != NULL)
3495                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3496                 else return self;
3497         }               
3498         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3499         Data_Get_Struct(aval, ParameterRef, pref);
3500         for (i = 0; i < n; i++) {
3501                 pref->idx = i;
3502                 rb_yield(aval);
3503         }
3504     return self;
3505 }
3506
3507 /*
3508  *  call-seq:
3509  *     reverse_each {|pref| ...}
3510  *  
3511  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3512  */
3513 VALUE
3514 s_ParEnumerable_ReverseEach(VALUE self)
3515 {
3516         VALUE aval;
3517         ParEnumerable *pen;
3518         ParameterRef *pref;
3519         int i, ofs, n;
3520     Data_Get_Struct(self, ParEnumerable, pen);
3521         if (pen->parType == kElementParType)
3522                 n = gCountElementParameters;
3523         else {
3524                 switch (pen->parType) {
3525                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3526                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3527                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3528                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3529                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3530                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3531                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3532                         default:
3533                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3534                 }
3535                 if (pen->mol == NULL)
3536                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3537                 else if (pen->mol->par != NULL)
3538                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3539                 else return self;
3540         }               
3541         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3542         Data_Get_Struct(aval, ParameterRef, pref);
3543         for (i = n - 1; i >= 0; i--) {
3544                 pref->idx = i;
3545                 rb_yield(aval);
3546         }
3547     return self;
3548 }
3549
3550 /*
3551  *  call-seq:
3552  *     insert(idx = nil, pref = nil)       -> ParameterRef
3553  *  
3554  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3555  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3556  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3557  *  parameter is left undefined.
3558  *  Throws an exception if ParEnumerable points to the global parameter.
3559  */
3560 static VALUE
3561 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3562 {
3563         VALUE ival, pval;
3564         ParEnumerable *pen;
3565         int i, n;
3566         IntGroup *ig;
3567         UnionPar u;
3568         MolAction *act;
3569     Data_Get_Struct(self, ParEnumerable, pen);
3570         if (pen->mol == NULL)
3571                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3572         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3573         rb_scan_args(argc, argv, "02", &ival, &pval);
3574         if (ival != Qnil) {
3575                 i = NUM2INT(rb_Integer(ival));
3576                 if (i < 0 || i > n)
3577                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3578                 n = i;
3579         }
3580         if (pval != Qnil) {
3581                 Int type;
3582                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3583                 if (up == NULL || type != pen->parType)
3584                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3585                 ParameterCopyOneWithType(&u, up, pen->parType);
3586                 u.bond.src = 0;
3587         } else {
3588                 memset(&u, 0, sizeof(u));
3589                 u.bond.src = 0;
3590         }
3591         ig = IntGroupNewWithPoints(n, 1, -1);
3592         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3593
3594         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3595         MolActionCallback_registerUndo(pen->mol, act);
3596         MolActionRelease(act);
3597         
3598         IntGroupRelease(ig);
3599         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3600 }
3601
3602 /*
3603  *  call-seq:
3604  *     delete(Integer)
3605  *     delete(IntGroup)
3606  *  
3607  *  Delete the parameter(s) specified by the argument.
3608  */
3609 static VALUE
3610 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3611 {
3612         ParEnumerable *pen;
3613         int i, n;
3614         IntGroup *ig;
3615     Data_Get_Struct(self, ParEnumerable, pen);
3616         if (pen->mol == NULL)
3617                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3618         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3619         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3620                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3621                 i = 1;
3622         } else {
3623                 ig = IntGroupFromValue(ival);
3624                 if ((i = IntGroupGetCount(ig)) == 0) {
3625                         IntGroupRelease(ig);
3626                         return Qnil;
3627                 }
3628         }
3629         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3630                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3631         n = i;
3632
3633         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3634         IntGroupRelease(ig);
3635         return ival;
3636 }
3637
3638 /*
3639  *  call-seq:
3640  *     lookup(atom_types, options, ...) -> ParameterRef
3641  *     lookup(atom_type_string, options, ...) -> ParameterRef
3642  *
3643  *  Find the parameter record that matches the given atom types. The arguments are
3644  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3645  *  specified.
3646  */
3647 static VALUE
3648 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3649 {
3650         ParEnumerable *pen;
3651     Data_Get_Struct(self, ParEnumerable, pen);
3652         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3653 }
3654
3655 /*
3656  *  call-seq:
3657  *     self == parEnumerable -> boolean
3658  *  
3659  *  True if the arguments point to the same parameter table and type.
3660  */
3661 static VALUE
3662 s_ParEnumerable_Equal(VALUE self, VALUE val)
3663 {
3664         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3665                 ParEnumerable *pen1, *pen2;
3666                 Data_Get_Struct(self, ParEnumerable, pen1);
3667                 Data_Get_Struct(val, ParEnumerable, pen2);
3668                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3669         } else return Qfalse;
3670 }
3671
3672 #pragma mark ====== AtomRef Class ======
3673
3674 /*  Forward declaration for register undo  */
3675 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3676
3677 /*  Ruby string "set_atom_attr"  */
3678 static VALUE s_SetAtomAttrString;
3679
3680 static int
3681 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3682 {
3683         AtomRef *aref;
3684         int idx;
3685         Data_Get_Struct(self, AtomRef, aref);
3686         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3687         if (idx < 0 || idx >= aref->mol->natoms)
3688                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3689         if (app != NULL)
3690                 *app = aref->mol->atoms + idx;
3691         if (mpp != NULL)
3692                 *mpp = aref->mol;
3693         return idx;
3694 }
3695
3696 static Atom *
3697 s_AtomFromValue(VALUE self)
3698 {
3699         Atom *ap;
3700         s_AtomIndexFromValue(self, &ap, NULL);
3701         return ap;
3702 }
3703
3704 static Atom *
3705 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3706 {
3707         Atom *ap;
3708         s_AtomIndexFromValue(self, &ap, mpp);
3709         return ap;
3710 }
3711
3712 static void
3713 s_NotifyModificationForAtomRef(VALUE self)
3714 {
3715         AtomRef *aref;
3716         Data_Get_Struct(self, AtomRef, aref);
3717         MoleculeIncrementModifyCount(aref->mol);
3718 }
3719
3720 static void
3721 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3722 {
3723         AtomRef *aref;
3724         Data_Get_Struct(self, AtomRef, aref);
3725         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3726                 /*  Register undo  */
3727                 MolAction *act;
3728                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3729                 MolActionCallback_registerUndo(aref->mol, act);
3730                 MoleculeCallback_notifyModification(aref->mol, 0);
3731                 /*  Request MD rebuilt if necessary  */
3732                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3733                         aref->mol->needsMDRebuild = 1;
3734         }
3735 }
3736
3737 VALUE
3738 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3739 {
3740         AtomRef *aref;
3741         aref = AtomRefNew(mol, idx);
3742         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3743 }
3744
3745 static VALUE
3746 s_AtomRef_GetMolecule(VALUE self)
3747 {
3748         Molecule *mpp;
3749         s_AtomIndexFromValue(self, NULL, &mpp);
3750         return ValueFromMolecule(mpp);
3751 }
3752
3753 static VALUE s_AtomRef_GetIndex(VALUE self) {
3754         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3755 }
3756
3757 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3758         return INT2NUM(s_AtomFromValue(self)->segSeq);
3759 }
3760
3761 static VALUE s_AtomRef_GetSegName(VALUE self) {
3762         char *p = s_AtomFromValue(self)->segName;
3763         return rb_str_new(p, strlen_limit(p, 4));
3764 }
3765
3766 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3767         return INT2NUM(s_AtomFromValue(self)->resSeq);
3768 }
3769
3770 static VALUE s_AtomRef_GetResName(VALUE self) {
3771         char *p = s_AtomFromValue(self)->resName;
3772         return rb_str_new(p, strlen_limit(p, 4));
3773 }
3774
3775 static VALUE s_AtomRef_GetName(VALUE self) {
3776         char *p = s_AtomFromValue(self)->aname;
3777         return rb_str_new(p, strlen_limit(p, 4));
3778 }
3779
3780 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3781         int type = s_AtomFromValue(self)->type;
3782         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3783         return rb_str_new(p, strlen_limit(p, 6));
3784 }
3785
3786 static VALUE s_AtomRef_GetCharge(VALUE self) {
3787         return rb_float_new(s_AtomFromValue(self)->charge);
3788 }
3789
3790 static VALUE s_AtomRef_GetWeight(VALUE self) {
3791         return rb_float_new(s_AtomFromValue(self)->weight);
3792 }
3793
3794 static VALUE s_AtomRef_GetElement(VALUE self) {
3795         char *p = s_AtomFromValue(self)->element;
3796         return rb_str_new(p, strlen_limit(p, 4));
3797 }
3798
3799 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3800         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3801 }
3802
3803 static VALUE s_AtomRef_GetConnects(VALUE self) {
3804         VALUE retval;
3805         Int i, *cp;
3806         Atom *ap = s_AtomFromValue(self);
3807         retval = rb_ary_new();
3808         cp = AtomConnectData(&ap->connect);
3809         for (i = 0; i < ap->connect.count; i++)
3810                 rb_ary_push(retval, INT2NUM(cp[i]));
3811         return retval;
3812 }
3813
3814 static VALUE s_AtomRef_GetR(VALUE self) {
3815         return ValueFromVector(&(s_AtomFromValue(self)->r));
3816 }
3817
3818 static VALUE s_AtomRef_GetX(VALUE self) {
3819         return rb_float_new(s_AtomFromValue(self)->r.x);
3820 }
3821
3822 static VALUE s_AtomRef_GetY(VALUE self) {
3823         return rb_float_new(s_AtomFromValue(self)->r.y);
3824 }
3825
3826 static VALUE s_AtomRef_GetZ(VALUE self) {
3827         return rb_float_new(s_AtomFromValue(self)->r.z);
3828 }
3829
3830 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3831         Atom *ap;
3832         Molecule *mp;
3833         Vector r1;
3834         s_AtomIndexFromValue(self, &ap, &mp);
3835         r1 = ap->r;
3836         if (mp->cell != NULL)
3837                 TransformVec(&r1, mp->cell->rtr, &r1);
3838         return r1;
3839 }
3840
3841 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3842         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3843         return ValueFromVector(&r1);
3844 }
3845
3846 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3847         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3848 }
3849
3850 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3851         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3852 }
3853
3854 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3855         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3856 }
3857
3858 static VALUE s_AtomRef_GetSigma(VALUE self) {
3859         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3860 }
3861
3862 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3863         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3864 }
3865
3866 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3867         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3868 }
3869
3870 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3871         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3872 }
3873
3874 static VALUE s_AtomRef_GetV(VALUE self) {
3875         return ValueFromVector(&(s_AtomFromValue(self)->v));
3876 }
3877
3878 static VALUE s_AtomRef_GetF(VALUE self) {
3879         Vector v = s_AtomFromValue(self)->f;
3880         VecScaleSelf(v, INTERNAL2KCAL);
3881         return ValueFromVector(&v);
3882 }
3883
3884 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3885         return rb_float_new(s_AtomFromValue(self)->occupancy);
3886 }
3887
3888 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3889         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3890 }
3891
3892 static VALUE s_AtomRef_GetAniso(VALUE self) {
3893         VALUE retval;
3894         int i;
3895         Atom *ap = s_AtomFromValue(self);
3896         if (ap->aniso == NULL)
3897                 return Qnil;
3898         retval = rb_ary_new();
3899         for (i = 0; i < 6; i++)
3900                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3901         if (ap->aniso->has_bsig) {
3902                 rb_ary_push(retval, INT2NUM(0));
3903                 for (i = 0; i < 6; i++)
3904                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3905         }
3906         return retval;
3907 }
3908
3909 static VALUE s_AtomRef_GetSymop(VALUE self) {
3910         VALUE retval;
3911         Atom *ap = s_AtomFromValue(self);
3912         if (!ap->symop.alive)
3913                 return Qnil;
3914         retval = rb_ary_new();
3915         rb_ary_push(retval, INT2NUM(ap->symop.sym));
3916         rb_ary_push(retval, INT2NUM(ap->symop.dx));
3917         rb_ary_push(retval, INT2NUM(ap->symop.dy));
3918         rb_ary_push(retval, INT2NUM(ap->symop.dz));
3919         rb_ary_push(retval, INT2NUM(ap->symbase));
3920         return retval;
3921 }
3922
3923 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3924         return INT2NUM(s_AtomFromValue(self)->intCharge);
3925 }
3926
3927 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3928         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3929 }
3930
3931 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3932         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3933 }
3934
3935 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3936         Molecule *mol;
3937         Atom *ap;
3938         int idx, i;
3939         MDExclusion *exinfo;
3940         Int *exlist;
3941         VALUE retval, aval;
3942         idx = s_AtomIndexFromValue(self, &ap, &mol);
3943         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3944                 VALUE mval = ValueFromMolecule(mol);
3945                 s_RebuildMDParameterIfNecessary(mval, Qnil);
3946         }
3947         if (mol->arena->exinfo == NULL)
3948                 return Qnil;
3949         exinfo = mol->arena->exinfo + idx;
3950         exlist = mol->arena->exlist;
3951         retval = rb_ary_new();
3952         aval = rb_ary_new();
3953         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
3954                 rb_ary_push(aval, INT2NUM(exlist[i]));
3955         rb_ary_push(retval, aval);
3956         aval = rb_ary_new();
3957         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
3958                 rb_ary_push(aval, INT2NUM(exlist[i]));
3959         rb_ary_push(retval, aval);
3960         aval = rb_ary_new();
3961         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
3962                 rb_ary_push(aval, INT2NUM(exlist[i]));
3963         rb_ary_push(retval, aval);
3964         return retval;
3965 }
3966
3967 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3968         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3969 }
3970
3971 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3972         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3973 }
3974
3975 static VALUE s_AtomRef_GetHidden(VALUE self) {
3976         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3977 }
3978
3979 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3980         VALUE retval;
3981         Int i, count, *cp;
3982         Atom *ap = s_AtomFromValue(self);
3983         if (ap->anchor == NULL)
3984                 return Qnil;
3985         count = ap->anchor->connect.count;
3986         retval = rb_ary_new2(count * 2);
3987         cp = AtomConnectData(&ap->anchor->connect);
3988         for (i = 0; i < count; i++) {
3989                 rb_ary_store(retval, i, INT2NUM(cp[i]));
3990                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3991         }
3992         return retval;
3993 }
3994
3995 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3996         char *p = s_AtomFromValue(self)->uff_type;
3997         return rb_str_new(p, strlen_limit(p, 5));
3998 }
3999
4000 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4001         rb_raise(rb_eMolbyError, "index cannot be directly set");
4002         return Qnil;
4003 }
4004
4005 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4006         VALUE oval = s_AtomRef_GetSegSeq(self);
4007         val = rb_Integer(val);
4008         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4009         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4010         return val;
4011 }
4012
4013 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4014         char *p = StringValuePtr(val);
4015         VALUE oval = s_AtomRef_GetSegName(self);
4016         strncpy(s_AtomFromValue(self)->segName, p, 4);
4017         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4018         return val;
4019 }
4020
4021 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4022         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4023         return val; /* Not reached */
4024 }
4025
4026 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4027         Atom *ap = s_AtomFromValue(self);
4028         char *p = StringValuePtr(val);
4029         VALUE oval = s_AtomRef_GetName(self);
4030         if (ap->anchor != NULL && p[0] == '_')
4031                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4032         strncpy(ap->aname, p, 4);
4033         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4034         return val;
4035 }
4036
4037 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4038         Molecule *mp;
4039         char *p = StringValuePtr(val);
4040         VALUE oval = s_AtomRef_GetAtomType(self);
4041         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4042         if (type != 0 && type < kAtomTypeMinimum)
4043                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4044         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4045         mp->needsMDRebuild = 1;
4046         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4047         return val;
4048 }
4049
4050 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4051         Molecule *mp;
4052         VALUE oval = s_AtomRef_GetCharge(self);
4053         val = rb_Float(val);
4054         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4055         mp->needsMDRebuild = 1;
4056         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4057         return val;
4058 }
4059
4060 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4061         Molecule *mp;
4062         VALUE oval = s_AtomRef_GetWeight(self);
4063         val = rb_Float(val);
4064         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4065         mp->needsMDRebuild = 1;
4066         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4067         return val;
4068 }
4069
4070 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4071         Double w;
4072         Molecule *mp;
4073         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4074         char *p = StringValuePtr(val);
4075         VALUE oval = s_AtomRef_GetElement(self);
4076         ap->atomicNumber = ElementToInt(p);
4077         ElementToString(ap->atomicNumber, ap->element);
4078         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4079                 ap->weight = w;
4080         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4081         mp->needsMDRebuild = 1;
4082         return val;
4083 }
4084
4085 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4086         Double w;
4087         Molecule *mp;
4088         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4089         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4090         val = rb_Integer(val);
4091         ap->atomicNumber = NUM2INT(val);
4092         ElementToString(ap->atomicNumber, ap->element);
4093         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4094                 ap->weight = w;
4095         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4096         mp->needsMDRebuild = 1;
4097         return val;
4098 }
4099
4100 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4101         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4102         return val; /* Not reached */
4103 }
4104
4105 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4106         Vector v;
4107         Molecule *mp;
4108         VALUE oval = s_AtomRef_GetR(self);
4109         VectorFromValue(val, &v);
4110         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4111         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4112         mp->needsMDCopyCoordinates = 1;
4113         return val;
4114 }
4115
4116 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4117         Double f;
4118         Molecule *mp;
4119         VALUE oval = s_AtomRef_GetX(self);
4120         val = rb_Float(val);
4121         f = NUM2DBL(val);
4122         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4123         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4124         mp->needsMDCopyCoordinates = 1;
4125         return val;
4126 }
4127
4128 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4129         Double f;
4130         Molecule *mp;
4131         VALUE oval = s_AtomRef_GetY(self);
4132         val = rb_Float(val);
4133         f = NUM2DBL(val);
4134         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4135         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4136         mp->needsMDCopyCoordinates = 1;
4137         return val;
4138 }
4139
4140 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4141         Double f;
4142         Molecule *mp;
4143         VALUE oval = s_AtomRef_GetZ(self);
4144         val = rb_Float(val);
4145         f = NUM2DBL(val);
4146         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4147         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4148         mp->needsMDCopyCoordinates = 1;
4149         return val;
4150 }
4151
4152 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4153         Vector v, ov;
4154         Atom *ap;
4155         Molecule *mp;
4156         s_AtomIndexFromValue(self, &ap, &mp);
4157         ov = ap->r;
4158         VectorFromValue(val, &v);
4159         if (mp->cell != NULL)
4160                 TransformVec(&v, mp->cell->tr, &v);
4161         ap->r = v;
4162         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4163         mp->needsMDCopyCoordinates = 1;
4164         return val;
4165 }
4166
4167 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4168         double f;
4169         Vector v, ov;
4170         Atom *ap;
4171         Molecule *mp;
4172         s_AtomIndexFromValue(self, &ap, &mp);
4173         ov = v = ap->r;
4174         val = rb_Float(val);
4175         f = NUM2DBL(val);
4176         if (mp->cell != NULL) {
4177                 TransformVec(&v, mp->cell->rtr, &v);
4178                 v.x = f;
4179                 TransformVec(&v, mp->cell->tr, &v);
4180         } else v.x = f;
4181         ap->r = v;
4182         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4183         mp->needsMDCopyCoordinates = 1;
4184         return val;
4185 }
4186
4187 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4188         double f;
4189         Vector v, ov;
4190         Atom *ap;
4191         Molecule *mp;
4192         s_AtomIndexFromValue(self, &ap, &mp);
4193         ov = v = ap->r;
4194         val = rb_Float(val);
4195         f = NUM2DBL(val);
4196         if (mp->cell != NULL) {
4197                 TransformVec(&v, mp->cell->rtr, &v);
4198                 v.y = f;
4199                 TransformVec(&v, mp->cell->tr, &v);
4200         } else v.y = f;
4201         ap->r = v;
4202         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4203         mp->needsMDCopyCoordinates = 1;
4204         return val;
4205 }
4206
4207 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4208         double f;
4209         Vector v, ov;
4210         Atom *ap;
4211         Molecule *mp;
4212         s_AtomIndexFromValue(self, &ap, &mp);
4213         ov = v = ap->r;
4214         val = rb_Float(val);
4215         f = NUM2DBL(val);
4216         if (mp->cell != NULL) {
4217                 TransformVec(&v, mp->cell->rtr, &v);
4218                 v.z = f;
4219                 TransformVec(&v, mp->cell->tr, &v);
4220         } else v.z = f;
4221         ap->r = v;
4222         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4223         mp->needsMDCopyCoordinates = 1;
4224         return val;
4225 }
4226
4227 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4228         Vector v;
4229         Molecule *mp;
4230         VALUE oval = s_AtomRef_GetSigma(self);
4231         VectorFromValue(val, &v);
4232         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4233         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4234         mp->needsMDCopyCoordinates = 1;
4235         return val;
4236 }
4237
4238 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4239         Double f;
4240         Molecule *mp;
4241         VALUE oval = s_AtomRef_GetSigmaX(self);
4242         val = rb_Float(val);
4243         f = NUM2DBL(val);
4244         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4245         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4246         mp->needsMDCopyCoordinates = 1;
4247         return val;
4248 }
4249
4250 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4251         Double f;
4252         Molecule *mp;
4253         VALUE oval = s_AtomRef_GetSigmaY(self);
4254         val = rb_Float(val);
4255         f = NUM2DBL(val);
4256         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4257         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4258         mp->needsMDCopyCoordinates = 1;
4259         return val;
4260 }
4261
4262 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4263         Double f;
4264         Molecule *mp;
4265         VALUE oval = s_AtomRef_GetSigmaZ(self);
4266         val = rb_Float(val);
4267         f = NUM2DBL(val);
4268         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4269         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4270         mp->needsMDCopyCoordinates = 1;
4271         return val;
4272 }
4273
4274 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4275         Vector v;
4276         Atom *ap;
4277         Molecule *mp;
4278         VALUE oval = s_AtomRef_GetV(self);
4279         VectorFromValue(val, &v);
4280         s_AtomIndexFromValue(self, &ap, &mp);
4281         ap->v = v;
4282         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4283         mp->needsMDCopyCoordinates = 1;
4284         return val;
4285 }
4286
4287 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4288         Vector v;
4289         Molecule *mp;
4290         VALUE oval = s_AtomRef_GetF(self);
4291         VectorFromValue(val, &v);
4292         VecScaleSelf(v, KCAL2INTERNAL);
4293         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4294         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4295         mp->needsMDCopyCoordinates = 1;
4296         return val;
4297 }
4298
4299 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4300         VALUE oval = s_AtomRef_GetOccupancy(self);
4301         Molecule *mp;
4302         val = rb_Float(val);
4303         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4304         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4305         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4306         return val;
4307 }
4308
4309 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4310         VALUE oval = s_AtomRef_GetTempFactor(self);
4311         val = rb_Float(val);
4312         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4313         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4314         return val;
4315 }
4316
4317 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4318         AtomRef *aref;
4319         int i, n, type;
4320         VALUE *valp;
4321         Double f[12];
4322         VALUE oval = s_AtomRef_GetAniso(self);
4323         Data_Get_Struct(self, AtomRef, aref);
4324         val = rb_funcall(val, rb_intern("to_a"), 0);
4325         n = RARRAY_LEN(val);
4326         valp = RARRAY_PTR(val);
4327         for (i = 0; i < 6; i++) {
4328                 if (i < n)
4329                         f[i] = NUM2DBL(rb_Float(valp[i]));
4330                 else f[i] = 0.0;
4331         }
4332         if (n >= 7)
4333                 type = NUM2INT(rb_Integer(valp[6]));
4334         else type = 0;
4335         if (n >= 13) {
4336                 for (i = 0; i < 6; i++)
4337                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4338         } else {
4339                 for (i = 0; i < 6; i++)
4340                         f[i + 6] = 0.0;
4341         }
4342         i = s_AtomIndexFromValue(self, NULL, NULL);
4343         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4344         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4345         return val;
4346 }
4347
4348 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4349         Molecule *mol;
4350         Atom *ap;
4351         int i, n;
4352         VALUE *valp;
4353         Int ival[5];
4354         VALUE oval = s_AtomRef_GetSymop(self);
4355         i = s_AtomIndexFromValue(self, &ap, &mol);
4356         if (val == Qnil) {
4357                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4358         } else {
4359                 val = rb_funcall(val, rb_intern("to_a"), 0);
4360                 n = RARRAY_LEN(val);
4361                 valp = RARRAY_PTR(val);
4362                 for (i = 0; i < 5; i++) {
4363                         if (i < n) {
4364                                 if (valp[i] == Qnil)
4365                                         ival[i] = -100000;
4366                                 else 
4367                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4368                         } else ival[i] = -100000;
4369                 }
4370         }
4371         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4372                 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));
4373         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4374                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4375         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4376                 return val;  /*  No need to change  */
4377         if (ival[0] != -100000)
4378                 ap->symop.sym = ival[0];
4379         if (ival[1] != -100000)
4380                 ap->symop.dx = ival[1];
4381         if (ival[2] != -100000)
4382                 ap->symop.dy = ival[2];
4383         if (ival[3] != -100000)
4384                 ap->symop.dz = ival[3];
4385         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4386         if (ival[4] != -100000)
4387                 ap->symbase = ival[4];
4388         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4389                 /*  The anisotropic parameters should be recalculated  */
4390                 VALUE oaval = s_AtomRef_GetAniso(self);
4391                 MoleculeSetAnisoBySymop(mol, i);
4392                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4393         }
4394         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4395         return val;
4396 }
4397
4398 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4399         VALUE oval = s_AtomRef_GetIntCharge(self);
4400         val = rb_Integer(val);
4401         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4402         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4403         return val;
4404 }
4405
4406 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4407         Molecule *mp;
4408         VALUE oval = s_AtomRef_GetFixForce(self);
4409         val = rb_Float(val);
4410         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4411         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4412         mp->needsMDRebuild = 1;
4413         return val;
4414 }
4415
4416 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4417         Vector v;
4418         Molecule *mp;
4419         VALUE oval = s_AtomRef_GetFixPos(self);
4420         VectorFromValue(val, &v);
4421         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4422         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4423         mp->needsMDRebuild = 1;
4424         return val;
4425 }
4426
4427 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4428         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4429         return val; /* Not reached */
4430 }
4431
4432 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4433         VALUE oval = s_AtomRef_GetIntCharge(self);
4434         val = rb_Integer(val);
4435         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4436         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4437         return val;
4438 }
4439
4440 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4441         VALUE oval = s_AtomRef_GetIntCharge(self);
4442         val = rb_Integer(val);
4443         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4444         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4445         return val;
4446 }
4447
4448 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4449         Atom *ap = s_AtomFromValue(self);
4450         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4451         if (RTEST(val)) {
4452                 ap->exflags |= kAtomHiddenFlag;
4453         } else {
4454                 ap->exflags &= ~kAtomHiddenFlag;
4455         }
4456         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4457         return val;
4458 }
4459
4460 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4461         Int idx, i, j, k, n, *ip;
4462         Double *dp;
4463         Atom *ap;
4464         Molecule *mol;
4465         VALUE oval, v;
4466         AtomConnect ac;
4467         Int nUndoActions;
4468         MolAction **undoActions;
4469         memset(&ac, 0, sizeof(ac));
4470         idx = s_AtomIndexFromValue(self, &ap, &mol);
4471         oval = s_AtomRef_GetAnchorList(self);
4472         if (val != Qnil) {
4473                 val = rb_ary_to_ary(val);
4474                 n = RARRAY_LEN(val);
4475         } else n = 0;
4476         if (n == 0) {
4477                 if (ap->anchor != NULL) {
4478                         AtomConnectResize(&ap->anchor->connect, 0);
4479                         free(ap->anchor->coeffs);
4480                         free(ap->anchor);
4481                         ap->anchor = NULL;
4482                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4483                 }
4484                 return val;
4485         }
4486         if (n < 2)
4487                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4488         if (ap->aname[0] == '_')
4489                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4490         ip = (Int *)malloc(sizeof(Int) * n);
4491         dp = NULL;
4492         for (i = 0; i < n; i++) {
4493                 v = RARRAY_PTR(val)[i];
4494                 if (rb_obj_is_kind_of(v, rb_cFloat))
4495                         break;
4496                 j = NUM2INT(rb_Integer(v));
4497                 if (j < 0 || j >= mol->natoms)
4498                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4499                 for (k = 0; k < i; k++) {
4500                         if (ip[k] == j)
4501                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4502                 }
4503                 ip[i] = j;
4504         }
4505         if (i < n) {
4506                 if (i < 2)
4507                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4508                 else if (i * 2 != n)
4509                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4510                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4511                 for (i = 0; i < n / 2; i++) {
4512                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4513                         if (dp[i] <= 0.0)
4514                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4515                 }
4516                 n /= 2;
4517         }
4518         nUndoActions = 0;
4519         undoActions = NULL;
4520         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4521         free(dp);
4522         free(ip);
4523         if (i != 0)
4524                 rb_raise(rb_eMolbyError, "invalid argument");
4525         if (nUndoActions > 0) {
4526                 for (i = 0; i < nUndoActions; i++) {
4527                         MolActionCallback_registerUndo(mol, undoActions[i]);
4528                         MolActionRelease(undoActions[i]);
4529                 }
4530                 free(undoActions);
4531         }
4532         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4533         return val;
4534 }
4535
4536 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4537         Atom *ap = s_AtomFromValue(self);
4538         char *p = StringValuePtr(val);
4539         VALUE oval = s_AtomRef_GetUFFType(self);
4540         strncpy(ap->uff_type, p, 5);
4541         ap->uff_type[5] = 0;
4542         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4543         return val;
4544 }
4545
4546 static struct s_AtomAttrDef {
4547         char *name;
4548         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4549         ID id;                  /*  Will be set within InitMolby()  */
4550         VALUE (*getter)(VALUE);
4551         VALUE (*setter)(VALUE, VALUE);
4552 } s_AtomAttrDefTable[] = {
4553         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4554         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4555         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4556         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4557         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4558         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4559         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4560         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4561         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4562         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4563         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4564         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4565         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4566         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4567         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4568     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4569         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4570         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4571         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4572         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4573         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4574         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4575         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4576         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4577         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4578         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4579         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4580         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4581         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4582         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4583         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4584         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4585         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4586         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4587         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4588         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4589         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4590         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4591         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4592         {NULL} /* Sentinel */
4593 };
4594
4595 static VALUE
4596 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4597 {
4598         int i;
4599         ID kid;
4600         if (TYPE(key) != T_SYMBOL) {
4601                 kid = rb_intern(StringValuePtr(key));
4602                 key = ID2SYM(kid);
4603         } else kid = SYM2ID(key);
4604         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4605                 if (s_AtomAttrDefTable[i].id == kid) {
4606                         if (value == Qundef)
4607                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4608                         else
4609                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4610                 }
4611         }
4612         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4613         return Qnil; /* not reached */
4614 }
4615
4616 static VALUE
4617 s_AtomRef_GetAttr(VALUE self, VALUE key)
4618 {
4619         return s_AtomRef_SetAttr(self, key, Qundef);
4620 }
4621
4622 /*
4623  *  call-seq:
4624  *     self == atomRef -> boolean
4625  *
4626  *  True if the two references point to the same atom.
4627  */
4628 static VALUE
4629 s_AtomRef_Equal(VALUE self, VALUE val)
4630 {
4631         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4632                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4633         } else return Qfalse;
4634 }
4635
4636 #pragma mark ====== MolEnumerable Class ======
4637
4638 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4639
4640 /*
4641  *  call-seq:
4642  *     self[idx] -> AtomRef or Array of Integers
4643  *  
4644  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4645  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4646  *  value is a String. Otherwise, the return value is an Array of Integers.
4647  */
4648 static VALUE
4649 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4650 {
4651         MolEnumerable *mseq;
4652         Molecule *mol;
4653         int idx1, idx2;
4654     Data_Get_Struct(self, MolEnumerable, mseq);
4655         mol = mseq->mol;
4656         if (mseq->kind == kAtomKind) {
4657                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4658         }
4659         idx1 = NUM2INT(arg1);
4660         switch (mseq->kind) {
4661                 case kBondKind: {
4662                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4663                         if (idx2 < 0 || idx2 >= mol->nbonds)
4664                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4665                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4666                 }
4667                 case kAngleKind: {
4668                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4669                         if (idx2 < 0 || idx2 >= mol->nangles)
4670                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4671                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4672                 }
4673                 case kDihedralKind: {
4674                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4675                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4676                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4677                         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]));
4678                 }
4679                 case kImproperKind: {
4680                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4681                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4682                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4683                         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]));
4684                 }
4685                 case kResidueKind: {
4686                         char *p;
4687                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4688                         if (idx2 < 0 || idx2 >= mol->nresidues)
4689                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4690                         p = mol->residues[idx2];
4691                         return rb_str_new(p, strlen_limit(p, 4));
4692                 }
4693         }
4694         return Qnil;
4695 }
4696
4697 /*
4698  *  call-seq:
4699  *     length          -> Integer
4700  *  
4701  *  Returns the number of objects included in this enumerable.
4702  */
4703 static VALUE
4704 s_MolEnumerable_Length(VALUE self)
4705 {
4706         MolEnumerable *mseq;
4707     Data_Get_Struct(self, MolEnumerable, mseq);
4708         switch (mseq->kind) {
4709                 case kAtomKind:
4710                         return INT2NUM(mseq->mol->natoms);
4711                 case kBondKind:
4712                         return INT2NUM(mseq->mol->nbonds);
4713                 case kAngleKind:
4714                         return INT2NUM(mseq->mol->nangles);
4715                 case kDihedralKind:
4716                         return INT2NUM(mseq->mol->ndihedrals);
4717                 case kImproperKind:
4718                         return INT2NUM(mseq->mol->nimpropers);
4719                 case kResidueKind:
4720                         return INT2NUM(mseq->mol->nresidues);
4721         }
4722         return INT2NUM(-1);
4723 }
4724
4725 /*
4726  *  call-seq:
4727  *     each {|obj| ...}
4728  *  
4729  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4730  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4731  *  For the atoms, a same AtomRef object is passed (with different internal information)
4732  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4733  *  for each iteration.
4734  */
4735 VALUE
4736 s_MolEnumerable_Each(VALUE self)
4737 {
4738         MolEnumerable *mseq;
4739         int i;
4740         int len = NUM2INT(s_MolEnumerable_Length(self));
4741     Data_Get_Struct(self, MolEnumerable, mseq);
4742         if (mseq->kind == kAtomKind) {
4743                 /*  The same AtomRef object will be used during the loop  */
4744                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4745                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4746                 for (i = 0; i < len; i++) {
4747                         aref->idx = i;
4748                         rb_yield(arval);
4749                 }
4750     } else {
4751                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4752                 for (i = 0; i < len; i++) {
4753                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4754                 }
4755         }
4756     return self;
4757 }
4758
4759 /*
4760  *  call-seq:
4761  *     self == molEnumerable -> boolean
4762  *
4763  *  True if the two arguments point to the same molecule and enumerable type.
4764  */
4765 static VALUE
4766 s_MolEnumerable_Equal(VALUE self, VALUE val)
4767 {
4768         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4769                 MolEnumerable *mseq1, *mseq2;
4770                 Data_Get_Struct(self, MolEnumerable, mseq1);
4771                 Data_Get_Struct(val, MolEnumerable, mseq2);
4772                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4773         } else return Qfalse;
4774 }
4775
4776
4777 #pragma mark ====== Molecule Class ======
4778
4779 #pragma mark ------ Allocate/Release/Accessor ------
4780
4781 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method.  */
4782 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4783 char *gLoadSaveErrorMessage = NULL;
4784
4785 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4786
4787 Molecule *
4788 MoleculeFromValue(VALUE val)
4789 {
4790         Molecule *mol;
4791         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4792                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4793     Data_Get_Struct(val, Molecule, mol);
4794         return mol;
4795 }
4796
4797 static VALUE sMoleculeRetainArray = Qnil;
4798
4799 /*  The function is called from MoleculeRelease()  */
4800 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4801 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4802 /*  object is always returned for the same Molecule.  */
4803 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4804 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4805 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4806 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4807 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4808
4809 /*  Register/unregister the exmolobj Ruby object  */
4810 void
4811 MoleculeReleaseExternalObj(Molecule *mol)
4812 {
4813         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4814                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4815                 mol->exmolobjProtected = 0;
4816         }
4817 }
4818
4819 void
4820 MoleculeRetainExternalObj(Molecule *mol)
4821 {
4822         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4823                 if (sMoleculeRetainArray == Qnil) {
4824                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4825                         sMoleculeRetainArray = rb_ary_new();
4826                 }
4827                 
4828                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4829                 mol->exmolobjProtected = 1;
4830         }
4831 }
4832
4833 /*  Release hook function for Ruby  */
4834 void
4835 MoleculeReleaseHook(Molecule *mol)
4836 {
4837         if (mol->exmolobj != NULL) {
4838                 /*  No need to remove from sMoleculeRetainArray  */
4839                 mol->exmolobj = NULL;
4840                 mol->exmolobjProtected = 0;
4841         }
4842         MoleculeRelease(mol);
4843 }
4844
4845 VALUE
4846 ValueFromMolecule(Molecule *mol)
4847 {
4848         if (mol == NULL)
4849                 return Qnil;
4850         if (mol->exmolobj != NULL)
4851                 return (VALUE)mol->exmolobj;
4852         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4853         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4854         return (VALUE)mol->exmolobj;
4855 }
4856
4857 /*  Allocator  */
4858 static VALUE
4859 s_Molecule_Alloc(VALUE klass)
4860 {
4861         VALUE val;
4862         Molecule *mol = MoleculeNew();
4863         val = ValueFromMolecule(mol);
4864         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4865         return val;
4866 }
4867
4868 static int
4869 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4870 {
4871         int n;
4872         char *p = "";
4873         if (FIXNUM_P(val)) {
4874                 n = FIX2INT(val);
4875                 if (n >= 0 && n < mol->natoms)
4876                         return n;
4877                 n = -1; /*  No such atom  */
4878                 val = rb_inspect(val);
4879         } else {
4880                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4881         }
4882         if (n >= 0 && n < mol->natoms)
4883                 return n;
4884         p = StringValuePtr(val);
4885         if (n == -1)
4886                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4887         else if (n == -2)
4888                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4889         else
4890                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4891         return 0; /* Not reached */
4892 }
4893
4894 static IntGroup *
4895 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4896 {
4897         IntGroup *ig;
4898         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4899         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4900                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4901         Data_Get_Struct(val, IntGroup, ig);
4902         IntGroupRetain(ig);
4903         return ig;
4904 }
4905
4906 /*
4907  *  call-seq:
4908  *     dup          -> Molecule
4909  *
4910  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
4911  *  created object does not affect the old object in any sense.
4912  */
4913 static VALUE
4914 s_Molecule_InitCopy(VALUE self, VALUE arg)
4915 {
4916         Molecule *mp1, *mp2;
4917         Data_Get_Struct(self, Molecule, mp1);
4918         mp2 = MoleculeFromValue(arg);
4919         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4920                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4921         return self;
4922 }
4923
4924 /*
4925  *  call-seq:
4926  *     atom_index(val)       -> Integer
4927  *
4928  *  Returns the atom index represented by val. val can be either a non-negative integer
4929  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4930  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
4931  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
4932  *  If val is a string and multiple atoms match the description, the atom with the lowest index
4933  *  is returned.
4934  */
4935 static VALUE
4936 s_Molecule_AtomIndex(VALUE self, VALUE val)
4937 {
4938     Molecule *mol;
4939     Data_Get_Struct(self, Molecule, mol);
4940         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4941 }
4942
4943 /*
4944  *  call-seq:
4945  *     self == Molecule -> boolean
4946  *
4947  *  True if the two arguments point to the same molecule.
4948  */
4949 static VALUE
4950 s_Molecule_Equal(VALUE self, VALUE val)
4951 {
4952         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4953                 Molecule *mol1, *mol2;
4954                 Data_Get_Struct(self, Molecule, mol1);
4955                 Data_Get_Struct(val, Molecule, mol2);
4956                 return (mol1 == mol2 ? Qtrue : Qfalse);
4957         } else return Qfalse;
4958 }
4959
4960 #pragma mark ------ Load/Save ------
4961
4962 static void
4963 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
4964 {
4965         if (gLoadSaveErrorMessage != NULL) {
4966                 MyAppCallback_setConsoleColor(1);
4967                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
4968                 MyAppCallback_setConsoleColor(0);
4969         }
4970         if (status != 0)
4971                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4972 }
4973
4974 /*
4975  *  call-seq:
4976  *     loadmbsf(file)       -> bool
4977  *
4978  *  Read a structure from a mbsf file.
4979  *  Return true if successful.
4980  */
4981 static VALUE
4982 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4983 {
4984         VALUE fname;
4985         char *fstr;
4986         Molecule *mol;
4987         int retval;
4988         MoleculeClearLoadSaveErrorMessage();
4989         Data_Get_Struct(self, Molecule, mol);
4990         rb_scan_args(argc, argv, "1", &fname);
4991         fstr = FileStringValuePtr(fname);
4992         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4993         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
4994         return Qtrue;   
4995 }
4996
4997 /*
4998  *  call-seq:
4999  *     loadpsf(file, pdbfile = nil)       -> bool
5000  *
5001  *  Read a structure from a psf file. molecule must be empty. The psf may be
5002  *  an "extended" version, which also contains coordinates. If pdbfile 
5003  *  is given, then atomic coordinates are read from that file.
5004  *  Return true if successful.
5005  */
5006 static VALUE
5007 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5008 {
5009         VALUE fname, pdbname;
5010         char *fstr, *pdbstr;
5011         Molecule *mol;
5012         int retval;
5013         Data_Get_Struct(self, Molecule, mol);
5014         if (mol->natoms > 0)
5015                 return Qnil;  /*  Must be a new molecule  */
5016         MoleculeClearLoadSaveErrorMessage();
5017         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5018         fstr = FileStringValuePtr(fname);
5019         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5020         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5021         pdbstr = NULL;
5022         if (!NIL_P(pdbname)) {
5023                 pdbstr = strdup(FileStringValuePtr(pdbname));
5024                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5025                 free(pdbstr);
5026                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5027         }
5028         return Qtrue;
5029 }
5030
5031 /*
5032  *  call-seq:
5033  *     loadpdb(file)       -> bool
5034  *
5035  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5036  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5037  *  Return true if successful.
5038  */
5039 static VALUE
5040 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5041 {
5042         VALUE fname;
5043         char *fstr;
5044         Molecule *mol;
5045         int retval;
5046         Data_Get_Struct(self, Molecule, mol);
5047         rb_scan_args(argc, argv, "1", &fname);
5048         MoleculeClearLoadSaveErrorMessage();
5049         fstr = FileStringValuePtr(fname);
5050         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5051         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5052         return Qtrue;   
5053 }
5054
5055 /*
5056  *  call-seq:
5057  *     loaddcd(file)       -> bool
5058  *
5059  *  Read coordinates from a dcd file. The molecule should not empty.
5060  *  Return true if successful.
5061  */
5062 static VALUE
5063 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5064 {
5065         VALUE fname;
5066         char *fstr;
5067         Molecule *mol;
5068         int retval;
5069         Data_Get_Struct(self, Molecule, mol);
5070         rb_scan_args(argc, argv, "1", &fname);
5071         MoleculeClearLoadSaveErrorMessage();
5072         fstr = FileStringValuePtr(fname);
5073         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5074         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5075         return Qtrue;   
5076 }
5077
5078 /*
5079  *  call-seq:
5080  *     loadtep(file)       -> bool
5081  *
5082  *  Read coordinates from an ortep .tep file.
5083  *  Return true if successful.
5084  */
5085 static VALUE
5086 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5087 {
5088         VALUE fname;
5089         char *fstr;
5090         Molecule *mol;
5091         int retval;
5092         Data_Get_Struct(self, Molecule, mol);
5093         rb_scan_args(argc, argv, "1", &fname);
5094         MoleculeClearLoadSaveErrorMessage();
5095         fstr = FileStringValuePtr(fname);
5096         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5097         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5098         return Qtrue;   
5099 }
5100
5101 /*
5102  *  call-seq:
5103  *     loadres(file)       -> bool
5104  *
5105  *  Read coordinates from a shelx .res file.
5106  *  Return true if successful.
5107  */
5108 static VALUE
5109 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5110 {
5111         VALUE fname;
5112         char *fstr;
5113         Molecule *mol;
5114         int retval;
5115         Data_Get_Struct(self, Molecule, mol);
5116         rb_scan_args(argc, argv, "1", &fname);
5117         MoleculeClearLoadSaveErrorMessage();
5118         fstr = FileStringValuePtr(fname);
5119         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5120         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5121         return Qtrue;   
5122 }
5123
5124 /*
5125  *  call-seq:
5126  *     loadfchk(file)       -> bool
5127  *
5128  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5129  *  Return true if successful.
5130  */
5131 static VALUE
5132 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5133 {
5134         VALUE fname;
5135         char *fstr;
5136         Molecule *mol;
5137         int retval;
5138         Data_Get_Struct(self, Molecule, mol);
5139         rb_scan_args(argc, argv, "1", &fname);
5140         MoleculeClearLoadSaveErrorMessage();
5141         fstr = FileStringValuePtr(fname);
5142         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5143         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5144         return Qtrue;   
5145 }
5146
5147 /*
5148  *  call-seq:
5149  *     loaddat(file)       -> bool
5150  *
5151  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5152  *  Return true if successful.
5153  */
5154 static VALUE
5155 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5156 {
5157         VALUE fname;
5158         char *fstr;
5159         Molecule *mol;
5160         int retval;
5161         Data_Get_Struct(self, Molecule, mol);
5162         rb_scan_args(argc, argv, "1", &fname);
5163         MoleculeClearLoadSaveErrorMessage();
5164         fstr = FileStringValuePtr(fname);
5165         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5166         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5167         MyAppCallback_hideProgressPanel();
5168         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5169         return Qtrue;   
5170 }
5171
5172 /*
5173  *  call-seq:
5174  *     savembsf(file)       -> bool
5175  *
5176  *  Write structure as a mbsf file. Returns true if successful.
5177  */
5178 static VALUE
5179 s_Molecule_Savembsf(VALUE self, VALUE fname)
5180 {
5181         char *fstr;
5182     Molecule *mol;
5183         int retval;
5184     Data_Get_Struct(self, Molecule, mol);
5185         MoleculeClearLoadSaveErrorMessage();
5186         fstr = FileStringValuePtr(fname);
5187         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5188         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5189         return Qtrue;
5190 }
5191
5192 /*
5193  *  call-seq:
5194  *     savepsf(file)       -> bool
5195  *
5196  *  Write structure as a psf file. Returns true if successful.
5197  */
5198 static VALUE
5199 s_Molecule_Savepsf(VALUE self, VALUE fname)
5200 {
5201         char *fstr;
5202     Molecule *mol;
5203         int retval;
5204     Data_Get_Struct(self, Molecule, mol);
5205         MoleculeClearLoadSaveErrorMessage();
5206         fstr = FileStringValuePtr(fname);
5207         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5208         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5209         return Qtrue;
5210 }
5211
5212 /*
5213  *  call-seq:
5214  *     savepdb(file)       -> bool
5215  *
5216  *  Write coordinates as a pdb file. Returns true if successful.
5217  */
5218 static VALUE
5219 s_Molecule_Savepdb(VALUE self, VALUE fname)
5220 {
5221         char *fstr;
5222     Molecule *mol;
5223         int retval;
5224     Data_Get_Struct(self, Molecule, mol);
5225         MoleculeClearLoadSaveErrorMessage();
5226         fstr = FileStringValuePtr(fname);
5227         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5228         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5229         return Qtrue;
5230 }
5231
5232 /*
5233  *  call-seq:
5234  *     savedcd(file)       -> bool
5235  *
5236  *  Write coordinates as a dcd file. Returns true if successful.
5237  */
5238 static VALUE
5239 s_Molecule_Savedcd(VALUE self, VALUE fname)
5240 {
5241         char *fstr;
5242     Molecule *mol;
5243         int retval;
5244     Data_Get_Struct(self, Molecule, mol);
5245         MoleculeClearLoadSaveErrorMessage();
5246         fstr = FileStringValuePtr(fname);
5247         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5248         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5249         return Qtrue;
5250 }
5251
5252 /*  load([ftype, ] fname, ...)  */
5253 static VALUE
5254 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5255 {
5256         VALUE rval;
5257         char *argstr, *methname, *p, *type = "";
5258         ID mid = 0;
5259         int i;
5260         const char *ls = (loadFlag ? "load" : "save");
5261         int lslen = strlen(ls);
5262
5263         if (argc == 0)
5264                 return Qnil;
5265
5266         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5267                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5268         if (argstr[0] == ':') {
5269                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5270                 methname = ALLOC_N(char, lslen + strlen(argstr));
5271                 strcpy(methname, ls);
5272                 strcat(methname, argstr + 1);
5273                 type = argstr + 1;
5274                 for (i = lslen; methname[i] != 0; i++)
5275                         methname[i] = tolower(methname[i]);
5276                 mid = rb_intern(methname);
5277                 xfree(methname);
5278                 argc--;
5279                 argv++;
5280                 rval = rb_funcall2(self, mid, argc, argv);
5281                 if (rval == Qnil)
5282                         goto failure;
5283                 else
5284                         goto success;
5285         }
5286         /*  Guess file type from extension  */
5287         p = strrchr(argstr, '.');
5288         if (p != NULL) {
5289                 p++;
5290                 type = p;
5291                 for (methname = p; *methname != 0; methname++) {
5292                         if (!isalpha(*methname))
5293                                 break;
5294                 }
5295                 if (*methname == 0) {
5296                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5297                         if (methname == NULL)
5298                                 rb_raise(rb_eMolbyError, "Low memory");
5299                         strcpy(methname, ls);
5300                         strcat(methname, p);
5301                         for (i = lslen; methname[i] != 0; i++)
5302                                 methname[i] = tolower(methname[i]);
5303                         mid = rb_intern(methname);
5304                         xfree(methname);
5305                         if (loadFlag) {
5306                                 if (rb_respond_to(self, mid)) {
5307                                         /*  Load: try to call the load procedure only if it is available  */
5308                                         rval = rb_funcall2(self, mid, argc, argv);
5309                                         if (rval != Qnil)
5310                                                 goto success;
5311                                 }
5312                         } else {
5313                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5314                                 rval = rb_funcall2(self, mid, argc, argv);
5315                                 if (rval != Qnil)
5316                                         goto success;
5317                         }
5318                 }
5319         }
5320 failure:
5321         rval = rb_str_to_str(argv[0]);
5322         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5323         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5324         return Qnil;  /*  Does not reach here  */
5325
5326 success:
5327         {
5328                 /*  Register the path  */
5329                 Molecule *mol;
5330         /*      Atom *ap; */
5331                 Data_Get_Struct(self, Molecule, mol);
5332                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5333                 
5334                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5335         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5336                         if (ap->occupancy != 0.0)
5337                                 break;
5338                 }
5339                 if (i == mol->natoms) {
5340                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5341                                 ap->occupancy = 1.0;
5342                         }
5343                 } */
5344         }
5345         return rval;
5346 }
5347
5348 /*
5349  *  call-seq:
5350  *     molload(file, *args)       -> bool
5351  *
5352  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5353  *  file type given by the extension). If this method fails, then all defined (public)
5354  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5355  */
5356 static VALUE
5357 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5358 {
5359         return s_Molecule_LoadSave(argc, argv, self, 1);
5360 }
5361
5362 /*
5363  *  call-seq:
5364  *     molsave(file, *args)       -> bool
5365  *
5366  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5367  *  (XXX is the file type given by the extension).
5368  */
5369 static VALUE
5370 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5371 {
5372         return s_Molecule_LoadSave(argc, argv, self, 0);
5373 }
5374
5375 /*
5376  *  call-seq:
5377  *     open        -> Molecule
5378  *     open(file)  -> Molecule
5379  *
5380  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5381  */
5382 static VALUE
5383 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5384 {
5385         VALUE fname;
5386         const char *p;
5387         Molecule *mp;
5388         VALUE iflag;
5389         rb_scan_args(argc, argv, "01", &fname);
5390         if (NIL_P(fname))
5391                 p = NULL;
5392         else
5393                 p = FileStringValuePtr(fname);
5394         iflag = Ruby_SetInterruptFlag(Qfalse);
5395         mp = MoleculeCallback_openNewMolecule(p);
5396         Ruby_SetInterruptFlag(iflag);
5397         if (mp == NULL) {
5398                 if (p == NULL)
5399                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5400                 else
5401                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5402         }
5403         return ValueFromMolecule(mp);
5404 }
5405
5406 /*
5407  *  call-seq:
5408  *     new  -> Molecule
5409  *     new(file, *args)  -> Molecule
5410  *
5411  *  Create a new molecule and call "load" method with the same arguments.
5412  */
5413 static VALUE
5414 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5415 {
5416         if (argc > 0)
5417                 return s_Molecule_Load(argc, argv, self);
5418         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5419 }
5420
5421 /*
5422  *  call-seq:
5423  *     error_message       -> String
5424  *
5425  *  Get the error_message from the last load/save method. If no error, returns nil.
5426  */
5427 static VALUE
5428 s_Molecule_ErrorMessage(VALUE klass)
5429 {
5430         if (gLoadSaveErrorMessage == NULL)
5431                 return Qnil;
5432         else return rb_str_new2(gLoadSaveErrorMessage);
5433 }
5434
5435 /*
5436  *  call-seq:
5437  *     set_error_message(String)
5438  *     Molecule.error_message = String
5439  *
5440  *  Get the error_message from the last load/save method. If no error, returns nil.
5441  */
5442 static VALUE
5443 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5444 {
5445         if (gLoadSaveErrorMessage != NULL) {
5446                 free(gLoadSaveErrorMessage);
5447                 gLoadSaveErrorMessage = NULL;
5448         }
5449         if (sval != Qnil) {
5450                 sval = rb_str_to_str(sval);
5451                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5452         }
5453         return sval;
5454 }
5455
5456 #pragma mark ------ Name attributes ------
5457
5458 /*
5459  *  call-seq:
5460  *     name       -> String
5461  *
5462  *  Returns the display name of the molecule. If the molecule has no associated
5463  *  document, then returns nil.
5464  */
5465 static VALUE
5466 s_Molecule_Name(VALUE self)
5467 {
5468     Molecule *mol;
5469         char buf[1024];
5470     Data_Get_Struct(self, Molecule, mol);
5471         MoleculeCallback_displayName(mol, buf, sizeof buf);
5472         if (buf[0] == 0)
5473                 return Qnil;
5474         else
5475                 return rb_str_new2(buf);
5476 }
5477
5478 /*
5479  *  call-seq:
5480  *     set_name(string) -> self
5481  *
5482  *  Set the name of an untitled molecule. If the molecule is not associated with window
5483  *  or it already has an associated file, then exception is thrown.
5484  */
5485 static VALUE
5486 s_Molecule_SetName(VALUE self, VALUE nval)
5487 {
5488     Molecule *mol;
5489     Data_Get_Struct(self, Molecule, mol);
5490         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5491                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5492         return self;
5493 }
5494
5495
5496 /*
5497  *  call-seq:
5498  *     path       -> String
5499  *
5500  *  Returns the full path name of the molecule, if it is associated with a file.
5501  *  If the molecule has no associated file, then returns nil.
5502  */
5503 static VALUE
5504 s_Molecule_Path(VALUE self)
5505 {
5506     Molecule *mol;
5507         char buf[1024];
5508     Data_Get_Struct(self, Molecule, mol);
5509         MoleculeCallback_pathName(mol, buf, sizeof buf);
5510         if (buf[0] == 0)
5511                 return Qnil;
5512         else
5513                 return Ruby_NewFileStringValue(buf);
5514 }
5515
5516 /*
5517  *  call-seq:
5518  *     dir       -> String
5519  *
5520  *  Returns the full path name of the directory in which the file associated with the
5521  *  molecule is located. If the molecule has no associated file, then returns nil.
5522  */
5523 static VALUE
5524 s_Molecule_Dir(VALUE self)
5525 {
5526     Molecule *mol;
5527         char buf[1024], *p;
5528     Data_Get_Struct(self, Molecule, mol);
5529         MoleculeCallback_pathName(mol, buf, sizeof buf);
5530 #if __WXMSW__
5531         translate_char(buf, '\\', '/');
5532 #endif
5533         if (buf[0] == 0)
5534                 return Qnil;
5535         else {
5536                 p = strrchr(buf, '/');
5537                 if (p != NULL)
5538                         *p = 0;
5539                 return rb_str_new2(buf);
5540         }
5541 }
5542
5543 /*
5544  *  call-seq:
5545  *     inspect       -> String
5546  *
5547  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5548  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5549  *  the Molecule structure) is returned.
5550  */
5551 static VALUE
5552 s_Molecule_Inspect(VALUE self)
5553 {
5554     Molecule *mol;
5555         char buf[256];
5556     Data_Get_Struct(self, Molecule, mol);
5557         MoleculeCallback_displayName(mol, buf, sizeof buf);
5558         if (buf[0] == 0) {
5559                 /*  No associated document  */
5560                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5561                 return rb_str_new2(buf);
5562         } else {
5563                 /*  Check whether the document name is duplicate  */
5564                 char buf2[256];
5565                 int idx, k, k2;
5566                 Molecule *mol2;
5567                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5568                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5569                         if (strcmp(buf, buf2) == 0) {
5570                                 k++;
5571                                 if (mol == mol2)
5572                                         k2 = k;
5573                         }
5574                 }
5575                 if (k > 1) {
5576                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5577                 } else {
5578                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5579                 }
5580                 return rb_str_new2(buf2);
5581         }
5582 }
5583
5584 #pragma mark ------ MolEnumerables ------
5585
5586 static VALUE
5587 s_Molecule_MolEnumerable(VALUE self, int kind)
5588 {
5589     Molecule *mol;
5590         MolEnumerable *mseq;
5591     Data_Get_Struct(self, Molecule, mol);
5592         mseq = MolEnumerableNew(mol, kind);
5593         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5594 }
5595
5596 /*
5597  *  call-seq:
5598  *     atoms       -> MolEnumerable
5599  *
5600  *  Returns a MolEnumerable object representing the array of atoms.
5601  */
5602 static VALUE
5603 s_Molecule_Atoms(VALUE self)
5604 {
5605         return s_Molecule_MolEnumerable(self, kAtomKind);
5606 }
5607
5608 /*
5609  *  call-seq:
5610  *     bonds       -> MolEnumerable
5611  *
5612  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5613  *  by an array of two atom indices.
5614  */
5615 static VALUE
5616 s_Molecule_Bonds(VALUE self)
5617 {
5618         return s_Molecule_MolEnumerable(self, kBondKind);
5619 }
5620
5621 /*
5622  *  call-seq:
5623  *     angles       -> MolEnumerable
5624  *
5625  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5626  *  by an array of three atom indices.
5627  */
5628 static VALUE
5629 s_Molecule_Angles(VALUE self)
5630 {
5631         return s_Molecule_MolEnumerable(self, kAngleKind);
5632 }
5633
5634 /*
5635  *  call-seq:
5636  *     dihedrals       -> MolEnumerable
5637  *
5638  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5639  *  by an array of four atom indices.
5640  */
5641 static VALUE
5642 s_Molecule_Dihedrals(VALUE self)
5643 {
5644         return s_Molecule_MolEnumerable(self, kDihedralKind);
5645 }
5646
5647 /*
5648  *  call-seq:
5649  *     impropers       -> MolEnumerable
5650  *
5651  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5652  *  by an array of four atom indices.
5653  */
5654 static VALUE
5655 s_Molecule_Impropers(VALUE self)
5656 {
5657         return s_Molecule_MolEnumerable(self, kImproperKind);
5658 }
5659
5660 /*
5661  *  call-seq:
5662  *     residues       -> MolEnumerable
5663  *
5664  *  Returns a MolEnumerable object representing the array of residue names.
5665  */
5666 static VALUE
5667 s_Molecule_Residues(VALUE self)
5668 {
5669         return s_Molecule_MolEnumerable(self, kResidueKind);
5670 }
5671
5672 /*
5673  *  call-seq:
5674  *     natoms       -> Integer
5675  *
5676  *  Returns the number of atoms.
5677  */
5678 static VALUE
5679 s_Molecule_Natoms(VALUE self)
5680 {
5681     Molecule *mol;
5682     Data_Get_Struct(self, Molecule, mol);
5683         return INT2NUM(mol->natoms);
5684 }
5685
5686 /*
5687  *  call-seq:
5688  *     nbonds       -> Integer
5689  *
5690  *  Returns the number of bonds.
5691  */
5692 static VALUE
5693 s_Molecule_Nbonds(VALUE self)
5694 {
5695     Molecule *mol;
5696     Data_Get_Struct(self, Molecule, mol);
5697         return INT2NUM(mol->nbonds);
5698 }
5699
5700 /*
5701  *  call-seq:
5702  *     nangles       -> Integer
5703  *
5704  *  Returns the number of angles.
5705  */
5706 static VALUE
5707 s_Molecule_Nangles(VALUE self)
5708 {
5709     Molecule *mol;
5710     Data_Get_Struct(self, Molecule, mol);
5711         return INT2NUM(mol->nangles);
5712 }
5713
5714 /*
5715  *  call-seq:
5716  *     ndihedrals       -> Integer
5717  *
5718  *  Returns the number of dihedrals.
5719  */
5720 static VALUE
5721 s_Molecule_Ndihedrals(VALUE self)
5722 {
5723     Molecule *mol;
5724     Data_Get_Struct(self, Molecule, mol);
5725         return INT2NUM(mol->ndihedrals);
5726 }
5727
5728 /*
5729  *  call-seq:
5730  *     nimpropers       -> Integer
5731  *
5732  *  Returns the number of impropers.
5733  */
5734 static VALUE
5735 s_Molecule_Nimpropers(VALUE self)
5736 {
5737     Molecule *mol;
5738     Data_Get_Struct(self, Molecule, mol);
5739         return INT2NUM(mol->nimpropers);
5740 }
5741
5742 /*
5743  *  call-seq:
5744  *     nresidues       -> Integer
5745  *
5746  *  Returns the number of residues.
5747  */
5748 static VALUE
5749 s_Molecule_Nresidues(VALUE self)
5750 {
5751     Molecule *mol;
5752     Data_Get_Struct(self, Molecule, mol);
5753         return INT2NUM(mol->nresidues);
5754 }
5755
5756 /*
5757  *  call-seq:
5758  *     nresidues = Integer
5759  *
5760  *  Change the number of residues.
5761  */
5762 static VALUE
5763 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5764 {
5765     Molecule *mol;
5766         int ival = NUM2INT(val);
5767     Data_Get_Struct(self, Molecule, mol);
5768         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5769         if (ival != mol->nresidues)
5770                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5771         return val;
5772 }
5773
5774 /*
5775  *  call-seq:
5776  *     max_residue_number(atom_group = nil)     -> Integer
5777  *
5778  *  Returns the maximum residue number actually used. If an atom group is given, only
5779  *  these atoms are examined. If no atom is present, nil is returned.
5780  */
5781 static VALUE
5782 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5783 {
5784     Molecule *mol;
5785         VALUE gval;
5786         int maxSeq;
5787         IntGroup *ig;
5788     Data_Get_Struct(self, Molecule, mol);
5789         rb_scan_args(argc, argv, "01", &gval);
5790         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5791         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5792         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5793 }
5794
5795 /*
5796  *  call-seq:
5797  *     min_residue_number(atom_group = nil)     -> Integer
5798  *
5799  *  Returns the minimum residue number actually used. If an atom group is given, only
5800  *  these atoms are examined. If no atom is present, nil is returned.
5801  */
5802 static VALUE
5803 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5804 {
5805     Molecule *mol;
5806         VALUE gval;
5807         int minSeq;
5808         IntGroup *ig;
5809     Data_Get_Struct(self, Molecule, mol);
5810         rb_scan_args(argc, argv, "01", &gval);
5811         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5812         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5813         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5814 }
5815
5816 /*
5817  *  call-seq:
5818  *     each_atom(atom_group = nil) {|aref| ...}
5819  *
5820  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5821  *  group is given, only these atoms are processed.
5822  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5823  *  is self (a Molecule object).
5824  */
5825 static VALUE
5826 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5827 {
5828         int i;
5829     Molecule *mol;
5830         AtomRef *aref;
5831         VALUE arval;
5832         VALUE gval;
5833         IntGroup *ig;
5834     Data_Get_Struct(self, Molecule, mol);
5835         rb_scan_args(argc, argv, "01", &gval);
5836         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5837         arval = ValueFromMoleculeAndIndex(mol, 0);
5838         Data_Get_Struct(arval, AtomRef, aref);
5839         for (i = 0; i < mol->natoms; i++) {
5840                 aref->idx = i;
5841                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5842                         rb_yield(arval);
5843         }
5844         if (ig != NULL)
5845                 IntGroupRelease(ig);
5846     return self;
5847 }
5848
5849 #pragma mark ------ Atom Group ------
5850
5851 static VALUE
5852 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5853 {
5854         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5855         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5856         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5857         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5858         return Qnil;
5859 }
5860
5861 /*
5862  *  call-seq:
5863  *     atom_group
5864  *     atom_group {|aref| ...}
5865  *     atom_group(arg1, arg2, ...)
5866  *     atom_group(arg1, arg2, ...) {|aref| ...}
5867  *
5868  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5869  *  If arguments are given, then the atoms reprensented by the arguments are added to the
5870  *  group. For a conversion of a string to an atom index, see the description
5871  *  of Molecule#atom_index.
5872  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
5873  *  representing each atom, and the atoms are removed from the result if the block returns false.
5874  *
5875  */
5876 static VALUE
5877 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5878 {
5879         IntGroup *ig1, *ig2;
5880     Molecule *mol;
5881         Int i, startPt, interval;
5882         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5883         Data_Get_Struct(retval, IntGroup, ig1);
5884     Data_Get_Struct(self, Molecule, mol);
5885         if (argc == 0) {
5886                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5887         } else {
5888                 while (argc > 0) {
5889                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5890                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5891                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5892                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5893                                 ig2 = IntGroupFromValue(*argv);
5894                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5895                                         interval = IntGroupGetInterval(ig2, i);
5896                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5897                                 }
5898                                 IntGroupRelease(ig2);
5899                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
5900                                 VALUE values[2];
5901                                 values[0] = (VALUE)mol;
5902                                 values[1] = (VALUE)ig1;
5903                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5904                         } else
5905                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5906                         argc--;
5907                         argv++;
5908                 }
5909         }
5910         if (rb_block_given_p()) {
5911                 /*  Evaluate the given block with an AtomRef as the argument, and delete
5912                         the index if the block returns false  */
5913                 AtomRef *aref = AtomRefNew(mol, 0);
5914                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5915                 ig2 = IntGroupNew();
5916                 IntGroupCopy(ig2, ig1);
5917                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5918                         VALUE resval;
5919                         if (startPt >= mol->natoms)
5920                                 break;
5921                         aref->idx = startPt;
5922                         resval = rb_yield(arval);
5923                         if (!RTEST(resval))
5924                                 IntGroupRemove(ig1, startPt, 1);
5925                 }
5926                 IntGroupRelease(ig2);
5927         }
5928         
5929         /*  Remove points that are out of bounds */
5930         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5931
5932         return retval;                  
5933 }
5934
5935 /*
5936  *  call-seq:
5937  *     selection       -> IntGroup
5938  *
5939  *  Returns the current selection.
5940  */
5941 static VALUE
5942 s_Molecule_Selection(VALUE self)
5943 {
5944     Molecule *mol;
5945         IntGroup *ig;
5946         VALUE val;
5947     Data_Get_Struct(self, Molecule, mol);
5948         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5949                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
5950                 val = ValueFromIntGroup(ig);
5951                 IntGroupRelease(ig);
5952         } else {
5953                 val = IntGroup_Alloc(rb_cIntGroup);
5954         }
5955         return val;
5956 }
5957
5958 static VALUE
5959 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5960 {
5961     Molecule *mol;
5962         IntGroup *ig;
5963     Data_Get_Struct(self, Molecule, mol);
5964         if (val == Qnil)
5965                 ig = NULL;
5966         else
5967                 ig = s_Molecule_AtomGroupFromValue(self, val);
5968         if (undoable)
5969                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
5970         else
5971                 MoleculeSetSelection(mol, ig);
5972         if (ig != NULL)
5973                 IntGroupRelease(ig);
5974         return val;
5975 }
5976
5977 /*
5978  *  call-seq:
5979  *     selection = IntGroup
5980  *
5981  *  Set the current selection. The right-hand operand may be nil.
5982  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
5983  */
5984 static VALUE
5985 s_Molecule_SetSelection(VALUE self, VALUE val)
5986 {
5987         return s_Molecule_SetSelectionSub(self, val, 0);
5988 }
5989
5990 /*
5991  *  call-seq:
5992  *     set_undoable_selection(IntGroup)
5993  *
5994  *  Set the current selection with undo registration. The right-hand operand may be nil.
5995  *  This operation is undoable.
5996  */
5997 static VALUE
5998 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
5999 {
6000         return s_Molecule_SetSelectionSub(self, val, 1);
6001 }
6002
6003 #pragma mark ------ Editing ------
6004
6005 /*
6006  *  call-seq:
6007  *     extract(group, dummy_flag = nil)       -> Molecule
6008  *
6009  *  Extract the atoms given by group and return as a new molecule object.
6010  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6011  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6012  *  names beginning with an underscore) and included in the new molecule object.
6013  */
6014 static VALUE
6015 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6016 {
6017     Molecule *mol1, *mol2;
6018         IntGroup *ig;
6019         VALUE group, dummy_flag, retval;
6020     Data_Get_Struct(self, Molecule, mol1);
6021         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6022         ig = s_Molecule_AtomGroupFromValue(self, group);
6023         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6024                 retval = Qnil;
6025         } else {
6026                 retval = ValueFromMolecule(mol2);
6027         }
6028         IntGroupRelease(ig);
6029         return retval;
6030 }
6031
6032 /*
6033  *  call-seq:
6034  *     add(molecule2)       -> self
6035  *
6036  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6037     conflicts.
6038     This operation is undoable.
6039  */
6040 static VALUE
6041 s_Molecule_Add(VALUE self, VALUE val)
6042 {
6043     Molecule *mol1, *mol2;
6044     Data_Get_Struct(self, Molecule, mol1);
6045         mol2 = MoleculeFromValue(val);
6046         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6047         return self; 
6048 }
6049
6050 /*
6051  *  call-seq:
6052  *     remove(group)       -> Molecule
6053  *
6054  *  The atoms designated by the given group are removed from the molecule.
6055  *  This operation is undoable.
6056  */
6057 static VALUE
6058 s_Molecule_Remove(VALUE self, VALUE group)
6059 {
6060     Molecule *mol1;
6061         IntGroup *ig, *bg;
6062         Int i;
6063         IntGroupIterator iter;
6064
6065     Data_Get_Struct(self, Molecule, mol1);
6066         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6067         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6068                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6069         Data_Get_Struct(group, IntGroup, ig);
6070
6071         /*  Remove the bonds between the two fragments  */
6072         /*  (This is necessary for undo to work correctly)  */
6073         IntGroupIteratorInit(ig, &iter);
6074         bg = NULL;
6075         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6076                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6077                 Int j, *cp;
6078                 cp = AtomConnectData(&ap->connect);
6079                 for (j = 0; j < ap->connect.count; j++) {
6080                         int n = cp[j];
6081                         if (!IntGroupLookup(ig, n, NULL)) {
6082                                 /*  bond i-n, i is in ig and n is not  */
6083                                 int k = MoleculeLookupBond(mol1, i, n);
6084                                 if (k >= 0) {
6085                                         if (bg == NULL)
6086                                                 bg = IntGroupNew();
6087                                         IntGroupAdd(bg, k, 1);
6088                                 }
6089                         }
6090                 }
6091         }
6092         IntGroupIteratorRelease(&iter);
6093         if (bg != NULL) {
6094                 /*  Remove bonds  */
6095                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6096                 IntGroupRelease(bg);
6097         }
6098         /*  Remove atoms  */
6099         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6100                 return Qnil;
6101         return self;
6102 }
6103
6104 /*
6105  *  call-seq:
6106  *     create_atom(name, pos = -1)  -> AtomRef
6107  *
6108  *  Create a new atom with the specified name (may contain residue 
6109  *  information) and position (if position is out of range, the atom is appended at
6110  *  the end). Returns the reference to the new atom.
6111  *  This operation is undoable.
6112  */
6113 static VALUE
6114 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6115 {
6116     Molecule *mol;
6117     Int i, pos;
6118         VALUE name, ival;
6119     Atom arec;
6120     AtomRef *aref;
6121         char *p, resName[6], atomName[6];
6122         int resSeq;
6123     Data_Get_Struct(self, Molecule, mol);
6124         rb_scan_args(argc, argv, "02", &name, &ival);
6125         if (ival != Qnil)
6126                 pos = NUM2INT(rb_Integer(ival));
6127         else pos = -1;
6128         if (name != Qnil) {
6129                 p = StringValuePtr(name);
6130                 if (p[0] != 0) {
6131                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6132                         if (atomName[0] == 0)
6133                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6134                 }
6135         } else p = NULL;
6136         if (p == NULL || p[0] == 0) {
6137                 memset(atomName, 0, 4);
6138                 resSeq = -1;
6139         }
6140     memset(&arec, 0, sizeof(arec));
6141     strncpy(arec.aname, atomName, 4);
6142     if (resSeq >= 0) {
6143       strncpy(arec.resName, resName, 4);
6144       arec.resSeq = resSeq;
6145     }
6146         arec.occupancy = 1.0;
6147 //    i = MoleculeCreateAnAtom(mol, &arec);
6148         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6149                 return Qnil;
6150     aref = AtomRefNew(mol, pos);
6151     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6152 }
6153
6154 /*
6155  *  call-seq:
6156  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6157  *
6158  *  Create a new atom with the same attributes (but no bonding information)
6159  *  with the specified atom. Returns the reference to the new atom.
6160  *  This operation is undoable.
6161  */
6162 static VALUE
6163 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6164 {
6165     Molecule *mol;
6166         const Atom *apsrc;
6167     Atom arec;
6168         AtomRef *aref;
6169         VALUE retval, aval, ival;
6170         Int pos;
6171     Data_Get_Struct(self, Molecule, mol);
6172         rb_scan_args(argc, argv, "11", &aval, &ival);
6173         if (FIXNUM_P(aval)) {
6174                 int idx = NUM2INT(aval);
6175                 if (idx < 0 || idx >= mol->natoms)
6176                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6177                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6178         } else {
6179                 apsrc = s_AtomFromValue(aval);
6180         }
6181         if (apsrc == NULL)
6182                 rb_raise(rb_eMolbyError, "bad atom specification");
6183         if (ival != Qnil)
6184                 pos = NUM2INT(rb_Integer(ival));
6185         else pos = -1;
6186         AtomDuplicate(&arec, apsrc);
6187         arec.connect.count = 0;
6188         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6189                 retval = Qnil;
6190         else {
6191                 aref = AtomRefNew(mol, pos);
6192                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6193         }
6194         AtomClean(&arec);
6195         return retval;
6196 }
6197
6198 /*
6199  *  call-seq:
6200  *     create_bond(n1, n2, ...)       -> Integer
6201  *
6202  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6203  *  do nothing for that pair. Returns the number of bonds actually created.
6204  *  This operation is undoable.
6205  */
6206 static VALUE
6207 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6208 {
6209     Molecule *mol;
6210         Int i, j, k, *ip, old_nbonds;
6211         if (argc == 0)
6212                 rb_raise(rb_eMolbyError, "missing arguments");
6213         if (argc % 2 != 0)
6214                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6215     Data_Get_Struct(self, Molecule, mol);
6216         ip = ALLOC_N(Int, argc + 1);
6217         for (i = j = 0; i < argc; i++, j++) {
6218                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6219                 if (i % 2 == 1) {
6220                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6221                                 j -= 2;  /*  This bond is already present: skip it  */
6222                         else {
6223                                 for (k = 0; k < j - 1; k += 2) {
6224                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6225                                                 j -= 2;   /*  The same entry is already in the argument  */
6226                                                 break;
6227                                         }
6228                                 }
6229                         }
6230                 }
6231         }
6232         old_nbonds = mol->nbonds;
6233         if (j > 0) {
6234                 ip[j] = kInvalidIndex;
6235                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6236         } else i = 0;
6237         xfree(ip);
6238         if (i == -1)
6239                 rb_raise(rb_eMolbyError, "atom index out of range");
6240         else if (i == -2)
6241                 rb_raise(rb_eMolbyError, "too many bonds");
6242         else if (i == -3)
6243                 rb_raise(rb_eMolbyError, "duplicate bonds");
6244         else if (i == -5)
6245                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6246         else if (i != 0)
6247                 rb_raise(rb_eMolbyError, "error in creating bonds");
6248         return INT2NUM(mol->nbonds - old_nbonds);
6249 }
6250
6251 /*
6252  *  call-seq:
6253  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6254  *
6255  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6256  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6257  *  This operation is undoable.
6258  */
6259 static VALUE
6260 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6261 {
6262     Molecule *mol;
6263         Int i, j, n[2];
6264         IntGroup *bg;
6265         if (argc == 0)
6266                 rb_raise(rb_eMolbyError, "missing arguments");
6267         if (argc % 2 != 0)
6268                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6269     Data_Get_Struct(self, Molecule, mol);
6270         bg = NULL;
6271         for (i = j = 0; i < argc; i++, j = 1 - j) {
6272                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6273                 if (j == 1) {
6274                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6275                         if (k >= 0) {
6276                                 if (bg == NULL)
6277                                         bg = IntGroupNew();
6278                                 IntGroupAdd(bg, k, 1);
6279                         }
6280                 }
6281         }
6282         if (bg != NULL) {
6283                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6284                 i = IntGroupGetCount(bg);
6285                 IntGroupRelease(bg);
6286         } else i = 0;
6287         return INT2NUM(i);
6288 }
6289
6290 /*
6291  *  call-seq:
6292  *     assign_bond_order(idx, d1)
6293  *     assign_bond_orders(group, [d1, d2, ...])
6294  *
6295  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6296  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6297  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6298  *  (This may change in the future)
6299  *  This operation is undoable.
6300  */
6301 static VALUE
6302 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6303 {
6304     Molecule *mol;
6305         IntGroup *ig;
6306     Data_Get_Struct(self, Molecule, mol);
6307         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6308                 /*  The first form  */
6309                 Int idx = NUM2INT(rb_Integer(idxval));
6310                 Double d1 = NUM2DBL(rb_Float(dval));
6311                 if (idx < 0 || idx >= mol->nbonds)
6312                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6313                 ig = IntGroupNewWithPoints(idx, 1, -1);
6314                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6315                 IntGroupRelease(ig);
6316         } else {
6317                 Int i, n;
6318                 Double *dp;
6319                 ig = IntGroupFromValue(idxval);
6320                 n = IntGroupGetCount(ig);
6321                 if (n == 0)
6322                         rb_raise(rb_eMolbyError, "the bond index is empty");
6323                 dval = rb_ary_to_ary(dval);
6324                 dp = (Double *)calloc(sizeof(Double), n);
6325                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6326                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6327                 }
6328                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6329                 free(dp);
6330                 IntGroupRelease(ig);
6331         }
6332         return self;
6333 }
6334
6335 /*
6336  *  call-seq:
6337  *     get_bond_order(idx) -> Float
6338  *     get_bond_orders(group) -> Array
6339  *
6340  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6341  *  In the second form, the bond orders at the indices in the group are returned as an array.
6342  *  If no bond order information have been assigned, returns nil (the first form)
6343  *  or an empty array (the second form).
6344  */
6345 static VALUE
6346 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6347 {
6348     Molecule *mol;
6349         IntGroup *ig;
6350         Double *dp;
6351         VALUE retval;
6352         Int i, n, numericArg;
6353     Data_Get_Struct(self, Molecule, mol);
6354         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6355                 /*  The first form  */
6356                 Int idx = NUM2INT(rb_Integer(idxval));
6357                 if (idx < 0 || idx >= mol->nbonds)
6358                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6359                 if (mol->bondOrders == NULL)
6360                         return Qnil;
6361                 ig = IntGroupNewWithPoints(idx, 1, -1);
6362                 n = 1;
6363                 numericArg = 1;
6364         } else {
6365                 if (mol->bondOrders == NULL)
6366                         return rb_ary_new();
6367                 ig = IntGroupFromValue(idxval);
6368                 n = IntGroupGetCount(ig);
6369                 if (n == 0)
6370                         rb_raise(rb_eMolbyError, "the bond index is empty");
6371                 numericArg = 0;
6372         }
6373         dp = (Double *)calloc(sizeof(Double), n);
6374         MoleculeGetBondOrders(mol, dp, ig);
6375         if (numericArg)
6376                 retval = rb_float_new(dp[0]);
6377         else {
6378                 retval = rb_ary_new();
6379                 for (i = 0; i < n; i++)
6380                         rb_ary_push(retval, rb_float_new(dp[i]));
6381         }
6382         free(dp);
6383         IntGroupRelease(ig);
6384         return retval;
6385 }
6386
6387 /*
6388  *  call-seq:
6389  *     bond_exist?(idx1, idx2) -> bool
6390  *
6391  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6392  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6393  */
6394 static VALUE
6395 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6396 {
6397         Molecule *mol;
6398         Int idx1, idx2, i;
6399         Atom *ap;
6400         Int *cp;
6401     Data_Get_Struct(self, Molecule, mol);
6402         idx1 = NUM2INT(rb_Integer(ival1));
6403         idx2 = NUM2INT(rb_Integer(ival2));
6404         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6405                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6406         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6407         cp = AtomConnectData(&ap->connect);
6408         for (i = 0; i < ap->connect.count; i++) {
6409                 if (cp[i] == idx2)
6410                         return Qtrue;
6411         }
6412         return Qfalse;
6413 }
6414
6415 /*
6416  *  call-seq:
6417  *     add_angle(n1, n2, n3)       -> Molecule
6418  *
6419  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6420  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6421  *  This operation is undoable.
6422  */
6423 static VALUE
6424 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6425 {
6426         Int n[4];
6427     Molecule *mol;
6428     Data_Get_Struct(self, Molecule, mol);
6429         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6430         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6431         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6432         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6433                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6434         n[3] = kInvalidIndex;
6435         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6436         return self;
6437 }
6438
6439 /*
6440  *  call-seq:
6441  *     remove_angle(n1, n2, n3)       -> Molecule
6442  *
6443  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6444  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6445  *  This operation is undoable.
6446  */
6447 static VALUE
6448 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6449 {
6450         Int n[4];
6451     Molecule *mol;
6452         IntGroup *ig;
6453     Data_Get_Struct(self, Molecule, mol);
6454         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6455         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6456         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6457         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6458                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6459         ig = IntGroupNewWithPoints(n[3], 1, -1);
6460         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6461         IntGroupRelease(ig);
6462         return self;
6463 }
6464
6465 /*
6466  *  call-seq:
6467  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6468  *
6469  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6470  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6471  *  This operation is undoable.
6472  */
6473 static VALUE
6474 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6475 {
6476         Int n[5];
6477     Molecule *mol;
6478     Data_Get_Struct(self, Molecule, mol);
6479         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6480         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6481         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6482         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6483         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6484                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6485         n[4] = kInvalidIndex;
6486         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6487         return self;
6488 }
6489
6490 /*
6491  *  call-seq:
6492  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6493  *
6494  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6495  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6496  *  This operation is undoable.
6497  */
6498 static VALUE
6499 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6500 {
6501         Int n[5];
6502     Molecule *mol;
6503         IntGroup *ig;
6504     Data_Get_Struct(self, Molecule, mol);
6505         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6506         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6507         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6508         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6509         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6510                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6511         ig = IntGroupNewWithPoints(n[4], 1, -1);
6512         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6513         IntGroupRelease(ig);
6514         return self;
6515 }
6516
6517 /*
6518  *  call-seq:
6519  *     add_improper(n1, n2, n3, n4)       -> Molecule
6520  *
6521  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6522  *  not automatically added when a new bond is created, so this method is more useful than
6523  *  the angle/dihedral counterpart.
6524  *  This operation is undoable.
6525  */
6526 static VALUE
6527 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6528 {
6529         Int n[5];
6530     Molecule *mol;
6531     Data_Get_Struct(self, Molecule, mol);
6532         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6533         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6534         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6535         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6536         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6537                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6538         n[4] = kInvalidIndex;
6539         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6540         return self;
6541 }
6542
6543 /*
6544  *  call-seq:
6545  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6546  *     remove_improper(intgroup)             -> Molecule
6547  *
6548  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6549  *  Returns self. Unlike angles and dihedrals, impropers are
6550  *  not automatically added when a new bond is created, so this method is more useful than
6551  *  the angle/dihedral counterpart.
6552  *  This operation is undoable.
6553  */
6554 static VALUE
6555 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6556 {
6557         Int n[5];
6558         VALUE v1, v2, v3, v4;
6559     Molecule *mol;
6560         IntGroup *ig;
6561     Data_Get_Struct(self, Molecule, mol);
6562         if (argc == 1) {
6563                 ig = IntGroupFromValue(argv[0]);
6564         } else {
6565                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6566                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6567                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6568                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6569                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6570                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6571                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6572                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6573         }
6574         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6575         IntGroupRelease(ig);
6576         return self;
6577 }
6578
6579 /*
6580  *  call-seq:
6581  *     assign_residue(group, res)       -> Molecule
6582  *
6583  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6584  *  or "resname.resno". When the residue number is not specified, the residue number of
6585  *  the first atom in the group is used.
6586  *  This operation is undoable.
6587  */
6588 static VALUE
6589 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6590 {
6591     Molecule *mol;
6592         IntGroup *ig;
6593         char *p, *pp, buf[16];
6594         Int resid, n;
6595         Atom *ap;
6596     Data_Get_Struct(self, Molecule, mol);
6597         
6598         /*  Parse the argument res  */
6599         if (FIXNUM_P(res)) {
6600                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6601                 resid = NUM2INT(res);
6602                 buf[0] = 0;
6603         } else {
6604                 p = StringValuePtr(res);
6605                 pp = strchr(p, '.');
6606                 if (pp != NULL) {
6607                         resid = atoi(pp + 1);
6608                         n = pp - p;
6609                 } else {
6610                         resid = -1;
6611                         n = strlen(p);
6612                 }
6613                 if (n > sizeof buf - 1)
6614                         n = sizeof buf - 1;
6615                 strncpy(buf, p, n);
6616                 buf[n] = 0;
6617         }
6618         ig = s_Molecule_AtomGroupFromValue(self, range);
6619         if (ig == NULL || IntGroupGetCount(ig) == 0)
6620                 return Qnil;
6621
6622         if (resid < 0) {
6623                 /*  Use the residue number of the first specified atom  */
6624                 n = IntGroupGetNthPoint(ig, 0);
6625                 if (n >= mol->natoms)
6626                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6627                 ap = ATOM_AT_INDEX(mol->atoms, n);
6628                 resid = ap->resSeq;
6629         }
6630         /*  Change the residue number  */
6631         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6632         /*  Change the residue name if necessary  */
6633         if (buf[0] != 0) {
6634         /*      Int seqs[2];
6635                 seqs[0] = resid;
6636                 seqs[1] = kInvalidIndex; */
6637                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6638         }
6639         IntGroupRelease(ig);
6640         return self;
6641 }
6642
6643 /*
6644  *  call-seq:
6645  *     offset_residue(group, offset)       -> Molecule
6646  *
6647  *  Offset the residue number of the specified atoms. If any of the residue number gets
6648  *  negative, then exception is thrown.
6649  *  This operation is undoable.
6650  */
6651 static VALUE
6652 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6653 {
6654     Molecule *mol;
6655         IntGroup *ig;
6656         int ofs, result;
6657     Data_Get_Struct(self, Molecule, mol);
6658         ig = s_Molecule_AtomGroupFromValue(self, range);
6659         ofs = NUM2INT(offset);
6660         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6661         if (result > 0)
6662                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6663         IntGroupRelease(ig);
6664         return self;
6665 }
6666
6667 /*
6668  *  call-seq:
6669  *     renumber_atoms(array)       -> IntGroup
6670  *
6671  *  Change the order of atoms so that the atoms specified in the array argument appear
6672  *  in this order from the top of the molecule. The atoms that are not included in array
6673  *  are placed after these atoms, and these atoms are returned as an intGroup.
6674  *  This operation is undoable.
6675  */
6676 static VALUE
6677 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6678 {
6679     Molecule *mol;
6680         Int *new2old;
6681         IntGroup *ig;
6682         int i, n;
6683         VALUE *valp, retval;
6684     Data_Get_Struct(self, Molecule, mol);
6685         if (TYPE(array) != T_ARRAY)
6686                 array = rb_funcall(array, rb_intern("to_a"), 0);
6687         n = RARRAY_LEN(array);
6688         valp = RARRAY_PTR(array);
6689         new2old = ALLOC_N(Int, n + 1);
6690         for (i = 0; i < n; i++)
6691                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6692         new2old[i] = kInvalidIndex;
6693         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6694         if (i == 1)
6695                 rb_raise(rb_eMolbyError, "Atom index out of range");
6696         else if (i == 2)
6697                 rb_raise(rb_eMolbyError, "Duplicate entry");
6698         else if (i == 3)
6699                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6700         retval = IntGroup_Alloc(rb_cIntGroup);
6701         Data_Get_Struct(retval, IntGroup, ig);
6702         if (mol->natoms > n)
6703                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6704         xfree(new2old);
6705         return retval;
6706 }
6707
6708 /*
6709  *  call-seq:
6710  *     set_atom_attr(index, key, value)
6711  *
6712  *  Set the atom attribute for the specified atom.
6713  *  This operation is undoable.
6714  */
6715 static VALUE
6716 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6717 {
6718         Molecule *mol;
6719         VALUE aref, oldval;
6720     Data_Get_Struct(self, Molecule, mol);
6721         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6722         oldval = s_AtomRef_GetAttr(aref, key);
6723         if (val == Qundef)
6724                 return oldval;
6725         s_AtomRef_SetAttr(aref, key, val);
6726         return val;
6727 }
6728
6729 /*
6730  *  call-seq:
6731  *     get_atom_attr(index, key)
6732  *
6733  *  Get the atom attribute for the specified atom.
6734  */
6735 static VALUE
6736 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6737 {
6738         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6739 }
6740
6741 #pragma mark ------ Undo Support ------
6742
6743 /*
6744  *  call-seq:
6745  *     register_undo(script, *args)
6746  *
6747  *  Register an undo operation with the current molecule.
6748  */
6749 static VALUE
6750 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6751 {
6752         Molecule *mol;
6753         VALUE script, args;
6754         MolAction *act;
6755     Data_Get_Struct(self, Molecule, mol);
6756         rb_scan_args(argc, argv, "1*", &script, &args);
6757         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6758         MolActionCallback_registerUndo(mol, act);
6759         return script;
6760 }
6761
6762 /*
6763  *  call-seq:
6764  *     undo_enabled? -> bool
6765  *
6766  *  Returns true if undo is enabled for this molecule; otherwise no.
6767  */
6768 static VALUE
6769 s_Molecule_UndoEnabled(VALUE self)
6770 {
6771     Molecule *mol;
6772     Data_Get_Struct(self, Molecule, mol);
6773         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6774                 return Qtrue;
6775         else return Qfalse;
6776 }
6777
6778 /*
6779  *  call-seq:
6780  *     undo_enabled = bool
6781  *
6782  *  Enable or disable undo.
6783  */
6784 static VALUE
6785 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6786 {
6787     Molecule *mol;
6788     Data_Get_Struct(self, Molecule, mol);
6789         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6790         return val;
6791 }
6792
6793 #pragma mark ------ Measure ------
6794
6795 static void
6796 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6797 {
6798         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6799                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6800                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6801                 case 0: break;
6802                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6803         }
6804 }
6805
6806 /*
6807  *  call-seq:
6808  *     center_of_mass(group = nil)       -> Vector3D
6809  *
6810  *  Calculate the center of mass for the given set of atoms. The argument
6811  *  group is null, then all atoms are considered.
6812  */
6813 static VALUE
6814 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6815 {
6816     Molecule *mol;
6817         VALUE group;
6818         IntGroup *ig;
6819         Vector v;
6820     Data_Get_Struct(self, Molecule, mol);
6821         rb_scan_args(argc, argv, "01", &group);
6822         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6823         s_Molecule_DoCenterOfMass(mol, &v, ig);
6824         if (ig != NULL)
6825                 IntGroupRelease(ig);
6826         return ValueFromVector(&v);
6827 }
6828
6829 /*
6830  *  call-seq:
6831  *     centralize(group = nil)       -> self
6832  *
6833  *  Translate the molecule so that the center of mass of the given group is located
6834  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6835  */
6836 static VALUE
6837 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6838 {
6839     Molecule *mol;
6840         VALUE group;
6841         IntGroup *ig;
6842         Vector v;
6843     Data_Get_Struct(self, Molecule, mol);
6844         rb_scan_args(argc, argv, "01", &group);
6845         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6846         s_Molecule_DoCenterOfMass(mol, &v, ig);
6847         if (ig != NULL)
6848                 IntGroupRelease(ig);
6849         v.x = -v.x;
6850         v.y = -v.y;
6851         v.z = -v.z;
6852         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6853         return self;
6854 }
6855
6856 /*
6857  *  call-seq:
6858  *     bounds(group = nil)       -> [min, max]
6859  *
6860  *  Calculate the boundary. The return value is an array of two Vector3D objects.
6861  */
6862 static VALUE
6863 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6864 {
6865     Molecule *mol;
6866         VALUE group;
6867         IntGroup *ig;
6868         Vector vmin, vmax;
6869         int n;
6870         Atom *ap;
6871     Data_Get_Struct(self, Molecule, mol);
6872         rb_scan_args(argc, argv, "01", &group);
6873         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6874         if (ig != NULL && IntGroupGetCount(ig) == 0)
6875                 rb_raise(rb_eMolbyError, "atom group is empty");
6876         vmin.x = vmin.y = vmin.z = 1e30;
6877         vmax.x = vmax.y = vmax.z = -1e30;
6878         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6879                 Vector r;
6880                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6881                         continue;
6882                 r = ap->r;
6883                 if (r.x < vmin.x)
6884                         vmin.x = r.x;
6885                 if (r.y < vmin.y)
6886                         vmin.y = r.y;
6887                 if (r.z < vmin.z)
6888                         vmin.z = r.z;
6889                 if (r.x > vmax.x)
6890                         vmax.x = r.x;
6891                 if (r.y > vmax.y)
6892                         vmax.y = r.y;
6893                 if (r.z > vmax.z)
6894                         vmax.z = r.z;
6895         }
6896         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6897 }
6898
6899 /*  Get atom position or a vector  */
6900 static void
6901 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6902 {
6903         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6904                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6905                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6906         } else {
6907                 VectorFromValue(val, vp);
6908         }
6909 }
6910
6911 /*
6912  *  call-seq:
6913  *     measure_bond(n1, n2)       -> Float
6914  *
6915  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
6916  *  or Vector3D values.
6917  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6918  */
6919 static VALUE
6920 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6921 {
6922     Molecule *mol;
6923         Vector v1, v2;
6924     Data_Get_Struct(self, Molecule, mol);
6925         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6926         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6927         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6928 }
6929
6930 /*
6931  *  call-seq:
6932  *     measure_angle(n1, n2, n3)       -> Float
6933  *
6934  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
6935  *  or Vector3D values. The return value is in degree.
6936  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6937  */
6938 static VALUE
6939 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6940 {
6941     Molecule *mol;
6942         Vector v1, v2, v3;
6943         Double d;
6944     Data_Get_Struct(self, Molecule, mol);
6945         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6946         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6947         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
6948         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6949         if (isnan(d))
6950                 return Qnil;  /*  Cannot define  */
6951         else return rb_float_new(d);
6952 }
6953
6954 /*
6955  *  call-seq:
6956  *     measure_dihedral(n1, n2, n3, n4)       -> Float
6957  *
6958  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
6959  *  or Vector3D values. The return value is in degree.
6960  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6961  */
6962 static VALUE
6963 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
6964 {
6965     Molecule *mol;
6966         Vector v1, v2, v3, v4;
6967         Double d;
6968     Data_Get_Struct(self, Molecule, mol);
6969         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6970         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6971         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
6972         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
6973         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
6974         if (isnan(d))
6975                 return Qnil;  /*  Cannot define  */
6976         else return rb_float_new(d);
6977 }
6978
6979 /*
6980  *  call-seq:
6981  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
6982  *
6983  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
6984  *  first and second atom in the pair should belong to group1 and group2, respectively.
6985  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
6986  */
6987 static VALUE
6988 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
6989 {
6990     Molecule *mol;
6991         VALUE limval, gval1, gval2, rval, igval;
6992         IntGroup *ig1, *ig2;
6993         IntGroupIterator iter1, iter2;
6994         Int npairs, *pairs;
6995         Int n[2], i;
6996         Double lim;
6997         Vector r1;
6998         Atom *ap1, *ap2;
6999         MDExclusion *exinfo;
7000         Int *exlist;
7001         
7002     Data_Get_Struct(self, Molecule, mol);
7003         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7004         lim = NUM2DBL(rb_Float(limval));
7005         if (lim <= 0.0)
7006                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7007         if (gval1 != Qnil)
7008                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7009         else
7010                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7011         if (gval2 != Qnil)
7012                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7013         else
7014                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7015         
7016         if (!RTEST(igval)) {
7017                 /*  Use the exclusion table in MDArena  */
7018                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7019                         VALUE mval = ValueFromMolecule(mol);
7020                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7021                 }
7022                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7023                 exlist = mol->arena->exlist;    
7024         } else {
7025                 exinfo = NULL;
7026                 exlist = NULL;
7027         }
7028         IntGroupIteratorInit(ig1, &iter1);
7029         IntGroupIteratorInit(ig2, &iter2);
7030         npairs = 0;
7031         pairs = NULL;
7032         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7033                 Int exn1, exn2;
7034                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7035                 r1 = ap1->r;
7036                 if (exinfo != NULL) {
7037                         exn1 = exinfo[n[0]].index1;
7038                         exn2 = exinfo[n[0] + 1].index1;
7039                 } else exn1 = exn2 = -1;
7040                 IntGroupIteratorReset(&iter2);
7041                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7042                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7043                         if (n[0] == n[1])
7044                                 continue;  /*  Same atom  */
7045                         if (exinfo != NULL) {
7046                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7047                                 for (i = exn1; i < exn2; i++) {
7048                                         if (exlist[i] == n[1])
7049                                                 break;
7050                                 }
7051                                 if (i < exn2)
7052                                         continue;  /*  Should be excluded  */
7053                         }
7054                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7055                                 /*  Is this pair already registered?  */
7056                                 Int *ip;
7057                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7058                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7059                                                 break;
7060                                 }
7061                                 if (i >= npairs) {
7062                                         /*  Not registered yet  */
7063                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7064                                 }
7065                         }
7066                 }
7067         }
7068         IntGroupIteratorRelease(&iter2);
7069         IntGroupIteratorRelease(&iter1);
7070         IntGroupRelease(ig2);
7071         IntGroupRelease(ig1);
7072         rval = rb_ary_new2(npairs);
7073         if (pairs != NULL) {
7074                 for (i = 0; i < npairs; i++) {
7075                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7076                 }
7077                 free(pairs);
7078         }
7079         return rval;
7080 }
7081
7082 /*
7083  *  call-seq:
7084  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7085  *
7086  *  Find atoms that are within the threshold distance from the given atom.
7087  *  (The atom argument can also be a vector, representing a cartesian coordinate. In that case, the van der Waals of the atom can also be specified.)
7088  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7089  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7090  *  If limit is not given, a default value of 1.2 is used.
7091  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7092  */
7093 static VALUE
7094 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7095 {
7096     Molecule *mol;
7097         VALUE aval, limval, radval;
7098         double limit, radius;
7099         Int n1, nbonds, *bonds, an;
7100         Vector v;
7101     Data_Get_Struct(self, Molecule, mol);
7102         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7103         if (rb_obj_is_kind_of(aval, rb_cVector3D) || rb_obj_is_kind_of(aval, rb_cLAMatrix) || rb_obj_is_kind_of(aval, rb_mEnumerable)) {
7104                 VectorFromValue(aval, &v);
7105                 if (radval == Qnil)
7106                         radius = gElementParameters[6].radius;
7107                 else
7108                         radius = NUM2DBL(rb_Float(radval));
7109                 n1 = mol->natoms;
7110         } else {
7111                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7112                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7113                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7114                 if (an >= 0 && an < gCountElementParameters)
7115                         radius = gElementParameters[an].radius;
7116                 else radius = gElementParameters[6].radius;
7117         }
7118         if (limval == Qnil)
7119                 limit = 1.2;
7120         else
7121                 limit = NUM2DBL(rb_Float(limval));
7122         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7123         bonds = NULL;
7124         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7125         aval = rb_ary_new();
7126         if (nbonds > 0) {
7127                 for (n1 = 0; n1 < nbonds; n1++)
7128                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7129                 free(bonds);
7130         }
7131         return aval;
7132 }
7133
7134 /*
7135  *  call-seq:
7136  *     guess_bonds(limit = 1.2)       -> Integer
7137  *
7138  *  Create bonds between atoms that are within the threshold distance.
7139  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7140  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7141  *  If limit is not given, a default value of 1.2 is used.
7142  *  The number of the newly created bonds is returned.
7143  *  This operation is undoable.
7144  */
7145 static VALUE
7146 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7147 {
7148     Molecule *mol;
7149         VALUE limval;
7150         double limit;
7151         Int nbonds, *bonds;
7152     Data_Get_Struct(self, Molecule, mol);
7153         rb_scan_args(argc, argv, "01", &limval);
7154         if (limval == Qnil)
7155                 limit = 1.2;
7156         else
7157                 limit = NUM2DBL(rb_Float(limval));
7158         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7159         if (nbonds > 0) {
7160                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7161                 free(bonds);
7162         }
7163         return INT2NUM(nbonds);
7164 }
7165
7166 #pragma mark ------ Cell and Symmetry ------
7167
7168 /*
7169  *  call-seq:
7170  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7171  *
7172  *  Returns the unit cell parameters. If cell is not set, returns nil.
7173  */
7174 static VALUE
7175 s_Molecule_Cell(VALUE self)
7176 {
7177     Molecule *mol;
7178         int i;
7179         VALUE val;
7180     Data_Get_Struct(self, Molecule, mol);
7181         if (mol->cell == NULL)
7182                 return Qnil;
7183         val = rb_ary_new2(6);
7184         for (i = 0; i < 6; i++)
7185                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7186         if (mol->cell->has_sigma) {
7187                 for (i = 0; i < 6; i++) {
7188                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7189                 }
7190         }
7191         return val;
7192 }
7193
7194 /*
7195  *  call-seq:
7196  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7197  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7198  *
7199  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7200  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7201  This operation is undoable.
7202  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7203  */
7204 static VALUE
7205 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7206 {
7207     Molecule *mol;
7208         VALUE val, cval;
7209         int i, convert_coord, n;
7210         double d[12];
7211     Data_Get_Struct(self, Molecule, mol);
7212         rb_scan_args(argc, argv, "11", &val, &cval);
7213         if (val == Qnil) {
7214                 n = 0;
7215         } else {
7216                 int len;
7217                 val = rb_ary_to_ary(val);
7218                 len = RARRAY_LEN(val);
7219                 if (len >= 12) {
7220                         n = 12;
7221                 } else if (len >= 6) {
7222                         n = 6;
7223                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7224                 for (i = 0; i < n; i++)
7225                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7226         }
7227         convert_coord = (RTEST(cval) ? 1 : 0);
7228         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7229         return val;
7230 }
7231
7232 /*
7233  *  call-seq:
7234  *     box -> [avec, bvec, cvec, origin, flags]
7235  *
7236  *  Get the unit cell information in the form of a periodic bounding box.
7237  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7238  *  Integers which define whether the system is periodic along the axis.
7239  *  If no unit cell is defined, nil is returned.
7240  */
7241 static VALUE
7242 s_Molecule_Box(VALUE self)
7243 {
7244     Molecule *mol;
7245         VALUE v[5], val;
7246     Data_Get_Struct(self, Molecule, mol);
7247         if (mol == NULL || mol->cell == NULL)
7248                 return Qnil;
7249         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7250         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7251         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7252         v[3] = ValueFromVector(&(mol->cell->origin));
7253         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7254         val = rb_ary_new4(5, v);
7255         return val;
7256 }
7257
7258 /*
7259  *  call-seq:
7260  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7261  *     set_box(d, origin = [0, 0, 0])
7262  *     set_box
7263  *
7264  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7265  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7266  as the box vector.
7267  Flags, if present, is a 3-member array of Integers defining whether the system is
7268  periodic along the axis.
7269  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7270  In the second form, an isotropic box with cell-length d is set.
7271  In the third form, the existing box is cleared.
7272  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7273  */
7274 static VALUE
7275 s_Molecule_SetBox(VALUE self, VALUE aval)
7276 {
7277     Molecule *mol;
7278         VALUE v[6];
7279         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7280         Vector vv[3];
7281         Vector origin = {0, 0, 0};
7282         char flags[3];
7283         Double d;
7284         int i, convertCoordinates = 0;
7285     Data_Get_Struct(self, Molecule, mol);
7286         if (aval == Qnil) {
7287                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7288                 return self;
7289         }
7290         aval = rb_ary_to_ary(aval);
7291         for (i = 0; i < 6; i++) {
7292                 if (i < RARRAY_LEN(aval))
7293                         v[i] = (RARRAY_PTR(aval))[i];
7294                 else v[i] = Qnil;
7295         }
7296         if (v[0] == Qnil) {
7297                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7298                 return self;
7299         }
7300         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7301                 d = NUM2DBL(rb_Float(v[0]));
7302                 for (i = 0; i < 3; i++)
7303                         VecScale(vv[i], ax[i], d);
7304                 if (v[1] != Qnil)
7305                         VectorFromValue(v[1], &origin);
7306                 flags[0] = flags[1] = flags[2] = 1;
7307         } else {
7308                 for (i = 0; i < 3; i++) {
7309                         if (v[i] == Qnil) {
7310                                 VecZero(vv[i]);
7311                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7312                                 d = NUM2DBL(rb_Float(v[i]));
7313                                 VecScale(vv[i], ax[i], d);
7314                         } else {
7315                                 VectorFromValue(v[i], &vv[i]);
7316                         }
7317                         flags[i] = (VecLength2(vv[i]) > 0.0);
7318                 }
7319                 if (v[3] != Qnil)
7320                         VectorFromValue(v[3], &origin);
7321                 if (v[4] != Qnil) {
7322                         for (i = 0; i < 3; i++) {
7323                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7324                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7325                         }
7326                 }
7327                 if (RTEST(v[5]))
7328                         convertCoordinates = 1;
7329         }
7330         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7331         return self;
7332 }
7333
7334 /*
7335  *  call-seq:
7336  *     cell_periodicity -> [n1, n2, n3]
7337  *
7338  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7339  *  nil is returned.
7340  */
7341 static VALUE
7342 s_Molecule_CellPeriodicity(VALUE self)
7343 {
7344     Molecule *mol;
7345     Data_Get_Struct(self, Molecule, mol);
7346         if (mol->cell == NULL)
7347                 return Qnil;
7348         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7349 }
7350
7351 /*
7352  *  call-seq:
7353  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7354  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7355  *
7356  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7357  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7358  *  If cell is not defined, exception is raised.
7359  *  This operation is undoable.
7360  */
7361 static VALUE
7362 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7363 {
7364     Molecule *mol;
7365         Int flag;
7366     Data_Get_Struct(self, Molecule, mol);
7367         if (mol->cell == NULL)
7368                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7369         if (arg == Qnil)
7370                 flag = 0;
7371         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7372                 flag = NUM2INT(rb_Integer(arg));
7373         else {
7374                 Int i;
7375                 VALUE arg0;
7376                 arg = rb_ary_to_ary(arg);
7377                 flag = 0;
7378                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7379                         arg0 = RARRAY_PTR(arg)[i];
7380                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7381                                 flag |= (1 << (2 - i));
7382                 }
7383         }
7384         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7385         return arg;
7386 }
7387
7388 /*
7389  *  call-seq:
7390  *     cell_flexibility -> bool
7391  *
7392  *  Returns the unit cell is flexible or not
7393  */
7394 static VALUE
7395 s_Molecule_CellFlexibility(VALUE self)
7396 {
7397         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7398         return Qtrue;
7399         /*    Molecule *mol;
7400          Data_Get_Struct(self, Molecule, mol);
7401          if (mol->cell == NULL)
7402          return Qfalse;
7403          if (mol->useFlexibleCell)
7404          return Qtrue;
7405          else return Qfalse; */
7406 }
7407
7408 /*
7409  *  call-seq:
7410  *     self.cell_flexibility = bool
7411  *     set_cell_flexibility(bool)
7412  *
7413  *  Change the unit cell is flexible or not
7414  */
7415 static VALUE
7416 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7417 {
7418         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7419         return self;
7420         /*    Molecule *mol;
7421          Data_Get_Struct(self, Molecule, mol);
7422          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7423          return self; */
7424 }
7425
7426 /*
7427  *  call-seq:
7428  *     cell_transform -> Transform
7429  *
7430  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7431  *  If cell is not defined, nil is returned.
7432  */
7433 static VALUE
7434 s_Molecule_CellTransform(VALUE self)
7435 {
7436     Molecule *mol;
7437     Data_Get_Struct(self, Molecule, mol);
7438         if (mol == NULL || mol->cell == NULL)
7439                 return Qnil;
7440         return ValueFromTransform(&(mol->cell->tr));
7441 }
7442
7443 /*
7444  *  call-seq:
7445  *     symmetry -> Array of Transforms
7446  *     symmetries -> Array of Transforms
7447  *
7448  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7449  *  returns an empty array.
7450  */
7451 static VALUE
7452 s_Molecule_Symmetry(VALUE self)
7453 {
7454     Molecule *mol;
7455         VALUE val;
7456         int i;
7457     Data_Get_Struct(self, Molecule, mol);
7458         if (mol->nsyms <= 0)
7459                 return rb_ary_new();
7460         val = rb_ary_new2(mol->nsyms);
7461         for (i = 0; i < mol->nsyms; i++) {
7462                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7463         }
7464         return val;
7465 }
7466
7467 /*
7468  *  call-seq:
7469  *     nsymmetries -> Integer
7470  *
7471  *  Get the number of currently defined symmetry operations.
7472  */
7473 static VALUE
7474 s_Molecule_Nsymmetries(VALUE self)
7475 {
7476     Molecule *mol;
7477     Data_Get_Struct(self, Molecule, mol);
7478         return INT2NUM(mol->nsyms);
7479 }
7480
7481 /*
7482  *  call-seq:
7483  *     add_symmetry(Transform) -> Integer
7484  *
7485  *  Add a new symmetry operation. If no symmetry operation is defined and the
7486  *  given argument is not an identity transform, then also add an identity
7487  *  transform at the index 0.
7488  *  Returns the total number of symmetries after operation.
7489  */
7490 static VALUE
7491 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7492 {
7493     Molecule *mol;
7494         Transform tr;
7495     Data_Get_Struct(self, Molecule, mol);
7496         TransformFromValue(trans, &tr);
7497         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7498         return INT2NUM(mol->nsyms);
7499 }
7500
7501 /*
7502  *  call-seq:
7503  *     remove_symmetry(count = nil) -> Integer
7504  *     remove_symmetries(count = nil) -> Integer
7505  *
7506  *  Remove the specified number of symmetry operations. The last added ones are removed
7507  *  first. If count is nil, then all symmetry operations are removed. Returns the
7508  *  number of leftover symmetries.
7509  */
7510 static VALUE
7511 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7512 {
7513     Molecule *mol;
7514         VALUE cval;
7515         int i, n;
7516     Data_Get_Struct(self, Molecule, mol);
7517         rb_scan_args(argc, argv, "01", &cval);
7518         if (cval == Qnil)
7519                 n = mol->nsyms - 1;
7520         else {
7521                 n = NUM2INT(rb_Integer(cval));
7522                 if (n < 0 || n > mol->nsyms)
7523                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7524                 if (n == mol->nsyms)
7525                         n = mol->nsyms - 1;
7526         }
7527         for (i = 0; i < n; i++)
7528                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7529         return INT2NUM(mol->nsyms);
7530 }
7531
7532 /*
7533  *  call-seq:
7534  *     wrap_unit_cell(group) -> Vector3D
7535  *
7536  *  Move the specified group so that the center of mass of the group is within the
7537  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7538  *  exception is raised.
7539  */
7540 static VALUE
7541 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7542 {
7543     Molecule *mol;
7544         IntGroup *ig;
7545         Vector v, cv, dv;
7546     Data_Get_Struct(self, Molecule, mol);
7547         if (mol->cell == NULL)
7548                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7549         ig = s_Molecule_AtomGroupFromValue(self, gval);
7550         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7551         TransformVec(&v, mol->cell->rtr, &cv);
7552         if (mol->cell->flags[0])
7553                 v.x -= floor(v.x);
7554         if (mol->cell->flags[1])
7555                 v.y -= floor(v.y);
7556         if (mol->cell->flags[2])
7557                 v.z -= floor(v.z);
7558         TransformVec(&dv, mol->cell->tr, &v);
7559         VecDec(dv, cv);
7560         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7561         IntGroupRelease(ig);
7562         return ValueFromVector(&dv);
7563 }
7564
7565 /*
7566  *  call-seq:
7567  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7568  *
7569  *  Expand the specified part of the molecule by the given symmetry operation.
7570  *  Returns the array of atom indices corresponding to the expanded atoms.
7571  *  If allow_overlap is true, then new atoms are created even when the
7572  *  coordinates coincide with the some other atom (special position) of the
7573  *  same element; otherwise, such atom will not be created and the index of the
7574  *  existing atom is given in the returned array.
7575  */
7576 static VALUE
7577 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7578 {
7579     Molecule *mol;
7580         VALUE gval, sval, xval, yval, zval, rval, oval;
7581         IntGroup *ig;
7582         Int n[4], allow_overlap;
7583         Int natoms;
7584         Int nidx, *idx;
7585         
7586     Data_Get_Struct(self, Molecule, mol);
7587         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7588         n[0] = NUM2INT(rb_Integer(sval));
7589         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7590         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7591         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7592         allow_overlap = (RTEST(oval) ? 1 : 0);
7593         ig = s_Molecule_AtomGroupFromValue(self, gval);
7594         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7595                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7596         natoms = mol->natoms;
7597         
7598         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7599         
7600         rval = rb_ary_new2(nidx);
7601         while (--nidx >= 0) {
7602                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7603         }
7604         /*      if (natoms == mol->natoms)
7605          rval = Qnil;
7606          else {
7607          rval = IntGroup_Alloc(rb_cIntGroup);
7608          Data_Get_Struct(rval, IntGroup, ig);
7609          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7610          } */
7611         return rval;
7612 }
7613
7614 /*
7615  *  call-seq:
7616  *     amend_by_symmetry(group = nil) -> IntGroup
7617  *
7618  *  Expand the specified part of the molecule by the given symmetry operation.
7619  *  Returns an IntGroup containing the added atoms.
7620  */
7621 static VALUE
7622 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7623 {
7624     Molecule *mol;
7625         IntGroup *ig, *ig2;
7626         VALUE rval, gval;
7627     Data_Get_Struct(self, Molecule, mol);
7628         rb_scan_args(argc, argv, "01", &gval);
7629         if (gval != Qnil)
7630                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7631         else ig = NULL;
7632         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7633         rval = ValueFromIntGroup(ig2);
7634         IntGroupRelease(ig2);
7635         return rval;
7636 }
7637
7638 #pragma mark ------ Transforms ------
7639
7640 /*
7641  *  call-seq:
7642  *     translate(vec, group = nil)       -> Molecule
7643  *
7644  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7645  *  This operation is undoable.
7646  */
7647 static VALUE
7648 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7649 {
7650     Molecule *mol;
7651         VALUE vec, group;
7652         Vector v;
7653         IntGroup *ig;
7654     Data_Get_Struct(self, Molecule, mol);
7655         rb_scan_args(argc, argv, "11", &vec, &group);
7656         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7657         VectorFromValue(vec, &v);
7658         //      MoleculeTranslate(mol, &v, ig);
7659         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7660         if (ig != NULL)
7661                 IntGroupRelease(ig);
7662         return self;
7663 }
7664
7665 /*
7666  *  call-seq:
7667  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7668  *
7669  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7670  *  If group is given, only atoms in the group are moved.
7671  *  This operation is undoable.
7672  */
7673 static VALUE
7674 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7675 {
7676     Molecule *mol;
7677         volatile VALUE aval, anval, cval, gval;
7678         Double angle;
7679         Vector av, cv;
7680         Transform tr;
7681         IntGroup *ig;
7682     Data_Get_Struct(self, Molecule, mol);
7683         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7684         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7685         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7686         VectorFromValue(aval, &av);
7687         if (NIL_P(cval))
7688                 cv.x = cv.y = cv.z = 0.0;
7689         else
7690                 VectorFromValue(cval, &cv);
7691         if (TransformForRotation(tr, &av, angle, &cv))
7692                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7693         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7694         if (ig != NULL)
7695                 IntGroupRelease(ig);
7696         return self;
7697 }
7698
7699 /*
7700  *  call-seq:
7701  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7702  *
7703  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7704  *  axis must not be a zero vector.
7705  *  If group is given, only atoms in the group are moved.
7706  *  This operation is undoable.
7707  */
7708 static VALUE
7709 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7710 {
7711     Molecule *mol;
7712         volatile VALUE aval, cval, gval;
7713         Vector av, cv;
7714         Transform tr;
7715         IntGroup *ig;
7716     Data_Get_Struct(self, Molecule, mol);
7717         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7718         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7719         VectorFromValue(aval, &av);
7720         if (NIL_P(cval))
7721                 cv.x = cv.y = cv.z = 0.0;
7722         else
7723                 VectorFromValue(cval, &cv);
7724         if (TransformForReflection(tr, &av, &cv))
7725                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7726         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7727         if (ig != NULL)
7728                 IntGroupRelease(ig);
7729         return self;
7730 }
7731
7732 /*
7733  *  call-seq:
7734  *     invert(center = [0,0,0], group = nil)       -> Molecule
7735  *
7736  *  Invert the molecule with the given center.
7737  *  If group is given, only atoms in the group are moved.
7738  *  This operation is undoable.
7739  */
7740 static VALUE
7741 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7742 {
7743         Molecule *mol;
7744         volatile VALUE cval, gval;
7745         Vector cv;
7746         Transform tr;
7747         IntGroup *ig;
7748     Data_Get_Struct(self, Molecule, mol);
7749         rb_scan_args(argc, argv, "02", &cval, &gval);
7750         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7751         if (NIL_P(cval))
7752                 cv.x = cv.y = cv.z = 0.0;
7753         else
7754                 VectorFromValue(cval, &cv);
7755         TransformForInversion(tr, &cv);
7756         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7757         if (ig != NULL)
7758                 IntGroupRelease(ig);
7759         return self;
7760 }
7761
7762 /*
7763  *  call-seq:
7764  *     transform(transform, group = nil)       -> Molecule
7765  *
7766  *  Transform the molecule by the given Transform object.
7767  *  If group is given, only atoms in the group are moved.
7768  *  This operation is undoable.
7769  */
7770 static VALUE
7771 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7772 {
7773     Molecule *mol;
7774         VALUE trans, group;
7775         Transform tr;
7776         IntGroup *ig;
7777     Data_Get_Struct(self, Molecule, mol);
7778         rb_scan_args(argc, argv, "11", &trans, &group);
7779         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7780         TransformFromValue(trans, &tr);
7781         /*      MoleculeTransform(mol, tr, ig); */
7782         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7783         if (ig != NULL)
7784                 IntGroupRelease(ig);
7785         return self;
7786 }
7787
7788 /*
7789  *  call-seq:
7790  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7791  *
7792  *  Get the transform corresponding to the symmetry operation. The symop can either be
7793  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7794  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7795  *  Otherwise, the returned transform is for fractional coordinates.
7796  *  Raises exception when no cell or no transform are defined.
7797  */
7798 static VALUE
7799 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7800 {
7801     Molecule *mol;
7802         VALUE sval, fval;
7803         Symop symop;
7804         Transform tr;
7805     Data_Get_Struct(self, Molecule, mol);
7806         if (mol->cell == NULL)
7807                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7808         if (mol->nsyms == 0)
7809                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7810         rb_scan_args(argc, argv, "11", &sval, &fval);
7811         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7812                 symop.sym = NUM2INT(rb_Integer(sval));
7813                 symop.dx = symop.dy = symop.dz = 0;
7814         } else {
7815                 sval = rb_ary_to_ary(sval);
7816                 if (RARRAY_LEN(sval) < 4)
7817                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7818                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7819                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7820                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7821                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7822         }
7823         if (symop.sym >= mol->nsyms)
7824                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7825         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7826         return ValueFromTransform(&tr);
7827 }
7828
7829 /*
7830  *  call-seq:
7831  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7832  *
7833  *  Get the symmetry operation corresponding to the given transform.
7834  *  If is_cartesian is true, the given transform is for cartesian coordinates.
7835  *  Otherwise, the given transform is for fractional coordinates.
7836  *  Raises exception when no cell or no transform are defined.
7837  */
7838 static VALUE
7839 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7840 {
7841     Molecule *mol;
7842         VALUE tval, fval;
7843         Symop symop;
7844         Transform tr;
7845         int n;
7846     Data_Get_Struct(self, Molecule, mol);
7847         if (mol->cell == NULL)
7848                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7849         if (mol->nsyms == 0)
7850                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7851         rb_scan_args(argc, argv, "11", &tval, &fval);
7852         TransformFromValue(tval, &tr);
7853         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7854         if (n == 0) {
7855                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7856         } else {
7857                 return Qnil;  /*  Not found  */
7858         }
7859 }
7860
7861 #pragma mark ------ Frames ------
7862
7863 /*
7864  *  call-seq:
7865  *     select_frame(index)
7866  *     frame = index
7867  *
7868  *  Select the specified frame. If successful, returns true, otherwise returns false.
7869  */
7870 static VALUE
7871 s_Molecule_SelectFrame(VALUE self, VALUE val)
7872 {
7873     Molecule *mol;
7874         int ival = NUM2INT(val);
7875     Data_Get_Struct(self, Molecule, mol);
7876         ival = MoleculeSelectFrame(mol, ival, 1);
7877         if (ival >= 0)
7878                 return Qtrue;
7879         else return Qfalse;
7880 }
7881
7882 /*
7883  *  call-seq:
7884  *     frame -> Integer
7885  *
7886  *  Get the current frame.
7887  */
7888 static VALUE
7889 s_Molecule_Frame(VALUE self)
7890 {
7891     Molecule *mol;
7892     Data_Get_Struct(self, Molecule, mol);
7893         return INT2NUM(mol->cframe);
7894 }
7895
7896 /*
7897  *  call-seq:
7898  *     nframes -> Integer
7899  *
7900  *  Get the number of frames.
7901  */
7902 static VALUE
7903 s_Molecule_Nframes(VALUE self)
7904 {
7905     Molecule *mol;
7906     Data_Get_Struct(self, Molecule, mol);
7907         return INT2NUM(MoleculeGetNumberOfFrames(mol));
7908 }
7909
7910 /*
7911  *  call-seq:
7912  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7913  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7914  *
7915  *  Insert new frames at the indices specified by the intGroup. If the first argument is
7916  *  an integer, a single new frame is inserted at that index. If the first argument is 
7917  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7918  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
7919  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
7920  *  to the new frame.
7921  *  Returns an intGroup representing the inserted frames if successful, nil if not.
7922  */
7923 static VALUE
7924 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7925 {
7926         VALUE val, coords, cells;
7927     Molecule *mol;
7928         IntGroup *ig;
7929         int count, ival, i, j, len, len_c, len2, nframes;
7930         VALUE *ptr, *ptr2;
7931         Vector *vp, *vp2;
7932     Data_Get_Struct(self, Molecule, mol);
7933         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7934         if (coords != Qnil) {
7935                 if (TYPE(coords) != T_ARRAY)
7936                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7937                 len = RARRAY_LEN(coords);
7938         } else len = 0;
7939         if (cells != Qnil) {
7940                 if (mol->cell == NULL)
7941                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7942                 if (TYPE(cells) != T_ARRAY)
7943                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7944                 len_c = RARRAY_LEN(cells);
7945         } else len_c = 0;
7946         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
7947         nframes = MoleculeGetNumberOfFrames(mol);
7948         if (val == Qnil) {
7949                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7950                 val = ValueFromIntGroup(ig);
7951         } else {
7952                 ig = IntGroupFromValue(val);
7953         }
7954         count = IntGroupGetCount(ig);  /*  Count is updated here  */
7955         vp = ALLOC_N(Vector, mol->natoms * count);
7956         if (cells != Qnil)
7957                 vp2 = ALLOC_N(Vector, 4 * count);
7958         else vp2 = NULL;
7959         if (len > 0) {
7960                 if (len < count)
7961                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7962                 ptr = RARRAY_PTR(coords);
7963                 for (i = 0; i < count; i++) {
7964                         if (TYPE(ptr[i]) != T_ARRAY)
7965                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7966                         len2 = RARRAY_LEN(ptr[i]);
7967                         if (len2 < mol->natoms)
7968                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7969                         ptr2 = RARRAY_PTR(ptr[i]);
7970                         for (j = 0; j < mol->natoms; j++)
7971                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7972                 }
7973         } else {
7974                 Atom *ap;
7975                 for (i = 0; i < count; i++) {
7976                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7977                                 vp[i * mol->natoms + j] = ap->r;
7978                         }
7979                 }
7980         }
7981         if (len_c > 0) {
7982                 if (len_c < count)
7983                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7984                 ptr = RARRAY_PTR(cells);
7985                 for (i = 0; i < count; i++) {
7986                         if (TYPE(ptr[i]) != T_ARRAY)
7987                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7988                         len2 = RARRAY_LEN(ptr[i]);
7989                         if (len2 < 4)
7990                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7991                         ptr2 = RARRAY_PTR(ptr[i]);
7992                         for (j = 0; j < 4; j++)
7993                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7994                 }
7995         }
7996         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7997         IntGroupRelease(ig);
7998         xfree(vp);
7999         if (vp2 != NULL)
8000                 xfree(vp2);
8001         return (ival >= 0 ? val : Qnil);
8002 }
8003
8004 /*
8005  *  call-seq:
8006  *     create_frame(coordinates = nil) -> Integer
8007  *     create_frames(coordinates = nil) -> Integer
8008  *
8009  *  Same as molecule.insert_frames(nil, coordinates).
8010  */
8011 static VALUE
8012 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8013 {
8014         VALUE vals[3];
8015         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8016         vals[0] = Qnil;
8017         return s_Molecule_InsertFrames(3, vals, self);
8018 }
8019
8020 /*
8021  *  call-seq:
8022  *     remove_frames(IntGroup, wantCoordinates = false)
8023  *
8024  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8025  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8026  *  removed frames is returned if operation is successful.
8027  */
8028 static VALUE
8029 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8030 {
8031         VALUE val, flag;
8032         VALUE retval;
8033     Molecule *mol;
8034         IntGroup *ig;
8035         int count;
8036     Data_Get_Struct(self, Molecule, mol);
8037         rb_scan_args(argc, argv, "11", &val, &flag);
8038         ig = IntGroupFromValue(val);
8039         count = IntGroupGetCount(ig);
8040         if (RTEST(flag)) {
8041                 /*  Create return value before removing frames  */
8042                 VALUE coords;
8043                 int i, j, n;
8044                 Atom *ap;
8045                 Vector v;
8046                 retval = rb_ary_new2(count);
8047                 for (i = 0; i < count; i++) {
8048                         n = IntGroupGetNthPoint(ig, i);
8049                         coords = rb_ary_new2(mol->natoms);
8050                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8051                                 if (n < ap->nframes && n != mol->cframe)
8052                                         v = ap->frames[n];
8053                                 else v = ap->r;
8054                                 rb_ary_push(coords, ValueFromVector(&v));
8055                         }
8056                         rb_ary_push(retval, coords);
8057                 }
8058         } else retval = Qtrue;
8059         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8060                 return retval;
8061         else return Qnil;
8062 }
8063
8064 /*
8065  *  call-seq:
8066  *     each_frame {|n| ...}
8067  *
8068  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8069  *  the frame number. After completion, the original frame number is restored.
8070  */
8071 static VALUE
8072 s_Molecule_EachFrame(VALUE self)
8073 {
8074         int i, cframe, nframes;
8075     Molecule *mol;
8076     Data_Get_Struct(self, Molecule, mol);
8077         cframe = mol->cframe;
8078         nframes = MoleculeGetNumberOfFrames(mol);
8079         if (nframes > 0) {
8080                 for (i = 0; i < nframes; i++) {
8081                         MoleculeSelectFrame(mol, i, 1);
8082                         rb_yield(INT2NUM(i));
8083                 }
8084                 MoleculeSelectFrame(mol, cframe, 1);
8085         }
8086     return self;
8087 }
8088
8089 /*
8090  *  call-seq:
8091  *     get_coord_from_frame(index, group = nil)
8092  *
8093  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8094  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8095  *  copied; now they are always copied)
8096  */
8097 static VALUE
8098 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8099 {
8100         Molecule *mol;
8101         VALUE ival, gval, cval;
8102         Int index, i, j, n, nn;
8103         IntGroup *ig;
8104         IntGroupIterator iter;
8105         Atom *ap;
8106         Vector *vp;
8107     Data_Get_Struct(self, Molecule, mol);
8108         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8109         if (argc == 3)
8110                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8111         index = NUM2INT(rb_Integer(ival));
8112         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8113                 if (n == 0)
8114                         rb_raise(rb_eMolbyError, "No frame is present");
8115                 else
8116                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8117         }
8118         if (gval == Qnil) {
8119                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8120         } else {
8121                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8122         }
8123         n = IntGroupGetCount(ig);
8124         if (n > 0) {
8125                 vp = (Vector *)calloc(sizeof(Vector), n);
8126                 IntGroupIteratorInit(ig, &iter);
8127                 j = 0;
8128                 nn = 0;
8129                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8130                         ap = ATOM_AT_INDEX(mol->atoms, i);
8131                         if (index < ap->nframes) {
8132                                 vp[j] = ap->frames[index];
8133                                 nn++;
8134                         } else {
8135                                 vp[j] = ap->r;
8136                         }
8137                         j++;
8138                 }
8139                 if (nn > 0)
8140                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8141                 free(vp);
8142                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8143                         vp = mol->frame_cells + index * 4;
8144                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8145                 }
8146                 IntGroupIteratorRelease(&iter);
8147         }
8148         /*  Copy the extra properties  */
8149         IntGroupRelease(ig);
8150         for (i = 0; i < mol->nmolprops; i++) {
8151                 Double *dp = (Double *)malloc(sizeof(Double));
8152                 ig = IntGroupNew();
8153                 IntGroupAdd(ig, mol->cframe, 1);
8154                 *dp = mol->molprops[i].propvals[index];
8155                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8156                 free(dp);
8157                 IntGroupRelease(ig);
8158         }
8159         
8160         return self;
8161 }
8162
8163 /*
8164  *  call-seq:
8165  *     reorder_frames(old_indices)
8166  *
8167  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8168  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8169  *  same as the old frames 2/0/1, respectively.
8170  *  The argument must have the same number of integers as the number of frames.
8171  */
8172 static VALUE
8173 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8174 {
8175         Molecule *mol;
8176         Int *ip, *ip2, i, n, nframes;
8177     Data_Get_Struct(self, Molecule, mol);
8178         aval = rb_ary_to_ary(aval);
8179         nframes = MoleculeGetNumberOfFrames(mol);
8180         if (RARRAY_LEN(aval) != nframes)
8181                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8182         ip2 = (Int *)calloc(sizeof(Int), nframes);
8183         ip = (Int *)calloc(sizeof(Int), nframes);
8184         for (i = 0; i < nframes; i++) {
8185                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8186                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8187                         free(ip2);
8188                         free(ip);
8189                         if (n < 0 || n >= nframes)
8190                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8191                         else
8192                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8193                 }
8194                 ip2[n] = 1;
8195                 ip[i] = n;
8196         }
8197         free(ip2);
8198         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8199         free(ip);
8200         return self;
8201 }
8202
8203 #pragma mark ------ Fragments ------
8204
8205 /*
8206  *  call-seq:
8207  *     fragment(n1, *exatoms)  -> IntGroup
8208  *     fragment(group, *exatoms)  -> IntGroup
8209  *
8210  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8211  *  those atoms will not be counted during the search.
8212  */
8213 static VALUE
8214 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8215 {
8216     Molecule *mol;
8217         IntGroup *baseg, *ig, *exatoms;
8218         int n;
8219         volatile VALUE nval, exval;
8220     Data_Get_Struct(self, Molecule, mol);
8221         rb_scan_args(argc, argv, "1*", &nval, &exval);
8222         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8223                 baseg = NULL;
8224                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8225         } else {
8226                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8227         }
8228         if (RARRAY_LEN(exval) == 0) {
8229                 exatoms = NULL;
8230         } else {
8231                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8232                 Data_Get_Struct(exval, IntGroup, exatoms);
8233         }
8234         if (baseg == NULL) {
8235                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8236         } else {
8237                 IntGroupIterator iter;
8238                 IntGroupIteratorInit(baseg, &iter);
8239                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8240                         ig = IntGroupNew();
8241                 } else {
8242                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8243                         if (ig != NULL) {
8244                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8245                                         IntGroup *subg;
8246                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8247                                         if (subg != NULL) {
8248                                                 IntGroupAddIntGroup(ig, subg);
8249                                                 IntGroupRelease(subg);
8250                                         }
8251                                 }
8252                         }
8253                 }
8254                 IntGroupIteratorRelease(&iter);
8255                 IntGroupRelease(baseg);
8256         }
8257         if (ig == NULL)
8258                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8259         nval = ValueFromIntGroup(ig);
8260         IntGroupRelease(ig);
8261         return nval;
8262 }
8263
8264 /*
8265  *  call-seq:
8266  *     fragments(exclude = nil)
8267  *
8268  *  Returns the fragments as an array of IntGroups. 
8269  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8270  *  in defining the fragment.
8271  */
8272 static VALUE
8273 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8274 {
8275     Molecule *mol;
8276         IntGroup *ag, *fg, *eg;
8277         VALUE gval, exval, retval;
8278     Data_Get_Struct(self, Molecule, mol);
8279         if (mol == NULL)
8280                 return Qnil;
8281         if (mol->natoms == 0)
8282                 return rb_ary_new();
8283         rb_scan_args(argc, argv, "01", &exval);
8284         if (exval == Qnil)
8285                 eg = NULL;
8286         else
8287                 eg = IntGroupFromValue(exval);
8288         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8289         if (eg != NULL)
8290                 IntGroupRemoveIntGroup(ag, eg);
8291         retval = rb_ary_new();
8292         while (IntGroupGetCount(ag) > 0) {
8293                 int n = IntGroupGetNthPoint(ag, 0);
8294                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8295                 if (fg == NULL)
8296                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8297                 gval = ValueFromIntGroup(fg);
8298                 rb_ary_push(retval, gval);
8299                 IntGroupRemoveIntGroup(ag, fg);
8300                 IntGroupRelease(fg);
8301         }
8302         IntGroupRelease(ag);
8303         if (eg != NULL)
8304                 IntGroupRelease(eg);
8305         return retval;
8306 }
8307
8308 /*
8309  *  call-seq:
8310  *     each_fragment(exclude = nil) {|group| ...}
8311  *
8312  *  Execute the block, with the IntGroup object for each fragment as the argument.
8313  *  Atoms or bonds should not be added or removed during the execution of the block.
8314  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8315  *  in defining the fragment.
8316  */
8317 static VALUE
8318 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8319 {
8320     Molecule *mol;
8321         IntGroup *ag, *fg, *eg;
8322         VALUE gval, exval;
8323     Data_Get_Struct(self, Molecule, mol);
8324         if (mol == NULL || mol->natoms == 0)
8325                 return self;
8326         rb_scan_args(argc, argv, "01", &exval);
8327         if (exval == Qnil)
8328                 eg = NULL;
8329         else
8330                 eg = IntGroupFromValue(exval);
8331         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8332         if (eg != NULL)
8333                 IntGroupRemoveIntGroup(ag, eg);
8334         while (IntGroupGetCount(ag) > 0) {
8335                 int n = IntGroupGetNthPoint(ag, 0);
8336                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8337                 if (fg == NULL)
8338                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8339                 gval = ValueFromIntGroup(fg);
8340                 rb_yield(gval);
8341                 IntGroupRemoveIntGroup(ag, fg);
8342                 IntGroupRelease(fg);
8343         }
8344         IntGroupRelease(ag);
8345         if (eg != NULL)
8346                 IntGroupRelease(eg);
8347         return self;
8348 }
8349
8350 /*
8351  *  call-seq:
8352  *     detachable?(group)  -> [n1, n2]
8353  *
8354  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8355  *  of the molecule via only one bond. If it is, then the indices of the atoms
8356  *  belonging to the bond is returned, the first element being the atom included
8357  *  in the fragment. Otherwise, Qnil is returned.
8358  */
8359 static VALUE
8360 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8361 {
8362         Molecule *mol;
8363         IntGroup *ig;
8364         int n1, n2;
8365         VALUE retval;
8366     Data_Get_Struct(self, Molecule, mol);
8367         ig = s_Molecule_AtomGroupFromValue(self, gval);
8368         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8369                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8370         } else retval = Qnil;
8371         IntGroupRelease(ig);
8372         return retval;
8373 }
8374
8375 /*
8376  *  call-seq:
8377  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8378  *
8379  *  Returns an array of bonds that connect an atom in the group and an atom out
8380  *  of the group. The first atom in the bond always belongs to the group. If no
8381  *  such bonds are present, an empty array is returned.
8382  */
8383 static VALUE
8384 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8385 {
8386         Molecule *mol;
8387         IntGroup *ig, *bg;
8388         VALUE gval, retval;
8389     Data_Get_Struct(self, Molecule, mol);
8390         rb_scan_args(argc, argv, "01", &gval);
8391         if (gval == Qnil) {
8392                 ig = MoleculeGetSelection(mol);
8393                 if (ig != NULL)
8394                         IntGroupRetain(ig);
8395         } else {
8396                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8397         }
8398         retval = rb_ary_new();
8399         if (ig == NULL)
8400                 return retval;
8401         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8402         if (bg != NULL) {
8403                 IntGroupIterator iter;
8404                 Int i;
8405                 IntGroupIteratorInit(bg, &iter);
8406                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8407                         /*  The atoms at the border  */
8408                         Int n1, n2;
8409                         n1 = mol->bonds[i * 2];
8410                         n2 = mol->bonds[i * 2 + 1];
8411                         if (IntGroupLookupPoint(ig, n1) < 0) {
8412                                 int w = n1;
8413                                 n1 = n2;
8414                                 n2 = w;
8415                                 if (IntGroupLookupPoint(ig, n1) < 0)
8416                                         continue;  /*  Actually this is an internal error  */
8417                         }
8418                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8419                 }
8420                 IntGroupIteratorRelease(&iter);
8421         }
8422         IntGroupRelease(bg);
8423         IntGroupRelease(ig);
8424         return retval;
8425 }
8426
8427 /*  Calculate the transform that moves the current coordinates to the reference
8428  coordinates with least displacements.   */
8429 static Double
8430 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8431 {
8432         Atom *ap, *ap1;
8433         Int natoms, nn;
8434         Vector org1, org2;
8435         Int i, in, j, k;
8436         Double w, w1;
8437         Mat33 r, q, u;
8438         Double eigen_val[3];
8439         Vector eigen_vec[3];
8440         Vector s[3];
8441         IntGroupIterator iter;
8442
8443         natoms = mol->natoms;
8444         ap = mol->atoms;
8445         IntGroupIteratorInit(ig, &iter);
8446         
8447         /*  Calculate the weighted center  */
8448         VecZero(org1);
8449         VecZero(org2);
8450         w = 0.0;
8451         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8452                 ap1 = ATOM_AT_INDEX(ap, in);
8453                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8454                 VecScaleInc(org1, ap1->r, w1);
8455                 VecScaleInc(org2, ref[i], w1);
8456                 w += w1;
8457         }
8458         w = 1.0 / w;
8459         VecScaleSelf(org1, w);
8460         VecScaleSelf(org2, w);
8461
8462     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8463     /*  Matrix to diagonalize = R * tR    */
8464         memset(r, 0, sizeof(Mat33));
8465         memset(q, 0, sizeof(Mat33));
8466         memset(u, 0, sizeof(Mat33));
8467         nn = 0;
8468         IntGroupIteratorReset(&iter);
8469         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8470                 Vector v1, v2;
8471                 ap1 = ATOM_AT_INDEX(ap, in);
8472                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8473                 w1 *= w1;
8474                 VecSub(v1, ap1->r, org1);
8475                 VecSub(v2, ref[i], org2);
8476                 r[0] += w1 * v1.x * v2.x;
8477                 r[1] += w1 * v1.y * v2.x;
8478                 r[2] += w1 * v1.z * v2.x;
8479                 r[3] += w1 * v1.x * v2.y;
8480                 r[4] += w1 * v1.y * v2.y;
8481                 r[5] += w1 * v1.z * v2.y;
8482                 r[6] += w1 * v1.x * v2.z;
8483                 r[7] += w1 * v1.y * v2.z;
8484                 r[8] += w1 * v1.z * v2.z;
8485                 nn++;
8486         }
8487         for (i = 0; i < 9; i++)
8488                 r[i] /= (nn * nn);
8489         for (i = 0; i < 3; i++) {
8490                 for (j = 0; j < 3; j++) {
8491                         for (k = 0; k < 3; k++) {
8492                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8493                         }
8494                 }
8495         }
8496         
8497         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8498                 IntGroupIteratorRelease(&iter);
8499                 return -1.0;  /*  Cannot determine the eigenvector  */
8500         }
8501
8502     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8503     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8504         MatrixTranspose(r, r);
8505         for (i = 0; i < 3; i++) {
8506                 MatrixVec(&s[i], r, &eigen_vec[i]);
8507                 w1 = 1.0 / sqrt(eigen_val[i]);
8508                 VecScaleSelf(s[i], w1);
8509         }
8510         for (k = 0; k < 3; k++) {
8511                 u[0] += s[k].x * eigen_vec[k].x;
8512                 u[1] += s[k].y * eigen_vec[k].x;
8513                 u[2] += s[k].z * eigen_vec[k].x;
8514                 u[3] += s[k].x * eigen_vec[k].y;
8515                 u[4] += s[k].y * eigen_vec[k].y;
8516                 u[5] += s[k].z * eigen_vec[k].y;
8517                 u[6] += s[k].x * eigen_vec[k].z;
8518                 u[7] += s[k].y * eigen_vec[k].z;
8519                 u[8] += s[k].z * eigen_vec[k].z;
8520         }
8521         
8522         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8523         MatrixVec(&org1, u, &org1);
8524         VecDec(org2, org1);
8525         for (i = 0; i < 9; i++)
8526                 trans[i] = u[i];
8527         trans[9] = org2.x;
8528         trans[10] = org2.y;
8529         trans[11] = org2.z;
8530         
8531         /*  Calculate rmsd  */
8532         IntGroupIteratorReset(&iter);
8533         w = 0.0;
8534         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8535                 Vector tv;
8536                 ap1 = ATOM_AT_INDEX(ap, in);
8537                 TransformVec(&tv, trans, &ap1->r);
8538                 VecDec(tv, ref[i]);
8539                 w += VecLength2(tv);
8540         }
8541         w = sqrt(w / nn);
8542         IntGroupIteratorRelease(&iter);
8543         return w;
8544 }
8545
8546 /*
8547  *  call-seq:
8548  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8549  *
8550  *  Calculate the transform to fit the given group to the set of reference coordinates.
8551  *  The reference coordinates ref is given as either a frame number, an array of
8552  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8553  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8554  *  Return values are the transform (that converts the present coordinates to the
8555  *  target coordinates) and root mean square deviation (without weight).
8556  */
8557 static VALUE
8558 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8559 {
8560         Molecule *mol;
8561         Atom *ap;
8562         VALUE gval, rval, wval;
8563         IntGroup *ig;
8564         IntGroupIterator iter;
8565         int nn, errno, i, j, in, status;
8566         Vector *ref;
8567         Double *weights, dval[3];
8568         Transform tr;
8569
8570         Data_Get_Struct(self, Molecule, mol);
8571         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8572         if (gval == Qnil)
8573                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8574         else
8575                 ig = IntGroupFromValue(gval);
8576         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8577                 IntGroupRelease(ig);
8578                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8579         }
8580         ref = (Vector *)calloc(sizeof(Vector), nn);
8581         weights = (Double *)calloc(sizeof(Double), nn);
8582         IntGroupIteratorInit(ig, &iter);
8583         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8584                 int fn = NUM2INT(rb_Integer(rval));
8585                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8586                         errno = 1;
8587                         status = fn;
8588                         goto err;
8589                 }
8590                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8591                         ap = ATOM_AT_INDEX(mol->atoms, in);
8592                         if (fn < ap->nframes)
8593                                 ref[i] = ap->frames[fn];
8594                         else ref[i] = ap->r;
8595                 }
8596         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8597                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8598                 if (m->row * m->column < nn * 3) {
8599                         errno = 2;
8600                         goto err;
8601                 }
8602                 for (i = 0; i < nn; i++) {
8603                         ref[i].x = m->data[i * 3];
8604                         ref[i].y = m->data[i * 3 + 1];
8605                         ref[i].z = m->data[i * 3 + 2];
8606                 }
8607         } else {
8608                 VALUE aval;
8609                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8610                 if (status != 0) {
8611                         errno = 3;
8612                         goto err;
8613                 }
8614                 if (RARRAY_LEN(rval) < nn) {
8615                         errno = 2;
8616                         goto err;
8617                 }
8618                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8619                         /*  Array of 3*nn numbers  */
8620                         if (RARRAY_LEN(rval) < nn * 3) {
8621                                 errno = 2;
8622                                 goto err;
8623                         }
8624                         for (i = 0; i < nn; i++) {
8625                                 for (j = 0; j < 3; j++) {
8626                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8627                                         if (status != 0) {
8628                                                 errno = 3;
8629                                                 goto err;
8630                                         }
8631                                         dval[j] = NUM2DBL(aval);
8632                                 }
8633                                 ref[i].x = dval[0];
8634                                 ref[i].y = dval[1];
8635                                 ref[i].z = dval[2];
8636                         }
8637                 } else {
8638                         /*  Array of nn Vector3Ds or Arrays  */
8639                         for (i = 0; i < nn; i++) {
8640                                 aval = (RARRAY_PTR(rval))[i];
8641                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8642                                         VectorFromValue(aval, &ref[i]);
8643                                 } else {
8644                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8645                                         if (status != 0) {
8646                                                 errno = 3;
8647                                                 goto err;
8648                                         }
8649                                         if (RARRAY_LEN(aval) < 3) {
8650                                                 errno = 4;
8651                                                 status = i;
8652                                                 goto err;
8653                                         }
8654                                         for (j = 0; j < 3; j++) {
8655                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8656                                                 if (status != 0) {
8657                                                         errno = 3;
8658                                                         goto err;
8659                                                 }
8660                                                 dval[j] = NUM2DBL(aaval);
8661                                         }
8662                                         ref[i].x = dval[0];
8663                                         ref[i].y = dval[1];
8664                                         ref[i].z = dval[2];
8665                                 }
8666                         }
8667                 }
8668         }
8669         if (wval == Qnil) {
8670                 /*  Use atomic weights  */
8671                 IntGroupIteratorReset(&iter);
8672                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8673                         ap = ATOM_AT_INDEX(mol->atoms, in);
8674                         weights[i] = ap->weight;
8675                 }
8676         } else {
8677                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8678                 if (status != 0) {
8679                         errno = 3;
8680                         goto err;
8681                 }
8682                 if (RARRAY_LEN(wval) < nn) {
8683                         errno = 5;
8684                         goto err;
8685                 }
8686                 for (i = 0; i < nn; i++) {
8687                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8688                         if (status != 0) {
8689                                 errno = 3;
8690                                 goto err;
8691                         }
8692                         weights[i] = NUM2DBL(wwval);
8693                 }
8694         }
8695         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8696         if (dval[0] < 0) {
8697                 errno = 6;
8698                 goto err;
8699         }
8700         errno = 0;
8701 err:
8702         IntGroupIteratorRelease(&iter);
8703         free(ref);
8704         free(weights);
8705         if (errno == 0) {
8706                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8707         } else if (errno == 1) {
8708                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8709         } else if (errno == 2) {
8710                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8711         } else if (errno == 3) {
8712                 rb_jump_tag(status);
8713         } else if (errno == 4) {
8714                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8715         } else if (errno == 5) {
8716                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8717         } else if (errno == 6) {
8718                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8719         }
8720         return Qnil;  /*  Not reached  */
8721 }
8722
8723 #pragma mark ------ Screen Display ------
8724
8725 /*
8726  *  call-seq:
8727  *     display
8728  *
8729  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8730  */
8731 static VALUE
8732 s_Molecule_Display(VALUE self)
8733 {
8734     Molecule *mol;
8735     Data_Get_Struct(self, Molecule, mol);
8736         if (mol->mview != NULL)
8737                 MainViewCallback_display(mol->mview);
8738         return Qnil;
8739 }
8740
8741 /*
8742  *  call-seq:
8743  *     make_front
8744  *
8745  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8746  */
8747 static VALUE
8748 s_Molecule_MakeFront(VALUE self)
8749 {
8750     Molecule *mol;
8751     Data_Get_Struct(self, Molecule, mol);
8752         if (mol->mview != NULL)
8753                 MainViewCallback_makeFront(mol->mview);
8754         return Qnil;
8755 }
8756
8757 /*
8758  *  call-seq:
8759  *     update_enabled? -> bool
8760  *
8761  *  Returns true if screen update is enabled; otherwise no.
8762  */
8763 static VALUE
8764 s_Molecule_UpdateEnabled(VALUE self)
8765 {
8766     Molecule *mol;
8767     Data_Get_Struct(self, Molecule, mol);
8768         if (mol->mview != NULL && !mol->mview->freezeScreen)
8769                 return Qtrue;
8770         else return Qfalse;
8771 }
8772
8773 /*
8774  *  call-seq:
8775  *     update_enabled = bool
8776  *
8777  *  Enable or disable screen update. This is effective for automatic update on modification.
8778  *  Explicit call to molecule.display() always updates the screen.
8779  */
8780 static VALUE
8781 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8782 {
8783     Molecule *mol;
8784     Data_Get_Struct(self, Molecule, mol);
8785         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8786         if (mol->mview != NULL)
8787                 mol->mview->freezeScreen = (val == Qfalse);
8788         else val = Qfalse;
8789         return val;
8790 }
8791
8792 /*
8793  *  call-seq:
8794  *     show_unitcell
8795  *     show_unitcell(bool)
8796  *     show_unitcell = bool
8797  *
8798  *  Set the flag whether to show the unit cell. If no argument is given, the
8799  *  current flag is returned.
8800  */
8801 static VALUE
8802 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8803 {
8804     Molecule *mol;
8805     Data_Get_Struct(self, Molecule, mol);
8806         if (mol->mview == NULL)
8807                 return Qnil;
8808         if (argc > 0) {
8809                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8810                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8811         }
8812         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8813 }
8814
8815 /*
8816  *  call-seq:
8817  *     show_hydrogens
8818  *     show_hydrogens(bool)
8819  *     show_hydrogens = bool
8820  *
8821  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8822  *  current flag is returned.
8823  */
8824 static VALUE
8825 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8826 {
8827     Molecule *mol;
8828     Data_Get_Struct(self, Molecule, mol);
8829         if (mol->mview == NULL)
8830                 return Qnil;
8831         if (argc > 0) {
8832                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8833                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8834         }
8835         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8836 }
8837
8838 /*
8839  *  call-seq:
8840  *     show_dummy_atoms
8841  *     show_dummy_atoms(bool)
8842  *     show_dummy_atoms = bool
8843  *
8844  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8845  *  current flag is returned.
8846  */
8847 static VALUE
8848 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8849 {
8850     Molecule *mol;
8851     Data_Get_Struct(self, Molecule, mol);
8852         if (mol->mview == NULL)
8853                 return Qnil;
8854         if (argc > 0) {
8855                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8856                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8857         }
8858         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8859 }
8860
8861 /*
8862  *  call-seq:
8863  *     show_expanded
8864  *     show_expanded(bool)
8865  *     show_expanded = bool
8866  *
8867  *  Set the flag whether to show the expanded atoms. If no argument is given, the
8868  *  current flag is returned.
8869  */
8870 static VALUE
8871 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8872 {
8873     Molecule *mol;
8874     Data_Get_Struct(self, Molecule, mol);
8875         if (mol->mview == NULL)
8876                 return Qnil;
8877         if (argc > 0) {
8878                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8879                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8880         }
8881         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8882 }
8883
8884 /*
8885  *  call-seq:
8886  *     show_ellipsoids
8887  *     show_ellipsoids(bool)
8888  *     show_ellipsoids = bool
8889  *
8890  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8891  *  current flag is returned.
8892  */
8893 static VALUE
8894 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8895 {
8896     Molecule *mol;
8897     Data_Get_Struct(self, Molecule, mol);
8898         if (mol->mview == NULL)
8899                 return Qnil;
8900         if (argc > 0) {
8901                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8902                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8903         }
8904         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8905 }
8906
8907 /*
8908  *  call-seq:
8909  *     is_atom_visible(index)  -> Boolean
8910  *
8911  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8912  *  as well as the molecule attributes (showHydrogens, etc.)
8913  */
8914 static VALUE
8915 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8916 {
8917         Molecule *mol;
8918         Int idx;
8919         Atom *ap;
8920     Data_Get_Struct(self, Molecule, mol);
8921         idx = s_Molecule_AtomIndexFromValue(mol, ival);
8922         if (idx < 0 || idx >= mol->natoms)
8923                 return Qnil;
8924         ap = ATOM_AT_INDEX(mol->atoms, idx);
8925         if (mol->mview != NULL) {
8926                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8927                         return Qfalse;
8928                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8929                         return Qfalse;
8930                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8931                         return Qfalse;
8932         }
8933         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8934 }
8935
8936 /*
8937  *  call-seq:
8938  *     hidden_atoms       -> IntGroup
8939  *
8940  *  Returns the currently hidden atoms.
8941  */
8942 static VALUE
8943 s_Molecule_HiddenAtoms(VALUE self)
8944 {
8945         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8946         return Qnil;  /*  Not reached  */
8947 }
8948
8949 /*
8950  *  call-seq:
8951  *     set_hidden_atoms(IntGroup)
8952  *     self.hidden_atoms = IntGroup
8953  *
8954  *  Hide the specified atoms. This operation is _not_ undoable.
8955  */
8956 static VALUE
8957 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8958 {
8959         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8960         return Qnil;  /*  Not reached  */
8961 }
8962
8963 /*
8964  *  call-seq:
8965  *     show_graphite -> Integer
8966  *     show_graphite = Integer
8967  *     show_graphite = boolean
8968  *
8969  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
8970  *  number of rings to display for each direction.
8971  *  If the argument is boolean, only the show/hide flag is set.
8972  */
8973 static VALUE
8974 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
8975 {
8976     Molecule *mol;
8977     Data_Get_Struct(self, Molecule, mol);
8978         if (mol->mview == NULL)
8979                 return Qnil;
8980         if (argc > 0) {
8981                 if (argv[0] == Qnil || argv[0] == Qfalse)
8982                         mol->mview->showGraphiteFlag = 0;
8983                 else if (argv[0] == Qtrue)
8984                         mol->mview->showGraphiteFlag = 1;
8985                 else {
8986                         int n = NUM2INT(rb_Integer(argv[0]));
8987                         if (n < 0)
8988                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
8989                         mol->mview->showGraphite = n;
8990                 }
8991                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8992         }
8993         return INT2NUM(mol->mview->showGraphite);
8994 }
8995
8996 /*
8997  *  call-seq:
8998  *     show_graphite? -> boolean
8999  *
9000  *  Return whether the graphite is set visible or not.
9001 */
9002 static VALUE
9003 s_Molecule_ShowGraphiteFlag(VALUE self)
9004 {
9005     Molecule *mol;
9006     Data_Get_Struct(self, Molecule, mol);
9007         if (mol->mview == NULL)
9008                 return Qnil;
9009         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9010 }
9011         
9012 /*
9013  *  call-seq:
9014  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9015  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9016  *     show_periodic_image = boolean
9017  *
9018  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9019  *  set but no visual effects are observed.
9020  *  If the argument is boolean, only the show/hide flag is modified.
9021  */
9022 static VALUE
9023 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9024 {
9025     Molecule *mol;
9026         VALUE val;
9027         int ival[6];
9028         int i;
9029     Data_Get_Struct(self, Molecule, mol);
9030         if (mol->mview == NULL)
9031                 return Qnil;
9032         rb_scan_args(argc, argv, "01", &val);
9033         if (argc > 0) {
9034                 /*  Change current settings  */
9035                 if (val == Qnil || val == Qfalse)
9036                         mol->mview->showPeriodicImageFlag = 0;
9037                 else if (val == Qtrue)
9038                         mol->mview->showPeriodicImageFlag = 1;
9039                 else {
9040                         val = rb_ary_to_ary(val);
9041                         for (i = 0; i < 6; i++) {
9042                                 if (i < RARRAY_LEN(val))
9043                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9044                         }
9045                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9046                                 rb_raise(rb_eMolbyError, "bad arguments");
9047                         for (i = 0; i < 6; i++)
9048                                 mol->mview->showPeriodicImage[i] = ival[i];
9049                 }
9050                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9051         }
9052         val = rb_ary_new();
9053         for (i = 0; i < 6; i++)
9054                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9055         return val;
9056 }
9057
9058 /*
9059  *  call-seq:
9060  *     show_periodic_image? -> boolean
9061  *
9062  *  Return whether the periodic images are set to visible or not. This flag is
9063  *  independent from the show_periodic_image settings.
9064  */
9065 static VALUE
9066 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9067 {
9068     Molecule *mol;
9069     Data_Get_Struct(self, Molecule, mol);
9070         if (mol->mview == NULL)
9071                 return Qnil;
9072         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9073 }
9074
9075 /*
9076  *  call-seq:
9077  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9078  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9079  *     show_rotation_center = boolean
9080  *
9081  *  Set to show the rotation center of the screen.
9082  *  If the argument is boolean, only the show/hide flag is modified.
9083  */
9084 static VALUE
9085 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9086 {
9087     Molecule *mol;
9088     Data_Get_Struct(self, Molecule, mol);
9089         if (mol->mview == NULL)
9090                 return Qnil;
9091         if (argc > 0) {
9092                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9093                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9094         }
9095         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9096 }
9097
9098 /*
9099  *  call-seq:
9100  *     line_mode
9101  *     line_mode(bool)
9102  *     line_mode = bool
9103  *
9104  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9105  *  current flag is returned.
9106  */
9107 static VALUE
9108 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9109 {
9110     Molecule *mol;
9111     Data_Get_Struct(self, Molecule, mol);
9112         if (mol->mview == NULL)
9113                 return Qnil;
9114         if (argc > 0) {
9115                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9116                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9117         }
9118         return (mol->mview->lineMode ? Qtrue : Qfalse);
9119 }
9120
9121 /*
9122  *  call-seq:
9123  *     atom_radius = float
9124  *     atom_radius
9125  *
9126  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9127  *  (Default = 0.4)
9128  *  If no argument is given, the current value is returned.
9129  */
9130 static VALUE
9131 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9132 {
9133     Molecule *mol;
9134     Data_Get_Struct(self, Molecule, mol);
9135         if (mol->mview == NULL)
9136                 return Qnil;
9137         if (argc > 0) {
9138                 double rad = NUM2DBL(rb_Float(argv[0]));
9139                 if (rad > 0.0) {
9140                         mol->mview->atomRadius = rad;
9141                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9142                 }
9143                 return argv[0];
9144         }
9145         return rb_float_new(mol->mview->atomRadius);
9146 }
9147
9148 /*
9149  *  call-seq:
9150  *     bond_radius = float
9151  *     bond_radius
9152  *
9153  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9154  *  If no argument is given, the current value is returned.
9155  */
9156 static VALUE
9157 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9158 {
9159     Molecule *mol;
9160     Data_Get_Struct(self, Molecule, mol);
9161         if (mol->mview == NULL)
9162                 return Qnil;
9163         if (argc > 0) {
9164                 double rad = NUM2DBL(rb_Float(argv[0]));
9165                 if (rad > 0.0) {
9166                         mol->mview->bondRadius = rad;
9167                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9168                 }
9169                 return argv[0];
9170         }
9171         return rb_float_new(mol->mview->bondRadius);
9172 }
9173
9174 /*
9175  *  call-seq:
9176  *     atom_resolution = integer
9177  *     atom_resolution
9178  *
9179  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9180  *  (Default = 12; minimum = 6)
9181  *  If no argument is given, the current value is returned.
9182  */
9183 static VALUE
9184 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9185 {
9186     Molecule *mol;
9187     Data_Get_Struct(self, Molecule, mol);
9188         if (mol->mview == NULL)
9189                 return Qnil;
9190         if (argc > 0) {
9191                 int res = NUM2INT(rb_Integer(argv[0]));
9192                 if (res < 6)
9193                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9194                 mol->mview->atomResolution = res;
9195                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9196                 return INT2NUM(res);
9197         }
9198         return INT2NUM(mol->mview->atomResolution);
9199 }
9200
9201 /*
9202  *  call-seq:
9203  *     bond_resolution = integer
9204  *     bond_resolution
9205  *
9206  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9207  *  (Default = 8; minimum = 4)
9208  *  If no argument is given, the current value is returned.
9209  */
9210 static VALUE
9211 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9212 {
9213     Molecule *mol;
9214     Data_Get_Struct(self, Molecule, mol);
9215         if (mol->mview == NULL)
9216                 return Qnil;
9217         if (argc > 0) {
9218                 int res = NUM2INT(rb_Integer(argv[0]));
9219                 if (res < 4)
9220                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9221                 mol->mview->bondResolution = res;
9222                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9223                 return INT2NUM(res);
9224         }
9225         return INT2NUM(mol->mview->bondResolution);
9226 }
9227
9228 /*
9229  *  call-seq:
9230  *     resize_to_fit
9231  *
9232  *  Resize the model drawing to fit in the window.
9233  */
9234 static VALUE
9235 s_Molecule_ResizeToFit(VALUE self)
9236 {
9237     Molecule *mol;
9238     Data_Get_Struct(self, Molecule, mol);
9239         if (mol->mview != NULL)
9240                 MainView_resizeToFit(mol->mview);
9241         return self;    
9242 }
9243
9244 /*
9245  *  call-seq:
9246  *     get_view_rotation -> [[ax, ay, az], angle]
9247  *
9248  *  Get the current rotation for the view. Angle is in degree, not radian.
9249  */
9250 static VALUE
9251 s_Molecule_GetViewRotation(VALUE self)
9252 {
9253     Molecule *mol;
9254         double f[4];
9255         Vector v;
9256     Data_Get_Struct(self, Molecule, mol);
9257         if (mol->mview == NULL)
9258                 return Qnil;
9259         TrackballGetRotate(mol->mview->track, f);
9260         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9261         v.x = f[1];
9262         v.y = f[2];
9263         v.z = f[3];
9264         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9265 }
9266
9267 /*
9268  *  call-seq:
9269  *     get_view_scale -> float
9270  *
9271  *  Get the current scale for the view.
9272  */
9273 static VALUE
9274 s_Molecule_GetViewScale(VALUE self)
9275 {
9276     Molecule *mol;
9277     Data_Get_Struct(self, Molecule, mol);
9278         if (mol->mview == NULL)
9279                 return Qnil;
9280         return rb_float_new(TrackballGetScale(mol->mview->track));
9281 }
9282
9283 /*
9284  *  call-seq:
9285  *     get_view_center -> Vector
9286  *
9287  *  Get the current center point of the view.
9288  */
9289 static VALUE
9290 s_Molecule_GetViewCenter(VALUE self)
9291 {
9292     Molecule *mol;
9293         double f[4];
9294         Vector v;
9295     Data_Get_Struct(self, Molecule, mol);
9296         if (mol->mview == NULL)
9297                 return Qnil;
9298         TrackballGetTranslate(mol->mview->track, f);
9299         v.x = -f[0] * mol->mview->dimension;
9300         v.y = -f[1] * mol->mview->dimension;
9301         v.z = -f[2] * mol->mview->dimension;
9302         return ValueFromVector(&v);
9303 }
9304
9305 /*
9306  *  call-seq:
9307  *     set_view_rotation([ax, ay, az], angle) -> self
9308  *
9309  *  Set the current rotation for the view. Angle is in degree, not radian.
9310  */
9311 static VALUE
9312 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9313 {
9314     Molecule *mol;
9315         double f[4];
9316         Vector v;
9317     Data_Get_Struct(self, Molecule, mol);
9318         if (mol->mview == NULL)
9319                 return Qnil;
9320         VectorFromValue(aval, &v);
9321         if (NormalizeVec(&v, &v))
9322                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9323         f[1] = v.x;
9324         f[2] = v.y;
9325         f[3] = v.z;
9326         f[0] = -NUM2DBL(rb_Float(angval));
9327         TrackballSetRotate(mol->mview->track, f);
9328         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9329         return self;
9330 }
9331
9332 /*
9333  *  call-seq:
9334  *     set_view_scale(scale) -> self
9335  *
9336  *  Set the current scale for the view.
9337  */
9338 static VALUE
9339 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9340 {
9341     Molecule *mol;
9342     Data_Get_Struct(self, Molecule, mol);
9343         if (mol->mview == NULL)
9344                 return Qnil;
9345         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9346         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9347         return self;
9348 }
9349
9350 /*
9351  *  call-seq:
9352  *     set_view_center(vec) -> self
9353  *
9354  *  Set the current center point of the view.
9355  */
9356 static VALUE
9357 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9358 {
9359     Molecule *mol;
9360         Vector v;
9361         double f[4];
9362     Data_Get_Struct(self, Molecule, mol);
9363         if (mol->mview == NULL)
9364                 return Qnil;
9365         VectorFromValue(aval, &v);
9366         f[0] = -v.x / mol->mview->dimension;
9367         f[1] = -v.y / mol->mview->dimension;
9368         f[2] = -v.z / mol->mview->dimension;
9369         TrackballSetTranslate(mol->mview->track, f);
9370         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9371         return self;
9372 }
9373
9374 /*
9375  *  call-seq:
9376  *     set_background_color(red, green, blue)
9377  *
9378  *  Set the background color of the model window.
9379  */
9380 static VALUE
9381 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9382 {
9383     Molecule *mol;
9384     Data_Get_Struct(self, Molecule, mol);
9385         if (mol->mview != NULL) {
9386                 VALUE rval, gval, bval;
9387                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9388                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9389         }
9390         return self;    
9391 }
9392
9393 /*
9394  *  call-seq:
9395  *     export_graphic(fname, scale = 1.0, bg_color = -1)
9396  *
9397  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9398  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9399  *  
9400  */
9401 static VALUE
9402 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9403 {
9404         Molecule *mol;
9405         VALUE fval, sval, bval;
9406         char *fname;
9407         float scale;
9408         int bg_color;
9409     Data_Get_Struct(self, Molecule, mol);
9410         if (mol->mview == NULL)
9411                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9412         rb_scan_args(argc, argv, "12", &fval, &sval, &bval);
9413         fname = FileStringValuePtr(fval);
9414         if (sval == Qnil)
9415                 scale = 1.0;
9416         else scale = NUM2DBL(rb_Float(sval));
9417         if (bval == Qnil)
9418                 bg_color = -1;
9419         else bg_color = NUM2INT(rb_Integer(bval));
9420         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color) == 0)
9421                 return fval;
9422         else return Qnil;
9423 }
9424
9425 #pragma mark ------ Graphics ------
9426
9427 /*
9428  *  call-seq:
9429  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9430  *
9431  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9432  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9433  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9434  *   points: an array of Vectors
9435  *   
9436  */
9437 static VALUE
9438 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9439 {
9440     Molecule *mol;
9441         MainViewGraphic g;
9442         int i, n, ni, idx;
9443         const char *p;
9444         VALUE kval, cval, pval, fval, ival;
9445     Data_Get_Struct(self, Molecule, mol);
9446         if (mol->mview == NULL)
9447                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9448         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9449         idx = NUM2INT(rb_Integer(ival));
9450         if (idx == -1)
9451                 idx = mol->mview->ngraphics;
9452         else if (idx < 0 || idx > mol->mview->ngraphics)
9453                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9454         memset(&g, 0, sizeof(g));
9455         g.visible = 1;
9456         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9457                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9458         } else {
9459                 kval = rb_obj_as_string(kval);
9460                 p = StringValuePtr(kval);
9461                 if (strcmp(p, "line") == 0)
9462                         g.kind = kMainViewGraphicLine;
9463                 else if (strcmp(p, "poly") == 0)
9464                         g.kind = kMainViewGraphicPoly;
9465                 else if (strcmp(p, "cylinder") == 0)
9466                         g.kind = kMainViewGraphicCylinder;
9467                 else if (strcmp(p, "cone") == 0)
9468                         g.kind = kMainViewGraphicCone;
9469                 else if (strcmp(p, "ellipsoid") == 0)
9470                         g.kind = kMainViewGraphicEllipsoid;
9471                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9472         }
9473         g.closed = (RTEST(fval) ? 1 : 0);
9474         cval = rb_ary_to_ary(cval);
9475         n = RARRAY_LEN(cval);
9476         if (n < 3 || n >= 5)
9477                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9478         if (n == 3)
9479                 g.rgba[3] = 1.0;
9480         for (i = 0; i < n; i++)
9481                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9482         pval = rb_ary_to_ary(pval);
9483         n = RARRAY_LEN(pval);
9484         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9485         if (n <= 0)
9486                 rb_raise(rb_eArgError, "no control points are given");
9487         switch (g.kind) {
9488                 case kMainViewGraphicLine:
9489                         if (n < 2)
9490                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9491                         break;
9492                 case kMainViewGraphicPoly:
9493                         if (n < 3)
9494                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9495                         break;
9496                 case kMainViewGraphicCylinder:
9497                 case kMainViewGraphicCone:
9498                         if (n != 3)
9499                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9500                         ni = 2;
9501                         break;
9502                 case kMainViewGraphicEllipsoid:
9503                         if (n == 2) {
9504                                 ni = 1;
9505                         } else if (n != 4)
9506                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9507                         break;
9508         }
9509         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9510         for (i = 0; i < n; i++) {
9511                 Vector v;
9512                 VALUE rval = RARRAY_PTR(pval)[i];
9513                 if (i == ni) {
9514                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9515                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9516                                 VectorFromValue(rval, &v);
9517                         } else {
9518                                 v.x = NUM2DBL(rb_Float(rval));
9519                         }
9520                         v.y = v.z = 0;
9521                 } else {
9522                         VectorFromValue(rval, &v);
9523                 }
9524                 g.points[i * 3] = v.x;
9525                 g.points[i * 3 + 1] = v.y;
9526                 g.points[i * 3 + 2] = v.z;
9527         }
9528         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9529                 /*  Sphere  */
9530                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9531                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9532                 g.points[7] = g.points[11] = g.points[3];
9533         }
9534         MainView_insertGraphic(mol->mview, idx, &g);
9535         
9536         {
9537                 /*  Register undo  */
9538                 MolAction *act;
9539                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9540                 MolActionCallback_registerUndo(mol, act);
9541                 MolActionRelease(act);
9542         }
9543
9544         return INT2NUM(idx);    
9545 }
9546
9547 /*
9548  *  call-seq:
9549  *     create_graphic(kind, color, points, fill = nil) -> integer
9550  *
9551  *  Create a new graphic object. The arguments are similar as insert_graphic.
9552  */
9553 static VALUE
9554 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9555 {
9556         VALUE args[5];
9557         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9558         args[0] = INT2NUM(-1);
9559         return s_Molecule_InsertGraphic(argc + 1, args, self);
9560 }
9561
9562 /*
9563  *  call-seq:
9564  *     remove_graphic(index) -> integer
9565  *
9566  *  Remove a graphic object.
9567  */
9568 static VALUE
9569 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9570 {
9571     Molecule *mol;
9572         int i;
9573     Data_Get_Struct(self, Molecule, mol);
9574         if (mol->mview == NULL)
9575                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9576         i = NUM2INT(rb_Integer(ival));
9577         if (i < 0 || i >= mol->mview->ngraphics)
9578                 rb_raise(rb_eArgError, "graphic index is out of range");
9579         {
9580                 /*  Prepare data for undo  */
9581                 MainViewGraphic *gp;
9582                 Vector *vp;
9583                 MolAction *act;
9584                 double col[4];
9585                 int n;
9586                 gp = mol->mview->graphics + i;
9587                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9588                 for (n = 0; n < gp->npoints; n++) {
9589                         vp[n].x = gp->points[n * 3];
9590                         vp[n].y = gp->points[n * 3 + 1];
9591                         vp[n].z = gp->points[n * 3 + 2];
9592                 }
9593                 col[0] = gp->rgba[0];
9594                 col[1] = gp->rgba[1];
9595                 col[2] = gp->rgba[2];
9596                 col[3] = gp->rgba[3];
9597                 if (gp->visible == 0) {
9598                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9599                         MolActionCallback_registerUndo(mol, act);
9600                         MolActionRelease(act);
9601                 }
9602                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9603                 MolActionCallback_registerUndo(mol, act);
9604                 free(vp);
9605                 MolActionRelease(act);
9606         }
9607         MainView_removeGraphic(mol->mview, i);
9608         return ival;
9609 }
9610
9611 /*
9612  *  call-seq:
9613  *     ngraphics -> integer
9614  *
9615  *  Get the number of graphic objects.
9616  */
9617 static VALUE
9618 s_Molecule_NGraphics(VALUE self)
9619 {
9620     Molecule *mol;
9621     Data_Get_Struct(self, Molecule, mol);
9622         if (mol->mview == NULL)
9623                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9624         return INT2NUM(mol->mview->ngraphics);
9625 }
9626
9627 /*
9628  *  call-seq:
9629  *     get_graphic_point(graphic_index, point_index) -> value
9630  *     get_graphic_points(graphic_index) -> values
9631  *
9632  *  Get the point_index-th control point of graphic_index-th graphic object.
9633  *  Get an array of all control points with the given values.
9634  *   
9635  */
9636 static VALUE
9637 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9638 {
9639         MainViewGraphic *gp;
9640     Molecule *mol;
9641         int index, pindex;
9642         Vector v;
9643         VALUE gval, pval;
9644     Data_Get_Struct(self, Molecule, mol);
9645         if (mol->mview == NULL)
9646                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9647         rb_scan_args(argc, argv, "11", &gval, &pval);
9648         index = NUM2INT(rb_Integer(gval));
9649         if (index < 0 || index >= mol->mview->ngraphics)
9650                 rb_raise(rb_eArgError, "the graphic index is out of range");
9651         gp = mol->mview->graphics + index;
9652         if (pval != Qnil) {
9653                 pindex = NUM2INT(rb_Integer(pval));
9654                 if (pindex < 0 || pindex >= gp->npoints)
9655                         rb_raise(rb_eArgError, "the point index is out of range");
9656                 v.x = gp->points[pindex * 3];
9657                 v.y = gp->points[pindex * 3 + 1];
9658                 v.z = gp->points[pindex * 3 + 2];
9659                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9660                         return rb_float_new(v.x);
9661                 } else {
9662                         return ValueFromVector(&v);
9663                 }
9664         } else {
9665                 pval = rb_ary_new();
9666                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9667                         v.x = gp->points[pindex * 3];
9668                         v.y = gp->points[pindex * 3 + 1];
9669                         v.z = gp->points[pindex * 3 + 2];
9670                         rb_ary_push(pval, ValueFromVector(&v));
9671                 }
9672                 return pval;
9673         }
9674 }
9675
9676 /*
9677  *  call-seq:
9678  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9679  *     set_graphic_points(graphic_index, new_values) -> new_values
9680  *
9681  *  Change the point_index-th control point of graphic_index-th graphic object.
9682  *  Replace the control points with the given values.
9683  *   
9684  */
9685 static VALUE
9686 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9687 {
9688         MainViewGraphic *gp;
9689     Molecule *mol;
9690         int index, pindex;
9691         Vector v, v0;
9692         VALUE gval, pval, nval;
9693         MolAction *act;
9694     Data_Get_Struct(self, Molecule, mol);
9695         if (mol->mview == NULL)
9696                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9697         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9698         index = NUM2INT(rb_Integer(gval));
9699         if (index < 0 || index >= mol->mview->ngraphics)
9700                 rb_raise(rb_eArgError, "the graphic index is out of range");
9701         gp = mol->mview->graphics + index;
9702         if (nval != Qnil) {
9703                 pindex = NUM2INT(rb_Integer(pval));
9704                 if (pindex < 0 || pindex >= gp->npoints)
9705                         rb_raise(rb_eArgError, "the point index is out of range");
9706                 v0.x = gp->points[pindex * 3];
9707                 v0.y = gp->points[pindex * 3 + 1];
9708                 v0.z = gp->points[pindex * 3 + 2];
9709                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9710                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9711                                 v.x = NUM2DBL(rb_Float(nval));
9712                                 v.y = v.z = 0;
9713                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9714                                 v.x = NUM2DBL(rb_Float(nval));
9715                                 v.y = v.z = 0;
9716                                 gp->points[7] = gp->points[11] = v.x;
9717                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9718                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9719                 } else {
9720                         if (nval == Qnil) {
9721                                 v.x = kInvalidFloat;
9722                                 v.y = v.z = 0.0;
9723                         } else VectorFromValue(nval, &v);
9724                 }
9725                 gp->points[pindex * 3] = v.x;
9726                 gp->points[pindex * 3 + 1] = v.y;
9727                 gp->points[pindex * 3 + 2] = v.z;
9728                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9729         } else {
9730                 VALUE aval;
9731                 int len;
9732                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9733                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9734                         vp[pindex].x = gp->points[pindex * 3];
9735                         vp[pindex].y = gp->points[pindex * 3 + 1];
9736                         vp[pindex].z = gp->points[pindex * 3 + 2];
9737                 }
9738                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9739                 free(vp);
9740                 pval = rb_ary_to_ary(pval);
9741                 len = RARRAY_LEN(pval);
9742                 if (gp->npoints < len) {
9743                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9744                         gp->npoints = len;
9745                 } else if (gp->npoints > len) {
9746                         int len2 = 3;
9747                         switch (gp->kind) {
9748                                 case kMainViewGraphicLine: len2 = 2; break;
9749                                 case kMainViewGraphicPoly: len2 = 3; break;
9750                                 case kMainViewGraphicCylinder: len2 = 3; break;
9751                                 case kMainViewGraphicCone: len2 = 3; break;
9752                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9753                         }
9754                         if (len2 < len)
9755                                 len2 = len;
9756                         gp->npoints = len2;
9757                 }
9758                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9759                         aval = RARRAY_PTR(pval)[pindex];
9760                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9761                                 v.x = NUM2DBL(rb_Float(aval));
9762                                 v.y = v.z = 0;
9763                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9764                                 v.x = NUM2DBL(rb_Float(aval));
9765                                 v.y = v.z = 0;
9766                                 gp->points[7] = gp->points[11] = v.x;
9767                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9768                                 break;
9769                         } else VectorFromValue(aval, &v);
9770                         gp->points[pindex * 3] = v.x;
9771                         gp->points[pindex * 3 + 1] = v.y;
9772                         gp->points[pindex * 3 + 2] = v.z;
9773                 }
9774         }
9775         MolActionCallback_registerUndo(mol, act);
9776         MolActionRelease(act);          
9777         MoleculeCallback_notifyModification(mol, 0);
9778         return nval;
9779 }
9780
9781 /*
9782  *  call-seq:
9783  *     get_graphic_color(graphic_index) -> value
9784  *
9785  *  Get the color of graphic_index-th graphic object
9786  */
9787 static VALUE
9788 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9789 {
9790         MainViewGraphic *gp;
9791     Molecule *mol;
9792         int index;
9793     Data_Get_Struct(self, Molecule, mol);
9794         if (mol->mview == NULL)
9795                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9796         index = NUM2INT(rb_Integer(gval));
9797         if (index < 0 || index >= mol->mview->ngraphics)
9798                 rb_raise(rb_eArgError, "the graphic index is out of range");
9799         gp = mol->mview->graphics + index;
9800         return rb_ary_new3(4, rb_float_new(gp->rgba[0]), rb_float_new(gp->rgba[1]), rb_float_new(gp->rgba[2]), rb_float_new(gp->rgba[3]));
9801 }
9802
9803 /*
9804  *  call-seq:
9805  *     set_graphic_color(graphic_index, new_value) -> new_value
9806  *
9807  *  Change the color of graphic_index-th graphic object
9808  *   
9809  */
9810 static VALUE
9811 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9812 {
9813         MainViewGraphic *gp;
9814     Molecule *mol;
9815         MolAction *act;
9816         double c[4];
9817         int index, i, n;
9818     Data_Get_Struct(self, Molecule, mol);
9819         if (mol->mview == NULL)
9820                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9821         index = NUM2INT(rb_Integer(gval));
9822         if (index < 0 || index >= mol->mview->ngraphics)
9823                 rb_raise(rb_eArgError, "the graphic index is out of range");
9824         gp = mol->mview->graphics + index;
9825         for (i = 0; i < 4; i++)
9826                 c[i] = gp->rgba[i];
9827         cval = rb_ary_to_ary(cval);
9828         n = RARRAY_LEN(cval);
9829         if (n != 3 && n != 4)
9830                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9831
9832         for (i = 0; i < n; i++) {
9833                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9834         }
9835         if (n == 3)
9836                 gp->rgba[3] = 1.0;
9837         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9838         MolActionCallback_registerUndo(mol, act);
9839         MolActionRelease(act);          
9840         MoleculeCallback_notifyModification(mol, 0);
9841         return cval;
9842 }
9843
9844 /*
9845  *  call-seq:
9846  *     show_graphic(graphic_index) -> self
9847  *
9848  *  Enable the visible flag of the graphic_index-th graphic object
9849  *   
9850  */
9851 static VALUE
9852 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9853 {
9854         MainViewGraphic *gp;
9855     Molecule *mol;
9856         int index;
9857     Data_Get_Struct(self, Molecule, mol);
9858         if (mol->mview == NULL)
9859                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9860         index = NUM2INT(rb_Integer(gval));
9861         if (index < 0 || index >= mol->mview->ngraphics)
9862                 rb_raise(rb_eArgError, "the graphic index is out of range");
9863         gp = mol->mview->graphics + index;
9864         gp->visible = 1;
9865         MoleculeCallback_notifyModification(mol, 0);
9866         return self;
9867 }
9868
9869 /*
9870  *  call-seq:
9871  *     hide_graphic(graphic_index) -> self
9872  *
9873  *  Disable the visible flag of the graphic_index-th graphic object
9874  *   
9875  */
9876 static VALUE
9877 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9878 {
9879         MainViewGraphic *gp;
9880     Molecule *mol;
9881         int index;
9882     Data_Get_Struct(self, Molecule, mol);
9883         if (mol->mview == NULL)
9884                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9885         index = NUM2INT(rb_Integer(gval));
9886         if (index < 0 || index >= mol->mview->ngraphics)
9887                 rb_raise(rb_eArgError, "the graphic index is out of range");
9888         gp = mol->mview->graphics + index;
9889         gp->visible = 0;
9890         MoleculeCallback_notifyModification(mol, 0);
9891         return self;
9892 }
9893
9894 /*
9895  *  call-seq:
9896  *     show_text(string)
9897  *
9898  *  Show the string in the info text box.
9899  */
9900 static VALUE
9901 s_Molecule_ShowText(VALUE self, VALUE arg)
9902 {
9903     Molecule *mol;
9904     Data_Get_Struct(self, Molecule, mol);
9905         if (mol->mview != NULL)
9906                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9907         return Qnil;
9908 }
9909
9910 #pragma mark ------ MD Support ------
9911
9912 /*
9913  *  call-seq:
9914  *     md_arena -> MDArena
9915  *
9916  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
9917  *  this molecule, a new arena is created.
9918  */
9919 static VALUE
9920 s_Molecule_MDArena(VALUE self)
9921 {
9922     Molecule *mol;
9923         VALUE retval;
9924     Data_Get_Struct(self, Molecule, mol);
9925         if (mol->arena == NULL)
9926                 md_arena_new(mol);
9927         retval = ValueFromMDArena(mol->arena);
9928         return retval;
9929 }
9930
9931 /*
9932  *  call-seq:
9933  *     set_parameter_attr(type, index, key, value, src) -> value
9934  *
9935  *  This method is used only internally.
9936  */
9937 static VALUE
9938 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9939 {
9940         /*  This method is called from MolAction to change a MM parameter attribute.  */
9941     Molecule *mol;
9942         VALUE pval;
9943         ParameterRef *pref;
9944         UnionPar *up;
9945     Data_Get_Struct(self, Molecule, mol);
9946         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
9947         vval = s_ParameterRef_SetAttr(pval, kval, vval);
9948         
9949         /*  This is the special part of this method; it allows modification of the src field. */
9950         /*  (ParameterRef#set_attr sets 0 to the src field)  */
9951         Data_Get_Struct(pval, ParameterRef, pref);
9952         up = ParameterRefGetPar(pref);
9953         up->bond.src = FIX2INT(sval);
9954         
9955         return vval;
9956 }
9957
9958 /*
9959  *  call-seq:
9960  *     parameter -> Parameter
9961  *
9962  *  Get the local parameter of this molecule. If not defined, returns nil.
9963  */
9964 static VALUE
9965 s_Molecule_Parameter(VALUE self)
9966 {
9967     Molecule *mol;
9968     Data_Get_Struct(self, Molecule, mol);
9969 /*      if (mol->par == NULL)
9970                 return Qnil; */
9971         return s_NewParameterValueFromValue(self);
9972 }
9973
9974 /*
9975  *  call-seq:
9976  *     start_step       -> Integer
9977  *
9978  *  Returns the start step (defined by dcd format).
9979  */
9980 static VALUE
9981 s_Molecule_StartStep(VALUE self)
9982 {
9983     Molecule *mol;
9984     Data_Get_Struct(self, Molecule, mol);
9985         return INT2NUM(mol->startStep);
9986 }
9987
9988 /*
9989  *  call-seq:
9990  *     start_step = Integer
9991  *
9992  *  Set the start step (defined by dcd format).
9993  */
9994 static VALUE
9995 s_Molecule_SetStartStep(VALUE self, VALUE val)
9996 {
9997     Molecule *mol;
9998     Data_Get_Struct(self, Molecule, mol);
9999         mol->startStep = NUM2INT(rb_Integer(val));
10000         return val;
10001 }
10002
10003 /*
10004  *  call-seq:
10005  *     steps_per_frame       -> Integer
10006  *
10007  *  Returns the number of steps between frames (defined by dcd format).
10008  */
10009 static VALUE
10010 s_Molecule_StepsPerFrame(VALUE self)
10011 {
10012     Molecule *mol;
10013     Data_Get_Struct(self, Molecule, mol);
10014         return INT2NUM(mol->stepsPerFrame);
10015 }
10016
10017 /*
10018  *  call-seq:
10019  *     steps_per_frame = Integer
10020  *
10021  *  Set the number of steps between frames (defined by dcd format).
10022  */
10023 static VALUE
10024 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10025 {
10026     Molecule *mol;
10027     Data_Get_Struct(self, Molecule, mol);
10028         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10029         return val;
10030 }
10031
10032 /*
10033  *  call-seq:
10034  *     ps_per_step       -> Float
10035  *
10036  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10037  */
10038 static VALUE
10039 s_Molecule_PsPerStep(VALUE self)
10040 {
10041     Molecule *mol;
10042     Data_Get_Struct(self, Molecule, mol);
10043         return rb_float_new(mol->psPerStep);
10044 }
10045
10046 /*
10047  *  call-seq:
10048  *     ps_per_step = Float
10049  *
10050  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10051  */
10052 static VALUE
10053 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10054 {
10055     Molecule *mol;
10056     Data_Get_Struct(self, Molecule, mol);
10057         mol->psPerStep = NUM2DBL(rb_Float(val));
10058         return val;
10059 }
10060
10061 static VALUE
10062 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10063 {
10064         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.");
10065 }
10066
10067 #pragma mark ------ MO Handling ------
10068
10069 /*
10070  *  call-seq:
10071  *     selectedMO -> IntGroup
10072  *
10073  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10074  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10075  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10076  */
10077 static VALUE
10078 s_Molecule_SelectedMO(VALUE self)
10079 {
10080     Molecule *mol;
10081         IntGroup *ig;
10082         VALUE val;
10083     Data_Get_Struct(self, Molecule, mol);
10084         if (mol->mview == NULL)
10085                 return Qnil;
10086         ig = MainView_selectedMO(mol->mview);
10087         if (ig == NULL)
10088                 return Qnil;
10089         IntGroupOffset(ig, 1);
10090         val = ValueFromIntGroup(ig);
10091         IntGroupRelease(ig);
10092         return val;
10093 }
10094
10095 /*
10096  *  call-seq:
10097  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10098  *
10099  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10100  *  If the molecule does not contain a basis set information, then returns nil.
10101  */
10102 static VALUE
10103 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10104 {
10105     Molecule *mol;
10106         Vector o, dx, dy, dz;
10107         Int nx, ny, nz;
10108         VALUE nval;
10109         Int npoints = 80 * 80 * 80;
10110     Data_Get_Struct(self, Molecule, mol);
10111         if (mol->bset == NULL)
10112                 return Qnil;
10113         rb_scan_args(argc, argv, "01", &nval);
10114         if (nval != Qnil)
10115                 npoints = NUM2INT(rb_Integer(nval));
10116         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10117                 return Qnil;
10118         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));
10119 }
10120
10121 static int
10122 s_Cubegen_callback(double progress, void *ref)
10123 {
10124         MyAppCallback_setProgressValue(progress);
10125         if (MyAppCallback_checkInterrupt())
10126                 return 1;
10127         else return 0;
10128 }
10129
10130 /*
10131  *  call-seq:
10132  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10133  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10134  *
10135  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10136  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10137  *  second form, the cube dimension is explicitly given.
10138  *  Returns fname when successful, nil otherwise.
10139  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10140  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10141  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10142  */
10143 static VALUE
10144 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10145 {
10146         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10147     Molecule *mol;
10148         Int mono, nx, ny, nz, npoints;
10149         Vector o, dx, dy, dz;
10150         int index, n;
10151         char buf[1024];
10152     Data_Get_Struct(self, Molecule, mol);
10153         if (mol->bset == NULL)
10154                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10155         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10156         
10157         /*  Set up parameters  */
10158         mono = NUM2INT(rb_Integer(mval));
10159         if (mono <= 0 || mono > mol->bset->ncomps)
10160                 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
10161         if (RTEST(bval)) {
10162                 if (mol->bset->rflag != 0)
10163                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10164                 mono += mol->bset->ncomps;
10165         }
10166                 
10167         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10168                 /*  Automatic grid formation  */
10169                 if (oval != Qnil)
10170                         npoints = NUM2INT(rb_Integer(oval));
10171                 else npoints = 0;
10172                 if (npoints == 0)
10173                         npoints = 1000000;
10174                 else if (npoints < 8)
10175                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10176                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10177                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10178                 ival = dxval;
10179                 bval = dyval;
10180         } else {
10181                 VectorFromValue(oval, &o);
10182                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10183                         VectorFromValue(dxval, &dx);
10184                 else {
10185                         dx.x = NUM2DBL(rb_Float(dxval));
10186                         dx.y = dx.z = 0.0;
10187                 }
10188                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10189                         VectorFromValue(dyval, &dy);
10190                 else {
10191                         dy.y = NUM2DBL(rb_Float(dyval));
10192                         dy.x = dy.z = 0.0;
10193                 }
10194                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10195                         VectorFromValue(dzval, &dz);
10196                 else {
10197                         dz.z = NUM2DBL(rb_Float(dzval));
10198                         dz.x = dz.y = 0.0;
10199                 }
10200                 nx = NUM2INT(rb_Integer(nxval));
10201                 ny = NUM2INT(rb_Integer(nyval));
10202                 nz = NUM2INT(rb_Integer(nzval));
10203                 if (nx <= 0 || ny <= 0 || nz <= 0)
10204                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10205                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10206                         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);
10207         }
10208         
10209         /*  Calc MO  */
10210         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10211         if (index == -2)
10212                 rb_interrupt();
10213         else if (index < 0)
10214                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10215         
10216         /*  Output to file  */
10217         MoleculeCallback_displayName(mol, buf, sizeof buf);
10218         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10219         if (n != 0)
10220                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10221         
10222         /*  Discard the cube  */
10223         MoleculeClearCubeAtIndex(mol, index);
10224         return fval;
10225 }
10226
10227 /*
10228  *  call-seq:
10229  *     create_surface(mo, attr = nil)
10230  *
10231  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10232  *  then it denotes the beta orbital.
10233  *  If mo is nil, then the attributes of the current surface are modified.
10234  *  Attributes:
10235  *    :npoints : the approximate number of grid points
10236  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10237  *    :thres   : the threshold for the isovalue surface
10238  *  If the molecule does not contain MO information, raises exception.
10239  */
10240 static VALUE
10241 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10242 {
10243     Molecule *mol;
10244         Vector o, dx, dy, dz;
10245         Int nmo, nx, ny, nz, i;
10246         Int need_recalc = 0;
10247         VALUE nval, hval, aval;
10248         Int npoints;
10249         Double expand;
10250         Double thres;
10251         Double d[4];
10252     Data_Get_Struct(self, Molecule, mol);
10253         rb_scan_args(argc, argv, "11", &nval, &hval);
10254         if (mol->bset == NULL)
10255                 rb_raise(rb_eMolbyError, "No MO information is given");
10256         if (nval == Qnil) {
10257                 nmo = -1;
10258         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10259                 nmo = mol->bset->nmos + 1;
10260         } else {
10261                 nmo = NUM2INT(rb_Integer(nval));
10262                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10263                         rb_raise(rb_eMolbyError, "MO index (%d) is out of range; should be 1..%d (or -1..-%d for beta orbitals); (0 is acceptable as arbitrary vector)", nmo, mol->bset->nmos, mol->bset->ncomps);
10264                 if (nmo < 0)
10265                         nmo = -nmo + mol->bset->ncomps;
10266         }
10267         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10268                 npoints = NUM2INT(rb_Integer(aval));
10269                 need_recalc = 1;
10270         } else if (mol->mcube != NULL) {
10271                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10272         } else npoints = 80 * 80 * 80;
10273         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10274                 expand = NUM2DBL(rb_Float(aval));
10275         } else if (mol->mcube != NULL) {
10276                 expand = mol->mcube->expand;
10277         } else expand = 1.0;
10278         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10279                 thres = NUM2DBL(rb_Float(aval));
10280         } else if (mol->mcube != NULL) {
10281                 thres = mol->mcube->thres;
10282         } else thres = 0.05;
10283         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10284                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10285                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10286                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10287                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10288         }
10289         for (nx = 0; nx < 2; nx++) {
10290                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10291                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10292                         aval = rb_ary_to_ary(aval);
10293                         if (RARRAY_LEN(aval) < 3) {
10294                         raise:
10295                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10296                         }
10297                         for (i = 0; i < 4; i++)
10298                                 d[i] = mol->mcube->c[nx].rgba[i];
10299                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10300                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10301                                 if (d[i] < 0.0 && d[i] > 1.0)
10302                                         goto raise;
10303                         }
10304                         for (i = 0; i < 4; i++)
10305                                 mol->mcube->c[nx].rgba[i] = d[i];
10306                 }
10307         }
10308         if (mol->mcube->expand != expand)
10309                 need_recalc = 1;
10310         mol->mcube->thres = thres;
10311         mol->mcube->expand = expand;
10312         if (nmo < 0) {
10313                 if (mol->mcube->idn < 0)
10314                         return self;  /*  Only set attributes for now  */
10315                 if (need_recalc)
10316                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10317         }
10318         if (MoleculeUpdateMCube(mol, nmo) != 0)
10319                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10320         return self;
10321 }
10322
10323 /*
10324  *  call-seq:
10325  *     set_surface_attr(attr = nil)
10326  *
10327  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10328  */
10329 static VALUE
10330 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10331 {
10332         VALUE args[2];
10333         args[0] = Qnil;
10334         args[1] = hval;
10335         return s_Molecule_CreateSurface(2, args, self);
10336 }
10337
10338 /*
10339  *  call-seq:
10340  *     nelpots
10341  *
10342  *  Get the number of electrostatic potential info.
10343  */
10344 static VALUE
10345 s_Molecule_NElpots(VALUE self)
10346 {
10347         Molecule *mol;
10348     Data_Get_Struct(self, Molecule, mol);
10349         return INT2NUM(mol->nelpots);
10350 }
10351
10352 /*
10353  *  call-seq:
10354  *     elpot(idx)
10355  *
10356  *  Get the electrostatic potential info at the given index. If present, then the
10357  *  return value is [Vector, Float] (position and potential). If not present, then
10358  *  returns nil.
10359  */
10360 static VALUE
10361 s_Molecule_Elpot(VALUE self, VALUE ival)
10362 {
10363         Molecule *mol;
10364         int idx;
10365     Data_Get_Struct(self, Molecule, mol);
10366         idx = NUM2INT(rb_Integer(ival));
10367         if (idx < 0 || idx >= mol->nelpots)
10368                 return Qnil;
10369         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10370 }
10371
10372 /*
10373  *  call-seq:
10374  *     clear_basis_set
10375  *
10376  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10377  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10378  */
10379 static VALUE
10380 s_Molecule_ClearBasisSet(VALUE self)
10381 {
10382         Molecule *mol;
10383     Data_Get_Struct(self, Molecule, mol);
10384         if (mol != NULL) {
10385                 if (mol->bset != NULL) {
10386                         BasisSetRelease(mol->bset);
10387                         mol->bset = NULL;
10388                 }
10389                 if (mol->mcube != NULL) {
10390                         MoleculeDeallocateMCube(mol->mcube);
10391                         mol->mcube = NULL;
10392                 }
10393         }
10394         return self;
10395 }
10396
10397 /*
10398  *  call-seq:
10399  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10400  *
10401  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10402  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10403  *  -2, D5-type.
10404  */
10405 static VALUE
10406 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10407 {
10408         Molecule *mol;
10409         int sym, nprims, a_idx, n;
10410     Data_Get_Struct(self, Molecule, mol);
10411         a_idx = NUM2INT(rb_Integer(aval));
10412         sym = NUM2INT(rb_Integer(symval));
10413         nprims = NUM2INT(rb_Integer(npval));
10414         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10415         if (n == -1)
10416                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10417         else if (n == -2)
10418                 rb_raise(rb_eMolbyError, "Low memory");
10419         else if (n == -3)
10420                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10421         else if (n != 0)
10422                 rb_raise(rb_eMolbyError, "Unknown error");
10423         return self;
10424 }
10425
10426 /*
10427  *  call-seq:
10428  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10429  *
10430  *  To be used internally. Add a gaussian primitive coefficients.
10431  */
10432 static VALUE
10433 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10434 {
10435         Molecule *mol;
10436         Int n;
10437         Double exponent, contraction, contraction_sp;
10438     Data_Get_Struct(self, Molecule, mol);
10439         exponent = NUM2DBL(rb_Float(expval));
10440         contraction = NUM2DBL(rb_Float(cval));
10441         contraction_sp = NUM2DBL(rb_Float(cspval));
10442         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10443         if (n == -1)
10444                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10445         else if (n == -2)
10446                 rb_raise(rb_eMolbyError, "Low memory");
10447         else if (n != 0)
10448                 rb_raise(rb_eMolbyError, "Unknown error");
10449         return self;
10450 }
10451
10452 /*
10453  *  call-seq:
10454  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10455  *
10456  *  Get the Gaussian shell information for the given MO coefficient index.
10457  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10458  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10459  *  is the number of MO component belonging to this shell.
10460  */
10461 static VALUE
10462 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10463 {
10464         Molecule *mol;
10465         ShellInfo *sp;
10466         int s_idx, sym;
10467     Data_Get_Struct(self, Molecule, mol);
10468         if (mol->bset == NULL)
10469                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10470         s_idx = NUM2INT(rb_Integer(sval));
10471         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10472                 return Qnil;
10473         sp = mol->bset->shells + s_idx;
10474         sym = sp->sym;
10475         switch (sym) {
10476                 case kGTOType_S:  sym = 0;  break;
10477                 case kGTOType_SP: sym = -1; break;
10478                 case kGTOType_P:  sym = 1;  break;
10479                 case kGTOType_D:  sym = 2;  break;
10480                 case kGTOType_D5: sym = -2; break;
10481                 case kGTOType_F:  sym = 3;  break;
10482                 case kGTOType_F7: sym = -3; break;
10483                 case kGTOType_G:  sym = 4;  break;
10484                 case kGTOType_G9: sym = -4; break;
10485                 default:
10486                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10487         }
10488         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10489 }
10490
10491 /*
10492  *  call-seq:
10493  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10494  *
10495  *  Get the Gaussian primitive coefficients for the given MO component.
10496  */
10497 static VALUE
10498 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10499 {
10500         Molecule *mol;
10501         ShellInfo *sp;
10502         PrimInfo *pp;
10503         int s_idx, i;
10504         VALUE retval, aval;
10505     Data_Get_Struct(self, Molecule, mol);
10506         if (mol->bset == NULL)
10507                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10508         s_idx = NUM2INT(rb_Integer(sval));
10509         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10510                 return Qnil;
10511         sp = mol->bset->shells + s_idx;
10512         pp = mol->bset->priminfos + sp->p_idx;
10513         retval = rb_ary_new2(sp->nprim);
10514         for (i = 0; i < sp->nprim; i++) {
10515                 if (sp->sym == kGTOType_SP) {
10516                         /*  With P contraction coefficient  */
10517                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10518                 } else {
10519                         /*  Without P contraction coefficient  */
10520                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10521                 }
10522                 rb_ary_store(retval, i, aval);
10523         }
10524         return retval;
10525 }
10526
10527 /*
10528  *  call-seq:
10529  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10530  *
10531  *  Get the Gaussian shell information for the given MO coefficient index.
10532  */
10533 static VALUE
10534 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10535 {
10536         Molecule *mol;
10537         Int n, c, atom_idx, shell_idx;
10538         char label[32];
10539     Data_Get_Struct(self, Molecule, mol);
10540         if (mol->bset == NULL)
10541                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10542         c = NUM2INT(rb_Integer(cval));
10543         if (c < 0 || c >= mol->bset->ncomps)
10544                 return Qnil;
10545         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10546         if (n != 0)
10547                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10548         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10549 }
10550
10551 /*
10552  *  call-seq:
10553  *     clear_mo_coefficients
10554  *
10555  *  Clear the existing MO coefficients.
10556  */
10557 static VALUE
10558 s_Molecule_ClearMOCoefficients(VALUE self)
10559 {
10560         Molecule *mol;
10561         Data_Get_Struct(self, Molecule, mol);
10562         if (mol->bset != NULL) {
10563                 if (mol->bset->moenergies != NULL) {
10564                         free(mol->bset->moenergies);
10565                         mol->bset->moenergies = NULL;
10566                 }
10567                 if (mol->bset->mo != NULL) {
10568                         free(mol->bset->mo);
10569                         mol->bset->mo = NULL;
10570                 }
10571                 mol->bset->nmos = 0;
10572         }
10573         return self;
10574 }
10575
10576 /*
10577  *  call-seq:
10578  *     set_mo_coefficients(idx, energy, coefficients)
10579  *
10580  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10581  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10582  *  Energy is the MO energy, and coefficients is an array
10583  *  of MO coefficients.
10584  */
10585 static VALUE
10586 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10587 {
10588         Molecule *mol;
10589         Int idx, ncomps, i;
10590         Double energy;
10591         Double *coeffs;
10592     Data_Get_Struct(self, Molecule, mol);
10593         idx = NUM2INT(rb_Integer(ival));
10594         energy = NUM2DBL(rb_Float(eval));
10595         aval = rb_ary_to_ary(aval);
10596         ncomps = RARRAY_LEN(aval);
10597         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10598         if (coeffs == NULL) {
10599                 i = -2;
10600                 goto end;
10601         }
10602         for (i = 0; i < ncomps; i++)
10603                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10604         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10605 end:
10606         if (i == -1)
10607                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10608         else if (i == -2)
10609                 rb_raise(rb_eMolbyError, "Low memory");
10610         else if (i == -3)
10611                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10612         else if (i == -4)
10613                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10614         else if (i == -5)
10615                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10616         else if (i != 0)
10617                 rb_raise(rb_eMolbyError, "Unknown error");
10618         return self;
10619 }
10620
10621 /*
10622  *  call-seq:
10623  *     get_mo_coefficients(idx)
10624  *
10625  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10626  */
10627 static VALUE
10628 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10629 {
10630         Molecule *mol;
10631         Int idx, ncomps, n;
10632         Double energy;
10633         Double *coeffs;
10634         VALUE retval;
10635     Data_Get_Struct(self, Molecule, mol);
10636         idx = NUM2INT(rb_Integer(ival));
10637         ncomps = 0;
10638         coeffs = NULL;
10639         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10640         if (n == -1)
10641                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10642         else if (n == -2)
10643                 rb_raise(rb_eMolbyError, "No basis set information is present");
10644         else if (n == -3)
10645                 return Qnil;  /*  Silently returns nil  */
10646         retval = rb_ary_new2(ncomps);
10647         for (n = 0; n < ncomps; n++)
10648                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10649         free(coeffs);
10650         return retval;
10651 }
10652
10653 /*
10654  *  call-seq:
10655  *     get_mo_energy(idx)
10656  *
10657  *  To be used internally. Get the MO energy for the given MO index (1-based).
10658  */
10659 static VALUE
10660 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10661 {
10662         Molecule *mol;
10663         Int idx, n;
10664         Double energy;
10665     Data_Get_Struct(self, Molecule, mol);
10666         idx = NUM2INT(rb_Integer(ival));
10667         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10668         if (n == -1)
10669                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10670         else if (n == -2)
10671                 rb_raise(rb_eMolbyError, "No basis set information is present");
10672         else if (n == -3)
10673                 return Qnil;
10674         return rb_float_new(energy);
10675 }
10676
10677 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10678
10679 static inline void
10680 s_InitMOInfoKeys(void)
10681 {
10682         if (sTypeSym == 0) {
10683                 sTypeSym = ID2SYM(rb_intern("type"));
10684                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10685                 sBetaSym = ID2SYM(rb_intern("beta"));
10686                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10687                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10688         }
10689 }
10690
10691 /*
10692  *  call-seq:
10693  *     set_mo_info(hash)
10694  *
10695  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10696  *  :alpha=>integer, :beta=>integer
10697  */
10698 static VALUE
10699 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10700 {
10701         Molecule *mol;
10702         VALUE aval;
10703         Int rflag, na, nb, n;
10704         char *s;
10705     Data_Get_Struct(self, Molecule, mol);
10706         if (mol->bset != NULL) {
10707                 rflag = mol->bset->rflag;
10708                 na = mol->bset->ne_alpha;
10709                 nb = mol->bset->ne_beta;
10710         } else {
10711                 rflag = 1;
10712                 na = 0;
10713                 nb = 0;
10714         }
10715         if (hval != Qnil) {
10716                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10717                         s = StringValuePtr(aval);
10718                         if (strcasecmp(s, "RHF") == 0)
10719                                 rflag = 1;
10720                         else if (strcasecmp(s, "UHF") == 0)
10721                                 rflag = 0;
10722                         else if (strcasecmp(s, "ROHF") == 0)
10723                                 rflag = 2;
10724                 }
10725                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10726                         n = NUM2INT(rb_Integer(aval));
10727                         if (n >= 0)
10728                                 na = n;
10729                 }
10730                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10731                         n = NUM2INT(rb_Integer(aval));
10732                         if (n >= 0)
10733                                 nb = n;
10734                 }
10735                 MoleculeSetMOInfo(mol, rflag, na, nb);
10736         }
10737         return self;
10738 }
10739
10740 /*
10741  *  call-seq:
10742  *     get_mo_info(key)
10743  *
10744  *  Get the MO info. The key is as described in set_mo_info.
10745  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10746  */
10747 static VALUE
10748 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10749 {
10750         Molecule *mol;
10751     Data_Get_Struct(self, Molecule, mol);
10752         if (mol->bset == NULL)
10753                 return Qnil;
10754         if (kval == sTypeSym) {
10755                 switch (mol->bset->rflag) {
10756                         case 0: return rb_str_new2("UHF");
10757                         case 1: return rb_str_new2("RHF");
10758                         case 2: return rb_str_new2("ROHF");
10759                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10760                 }
10761         } else if (kval == sAlphaSym) {
10762                 return INT2NUM(mol->bset->ne_alpha);
10763         } else if (kval == sBetaSym) {
10764                 return INT2NUM(mol->bset->ne_beta);
10765         } else if (kval == sNcompsSym) {
10766                 return INT2NUM(mol->bset->ncomps);
10767         } else if (kval == sNshellsSym) {
10768                 return INT2NUM(mol->bset->nshells);
10769         } else {
10770                 kval = rb_inspect(kval);
10771                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10772                 return Qnil;  /*  Does not reach here  */
10773         }
10774 }
10775
10776 /*
10777  *  call-seq:
10778  *     mo_type
10779  *
10780  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10781  */
10782 static VALUE
10783 s_Molecule_MOType(VALUE self)
10784 {
10785         return s_Molecule_GetMOInfo(self, sTypeSym);
10786 }
10787
10788 #pragma mark ------ Molecular Topology ------
10789
10790 /*
10791  *  call-seq:
10792  *     search_equivalent_atoms(ig = nil)
10793  *
10794  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
10795  */
10796 static VALUE
10797 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10798 {
10799         Molecule *mol;
10800         Int *result, i;
10801         VALUE val;
10802         IntGroup *ig;
10803     Data_Get_Struct(self, Molecule, mol);
10804         if (mol->natoms == 0)
10805                 return Qnil;
10806         rb_scan_args(argc, argv, "01", &val);
10807         if (val != Qnil)
10808                 ig = IntGroupFromValue(val);
10809         else ig = NULL;
10810         result = MoleculeSearchEquivalentAtoms(mol, ig);
10811         if (result == NULL)
10812                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10813         if (ig != NULL)
10814                 IntGroupRelease(ig);
10815         val = rb_ary_new2(mol->natoms);
10816         for (i = 0; i < mol->natoms; i++)
10817                 rb_ary_push(val, INT2NUM(result[i]));
10818         free(result);
10819         return val;
10820 }
10821
10822 /*
10823  *  call-seq:
10824  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10825  *
10826  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10827  *  Name is the name of the new pi anchor, and group is the atoms that define
10828  *  the pi system. Type (a String) is an atom type for MM implementation.
10829  *  Weights represent the relative significance of the component atoms; if omitted, then
10830  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
10831  *  The weight values will be normalized so that the sum of the weights is 1.0.
10832  *  The weight values must be positive.
10833  *  Index is the atom index where the created pi-anchor is inserted in the 
10834  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
10835  *  having the largest index.
10836  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
10837  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10838  */
10839 static VALUE
10840 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10841 {
10842         Molecule *mol;
10843         VALUE nval, gval;
10844         IntGroup *ig;
10845         Int i, n, idx, last_component;
10846         Atom a, *ap;
10847         PiAnchor an;
10848         AtomRef *aref;
10849         if (argc < 2 || argc >= 6)
10850                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10851         nval = *argv++;
10852         gval = *argv++;
10853         argc -= 2;
10854     Data_Get_Struct(self, Molecule, mol);
10855         ig = IntGroupFromValue(gval);
10856         memset(&a, 0, sizeof(a));
10857         memset(&an, 0, sizeof(an));
10858         strncpy(a.aname, StringValuePtr(nval), 4);
10859         if (a.aname[0] == '_')
10860                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10861         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
10862         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10863                 if (n >= mol->natoms) {
10864                         AtomConnectResize(&an.connect, 0);
10865                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10866                 }
10867                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10868                 last_component = n;
10869         }
10870         if (an.connect.count == 0)
10871                 rb_raise(rb_eMolbyError, "no atoms are specified");
10872         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10873         for (i = 0; i < an.connect.count; i++) {
10874                 an.coeffs[i] = 1.0 / an.connect.count;
10875         }
10876         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10877                 /*  Atom type  */
10878                 if (argv[0] != Qnil)
10879                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10880                 argc--;
10881                 argv++;
10882         }
10883         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10884                 if (argv[0] != Qnil) {
10885                         VALUE aval = rb_ary_to_ary(argv[0]);
10886                         Double d, sum;
10887                         if (RARRAY_LEN(aval) != an.connect.count)
10888                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10889                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10890                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10891                                 if (d <= 0.0)
10892                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
10893                                 sum += d;
10894                                 an.coeffs[i] = d;
10895                         }
10896                         for (i = 0; i < an.connect.count; i++)
10897                                 an.coeffs[i] /= sum;
10898                 }
10899                 argc--;
10900                 argv++;
10901         }
10902         if (argc > 0 && argv[0] != Qnil) {
10903                 /*  Index  */
10904                 idx = NUM2INT(rb_Integer(argv[0]));
10905         } else idx = -1;
10906         if (idx < 0 || idx > mol->natoms) {
10907                 /*  Immediately after the last specified atom  */
10908                 idx = last_component + 1;
10909         }
10910         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10911         memmove(a.anchor, &an, sizeof(PiAnchor));
10912         /*  Use residue information of the last specified atom  */
10913         ap = ATOM_AT_INDEX(mol->atoms, last_component);
10914         a.resSeq = ap->resSeq;
10915         strncpy(a.resName, ap->resName, 4);
10916         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10917                 return Qnil;
10918         MoleculeCalculatePiAnchorPosition(mol, idx);
10919     aref = AtomRefNew(mol, idx);
10920     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10921 }
10922
10923 #pragma mark ------ Molecular Properties ------
10924
10925 /*
10926  *  call-seq:
10927  *     set_property(name, value[, index]) -> value
10928  *     set_property(name, values, group) -> values
10929  *
10930  *  Set molecular property. A property is a floating-point number with a specified name,
10931  *  and can be set for each frame separately. The name of the property is given as a String.
10932  *  The value can be a single floating point number, which is set to the current frame.
10933  *  
10934  */
10935 static VALUE
10936 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
10937 {
10938         Molecule *mol;
10939         VALUE nval, vval, ival;
10940         char *name;
10941         IntGroup *ig;
10942         Int i, n, idx, fidx;
10943         Double *dp;
10944         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
10945     Data_Get_Struct(self, Molecule, mol);
10946         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
10947                 idx = NUM2INT(rb_Integer(nval));
10948                 if (idx < 0 || idx >= mol->nmolprops)
10949                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
10950         } else {
10951                 name = StringValuePtr(nval);
10952                 idx = MoleculeLookUpProperty(mol, name);
10953                 if (idx < 0) {
10954                         idx = MoleculeCreateProperty(mol, name);
10955                         if (idx < 0)
10956                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
10957                 }
10958         }
10959         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
10960                 if (ival == Qnil)
10961                         fidx = mol->cframe;
10962                 else {
10963                         fidx = NUM2INT(rb_Integer(ival));
10964                         n = MoleculeGetNumberOfFrames(mol);
10965                         if (fidx < 0 || fidx >= n)
10966                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
10967                 }
10968                 ig = IntGroupNewWithPoints(fidx, 1, -1);
10969                 dp = (Double *)malloc(sizeof(Double));
10970                 *dp = NUM2DBL(rb_Float(vval));
10971                 n = 1;
10972         } else {
10973                 vval = rb_ary_to_ary(vval);
10974                 ig = IntGroupFromValue(ival);
10975                 n = IntGroupGetCount(ig);
10976                 if (n == 0)
10977                         rb_raise(rb_eMolbyError, "No frames are specified");
10978                 if (RARRAY_LEN(vval) < n)
10979                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
10980                 dp = (Double *)calloc(sizeof(Double), n);
10981                 for (i = 0; i < n; i++)
10982                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
10983         }
10984         
10985         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
10986         free(dp);
10987         IntGroupRelease(ig);
10988         return self;
10989 }
10990
10991 /*
10992  *  call-seq:
10993  *     get_property(name[, index]) -> value
10994  *     get_property(name, group) -> values
10995  *
10996  *  Get molecular property. In the first form, a property value for a single frame is returned.
10997  *  (If index is omitted, then the value for the current frame is given)
10998  *  In the second form, an array of property values for the given frames is returned.
10999  *  If name is not one of known properties or a valid index integer, exception is raised.
11000  */
11001 static VALUE
11002 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11003 {
11004         Molecule *mol;
11005         VALUE nval, ival;
11006         char *name;
11007         IntGroup *ig;
11008         Int i, n, idx, fidx;
11009         Double *dp;
11010         rb_scan_args(argc, argv, "11", &nval, &ival);
11011     Data_Get_Struct(self, Molecule, mol);
11012         if (mol->nmolprops == 0)
11013                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11014         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11015                 idx = NUM2INT(rb_Integer(nval));
11016                 if (idx < 0 || idx >= mol->nmolprops)
11017                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11018         } else {
11019                 name = StringValuePtr(nval);
11020                 idx = MoleculeLookUpProperty(mol, name);
11021                 if (idx < 0)
11022                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11023         }
11024         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11025                 if (ival == Qnil)
11026                         fidx = mol->cframe;
11027                 else {
11028                         fidx = NUM2INT(rb_Integer(ival));
11029                         n = MoleculeGetNumberOfFrames(mol);
11030                         if (fidx < 0 || fidx >= n)
11031                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11032                 }
11033                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11034                 ival = INT2FIX(fidx);
11035                 n = 1;
11036         } else {
11037                 ig = IntGroupFromValue(ival);
11038                 n = IntGroupGetCount(ig);
11039                 if (n == 0)
11040                         return rb_ary_new();
11041         }
11042         dp = (Double *)calloc(sizeof(Double), n);
11043         MoleculeGetProperty(mol, idx, ig, dp);  
11044         if (FIXNUM_P(ival))
11045                 ival = rb_float_new(dp[0]);
11046         else {
11047                 ival = rb_ary_new();
11048                 for (i = n - 1; i >= 0; i--) {
11049                         nval = rb_float_new(dp[i]);
11050                         rb_ary_store(ival, i, nval);
11051                 }
11052         }
11053         free(dp);
11054         IntGroupRelease(ig);
11055         return ival;
11056 }
11057
11058 /*
11059  *  call-seq:
11060  *     property_names -> Array
11061  *
11062  *  Get an array of property names.
11063  */
11064 static VALUE
11065 s_Molecule_PropertyNames(VALUE self)
11066 {
11067         Molecule *mol;
11068         VALUE rval, nval;
11069         int i;
11070     Data_Get_Struct(self, Molecule, mol);
11071         rval = rb_ary_new();
11072         for (i = mol->nmolprops - 1; i >= 0; i--) {
11073                 nval = rb_str_new2(mol->molprops[i].propname);
11074                 rb_ary_store(rval, i, nval);
11075         }
11076         return rval;
11077 }
11078
11079 #pragma mark ------ Class methods ------
11080
11081 /*
11082  *  call-seq:
11083  *     current       -> Molecule
11084  *
11085  *  Get the currently "active" molecule.
11086  */
11087 static VALUE
11088 s_Molecule_Current(VALUE klass)
11089 {
11090         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11091 }
11092
11093 /*
11094  *  call-seq:
11095  *     Molecule[]          -> Molecule
11096  *     Molecule[n]         -> Molecule
11097  *     Molecule[name]      -> Molecule
11098  *     Molecule[name, k]   -> Molecule
11099  *     Molecule[regex]     -> Molecule
11100  *     Molecule[regex, k]  -> Molecule
11101  *
11102  *  Molecule[] is equivalent to Molecule.current.
11103  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11104  *  Molecule[name] gives the first document (in the order of creation time) that has
11105  *  the given name. If a second argument (k) is given, the k-th document that has the
11106  *  given name is returned.
11107  *  Molecule[regex] gives the first document (in the order of creation time) that
11108  *  has a name matching the regular expression. If a second argument (k) is given, 
11109  *  the k-th document that has a name matching the re is returned.
11110  */
11111 static VALUE
11112 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11113 {
11114         VALUE val, kval;
11115         int idx, k;
11116         Molecule *mol;
11117         char buf[1024];
11118         rb_scan_args(argc, argv, "02", &val, &kval);
11119         if (val == Qnil)
11120                 return s_Molecule_Current(klass);
11121         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11122                 idx = NUM2INT(val);
11123                 mol = MoleculeCallback_moleculeAtIndex(idx);
11124         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11125                 char *p = StringValuePtr(val);
11126                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11127                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11128                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11129                         if (strcmp(buf, p) == 0 && --k == 0)
11130                                 break;
11131                 }
11132         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11133                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11134                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11135                         VALUE name;
11136                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11137                         name = rb_str_new2(buf);
11138                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11139                                 break;
11140                 }       
11141         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11142         
11143         if (mol == NULL)
11144                 return Qnil;
11145         else return ValueFromMolecule(mol);
11146 }
11147
11148 /*
11149  *  call-seq:
11150  *     list         -> array of Molecules
11151  *
11152  *  Get the list of molecules associated to the documents, in the order of creation
11153  *  time of the document. If no document is open, returns an empry array.
11154  */
11155 static VALUE
11156 s_Molecule_List(VALUE klass)
11157 {
11158         Molecule *mol;
11159         int i;
11160         VALUE ary;
11161         i = 0;
11162         ary = rb_ary_new();
11163         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11164                 rb_ary_push(ary, ValueFromMolecule(mol));
11165                 i++;
11166         }
11167         return ary;
11168 }
11169
11170 /*
11171  *  call-seq:
11172  *     ordered_list         -> array of Molecules
11173  *
11174  *  Get the list of molecules associated to the documents, in the order of front-to-back
11175  *  ordering of the associated window. If no document is open, returns an empry array.
11176  */
11177 static VALUE
11178 s_Molecule_OrderedList(VALUE klass)
11179 {
11180         Molecule *mol;
11181         int i;
11182         VALUE ary;
11183         i = 0;
11184         ary = rb_ary_new();
11185         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11186                 rb_ary_push(ary, ValueFromMolecule(mol));
11187                 i++;
11188         }
11189         return ary;
11190 }
11191
11192 #pragma mark ------ Call Subprocess ------
11193
11194 /*  The callback functions for call_subprocess_async  */
11195 static int
11196 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11197 {
11198         int ruby_status;
11199         VALUE procval, retval, args[2];
11200         args[0] = ValueFromMolecule(mol);
11201         args[1] = INT2NUM(status);
11202         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11203         if (procval != Qnil) {
11204                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11205                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11206                         return 1;
11207         }
11208         return 0;
11209 }
11210
11211 static int
11212 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11213 {
11214         int ruby_status;
11215         VALUE procval, retval, args[2];
11216         args[0] = ValueFromMolecule(mol);
11217         args[1] = INT2NUM(tcount);
11218         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11219         if (procval != Qnil) {
11220                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11221                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11222                         return 1;
11223         }
11224         return 0;
11225 }
11226
11227 /*
11228  *  call-seq:
11229  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11230  *
11231  *  Call subprocess asynchronically.
11232  *  If end_callback is given, it will be called (with two arguments self and termination status)
11233  *  when the subprocess terminated.
11234  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11235  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11236  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11237  *  filename begins with ">>", then the message will be appended to the file.
11238  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11239  *  If the argument is nil, then the message will be sent to the Ruby console.
11240  *  Returns the process ID as an integer.
11241  */
11242 static VALUE
11243 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11244 {
11245         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11246         Molecule *mol;
11247         char *sout, *serr;
11248         int n;
11249         FILE *fpout, *fperr;
11250         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11251         Data_Get_Struct(self, Molecule, mol);
11252
11253         if (stdout_val == Qnil) {
11254                 fpout = (FILE *)1;
11255         } else {
11256                 sout = StringValuePtr(stdout_val);
11257                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11258                         fpout = NULL;
11259                 else {
11260                         if (strncmp(sout, ">>", 2) == 0) {
11261                                 sout += 2;
11262                                 fpout = fopen(sout, "a");
11263                         } else {
11264                                 if (*sout == '>')
11265                                         sout++;
11266                                 fpout = fopen(sout, "w");
11267                         }
11268                         if (fpout == NULL)
11269                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11270                 }
11271         }
11272         if (stderr_val == Qnil) {
11273                 fperr = (FILE *)1;
11274         } else {
11275                 serr = StringValuePtr(stderr_val);
11276                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11277                         fperr = NULL;
11278                 else {
11279                         if (strncmp(serr, ">>", 2) == 0) {
11280                                 serr += 2;
11281                                 fpout = fopen(serr, "a");
11282                         } else {
11283                                 if (*serr == '>')
11284                                         serr++;
11285                                 fperr = fopen(serr, "w");
11286                         }
11287                         if (fperr == NULL)
11288                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11289                 }
11290         }
11291         
11292         /*  Register procs as instance variables  */
11293         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11294         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11295         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11296         if (fpout != NULL && fpout != (FILE *)1)
11297                 fclose(fpout);
11298         if (fperr != NULL && fperr != (FILE *)1)
11299                 fclose(fperr);
11300         return INT2NUM(n);
11301 }
11302
11303 #pragma mark ====== Define Molby Classes ======
11304
11305 void
11306 Init_Molby(void)
11307 {
11308         int i;
11309         
11310         /*  Define module Molby  */
11311         rb_mMolby = rb_define_module("Molby");
11312         
11313         /*  Define Vector3D, Transform, IntGroup  */
11314         Init_MolbyTypes();
11315         
11316         /*  Define MDArena  */
11317         Init_MolbyMDTypes();
11318
11319         /*  class Molecule  */
11320         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11321
11322         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11323     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11324     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11325         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11326         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11327
11328     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11329     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11330     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11331     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11332     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11333     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11334     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11335     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11336     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11337     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11338         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11339     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11340     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11341     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11342     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11343     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11344     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11345         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11346         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11347         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11348         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11349         
11350     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11351         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11352     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11353     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11354     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11355
11356     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11357     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11358     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11359     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11360     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11361     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11362         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11363         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11364         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11365         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11366         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11367         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11368         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11369         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11370         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11371         
11372         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11373         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11374         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11375         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11376         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11377         
11378         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11379     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11380         rb_define_alias(rb_cMolecule, "+", "add");
11381     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11382         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11383         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11384         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11385         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11386         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11387         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11388         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11389         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11390         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11391         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11392         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11393         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11394         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11395         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11396         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11397         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11398         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11399         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11400         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11401         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11402
11403         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11404         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11405         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11406         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11407         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11408
11409         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11410         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11411         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11412         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11413         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11414         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11415         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11416         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11417         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11418
11419         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11420         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11421         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11422         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11423         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11424         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11425         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11426         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11427         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11428         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11429         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11430         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11431         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11432         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11433         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11434         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11435         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11436         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11437         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11438         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11439         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11440         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11441
11442         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11443         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11444         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11445         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11446         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11447         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11448         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11449
11450         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11451         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11452         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11453         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11454         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11455         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11456         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11457         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11458         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11459         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11460         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11461         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11462         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11463
11464         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11465         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11466         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11467         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11468         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11469         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11470
11471         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11472         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11473         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11474         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11475         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11476         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11477         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11478         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11479         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11480         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11481         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11482         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11483         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11484         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11485         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11486         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11487         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11488         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11489         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11490         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11491         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11492         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11493         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11494         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11495         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11496         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11497         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11498         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11499         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11500         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11501         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11502         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11503         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11504         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11505         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11506         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11507         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11508         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11509         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11510         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11511         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11512         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11513         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11514         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11515         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11516         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11517         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11518         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11519         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11520         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11521         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11522         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11523         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11524         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11525         
11526         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11527         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11528         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11529         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11530         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11531         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11532         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11533         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11534         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11535         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11536         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11537         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11538         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11539
11540         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11541         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11542         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11543         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11544         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11545         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11546         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11547         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11548         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11549         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11550         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11551         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11552         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11553         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11554                 
11555         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11556         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11557         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11558         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11559         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11560         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11561         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11562         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11563         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11564         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11565         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11566         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11567         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11568         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11569         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11570         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11571         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11572         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11573         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11574         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11575
11576         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11577         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11578         
11579         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11580         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11581         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11582                 
11583         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11584         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11585         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11586         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11587         
11588         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11589         
11590         /*  class MolEnumerable  */
11591         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11592     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11593         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11594         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11595     rb_define_alias(rb_cMolEnumerable, "size", "length");
11596         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11597         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11598
11599         /*  class AtomRef  */
11600         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11601         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11602                 char buf[64];
11603                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11604                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11605                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11606                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11607                 strcat(buf, "=");
11608                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11609         }
11610         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11611         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11612         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11613         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11614         s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11615         rb_global_variable(&s_SetAtomAttrString);
11616         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11617         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11618
11619         /*  class Parameter  */
11620         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11621         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11622         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11623         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11624         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11625         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11626         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11627         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11628         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11629         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11630         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11631         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11632         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11633         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11634         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11635         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11636         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11637         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11638         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11639         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11640         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11641         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11642         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11643         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11644         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11645         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11646         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11647         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11648         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11649         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11650         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11651         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11652         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11653         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11654         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11655         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11656         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11657         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11658         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11659         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11660         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11661         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11662         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11663         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11664         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11665         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11666         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11667         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11668         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11669         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11670         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11671         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11672         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11673         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11674
11675         /*  class ParEnumerable  */
11676         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11677     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11678         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11679         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11680         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11681         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11682         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11683         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11684         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11685         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11686         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11687         
11688         /*  class ParameterRef  */
11689         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11690         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11691                 char buf[64];
11692                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11693                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11694                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11695                 if (s_ParameterAttrDefTable[i].symref != NULL)
11696                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11697                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11698                         strcat(buf, "=");
11699                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11700                 }
11701         }
11702         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11703         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11704         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11705         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11706         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11707         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11708         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11709         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11710
11711         /*  class MolbyError  */
11712         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11713
11714         /*  module Kernel  */
11715         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11716         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11717         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11718         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11719         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11720         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11721         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11722         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11723         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11724         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11725         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11726         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11727         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11728         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11729         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11730         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11731         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11732         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11733         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11734         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11735         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11736         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11737         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11738         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11739
11740         /*  class IO  */
11741         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11742         
11743         s_ID_equal = rb_intern("==");
11744         g_RubyID_call = rb_intern("call");
11745         
11746         s_InitMOInfoKeys();
11747         
11748         /*  Symbols for graphics  */
11749         s_LineSym = ID2SYM(rb_intern("line"));
11750         s_PolySym = ID2SYM(rb_intern("poly"));
11751         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11752         s_ConeSym = ID2SYM(rb_intern("cone"));
11753         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11754 }
11755
11756 #pragma mark ====== Interface with RubyDialog class ======
11757
11758 RubyValue
11759 RubyDialogCallback_parentModule(void)
11760 {
11761         return (RubyValue)rb_mMolby;
11762 }
11763
11764 #pragma mark ====== External functions ======
11765
11766 static VALUE s_ruby_top_self = Qfalse;
11767 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11768 static VALUE s_ruby_export_local_variables = Qfalse;
11769
11770 static VALUE
11771 s_evalRubyScriptOnMoleculeSub(VALUE val)
11772 {
11773         void **ptr = (void **)val;
11774         Molecule *mol = (Molecule *)ptr[1];
11775         VALUE sval, fnval, lnval, retval;
11776         VALUE binding;
11777
11778         /*  Clear the error information (store in the history array if necessary)  */
11779         sval = rb_errinfo();
11780         if (sval != Qnil) {
11781                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11782                 rb_set_errinfo(Qnil);
11783         }
11784
11785         if (s_ruby_top_self == Qfalse) {
11786                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11787         }
11788         if (s_ruby_get_binding_for_molecule == Qfalse) {
11789                 const char *s1 =
11790                  "lambda { |_mol_, _bind_| \n"
11791                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11792                  "  _proc_.call(_mol_) } ";
11793                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11794                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11795         }
11796         if (s_ruby_export_local_variables == Qfalse) {
11797                 const char *s2 =
11798                 "lambda { |_bind_| \n"
11799                 "   # find local variables newly defined in _bind_ \n"
11800                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11801                 " _a_.each { |_vsym_| \n"
11802                 "   _vname_ = _vsym_.to_s \n"
11803                 "   _vval_ = _bind_.eval(_vname_) \n"
11804                 "   #  Define local variable \n"
11805                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11806                 "   #  Then set value  \n"
11807                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11808                 " } \n"
11809                 "}";
11810                 s_ruby_export_local_variables = rb_eval_string(s2);
11811                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11812         }
11813         if (ptr[2] == NULL) {
11814                 char *scr;
11815                 /*  String literal: we need to specify string encoding  */
11816 #if defined(__WXMSW__)
11817                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11818 #else
11819                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11820 #endif
11821                 sval = rb_str_new2(scr);
11822                 free(scr);
11823                 fnval = rb_str_new2("(eval)");
11824                 lnval = INT2FIX(0);
11825         } else {
11826                 sval = rb_str_new2((char *)ptr[0]);
11827                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11828                 lnval = INT2FIX(1);
11829         }
11830         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11831         if (mol != NULL) {
11832                 VALUE mval = ValueFromMolecule(mol);
11833                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11834         }
11835         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11836         if (mol != NULL) {
11837                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11838         }
11839         return retval;
11840 }
11841
11842 RubyValue
11843 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
11844 {
11845         RubyValue retval;
11846         void *args[3];
11847         VALUE save_interrupt_flag;
11848 /*      char *save_ruby_sourcefile;
11849         int save_ruby_sourceline; */
11850         if (gMolbyIsCheckingInterrupt) {
11851                 MolActionAlertRubyIsRunning();
11852                 *status = -1;
11853                 return (RubyValue)Qnil;
11854         }
11855         gMolbyRunLevel++;
11856         args[0] = (void *)script;
11857         args[1] = (void *)mol;
11858         args[2] = (void *)fname;
11859         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
11860 /*      save_ruby_sourcefile = ruby_sourcefile;
11861         save_ruby_sourceline = ruby_sourceline; */
11862         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
11863         if (*status != 0) {
11864                 /*  Is this 'exit' exception?  */
11865                 VALUE last_exception = rb_gv_get("$!");
11866                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
11867                         /*  Capture exit and return the status value  */
11868                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
11869                         *status = 0;
11870                         rb_set_errinfo(Qnil);
11871                 }
11872         }
11873         s_SetInterruptFlag(Qnil, save_interrupt_flag);
11874 /*      ruby_sourcefile = save_ruby_sourcefile;
11875         ruby_sourceline = save_ruby_sourceline; */
11876         gMolbyRunLevel--;
11877         return retval;
11878 }
11879
11880 int
11881 Ruby_showValue(RubyValue value, char **outValueString)
11882 {
11883         VALUE val = (VALUE)value;
11884         if (gMolbyIsCheckingInterrupt) {
11885                 MolActionAlertRubyIsRunning();
11886                 return 0;
11887         }
11888         if (val != Qnil) {
11889                 int status;
11890                 char *str;
11891                 gMolbyRunLevel++;
11892                 val = rb_protect(rb_inspect, val, &status);
11893                 gMolbyRunLevel--;
11894                 if (status != 0)
11895                         return status;
11896                 str = StringValuePtr(val);
11897                 if (outValueString != NULL)
11898                         *outValueString = strdup(str);
11899                 MyAppCallback_showScriptMessage("%s", str);
11900         } else {
11901                 if (outValueString != NULL)
11902                         *outValueString = NULL;
11903         }
11904         return 0;
11905 }
11906
11907 void
11908 Ruby_showError(int status)
11909 {
11910         static const int tag_raise = 6;
11911         char *msg = NULL, *msg2;
11912         VALUE val, backtrace;
11913         int interrupted = 0;
11914         if (status == tag_raise) {
11915                 VALUE errinfo = rb_errinfo();
11916                 VALUE eclass = CLASS_OF(errinfo);
11917                 if (eclass == rb_eInterrupt) {
11918                         msg = "Interrupt";
11919                         interrupted = 1;
11920                 }
11921         }
11922         gMolbyRunLevel++;
11923         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
11924         if (msg == NULL) {
11925                 val = rb_eval_string_protect("$!.to_s", &status);
11926                 if (status == 0)
11927                         msg = RSTRING_PTR(val);
11928                 else msg = "(message not available)";
11929         }
11930         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
11931         MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
11932         free(msg2);
11933         gMolbyRunLevel--;
11934 }
11935
11936 char *
11937 Molby_getDescription(void)
11938 {
11939         extern const char *gVersionString, *gCopyrightString;
11940         extern int gRevisionNumber;
11941         extern char *gLastBuildString;
11942         char *s;
11943         char *revisionString;
11944         if (gRevisionNumber > 0) {
11945                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
11946         } else revisionString = "";
11947         asprintf(&s, 
11948                          "Molby %s%s\n%s\nLast compile: %s\n"
11949 #if !defined(__CMDMAC__)
11950                          "\nIncluding:\n"
11951                          "%s"
11952 #else
11953                          "Including "
11954 #endif
11955                          "ruby %s, http://www.ruby-lang.org/\n"
11956                          "%s\n"
11957                          "FFTW 3.3.2, http://www.fftw.org/\n"
11958                          "  Copyright (C) 2003, 2007-11 Matteo Frigo\n"
11959                          "  and Massachusetts Institute of Technology",
11960                          gVersionString, revisionString, gCopyrightString, gLastBuildString,
11961 #if !defined(__CMDMAC__)
11962                          MyAppCallback_getGUIDescriptionString(),
11963 #endif
11964                          gRubyVersion, gRubyCopyright);
11965         if (revisionString[0] != 0)
11966                 free(revisionString);
11967         return s;
11968 }
11969
11970 void
11971 Molby_startup(const char *script, const char *dir)
11972 {
11973         VALUE val;
11974         int status;
11975         char *libpath;
11976         char *respath, *p, *wbuf;
11977
11978         /*  Get version/copyright string from Ruby interpreter  */
11979         {
11980                 gRubyVersion = strdup(ruby_version);
11981                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
11982 #if defined(__CMDMAC__)
11983                                  "",
11984 #else
11985                                  "  ",  /*  Indent for displaying in About dialog  */
11986 #endif
11987                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
11988         }
11989         
11990         /*  Read build and revision information for Molby  */
11991 /*      {
11992                 char buf[200];
11993                 extern int gRevisionNumber;
11994                 extern char *gLastBuildString;
11995                 FILE *fp = fopen("../buildInfo.txt", "r");
11996                 gLastBuildString = "";
11997                 if (fp != NULL) {
11998                         if (fgets(buf, sizeof(buf), fp) != NULL) {
11999                                 char *p1 = strchr(buf, '\"');
12000                                 char *p2 = strrchr(buf, '\"');
12001                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12002                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12003                                         buf[p2 - p1 - 1] = 0;
12004                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12005                                 }
12006                         }
12007                         fclose(fp);
12008                 }
12009                 fp = fopen("../revisionInfo.txt", "r");
12010                 gRevisionNumber = 0;
12011                 if (fp != NULL) {
12012                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12013                                 gRevisionNumber = strtol(buf, NULL, 0);
12014                         }
12015                         fclose(fp);
12016                 }
12017     } */
12018
12019 #if defined(__CMDMAC__)
12020         wbuf = Molby_getDescription();
12021         printf("%s\n", wbuf);
12022         free(wbuf);
12023 #endif
12024         
12025         /*  Read atom display parameters  */
12026         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12027 #if defined(__CMDMAC__)
12028                 fprintf(stderr, "%s\n", wbuf);
12029 #else
12030                 MyAppCallback_setConsoleColor(1);
12031                 MyAppCallback_showScriptMessage("%s", wbuf);
12032                 MyAppCallback_setConsoleColor(0);
12033 #endif
12034                 free(wbuf);
12035         }
12036         
12037         /*  Read default parameters  */
12038         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12039         if (wbuf != NULL) {
12040 #if defined(__CMDMAC__)
12041                 fprintf(stderr, "%s\n", wbuf);
12042 #else
12043                 MyAppCallback_setConsoleColor(1);
12044                 MyAppCallback_showScriptMessage("%s", wbuf);
12045                 MyAppCallback_setConsoleColor(0);
12046 #endif
12047                 free(wbuf);
12048         }
12049                 
12050         /*  Initialize Ruby interpreter  */
12051 #if __WXMSW__
12052         {
12053                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12054                     it causes rb_bug() (= fatal error) during ruby_init().
12055                     As a workaround, these standard streams are reopend as
12056                     NUL stream.  */
12057                 freopen("NUL", "r", stdin);
12058                 freopen("NUL", "w", stdout);
12059                 freopen("NUL", "w", stderr);
12060         }
12061 #endif
12062         ruby_init();
12063
12064         {
12065                 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12066                 Init_shift_jis();
12067                 Init_trans_japanese_sjis();
12068         }
12069         
12070         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12071         ruby_incpush(".");
12072         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12073         ruby_incpush(libpath);
12074         free(libpath);
12075         ruby_incpush(dir);
12076
12077         ruby_script("Molby");
12078         
12079         /*  Find the resource path (the parent directory of the given directory)  */
12080         respath = strdup(dir);
12081         p = strrchr(respath, '/');
12082         if (p == NULL && PATH_SEPARATOR != '/')
12083                 p = strrchr(respath, PATH_SEPARATOR);
12084         if (p != NULL)
12085                 *p = 0;
12086         val = Ruby_NewFileStringValue(respath);
12087         rb_define_global_const("MolbyResourcePath", val);
12088         free(respath);
12089
12090         /*  Define Molby classes  */
12091         Init_Molby();
12092         RubyDialogInitClass();
12093
12094         rb_define_const(rb_mMolby, "ResourcePath", val);
12095         val = Ruby_NewFileStringValue(dir);
12096         rb_define_const(rb_mMolby, "ScriptPath", val);
12097         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12098         val = Ruby_NewFileStringValue(p);
12099         rb_define_const(rb_mMolby, "MbsfPath", val);    
12100         free(p);
12101         
12102         p = MyAppCallback_getHomeDir();
12103         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12104         rb_define_const(rb_mMolby, "HomeDirectory", val);
12105         free(p);
12106         p = MyAppCallback_getDocumentHomeDir();
12107         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12108         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12109         free(p);
12110         
12111 #if defined(__CMDMAC__)
12112         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12113 #else
12114         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12115 #endif
12116
12117 #if !__CMDMAC__
12118         
12119         /*  Create objects for stdout and stderr  */
12120         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12121         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12122         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12123         rb_gv_set("$stdout", val);
12124         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12125         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12126         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12127         rb_gv_set("$stderr", val);
12128
12129         /*  Create objects for stdin  */
12130         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12131         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12132         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12133         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12134         rb_gv_set("$stdin", val);
12135         
12136 #endif
12137         
12138         /*  Global variable to hold error information  */
12139         rb_define_variable("$backtrace", &gMolbyBacktrace);
12140         rb_define_variable("$error_history", &gMolbyErrorHistory);
12141         gMolbyErrorHistory = rb_ary_new();
12142         
12143         /*  Global variables for script menus  */
12144         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12145         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12146         gScriptMenuCommands = rb_ary_new();
12147         gScriptMenuEnablers = rb_ary_new();
12148         
12149 #if !__CMDMAC__
12150         /*  Register interrupt check code  */
12151         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12152 #endif
12153         
12154 #if !__CMDMAC__
12155         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12156         s_SetIntervalTimer(0, 50);
12157 #endif
12158         
12159         /*  Read the startup script  */
12160         if (script != NULL && script[0] != 0) {
12161                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12162                 gMolbyRunLevel++;
12163                 rb_load_protect(rb_str_new2(script), 0, &status);
12164                 gMolbyRunLevel--;
12165                 if (status != 0)
12166                         Ruby_showError(status);
12167                 else
12168                         MyAppCallback_showScriptMessage("Done.\n");
12169         }
12170 }
12171
12172 void
12173 Molby_buildARGV(int argc, const char **argv)
12174 {
12175         int i;
12176     rb_ary_clear(rb_argv);
12177     for (i = 0; i < argc; i++) {
12178                 VALUE arg = rb_tainted_str_new2(argv[i]);
12179                 OBJ_FREEZE(arg);
12180                 rb_ary_push(rb_argv, arg);
12181     }
12182 }