OSDN Git Service

3cf97c4173e6c1e084a05763473bae00adb9a2d9
[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_AnisoEigvalSym, s_SymopSym, s_IntChargeSym,
80     s_FixForceSym, s_FixPosSym, s_ExclusionSym, s_MMExcludeSym,
81     s_PeriodicExcludeSym, 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 = Ruby_NewEncodedStringValue2(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 = Ruby_NewEncodedStringValue2(p);
139         free(p);
140         return retval;
141 #else
142         return Ruby_NewEncodedStringValue2(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_NewEncodedStringValue2(const char *str)
164 {
165     return Ruby_NewEncodedStringValue(str, -1);
166 }
167
168 VALUE
169 Ruby_ObjToStringObj(VALUE val)
170 {
171         switch (TYPE(val)) {
172                 case T_STRING:
173                         return val;
174                 case T_SYMBOL:
175                         return rb_str_new2(rb_id2name(SYM2ID(val)));
176                 default:
177                         return rb_str_to_str(val);
178         }
179 }
180
181 #pragma mark ====== Message input/output ======
182
183 /*
184  *  call-seq:
185  *     message_box(str, title, button = nil, icon = :info)
186  *
187  *  Show a message box.
188  *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
189  *  Icon: :info, :warning, :error
190  */
191 static VALUE
192 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
193 {
194         char *str, *title, *s;
195         int buttons, icon;
196         VALUE sval, tval, bval, ival;
197         rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
198         str = StringValuePtr(sval);
199         title = StringValuePtr(tval);
200         if (bval != Qnil) {
201                 bval = Ruby_ObjToStringObj(bval);
202                 s = RSTRING_PTR(bval);
203                 if (strncmp(s, "ok", 2) == 0)
204                         buttons = 1;
205                 else if (strncmp(s, "cancel", 6) == 0)
206                         buttons = 2;
207                 else
208                         rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
209         } else buttons = 3;
210         if (ival != Qnil) {
211                 ival = Ruby_ObjToStringObj(ival);
212                 s = RSTRING_PTR(ival);
213                 if (strncmp(s, "info", 4) == 0)
214                         icon = 1;
215                 else if (strncmp(s, "warn", 4) == 0)
216                         icon = 2;
217                 else if (strncmp(s, "err", 3) == 0)
218                         icon = 3;
219                 else
220                         rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
221         } else icon = 1;
222         MyAppCallback_messageBox(str, title, buttons, icon);
223         return Qnil;
224 }
225
226 /*
227  *  call-seq:
228  *     error_message_box(str)
229  *
230  *  Show an error message box.
231  */
232 static VALUE
233 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
234 {
235         char *str = StringValuePtr(sval);
236         MyAppCallback_errorMessageBox("%s", str);
237         return Qnil;
238 }
239
240 /*
241  *  call-seq:
242  *     ask(prompt, default = nil) -> string
243  *
244  *  Open a modal dialog and get a line of text.
245  */
246 static VALUE
247 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
248 {
249         volatile VALUE prompt, message;
250         char buf[1024];
251         int retval;
252         rb_scan_args(argc, argv, "11", &prompt, &message);
253         if (message != Qnil) {
254                 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
255                 buf[sizeof buf - 1] = 0;
256         } else buf[0] = 0;
257         retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
258         if (retval)
259                 return Ruby_NewEncodedStringValue2(buf);
260         else
261                 return Qnil;    
262 }
263
264 /*
265  *  call-seq:
266  *     show_console_window
267  *
268  *  Show the console window and bring to the front.
269  */
270 static VALUE
271 s_Kernel_ShowConsoleWindow(VALUE self)
272 {
273         MyAppCallback_showConsoleWindow();
274         return Qnil;
275 }
276
277 /*
278  *  call-seq:
279  *     hide_console_window
280  *
281  *  Hide the console window.
282  */
283 static VALUE
284 s_Kernel_HideConsoleWindow(VALUE self)
285 {
286         MyAppCallback_hideConsoleWindow();
287         return Qnil;
288 }
289
290 /*
291  *  call-seq:
292  *     bell
293  *
294  *  Ring the system bell.
295  */
296 static VALUE
297 s_Kernel_Bell(VALUE self)
298 {
299         MyAppCallback_bell();
300         return Qnil;
301 }
302
303 /*
304  *  call-seq:
305  *     play_sound(filename, flag = 0)
306  *
307  *  Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
308  *  1, play the sound asynchronously; 3, play the sound with loop asynchronously
309  */
310 static VALUE
311 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
312 {
313         VALUE fnval, flval;
314         int flag, retval;
315         char *fname;
316         rb_scan_args(argc, argv, "11", &fnval, &flval);
317         if (flval == Qnil)
318                 flag = 0;
319         else flag = NUM2INT(rb_Integer(flval));
320         fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
321         fname = StringValuePtr(fnval);
322         retval = MyAppCallback_playSound(fname, flag);
323         return (retval ? Qtrue : Qnil);
324 }
325
326 /*
327  *  call-seq:
328  *     stop_sound
329  *
330  *  Stop the sound if playing.
331  */
332 static VALUE
333 s_Kernel_StopSound(VALUE self)
334 {
335         MyAppCallback_stopSound();
336         return Qnil;
337 }
338
339 /*
340  *  call-seq:
341  *     export_to_clipboard(str)
342  *
343  *  Export the given string to clipboard.
344  */
345 static VALUE
346 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
347 {
348 #if !defined(__CMDMAC__)
349     const char *s;
350         char *ns;
351     if (!gUseGUI)
352         return Qnil;
353     s = StringValuePtr(sval);
354 #if __WXMSW__
355         /*  Convert the end-of-line characters  */
356         {       const char *p; int nc; char *np;
357                 nc = 0;
358                 for (p = s; *p != 0; p++) {
359                         if (*p == '\n')
360                                 nc++;
361                 }       
362                 ns = (char *)malloc(strlen(s) + nc + 1);
363                 for (np = ns, p = s; *p != 0; p++, np++) {
364                         if (*p == '\n')
365                                 *np++ = '\r';
366                         *np = *p;
367                 }
368                 *np = 0;
369         }
370 #else
371         ns = (char *)malloc(strlen(s) + 1);
372         strcpy(ns, s);
373 #if __WXMAC__
374         {       char *np;
375                 /*  wxMac still has Carbon code. Oops.  */
376                 for (np = ns; *np != 0; np++) {
377                         if (*np == '\n')
378                                 *np = '\r';
379                 }
380         }
381 #endif
382 #endif
383         if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
384                 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
385 #endif
386         return Qnil;
387 }
388
389 /*
390  *  call-seq:
391  *     stdout.write(str)
392  *
393  *  Put the message in the main text view in black color.
394  */
395 static VALUE
396 s_StandardOutput(VALUE self, VALUE str)
397 {
398         int n;
399         MyAppCallback_setConsoleColor(0);
400         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
401         return INT2NUM(n);
402 }
403
404 /*
405  *  call-seq:
406  *     stderr.write(str)
407  *
408  *  Put the message in the main text view in red color.
409  */
410 static VALUE
411 s_StandardErrorOutput(VALUE self, VALUE str)
412 {
413         int n;
414         MyAppCallback_setConsoleColor(1);
415         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
416         MyAppCallback_setConsoleColor(0);
417         return INT2NUM(n);
418 }
419
420 /*
421  *  call-seq:
422  *     stdout.flush
423  *     stderr.flush
424  *
425  *  Flush the standard (error) output. Actually do nothing.
426  */
427 static VALUE
428 s_FlushConsoleOutput(VALUE self)
429 {
430         return self;
431 }
432
433 /*
434  *  call-seq:
435  *     stdin.gets(rs = $/)
436  *
437  *  Read one line message via dialog box.
438  */
439 static VALUE
440 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
441 {
442         VALUE pval, rval;
443         pval = Ruby_NewEncodedStringValue2("Enter a line:");
444         rval = s_Kernel_Ask(1, &pval, self);
445         if (rval == Qnil)
446                 rb_interrupt();
447         rb_str_cat2(rval, "\n");
448         return rval;
449 }
450
451 /*
452  *  call-seq:
453  *     stdin.method_missing(name, args, ...)
454  *
455  *  Throw an exception, noting only gets and readline are defined.
456  */
457 static VALUE
458 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
459 {
460         VALUE nval;
461         rb_scan_args(argc, argv, "10", &nval);
462         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
463         return Qnil;  /*  Not reached  */
464 }
465
466 #pragma mark ====== Track key events ======
467
468 /*  User interrupt handling
469  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
470  *  key events. This polling should only be enabled during "normal" execution
471  *  of scripts and must be disabled when the rest of the application (or Ruby
472  *  script itself) is handling GUI. This is ensured by appropriate calls to
473  *  enable_interrupt and disable_interrupt.  */
474
475 static VALUE s_interrupt_flag = Qfalse;
476
477 static VALUE
478 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
479 {
480         volatile VALUE message;
481         const char *p;
482         if (Ruby_GetInterruptFlag() == Qtrue) {
483                 rb_scan_args(argc, argv, "01", &message);
484                 if (message != Qnil)
485                         p = StringValuePtr(message);
486                 else
487                         p = NULL;
488                 MyAppCallback_showProgressPanel(p);
489         }
490         return Qnil;
491 }
492
493 static VALUE
494 s_HideProgressPanel(VALUE self)
495 {
496         MyAppCallback_hideProgressPanel();
497         return Qnil;
498 }
499
500 static VALUE
501 s_SetProgressValue(VALUE self, VALUE val)
502 {
503         double dval = NUM2DBL(rb_Float(val));
504         MyAppCallback_setProgressValue(dval);
505         return Qnil;
506 }
507
508 static VALUE
509 s_SetProgressMessage(VALUE self, VALUE msg)
510 {
511         const char *p;
512         if (msg == Qnil)
513                 p = NULL;
514         else p = StringValuePtr(msg);
515         MyAppCallback_setProgressMessage(p);
516         return Qnil;
517 }
518
519 static VALUE
520 s_SetInterruptFlag(VALUE self, VALUE val)
521 {
522         VALUE oldval;
523         if (val != Qundef) {
524                 if (val == Qfalse || val == Qnil)
525                         val = Qfalse;
526                 else val = Qtrue;
527         }
528         oldval = s_interrupt_flag;
529         if (val != Qundef) {
530                 s_interrupt_flag = val;
531         }
532         return oldval;
533 }
534
535 static VALUE
536 s_GetInterruptFlag(VALUE self)
537 {
538         return s_SetInterruptFlag(self, Qundef);
539 }
540
541 VALUE
542 Ruby_SetInterruptFlag(VALUE val)
543 {
544         return s_SetInterruptFlag(Qnil, val);
545 }
546
547 VALUE
548 Ruby_GetInterruptFlag(void)
549 {
550         return s_SetInterruptFlag(Qnil, Qundef);
551 }
552
553 /*
554  *  call-seq:
555  *     check_interrupt -> integer
556  *
557  *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
558  */
559 static VALUE
560 s_Kernel_CheckInterrupt(VALUE self)
561 {
562         if (Ruby_GetInterruptFlag() == Qfalse)
563                 return INT2NUM(-1);
564         else if (MyAppCallback_checkInterrupt())
565                 return INT2NUM(1);
566         else return INT2NUM(0);
567 }
568
569 static volatile unsigned long sITimerCount = 0;
570
571 #if __WXMSW__
572 static HANDLE sITimerEvent;
573 static HANDLE sITimerThread;
574 static int sITimerInterval;
575
576 static __stdcall unsigned
577 s_ITimerThreadFunc(void *p)
578 {
579         while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
580                 sITimerCount++;
581         }
582         return 0;
583 }
584
585 #elif USE_PTHREAD_FOR_TIMER
586
587 /*  Timer thread  */
588 static pthread_t sTimerThread;
589
590 /*  -1: uninitiated; 0: active, 1: inactive, -2: request to terminate  */
591 static volatile signed char sTimerFlag = -1;
592 static volatile int sTimerIntervalMicrosec = 0;
593
594 static void *
595 s_TimerThreadEntry(void *param)
596 {
597         while (1) {
598                 usleep(sTimerIntervalMicrosec);
599                 if (sTimerFlag == 0)
600                         sITimerCount++;
601                 else if (sTimerFlag == -2)
602                         break;
603         }
604         return NULL;    
605 }
606
607 #endif
608
609 static void
610 s_SignalAction(int n)
611 {
612         sITimerCount++;
613 }
614
615 static void
616 s_SetIntervalTimer(int n, int msec)
617 {
618 #if __WXMSW__
619         if (n == 0) {
620                 /*  Start interval timer  */
621                 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
622                 sITimerInterval = msec;
623                 if (sITimerEvent) {
624                         sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
625                 }
626         } else {
627                 /*  Stop interval timer  */
628                 if (sITimerEvent)
629                         SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
630                 if (sITimerThread) {
631                         WaitForSingleObject(sITimerThread, 1000);
632                         CloseHandle(sITimerThread);
633                 }
634                 if (sITimerEvent)
635                         CloseHandle(sITimerEvent);
636                 sITimerEvent = NULL;
637                 sITimerThread = NULL;
638         }
639 #elif USE_PTHREAD_FOR_TIMER
640         if (n == 0) {
641                 if (sTimerFlag == -1) {
642                         int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
643                         if (status != 0) {
644                                 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
645                         }
646                 }
647                 sTimerFlag = 0;  /*  Active  */
648                 sTimerIntervalMicrosec = msec * 1000;
649         } else if (sTimerFlag != -1)
650                 sTimerFlag = 1;  /*  Inactive  */       
651 #else
652         static struct itimerval sOldValue;
653         static struct sigaction sOldAction;
654         struct itimerval val;
655         struct sigaction act;
656         if (n == 0) {
657                 sITimerCount = 0;
658                 act.sa_handler = s_SignalAction;
659                 act.sa_mask = 0;
660                 act.sa_flags = 0;
661                 sigaction(SIGALRM, &act, &sOldAction);
662                 val.it_value.tv_sec = 0;
663                 val.it_value.tv_usec = msec * 1000;
664                 val.it_interval.tv_sec = 0;
665                 val.it_interval.tv_usec = msec * 1000;
666                 setitimer(ITIMER_REAL, &val, &sOldValue);
667         } else {
668                 setitimer(ITIMER_REAL, &sOldValue, &val);
669                 sigaction(SIGALRM, &sOldAction, &act);
670         }
671 #endif
672 }
673
674 static unsigned long
675 s_GetTimerCount(void)
676 {
677         return sITimerCount;
678 }
679
680 static void
681 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
682 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
683 {
684         if (s_interrupt_flag != Qfalse) {
685                 static unsigned long sLastTime = 0;
686                 unsigned long currentTime;
687                 int flag;
688                 currentTime = s_GetTimerCount();
689                 if (currentTime != sLastTime) {
690                         sLastTime = currentTime;
691                         gMolbyIsCheckingInterrupt = 1;
692                         flag = MyAppCallback_checkInterrupt();
693                         gMolbyIsCheckingInterrupt = 0;
694                         if (flag) {
695                                 s_SetInterruptFlag(Qnil, Qfalse);
696                                 rb_interrupt();
697                         }
698                 }
699         }
700 }
701
702 #pragma mark ====== Menu handling ======
703
704 /*
705  *  call-seq:
706  *     register_menu(title, method, enable_proc = nil)
707  *
708  *  Register the method (specified as a symbol) in the script menu.
709  *  The method must be either an instance method of Molecule with no argument,
710  *  or a class method of Molecule with one argument (the current molecule),
711  *  or a proc object with one argument (the current molecule).
712  *  The menu associated with the class method can be invoked even when no document
713  *  is open (the argument is set to Qnil in this case). On the other hand, the
714  *  menu associated with the instance method can only be invoked when at least one 
715  *  document is active.
716  *  If enable_proc is non-nil, then it is called whenever the availability of
717  *  the menu command is tested. It is usually a proc object with one argument
718  *  (the current molecule or nil). As a special case, the following symbols can
719  *  be given; :mol (enabled when any molecule is open), :non_empty (enabled when
720  *  the top-level molecule has at least one atom), :selection (enabled when
721  *  the top-level molecule has one or more selected atoms).
722  */
723 static VALUE
724 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
725 {
726         int n, mtype = 0;
727         VALUE tval, mval, pval;
728         static VALUE sMolSym, sNonEmptySym, sSelectionSym;
729         static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
730         rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
731         tval = rb_str_to_str(tval);
732         n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
733         if (n < 0)
734                 return Qnil;
735         if (TYPE(mval) == T_SYMBOL) {
736                 /*  Create an appropriate proc object  */
737                 const char *name = rb_id2name(SYM2ID(mval));
738                 char *s;
739                 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
740                         /*  Defined as a Molecule method  */
741                         asprintf(&s, "lambda { |m| m.%s }", name);
742                         mtype = 1;
743                 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
744                         /*  Defined as a Molecule class method  */
745                         asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
746                         mtype = 2;
747                 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
748                 mval = rb_eval_string(s);
749                 free(s);
750         }
751         if (sMolSym == Qfalse) {
752                 sMolSym = ID2SYM(rb_intern("mol"));
753                 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
754                 sSelectionSym = ID2SYM(rb_intern("selection"));
755                 sMolProc = rb_eval_string("lambda { |m| m != nil }");
756                 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
757                 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
758                 sTrueProc = rb_eval_string("lambda { |m| true }");
759                 rb_global_variable(&sMolProc);
760                 rb_global_variable(&sNonEmptyProc);
761                 rb_global_variable(&sSelectionProc);
762                 rb_global_variable(&sTrueProc);
763         }
764         
765         if (pval == Qnil) {
766                 if (mtype == 1)
767                         pval = sMolProc;
768                 else
769                         pval = sTrueProc;
770         } else if (pval == sMolSym)
771                 pval = sMolProc;
772         else if (pval == sNonEmptySym)
773                 pval = sNonEmptyProc;
774         else if (pval == sSelectionSym)
775                 pval = sSelectionProc;
776         rb_ary_store(gScriptMenuCommands, n, mval);
777         rb_ary_store(gScriptMenuEnablers, n, pval);
778         return INT2NUM(n);
779 }
780
781 static VALUE
782 s_Kernel_LookupMenu(VALUE self, VALUE title)
783 {
784         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
785         return INT2NUM(n);
786 }
787
788 static VALUE
789 s_Ruby_UpdateUI_handler(VALUE data)
790 {
791         void **p = (void **)data;
792         int index = (int)p[0];
793         Molecule *mol = (Molecule *)p[1];
794         int *outChecked = (int *)p[2];
795         char **outTitle = (char **)p[3];
796         VALUE mval = ValueFromMolecule(mol);
797         VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
798         static ID call_id = 0;
799         if (call_id == 0)
800                 call_id = rb_intern("call");
801         if (pval == Qnil)
802                 return Qnil;
803         pval = rb_funcall(pval, call_id, 1, mval);
804         if (rb_obj_is_kind_of(pval, rb_cArray)) {
805                 VALUE val;
806                 if (outChecked != NULL) {
807                         val = rb_ary_entry(pval, 1);  /*  Checked or not  */
808                         *outChecked = (RTEST(val) ? 1 : 0);
809                 }
810                 if (outTitle != NULL) {
811                         val = rb_ary_entry(pval, 2);  /*  Text  */
812                         if (TYPE(val) == T_STRING) {
813                                 *outTitle = strdup(StringValuePtr(val));
814                         }
815                 }
816                 pval = rb_ary_entry(pval, 0);
817         }
818         return pval;
819 }
820
821 int
822 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
823 {
824         int status;
825         void *p[4];
826         VALUE retval;
827         p[0] = (void *)(intptr_t)index;
828         p[1] = mol;
829         p[2] = outChecked;
830         p[3] = outTitle;
831         retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
832         return (RTEST(retval) ? 1 : 0);
833 }
834
835 /*
836 static VALUE
837 s_Ruby_methodType_sub(VALUE data)
838 {
839         const char **p = (const char **)data;
840         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
841         ID mid = rb_intern(p[1]);
842         int ival;
843         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
844                 ival = 1;
845         else if (rb_respond_to(klass, mid))
846                 ival = 2;
847         else ival = 0;
848         return INT2FIX(ival);
849 }
850 */      
851 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
852     has the singleton method (class method) with the given name, 0 otherwise.  */
853 /*int
854 Ruby_methodType(const char *className, const char *methodName)
855 {
856         int status;
857         VALUE retval;
858         const char *p[2];
859         p[0] = className;
860         p[1] = methodName;
861         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
862         if (status == 0)
863                 return FIX2INT(retval);
864         else return 0;
865 }
866 */
867
868 /*
869  *  call-seq:
870  *     execute_script_file(fname)
871  *
872  *  Execute the script in the given file. If a molecule is active, then
873  *  the script is evaluated as Molecule.current.instance_eval(script).
874  *  Before entering the script, the current directory is set to the parent
875  *  directory of the script.
876  */
877 static VALUE
878 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
879 {
880         int status;
881         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
882         if (retval == (VALUE)6 && status == -1)
883                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
884         if (status != 0)
885                 rb_jump_tag(status);
886         return retval;
887 }
888
889 /*
890  *  call-seq:
891  *     document_home
892  *
893  *  Get the directory suitable for storing user documents. On Windows
894  *  it is the home directory + "My Documents". On other platforms
895  *  it is the home directory.
896  */
897 static VALUE
898 s_Kernel_DocumentHome(VALUE self)
899 {
900         char *s = MyAppCallback_getDocumentHomeDir();
901         VALUE retval = Ruby_NewFileStringValue(s);
902         free(s);
903         return retval;
904 }
905
906 /*  The callback function for call_subprocess  */
907 static int
908 s_Kernel_CallSubProcess_Callback(void *data)
909 {
910         int status;
911         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
912         if (status != 0 || retval == Qnil || retval == Qfalse)
913                 return 1;
914         else return 0;
915 }
916
917 /*
918  *  call-seq:
919  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
920  *
921  *  Call subprocess. A progress dialog window is displayed, with a message
922  *  "Running #{process_name}...".
923  *  A callback proc can be given, which is called periodically during execution. If the proc returns
924  *  nil or false, then the execution will be interrupted.
925  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
926  *  filename begins with ">>", then the message will be appended to the file.
927  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
928  *  If the argument is nil, then the message will be sent to the Ruby console.
929  */
930 static VALUE
931 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
932 {
933         VALUE cmd, procname, cproc, stdout_val, stderr_val;
934     VALUE save_interruptFlag;
935         int n, exitstatus, pid;
936         char *sout, *serr;
937         FILE *fpout, *fperr;
938
939         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
940
941         if (stdout_val == Qnil) {
942                 fpout = (FILE *)1;
943         } else {
944                 sout = StringValuePtr(stdout_val);
945                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
946                         fpout = NULL;
947                 else {
948                         if (strncmp(sout, ">>", 2) == 0) {
949                                 sout += 2;
950                                 fpout = fopen(sout, "a");
951                         } else {
952                                 if (*sout == '>')
953                                         sout++;
954                                 fpout = fopen(sout, "w");
955                         }
956                         if (fpout == NULL)
957                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
958                 }
959         }
960         if (stderr_val == Qnil) {
961                 fperr = (FILE *)1;
962         } else {
963                 serr = StringValuePtr(stderr_val);
964                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
965                         fperr = NULL;
966                 else {
967                         if (strncmp(serr, ">>", 2) == 0) {
968                                 serr += 2;
969                                 fpout = fopen(serr, "a");
970                         } else {
971                                 if (*serr == '>')
972                                         serr++;
973                                 fperr = fopen(serr, "w");
974                         }
975                         if (fperr == NULL)
976                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
977                 }
978         }
979     
980     save_interruptFlag = s_SetInterruptFlag(self, Qnil);
981         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
982     s_SetInterruptFlag(self, save_interruptFlag);
983     
984         if (fpout != NULL && fpout != (FILE *)1)
985                 fclose(fpout);
986         if (fperr != NULL && fperr != (FILE *)1)
987                 fclose(fperr);
988
989         return INT2NUM(n);
990
991         
992 }
993
994 /*
995  *  call-seq:
996  *     backquote(cmd)
997  *
998  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
999  */
1000 static VALUE
1001 s_Kernel_Backquote(VALUE self, VALUE cmd)
1002 {
1003         char *buf;
1004         int n, exitstatus, pid;
1005         VALUE val;
1006         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1007 /*      fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1008         if (n >= 0 && buf != NULL) {
1009                 val = Ruby_NewEncodedStringValue(buf, 0);
1010                 free(buf);
1011         } else {
1012                 val = Ruby_NewEncodedStringValue("", 0);
1013         }
1014         rb_last_status_set(exitstatus, pid);
1015         return val;
1016 }
1017
1018 #pragma mark ====== User defaults ======
1019
1020 /*
1021  *  call-seq:
1022  *     get_global_settings(key)
1023  *
1024  *  Get a setting data for key from the application preferences.
1025  */
1026 static VALUE
1027 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1028 {
1029         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1030         if (p != NULL) {
1031                 VALUE retval = rb_eval_string(p);
1032                 free(p);
1033                 return retval;
1034         } else return Qnil;
1035 }
1036
1037 /*
1038  *  call-seq:
1039  *     set_global_settings(key, value)
1040  *
1041  *  Set a setting data for key to the application preferences.
1042  */
1043 static VALUE
1044 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1045 {
1046         VALUE sval = rb_inspect(value);
1047         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1048         return value;
1049 }
1050
1051 #pragma mark ====== IO extension ======
1052
1053 static VALUE
1054 s_Ruby_str_encode_protected(VALUE val)
1055 {
1056         return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1057                                   ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1058 }
1059
1060 /*
1061  *  call-seq:
1062  *     gets_any_eol
1063  *
1064  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1065  */
1066 static VALUE
1067 s_IO_gets_any_eol(VALUE self)
1068 {
1069         VALUE val, val2, cval;
1070         char buf[1024];
1071         int i, c, status;
1072         static ID id_getbyte = 0, id_ungetbyte;
1073         if (id_getbyte == 0) {
1074                 id_getbyte = rb_intern("getbyte");
1075                 id_ungetbyte = rb_intern("ungetbyte");
1076         }
1077         i = 0;
1078         val = Qnil;
1079         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1080                 c = NUM2INT(rb_Integer(cval));
1081                 if (c == 0x0d) {
1082                         cval = rb_funcall(self, id_getbyte, 0);
1083                         if (cval != Qnil) {
1084                                 c = NUM2INT(rb_Integer(cval));
1085                                 if (c != 0x0a)
1086                                         rb_funcall(self, id_ungetbyte, 1, cval);
1087                         }
1088                         break;
1089                 } else if (c != 0x0a) {
1090                         buf[i++] = c;
1091                         if (i >= 1020) {
1092                                 buf[i] = 0;
1093                                 if (val == Qnil)
1094                                         val = rb_str_new(buf, i);
1095                                 else
1096                                         rb_str_append(val, rb_str_new(buf, i));
1097                                 i = 0;
1098                         }
1099                 } else break;
1100         }
1101         if (cval == Qnil && i == 0 && val == Qnil)
1102                 return Qnil;  /*  End of file  */
1103         buf[i] = 0;
1104         if (val == Qnil)
1105                 val = rb_str_new(buf, i);
1106         else if (i > 0)
1107                 rb_str_append(val, rb_str_new(buf, i));
1108         val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /*  Ignore exception  */
1109         if (status == 0)
1110                 val = val2;
1111         if (cval != Qnil) {
1112                 /*  Needs a end-of-line mark  */
1113                 cval = rb_gv_get("$/");
1114                 rb_str_append(val, cval);
1115         }
1116         rb_gv_set("$_", val);
1117         return val;
1118 }
1119
1120 #pragma mark ====== Utility functions (protected funcall) ======
1121
1122 struct Ruby_funcall2_record {
1123         VALUE recv;
1124         ID mid;
1125         int argc;
1126         VALUE *argv;
1127 };
1128
1129 static VALUE
1130 s_Ruby_funcall2_sub(VALUE data)
1131 {
1132         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1133         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1134 }
1135
1136 VALUE
1137 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1138 {
1139         struct Ruby_funcall2_record rec;
1140         rec.recv = recv;
1141         rec.mid = mid;
1142         rec.argc = argc;
1143         rec.argv = argv;
1144         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1145 }
1146
1147 RubyValue
1148 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1149 {
1150         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1151 }
1152
1153 #pragma mark ====== ParameterRef Class ======
1154
1155 static UnionPar *
1156 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1157 {
1158         UnionPar *up;
1159         ParameterRef *pref;
1160         Data_Get_Struct(self, ParameterRef, pref);
1161         if (typep != NULL)
1162                 *typep = pref->parType;
1163         if (pref->parType == kElementParType) {
1164                 up = (UnionPar *)&gElementParameters[pref->idx];
1165         } else {
1166                 up = ParameterRefGetPar(pref);
1167                 if (checkEditable) {
1168                         if (pref->idx < 0)
1169                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1170                         if (up->bond.src != 0 && up->bond.src != -1)
1171                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1172                 }
1173         }
1174         return up;
1175 }
1176
1177 static void
1178 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1179 {
1180         UnionPar *up;
1181         ParameterRef *pref;
1182         Data_Get_Struct(self, ParameterRef, pref);
1183         if (pref->mol == NULL)
1184                 return;
1185         up = ParameterRefGetPar(pref);
1186         if (key != s_SourceSym)
1187                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1188         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1189                 /*  Register undo  */
1190                 MolAction *act;
1191                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1192                 MolActionCallback_registerUndo(pref->mol, act);
1193                 MoleculeCallback_notifyModification(pref->mol, 0);
1194                 pref->mol->needsMDRebuild = 1;
1195         }
1196 }
1197
1198 VALUE
1199 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1200 {
1201         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1202         if (pref != NULL)
1203                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1204         else
1205                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1206 }
1207
1208 static int
1209 s_AtomTypeIndexFromValue(VALUE val)
1210 {
1211         if (rb_obj_is_kind_of(val, rb_cNumeric))
1212                 return NUM2INT(val);
1213         else
1214                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1215 }
1216
1217 static const char *s_ParameterTypeNames[] = {
1218         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1219 };
1220 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1221
1222 static int
1223 s_ParTypeFromValue(VALUE val)
1224 {
1225         int i, n;
1226         ID valid;
1227         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1228         if (s_ParameterTypeIDs[0] == 0) {
1229                 for (i = 0; i < n; i++)
1230                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1231         }
1232         valid = rb_to_id(val);
1233         for (i = 0; i < n; i++) {
1234                 if (valid == s_ParameterTypeIDs[i]) {
1235                         if (i == 7)
1236                                 return kElementParType;
1237                         else return kFirstParType + i;
1238                 }
1239         }
1240         return kInvalidParType;
1241 }
1242
1243 /*
1244  *  call-seq:
1245  *     index -> Integer
1246  *
1247  *  Get the index in the parameter list.
1248  */
1249 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1250         ParameterRef *pref;
1251         Data_Get_Struct(self, ParameterRef, pref);
1252         return INT2NUM(pref->idx);
1253 }
1254
1255 /*
1256  *  call-seq:
1257  *     par_type -> String
1258  *
1259  *  Get the parameter type, like "bond", "angle", etc.
1260  */
1261 static VALUE s_ParameterRef_GetParType(VALUE self) {
1262         Int tp;
1263         s_UnionParFromValue(self, &tp, 0);
1264         if (tp == kElementParType)
1265                 return Ruby_NewEncodedStringValue2("element");
1266         tp -= kFirstParType;
1267         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1268                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1269         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1270 }
1271
1272 /*
1273  *  call-seq:
1274  *     atom_type -> String or Array of String
1275  *     atom_types -> String or Array of String
1276  *
1277  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1278  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1279  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1280  *  The atom type may be "X", which is a wildcard that matches any atom type.
1281  */
1282 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1283         UnionPar *up;
1284         Int tp, i, n;
1285         UInt types[4];
1286         VALUE vals[4];
1287         up = s_UnionParFromValue(self, &tp, 0);
1288         n = ParameterGetAtomTypes(tp, up, types);
1289         if (n == 0)
1290                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1291         for (i = 0; i < n; i++) {
1292                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1293                         vals[i] = INT2NUM(types[i]);
1294                 else
1295                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1296         }
1297         if (n == 1)
1298                 return vals[0];
1299         else
1300                 return rb_ary_new4(n, vals);
1301 }
1302
1303 /*
1304  *  call-seq:
1305  *     k -> Float
1306  *
1307  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1308  */
1309 static VALUE s_ParameterRef_GetK(VALUE self) {
1310         UnionPar *up;
1311         Int tp, i, n;
1312         VALUE vals[3];
1313         up = s_UnionParFromValue(self, &tp, 0);
1314         switch (tp) {
1315                 case kBondParType:
1316                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1317                 case kAngleParType:
1318                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1319                 case kDihedralParType:
1320                 case kImproperParType:
1321                         if (up->torsion.mult == 1)
1322                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1323                         n = up->torsion.mult;
1324                         if (n > 3)
1325                                 n = 3;
1326                         for (i = 0; i < n; i++)
1327                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1328                         return rb_ary_new4(n, vals);
1329                 default:
1330                         rb_raise(rb_eMolbyError, "invalid member k");
1331         }
1332 }
1333
1334 /*
1335  *  call-seq:
1336  *     r0 -> Float
1337  *
1338  *  Get the equilibrium bond length. Only available for bond parameters.
1339  */
1340 static VALUE s_ParameterRef_GetR0(VALUE self) {
1341         UnionPar *up;
1342         Int tp;
1343         up = s_UnionParFromValue(self, &tp, 0);
1344         if (tp == kBondParType)
1345                 return rb_float_new(up->bond.r0);
1346         else rb_raise(rb_eMolbyError, "invalid member r0");
1347 }
1348
1349 /*
1350  *  call-seq:
1351  *     a0 -> Float
1352  *
1353  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1354  */
1355 static VALUE s_ParameterRef_GetA0(VALUE self) {
1356         UnionPar *up;
1357         Int tp;
1358         up = s_UnionParFromValue(self, &tp, 0);
1359         if (tp == kAngleParType)
1360                 return rb_float_new(up->angle.a0 * kRad2Deg);
1361         else rb_raise(rb_eMolbyError, "invalid member a0");
1362 }
1363
1364 /*
1365  *  call-seq:
1366  *     mult -> Float
1367  *
1368  *  Get the multiplicity. Only available for dihedral and improper parameters.
1369  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1370  */
1371 static VALUE s_ParameterRef_GetMult(VALUE self) {
1372         UnionPar *up;
1373         Int tp;
1374         up = s_UnionParFromValue(self, &tp, 0);
1375         if (tp == kDihedralParType || tp == kImproperParType)
1376                 return rb_float_new(up->torsion.mult);
1377         else rb_raise(rb_eMolbyError, "invalid member mult");
1378 }
1379
1380 /*
1381  *  call-seq:
1382  *     period -> Integer or Array of Integers
1383  *
1384  *  Get the periodicity. Only available for dihedral and improper parameters.
1385  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1386  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1387  */
1388 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1389         UnionPar *up;
1390         Int tp, i, n;
1391         VALUE vals[3];
1392         up = s_UnionParFromValue(self, &tp, 0);
1393         if (tp == kDihedralParType || tp == kImproperParType) {
1394                 if (up->torsion.mult == 1)
1395                         return INT2NUM(up->torsion.period[0]);
1396                 n = up->torsion.mult;
1397                 if (n > 3)
1398                         n = 3;
1399                 for (i = 0; i < n; i++)
1400                         vals[i] = INT2NUM(up->torsion.period[i]);
1401                 return rb_ary_new4(n, vals);
1402         } else rb_raise(rb_eMolbyError, "invalid member period");
1403 }
1404
1405 /*
1406  *  call-seq:
1407  *     phi0 -> Float or Array of Floats
1408  *
1409  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1410  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1411  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1412  */
1413 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1414         UnionPar *up;
1415         Int tp, i, n;
1416         VALUE vals[3];
1417         up = s_UnionParFromValue(self, &tp, 0);
1418         if (tp == kDihedralParType || tp == kImproperParType) {
1419                 if (up->torsion.mult == 1)
1420                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1421                 n = up->torsion.mult;
1422                 if (n > 3)
1423                         n = 3;
1424                 for (i = 0; i < n; i++)
1425                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1426                 return rb_ary_new4(n, vals);
1427         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1428 }
1429
1430 /*
1431  *  call-seq:
1432  *     A -> Float
1433  *
1434  *  Get the "A" value for the van der Waals parameter.
1435  */
1436 /*
1437  static VALUE s_ParameterRef_GetA(VALUE self) {
1438         UnionPar *up;
1439         Int tp;
1440         up = s_UnionParFromValue(self, &tp, 0);
1441         if (tp == kVdwParType)
1442                 return rb_float_new(up->vdw.A);
1443         else if (tp == kVdwPairParType)
1444                 return rb_float_new(up->vdwp.A);
1445         else rb_raise(rb_eMolbyError, "invalid member A");
1446 }
1447 */
1448
1449 /*
1450  *  call-seq:
1451  *     B -> Float
1452  *
1453  *  Get the "B" value for the van der Waals parameter.
1454  */
1455 /*
1456 static VALUE s_ParameterRef_GetB(VALUE self) {
1457         UnionPar *up;
1458         Int tp;
1459         up = s_UnionParFromValue(self, &tp, 0);
1460         if (tp == kVdwParType)
1461                 return rb_float_new(up->vdw.B);
1462         else if (tp == kVdwPairParType)
1463                 return rb_float_new(up->vdwp.B);
1464         else rb_raise(rb_eMolbyError, "invalid member B");
1465 }
1466 */
1467
1468 /*
1469  *  call-seq:
1470  *     r_eq -> Float
1471  *
1472  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1473  */
1474 static VALUE s_ParameterRef_GetReq(VALUE self) {
1475         UnionPar *up;
1476         Int tp;
1477 /*      Double a, b, r; */
1478         Double r;
1479         up = s_UnionParFromValue(self, &tp, 0);
1480         if (tp == kVdwParType) {
1481         /*      a = up->vdw.A;
1482                 b = up->vdw.B;  */
1483                 r = up->vdw.r_eq;
1484         } else if (tp == kVdwPairParType) {
1485         /*      a = up->vdwp.A;
1486                 b = up->vdwp.B;  */
1487                 r = up->vdwp.r_eq;
1488         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1489 /*      if (a == 0.0 || b == 0.0) */
1490         return rb_float_new(r);
1491 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1492 }
1493
1494 /*
1495  *  call-seq:
1496  *     eps -> Float
1497  *
1498  *  Get the minimum energy for the van der Waals parameter.
1499  */
1500 static VALUE s_ParameterRef_GetEps(VALUE self) {
1501         UnionPar *up;
1502         Int tp;
1503 /*      Double a, b; */
1504         Double eps;
1505         up = s_UnionParFromValue(self, &tp, 0);
1506         if (tp == kVdwParType) {
1507         /*      a = up->vdw.A;
1508                 b = up->vdw.B;  */
1509                 eps = up->vdw.eps;
1510         } else if (tp == kVdwPairParType) {
1511         /*      a = up->vdwp.A;
1512                 b = up->vdwp.B; */
1513                 eps = up->vdwp.eps;
1514         } else rb_raise(rb_eMolbyError, "invalid member eps");
1515 /*      if (a == 0.0 || b == 0.0)  */
1516                 return rb_float_new(eps * INTERNAL2KCAL);
1517 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1518 }
1519
1520 /*
1521  *  call-seq:
1522  *     A14 -> Float
1523  *
1524  *  Get the "A" value for the 1-4 van der Waals parameter.
1525  */
1526 /*
1527 static VALUE s_ParameterRef_GetA14(VALUE self) {
1528         UnionPar *up;
1529         Int tp;
1530         up = s_UnionParFromValue(self, &tp, 0);
1531         if (tp == kVdwParType)
1532                 return rb_float_new(up->vdw.A14);
1533         else if (tp == kVdwPairParType)
1534                 return rb_float_new(up->vdwp.A14);
1535         else rb_raise(rb_eMolbyError, "invalid member A14");
1536 }
1537 */
1538
1539 /*
1540  *  call-seq:
1541  *     B14 -> Float
1542  *
1543  *  Get the "B" value for the 1-4 van der Waals parameter.
1544  */
1545 /*
1546 static VALUE s_ParameterRef_GetB14(VALUE self) {
1547         UnionPar *up;
1548         Int tp;
1549         up = s_UnionParFromValue(self, &tp, 0);
1550         if (tp == kVdwParType)
1551                 return rb_float_new(up->vdw.B14);
1552         else if (tp == kVdwPairParType)
1553                 return rb_float_new(up->vdwp.B14);
1554         else rb_raise(rb_eMolbyError, "invalid member B14");
1555 }
1556 */
1557
1558 /*
1559  *  call-seq:
1560  *     r_eq14 -> Float
1561  *
1562  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1563  */
1564 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1565         UnionPar *up;
1566         Int tp;
1567 /*      Double a, b, r; */
1568         Double r;
1569         up = s_UnionParFromValue(self, &tp, 0);
1570         if (tp == kVdwParType) {
1571         /*      a = up->vdw.A14;
1572                 b = up->vdw.B14; */
1573                 r = up->vdw.r_eq14;
1574         } else if (tp == kVdwPairParType) {
1575         /*      a = up->vdwp.A14;
1576                 b = up->vdwp.B14;  */
1577                 r = up->vdwp.r_eq14;
1578         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1579 /*      if (a == 0.0 || b == 0.0)  */
1580         return rb_float_new(r);
1581 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1582 }
1583
1584 /*
1585  *  call-seq:
1586  *     eps14 -> Float
1587  *
1588  *  Get the minimum energy for the 1-4 van der Waals parameter.
1589  */
1590 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1591         UnionPar *up;
1592         Int tp;
1593 /*      Double a, b;  */
1594         Double eps;
1595         up = s_UnionParFromValue(self, &tp, 0);
1596         if (tp == kVdwParType) {
1597         /*      a = up->vdw.A14;
1598                 b = up->vdw.B14;  */
1599                 eps = up->vdw.eps14;
1600         } else if (tp == kVdwPairParType) {
1601         /*      a = up->vdwp.A14;
1602                 b = up->vdwp.B14; */
1603                 eps = up->vdwp.eps14;
1604         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1605 /*      if (a == 0.0 || b == 0.0) */
1606         return rb_float_new(eps * INTERNAL2KCAL);
1607 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1608 }
1609
1610 /*
1611  *  call-seq:
1612  *     cutoff -> Float
1613  *
1614  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1615  */
1616 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1617         UnionPar *up;
1618         Int tp;
1619         up = s_UnionParFromValue(self, &tp, 0);
1620         if (tp == kVdwCutoffParType)
1621                 return rb_float_new(up->vdwcutoff.cutoff);
1622         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1623 }
1624
1625 /*
1626  *  call-seq:
1627  *     radius -> Float
1628  *
1629  *  Get the atomic (covalent) radius for the element parameter.
1630  */
1631 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1632         UnionPar *up;
1633         Int tp;
1634         up = s_UnionParFromValue(self, &tp, 0);
1635         if (tp == kElementParType)
1636                 return rb_float_new(up->atom.radius);
1637         else rb_raise(rb_eMolbyError, "invalid member radius");
1638 }
1639
1640 /*
1641  *  call-seq:
1642  *     vdw_radius -> Float
1643  *
1644  *  Get the van der Waals radius for the element parameter. (0 if not given)
1645  */
1646 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1647         UnionPar *up;
1648         Int tp;
1649         up = s_UnionParFromValue(self, &tp, 0);
1650         if (tp == kElementParType)
1651                 return rb_float_new(up->atom.vdw_radius);
1652         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1653 }
1654
1655 /*
1656  *  call-seq:
1657  *     color -> [Float, Float, Float]
1658  *
1659  *  Get the rgb color for the element parameter.
1660  */
1661 static VALUE s_ParameterRef_GetColor(VALUE self) {
1662         UnionPar *up;
1663         Int tp;
1664         up = s_UnionParFromValue(self, &tp, 0);
1665         if (tp == kElementParType)
1666                 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));
1667         else rb_raise(rb_eMolbyError, "invalid member color");
1668 }
1669
1670 /*
1671  *  call-seq:
1672  *     atomic_number -> Integer
1673  *
1674  *  Get the atomic number for the vdw or element parameter.
1675  */
1676 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1677         UnionPar *up;
1678         Int tp;
1679         up = s_UnionParFromValue(self, &tp, 0);
1680         if (tp == kElementParType)
1681                 return INT2NUM(up->atom.number);
1682         else if (tp == kVdwParType)
1683                 return INT2NUM(up->vdw.atomicNumber);
1684         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1685 }
1686
1687 /*
1688  *  call-seq:
1689  *     name -> String
1690  *
1691  *  Get the name for the element parameter.
1692  */
1693 static VALUE s_ParameterRef_GetName(VALUE self) {
1694         UnionPar *up;
1695         Int tp;
1696         up = s_UnionParFromValue(self, &tp, 0);
1697         if (tp == kElementParType) {
1698                 char name[5];
1699                 strncpy(name, up->atom.name, 4);
1700                 name[4] = 0;
1701                 return Ruby_NewEncodedStringValue2(name);
1702         } else rb_raise(rb_eMolbyError, "invalid member name");
1703 }
1704
1705 /*
1706  *  call-seq:
1707  *     weight -> Float
1708  *
1709  *  Get the atomic weight for the element parameter.
1710  */
1711 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1712         UnionPar *up;
1713         Int tp;
1714         up = s_UnionParFromValue(self, &tp, 0);
1715         if (tp == kElementParType)
1716                 return rb_float_new(up->atom.weight);
1717         else if (tp == kVdwParType)
1718                 return rb_float_new(up->vdw.weight);
1719         else rb_raise(rb_eMolbyError, "invalid member weight");
1720 }
1721
1722 /*
1723  *  call-seq:
1724  *     fullname -> String
1725  *
1726  *  Get the full name for the element parameter.
1727  */
1728 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1729         UnionPar *up;
1730         Int tp;
1731         up = s_UnionParFromValue(self, &tp, 0);
1732         if (tp == kElementParType) {
1733                 char fullname[16];
1734                 strncpy(fullname, up->atom.fullname, 15);
1735                 fullname[15] = 0;
1736                 return Ruby_NewEncodedStringValue2(fullname);
1737         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1738 }
1739
1740 /*
1741  *  call-seq:
1742  *     comment -> String
1743  *
1744  *  Get the comment for the parameter.
1745  */
1746 static VALUE s_ParameterRef_GetComment(VALUE self) {
1747         UnionPar *up;
1748         Int tp, com;
1749         up = s_UnionParFromValue(self, &tp, 0);
1750         com = up->bond.com;
1751         if (com == 0)
1752                 return Qnil;
1753         else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1754 }
1755
1756 /*
1757  *  call-seq:
1758  *     source -> String
1759  *
1760  *  Get the source string for the parameter. Returns false for undefined parameter,
1761  *  and nil for "local" parameter that is specific for the molecule.
1762  */
1763 static VALUE s_ParameterRef_GetSource(VALUE self) {
1764         UnionPar *up;
1765         Int tp, src;
1766         up = s_UnionParFromValue(self, &tp, 0);
1767         src = up->bond.src;
1768         if (src < 0)
1769                 return Qfalse;  /* undefined */
1770         else if (src == 0)
1771                 return Qnil;  /*  local  */
1772         else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1773 }
1774
1775 static void
1776 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1777 {
1778         VALUE *valp;
1779         int i;
1780         if (n == 1)
1781                 valp = &val;
1782         else {
1783                 if (rb_obj_is_kind_of(val, rb_cString)) {
1784                         char *s = StringValuePtr(val);
1785                         char *p;
1786                         for (i = 0; i < n; i++) {
1787                                 char buf[40];
1788                                 int len;
1789                                 /*  Skip leading separaters  */
1790                                 while (*s == '-' || *s == ' ' || *s == '\t')
1791                                         s++;
1792                                 for (p = s; *p != 0; p++) {
1793                                         if (*p == '-' || *p == ' ' || *p == '\t')
1794                                                 break;
1795                                 }
1796                                 len = p - s;
1797                                 if (len >= sizeof(buf))
1798                                         len = sizeof(buf) - 1;
1799                                 strncpy(buf, s, len);
1800                                 buf[len] = 0;
1801                                 /*  Skip trailing blanks  */
1802                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1803                                         buf[len] = 0;
1804                                 if (buf[0] == 0)
1805                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1806                                 if (buf[0] >= '0' && buf[0] <= '9')
1807                                         types[i] = atoi(buf);
1808                                 else
1809                                         types[i] = AtomTypeEncodeToUInt(buf);
1810                                 if (p == NULL || *p == 0) {
1811                                         i++;
1812                                         break;
1813                                 } else s = p + 1;
1814                         }
1815                         if (i < n)
1816                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1817                         return;
1818                 }
1819                 val = rb_ary_to_ary(val);
1820                 if (RARRAY_LEN(val) != n)
1821                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1822                 valp = RARRAY_PTR(val);
1823         }
1824         for (i = 0; i < n; i++) {
1825                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1826                         types[i] = NUM2INT(rb_Integer(valp[i]));
1827                 else {
1828                         VALUE sval = valp[i];
1829                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1830                 }
1831         }
1832 }
1833
1834 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1835         UnionPar *up;
1836         VALUE oldval;
1837         Int oldsrc, tp;
1838         UInt types[4];
1839         up = s_UnionParFromValue(self, &tp, 1);
1840         oldval = s_ParameterRef_GetAtomTypes(self);
1841         oldsrc = up->bond.src;
1842         switch (tp) {
1843                 case kBondParType:
1844                         s_ScanAtomTypes(val, 2, types);
1845                         up->bond.type1 = types[0];
1846                         up->bond.type2 = types[1];
1847                         break;
1848                 case kAngleParType:
1849                         s_ScanAtomTypes(val, 3, types);
1850                         up->angle.type1 = types[0];
1851                         up->angle.type2 = types[1];
1852                         up->angle.type3 = types[2];
1853                         break;
1854                 case kDihedralParType:
1855                 case kImproperParType:
1856                         s_ScanAtomTypes(val, 4, types);
1857                         up->torsion.type1 = types[0];
1858                         up->torsion.type2 = types[1];
1859                         up->torsion.type3 = types[2];
1860                         up->torsion.type4 = types[3];
1861                         break;
1862                 case kVdwParType:
1863                         s_ScanAtomTypes(val, 1, types);
1864                         up->vdw.type1 = types[0];
1865                         break;
1866                 case kVdwPairParType:
1867                         s_ScanAtomTypes(val, 2, types);
1868                         up->vdwp.type1 = types[0];
1869                         up->vdwp.type2 = types[1];
1870                         break;
1871                 case kVdwCutoffParType:
1872                         s_ScanAtomTypes(val, 2, types);
1873                         up->vdwcutoff.type1 = types[0];
1874                         up->vdwcutoff.type2 = types[1];
1875                         break;
1876                 default:
1877                         return Qnil;
1878         }
1879         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1880         return val;
1881 }
1882
1883 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1884         UnionPar *up;
1885         Int tp, i, n, oldsrc;
1886         VALUE *valp, oldval;
1887         up = s_UnionParFromValue(self, &tp, 1);
1888         oldval = s_ParameterRef_GetK(self);
1889         oldsrc = up->bond.src;
1890         switch (tp) {
1891                 case kBondParType:
1892                         val = rb_Float(val);
1893                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1894                         break;
1895                 case kAngleParType:
1896                         val = rb_Float(val);
1897                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1898                         break;
1899                 case kDihedralParType:
1900                 case kImproperParType:
1901                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1902                                 up->torsion.mult = 1;
1903                                 val = rb_Float(val);
1904                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1905                                 break;
1906                         }
1907                         n = up->torsion.mult;
1908                         if (n > 3)
1909                                 n = 3;
1910                         val = rb_ary_to_ary(val);
1911                         if (RARRAY_LEN(val) != n)
1912                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1913                         valp = RARRAY_PTR(val);
1914                         for (i = 0; i < n; i++) {
1915                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1916                         }
1917                         break;
1918                 default:
1919                         rb_raise(rb_eMolbyError, "invalid member k");
1920         }
1921         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1922         return val;
1923 }
1924
1925 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1926         UnionPar *up;
1927         Int tp, oldsrc;
1928         VALUE oldval;
1929         up = s_UnionParFromValue(self, &tp, 1);
1930         oldval = s_ParameterRef_GetR0(self);
1931         oldsrc = up->bond.src;
1932         if (tp == kBondParType) {
1933                 val = rb_Float(val);
1934                 up->bond.r0 = NUM2DBL(val);
1935         } else rb_raise(rb_eMolbyError, "invalid member r0");
1936         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1937         return val;
1938 }
1939
1940 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1941         UnionPar *up;
1942         Int tp, oldsrc;
1943         VALUE oldval;
1944         up = s_UnionParFromValue(self, &tp, 1);
1945         oldval = s_ParameterRef_GetA0(self);
1946         oldsrc = up->bond.src;
1947         if (tp == kAngleParType) {
1948                 val = rb_Float(val);
1949                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1950         } else rb_raise(rb_eMolbyError, "invalid member a0");
1951         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1952         return val;
1953 }
1954
1955 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1956         UnionPar *up;
1957         Int tp, oldsrc;
1958         VALUE oldval;
1959         up = s_UnionParFromValue(self, &tp, 1);
1960         oldval = s_ParameterRef_GetMult(self);
1961         oldsrc = up->bond.src;
1962         if (tp == kDihedralParType || tp == kImproperParType) {
1963                 int i;
1964                 val = rb_Integer(val);
1965                 i = NUM2INT(val);
1966                 if (i < 0 || i > 3)
1967                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1968                 up->torsion.mult = i;
1969         } else rb_raise(rb_eMolbyError, "invalid member mult");
1970         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1971         return val;
1972 }
1973
1974 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1975         UnionPar *up;
1976         Int tp, i, n, oldsrc;
1977         VALUE *valp, oldval;
1978         up = s_UnionParFromValue(self, &tp, 1);
1979         oldval = s_ParameterRef_GetPeriod(self);
1980         oldsrc = up->bond.src;
1981         if (tp == kDihedralParType || tp == kImproperParType) {
1982                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1983                         up->torsion.mult = 1;
1984                         val = rb_Integer(val);
1985                         up->torsion.period[0] = NUM2INT(val);
1986                 } else {
1987                         n = up->torsion.mult;
1988                         if (n > 3)
1989                                 n = 3;
1990                         val = rb_ary_to_ary(val);
1991                         if (RARRAY_LEN(val) != n)
1992                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1993                         valp = RARRAY_PTR(val);
1994                         for (i = 0; i < n; i++) {
1995                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1996                         }
1997                 }
1998         } else rb_raise(rb_eMolbyError, "invalid member period");
1999         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2000         return val;
2001 }
2002
2003 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2004         UnionPar *up;
2005         Int tp, i, n, oldsrc;
2006         VALUE *valp, oldval;
2007         up = s_UnionParFromValue(self, &tp, 1);
2008         oldval = s_ParameterRef_GetPhi0(self);
2009         oldsrc = up->bond.src;
2010         if (tp == kDihedralParType || tp == kImproperParType) {
2011                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2012                         up->torsion.mult = 1;
2013                         val = rb_Float(val);
2014                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2015                 } else {
2016                         n = up->torsion.mult;
2017                         if (n > 3)
2018                                 n = 3;
2019                         val = rb_ary_to_ary(val);
2020                         if (RARRAY_LEN(val) != n)
2021                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2022                         valp = RARRAY_PTR(val);
2023                         for (i = 0; i < n; i++)
2024                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2025                 }
2026         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2027         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2028         return val;
2029 }
2030
2031 /*
2032 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2033         UnionPar *up;
2034         Int tp, oldsrc;
2035         double d;
2036         VALUE oldval;
2037         up = s_UnionParFromValue(self, &tp, 1);
2038         oldval = s_ParameterRef_GetA(self);
2039         oldsrc = up->bond.src;
2040         val = rb_Float(val);
2041         d = NUM2DBL(val);
2042         if (tp == kVdwParType)
2043                 up->vdw.A = d;
2044         else if (tp == kVdwPairParType)
2045                 up->vdwp.A = d;
2046         else rb_raise(rb_eMolbyError, "invalid member A");
2047         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2048         return val;
2049 }
2050
2051 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2052         UnionPar *up;
2053         Int tp, oldsrc;
2054         double d;
2055         VALUE oldval;
2056         up = s_UnionParFromValue(self, &tp, 1);
2057         oldval = s_ParameterRef_GetB(self);
2058         oldsrc = up->bond.src;
2059         val = rb_Float(val);
2060         d = NUM2DBL(val);
2061         if (tp == kVdwParType)
2062                 up->vdw.B = d;
2063         else if (tp == kVdwPairParType)
2064                 up->vdwp.B = d;
2065         else rb_raise(rb_eMolbyError, "invalid member B");
2066         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2067         return val;
2068 }
2069 */
2070
2071 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2072         UnionPar *up;
2073         Int tp, oldsrc;
2074         Double r;
2075         VALUE oldval;
2076         up = s_UnionParFromValue(self, &tp, 1);
2077         oldval = s_ParameterRef_GetReq(self);
2078         oldsrc = up->bond.src;
2079         val = rb_Float(val);
2080         r = NUM2DBL(val);
2081         if (tp == kVdwParType) {
2082                 up->vdw.r_eq = r;
2083                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2084                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2085         } else if (tp == kVdwPairParType) {
2086                 up->vdwp.r_eq = r;
2087                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2088                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2089         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2090         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2091         return val;
2092 }
2093
2094 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2095         UnionPar *up;
2096         Int tp, oldsrc;
2097         Double e;
2098         VALUE oldval;
2099         up = s_UnionParFromValue(self, &tp, 1);
2100         oldval = s_ParameterRef_GetEps(self);
2101         oldsrc = up->bond.src;
2102         val = rb_Float(val);
2103         e = NUM2DBL(val) * KCAL2INTERNAL;
2104         if (tp == kVdwParType) {
2105                 up->vdw.eps = e;
2106                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2107                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2108         } else if (tp == kVdwPairParType) {
2109                 up->vdwp.eps = e;
2110                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2111                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2112         } else rb_raise(rb_eMolbyError, "invalid member eps");
2113         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2114         return val;
2115 }
2116
2117 /*
2118 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2119         UnionPar *up;
2120         Int tp, oldsrc;
2121         double d;
2122         VALUE oldval;
2123         up = s_UnionParFromValue(self, &tp, 1);
2124         oldval = s_ParameterRef_GetA14(self);
2125         oldsrc = up->bond.src;
2126         val = rb_Float(val);
2127         d = NUM2DBL(val);
2128         if (tp == kVdwParType)
2129                 up->vdw.A14 = d;
2130         else if (tp == kVdwPairParType)
2131                 up->vdwp.A14 = d;
2132         else rb_raise(rb_eMolbyError, "invalid member A14");
2133         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2134         return val;
2135 }
2136
2137 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2138         UnionPar *up;
2139         Int tp, oldsrc;
2140         double d;
2141         VALUE oldval;
2142         up = s_UnionParFromValue(self, &tp, 1);
2143         oldval = s_ParameterRef_GetB14(self);
2144         oldsrc = up->bond.src;
2145         val = rb_Float(val);
2146         d = NUM2DBL(val);
2147         if (tp == kVdwParType)
2148                 up->vdw.B14 = d;
2149         else if (tp == kVdwPairParType)
2150                 up->vdwp.B14 = d;
2151         else rb_raise(rb_eMolbyError, "invalid member B14");
2152         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2153         return val;
2154 }
2155 */
2156
2157 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2158         UnionPar *up;
2159         Int tp, oldsrc;
2160         Double r;
2161         VALUE oldval;
2162         up = s_UnionParFromValue(self, &tp, 1);
2163         oldval = s_ParameterRef_GetReq14(self);
2164         oldsrc = up->bond.src;
2165         val = rb_Float(val);
2166         r = NUM2DBL(val);
2167         if (tp == kVdwParType) {
2168                 up->vdw.r_eq14 = r;
2169                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2170                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2171         } else if (tp == kVdwPairParType) {
2172                 up->vdwp.r_eq14 = r;
2173                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2174                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2175         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2176         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2177         return val;
2178 }
2179
2180 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2181         UnionPar *up;
2182         Int tp, oldsrc;
2183         Double e;
2184         VALUE oldval;
2185         up = s_UnionParFromValue(self, &tp, 1);
2186         oldval = s_ParameterRef_GetEps14(self);
2187         oldsrc = up->bond.src;
2188         val = rb_Float(val);
2189         e = NUM2DBL(val) * KCAL2INTERNAL;
2190         if (tp == kVdwParType) {
2191                 up->vdw.eps14 = e;
2192                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2193                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2194         } else if (tp == kVdwPairParType) {
2195                 up->vdwp.eps14 = e;
2196                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2197                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2198         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2199         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2200         return val;
2201 }
2202
2203 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2204         UnionPar *up;
2205         Int tp, oldsrc;
2206         VALUE oldval;
2207         oldval = s_ParameterRef_GetCutoff(self);
2208         oldsrc = up->bond.src;
2209         up = s_UnionParFromValue(self, &tp, 1);
2210         val = rb_Float(val);
2211         if (tp == kVdwCutoffParType) {
2212                 up->vdwcutoff.cutoff = NUM2DBL(val);
2213         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2214         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2215         return val;
2216 }
2217
2218 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2219         UnionPar *up;
2220         Int tp, oldsrc;
2221         VALUE oldval;
2222         up = s_UnionParFromValue(self, &tp, 1);
2223         oldval = s_ParameterRef_GetRadius(self);
2224         oldsrc = up->bond.src;
2225         val = rb_Float(val);
2226         if (tp == kElementParType) {
2227                 up->atom.radius = NUM2DBL(val);
2228         } else rb_raise(rb_eMolbyError, "invalid member radius");
2229         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2230         return val;
2231 }
2232
2233 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2234         UnionPar *up;
2235         Int tp, oldsrc;
2236         VALUE oldval;
2237         up = s_UnionParFromValue(self, &tp, 1);
2238         oldval = s_ParameterRef_GetVdwRadius(self);
2239         oldsrc = up->bond.src;
2240         val = rb_Float(val);
2241         if (tp == kElementParType) {
2242                 up->atom.vdw_radius = NUM2DBL(val);
2243         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2244         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2245         return val;
2246 }
2247
2248 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2249         UnionPar *up;
2250         Int tp, oldsrc;
2251         VALUE *valp, oldval;
2252         up = s_UnionParFromValue(self, &tp, 1);
2253         oldval = s_ParameterRef_GetColor(self);
2254         oldsrc = up->bond.src;
2255         val = rb_ary_to_ary(val);
2256         if (RARRAY_LEN(val) != 3)
2257                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2258         valp = RARRAY_PTR(val);
2259         if (tp == kElementParType) {
2260                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2261                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2262                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2263         } else rb_raise(rb_eMolbyError, "invalid member color");
2264         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2265         return val;
2266 }
2267
2268 static VALUE s_ParameterRef_SetAtomicNumber(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_GetAtomicNumber(self);
2274         oldsrc = up->bond.src;
2275         val = rb_Integer(val);
2276         if (tp == kElementParType)
2277                 up->atom.number = NUM2INT(val);
2278         else if (tp == kVdwParType) {
2279                 up->vdw.atomicNumber = NUM2INT(val);
2280                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2281         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2282         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2283         return val;
2284 }
2285
2286 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2287         UnionPar *up;
2288         Int tp, oldsrc;
2289         VALUE oldval;
2290         up = s_UnionParFromValue(self, &tp, 1);
2291         oldval = s_ParameterRef_GetName(self);
2292         oldsrc = up->bond.src;
2293         if (tp == kElementParType) {
2294                 strncpy(up->atom.name, StringValuePtr(val), 4);
2295         } else rb_raise(rb_eMolbyError, "invalid member name");
2296         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2297         return val;
2298 }
2299
2300 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2301         UnionPar *up;
2302         Int tp, oldsrc;
2303         VALUE oldval;
2304         val = rb_Float(val);
2305         oldval = s_ParameterRef_GetWeight(self);
2306         up = s_UnionParFromValue(self, &tp, 1);
2307         oldsrc = up->bond.src;
2308         if (tp == kElementParType)
2309                 up->atom.weight = NUM2DBL(val);
2310         else if (tp == kVdwParType)
2311                 up->vdw.weight = NUM2DBL(val);
2312         else rb_raise(rb_eMolbyError, "invalid member weight");
2313         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2314         return val;
2315 }
2316
2317 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2318         UnionPar *up;
2319         Int tp, oldsrc;
2320         VALUE oldval;
2321         up = s_UnionParFromValue(self, &tp, 1);
2322         oldval = s_ParameterRef_GetFullName(self);
2323         oldsrc = up->bond.src;
2324         if (tp == kElementParType) {
2325                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2326                 up->atom.fullname[15] = 0;
2327         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2328         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2329         return val;
2330 }
2331
2332 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2333         UnionPar *up;
2334         Int tp, com, oldsrc;
2335         VALUE oldval;
2336         up = s_UnionParFromValue(self, &tp, 1);
2337         oldval = s_ParameterRef_GetComment(self);
2338         oldsrc = up->bond.src;
2339         if (val == Qnil)
2340                 up->bond.com = 0;
2341         else {
2342                 com = ParameterCommentIndex(StringValuePtr(val));
2343                 up->bond.com = com;
2344         }
2345         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2346         return val;     
2347 }
2348
2349 /*  Only false (undefined) and nil (local) can be set  */
2350 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2351         UnionPar *up;
2352         Int tp, oldsrc;
2353         VALUE oldval;
2354         up = s_UnionParFromValue(self, &tp, 1);
2355         if (val != Qfalse && val != Qnil)
2356                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2357         oldval = s_ParameterRef_GetSource(self);
2358         oldsrc = up->bond.src;
2359         if (oldsrc != 0 && oldsrc != -1)
2360                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2361         up->bond.src = (val == Qfalse ? -1 : 0);
2362         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2363         return val;     
2364 }
2365
2366 static struct s_ParameterAttrDef {
2367         char *name;
2368         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2369         ID id;                  /*  Will be set within InitMolby()  */
2370         VALUE (*getter)(VALUE);
2371         VALUE (*setter)(VALUE, VALUE);
2372 } s_ParameterAttrDefTable[] = {
2373         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2374         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2375         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2376         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2377         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2378         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2379         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2380         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2381         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2382         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2383 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2384         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2385         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2386         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2387 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2388         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2389         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2390         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2391         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2392         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2393         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2394         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2395         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2396         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2397         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2398         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2399         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2400         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2401         {NULL} /* Sentinel */
2402 };
2403
2404 static VALUE
2405 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2406 {
2407         int i;
2408         ID kid;
2409         if (TYPE(key) != T_SYMBOL) {
2410                 kid = rb_intern(StringValuePtr(key));
2411                 key = ID2SYM(kid);
2412         } else kid = SYM2ID(key);
2413         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2414                 if (s_ParameterAttrDefTable[i].id == kid) {
2415                         if (value == Qundef)
2416                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2417                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2418                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2419                         else
2420                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2421                 }
2422         }
2423         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2424         return Qnil; /* not reached */
2425 }
2426
2427 static VALUE
2428 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2429 {
2430         return s_ParameterRef_SetAttr(self, key, Qundef);
2431 }
2432
2433 /*
2434  *  call-seq:
2435  *     keys(idx)          -> array of valid parameter attributes
2436  *  
2437  *  Returns an array of valid parameter attributes (as Symbols).
2438  */
2439 static VALUE
2440 s_ParameterRef_Keys(VALUE self)
2441 {
2442         ParameterRef *pref;
2443         Data_Get_Struct(self, ParameterRef, pref);
2444         switch (pref->parType) {
2445                 case kBondParType:
2446                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2447                 case kAngleParType:
2448                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2449                 case kDihedralParType:
2450                 case kImproperParType:
2451                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2452                 case kVdwParType:
2453                         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);
2454                 case kVdwPairParType:
2455                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2456                 case kVdwCutoffParType:
2457                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2458                 case kElementParType:
2459                         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);
2460                 default:
2461                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2462         }
2463         return Qnil;  /*  Not reached  */
2464 }
2465
2466 /*
2467  *  call-seq:
2468  *     to_hash(idx)          -> Hash
2469  *  
2470  *  Returns a hash containing valid parameter names and values
2471  */
2472 static VALUE
2473 s_ParameterRef_ToHash(VALUE self)
2474 {
2475         VALUE keys = s_ParameterRef_Keys(self);
2476         VALUE retval;
2477         int i;
2478         if (keys == Qnil)
2479                 return Qnil;
2480         retval = rb_hash_new();
2481         for (i = 0; i < RARRAY_LEN(keys); i++) {
2482                 VALUE key = RARRAY_PTR(keys)[i];
2483                 VALUE val = s_ParameterRef_GetAttr(self, key);
2484                 rb_hash_aset(retval, key, val);
2485         }
2486         return retval;
2487 }
2488
2489 /*
2490  *  call-seq:
2491  *     parameter.to_s(idx)          -> String
2492  *  
2493  *  Returns a string representation of the given parameter
2494  */
2495 static VALUE
2496 s_ParameterRef_ToString(VALUE self)
2497 {
2498         Int tp, i, n;
2499         char buf[1024], types[4][8];
2500         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2501         switch (tp) {
2502                 case kBondParType:
2503                         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);
2504                         break;
2505                 case kAngleParType:
2506                         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);
2507                         break;
2508                 case kDihedralParType:
2509                 case kImproperParType:
2510                         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]));
2511                         n = strlen(buf);
2512                         for (i = 0; i < up->torsion.mult; i++) {
2513                                 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);
2514                                 n = strlen(buf);
2515                         }
2516                         break;
2517                 case kVdwParType:
2518                         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);
2519                         break;
2520                 case kVdwPairParType:
2521                         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);
2522                         break;
2523                 case kVdwCutoffParType:
2524                         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);
2525                         break;
2526                 case kElementParType:
2527                         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);
2528                         break;
2529         }
2530         return Ruby_NewEncodedStringValue2(buf);
2531 }
2532
2533 /*
2534  *  call-seq:
2535  *     self == parameterRef -> boolean
2536  *  
2537  *  True if the parameters point to the same parameter record.
2538  */
2539 static VALUE
2540 s_ParameterRef_Equal(VALUE self, VALUE val)
2541 {
2542         Int tp1, tp2;
2543         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2544                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2545         } else return Qfalse;
2546 }
2547         
2548 #pragma mark ====== Parameter Class ======
2549
2550 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2551  *  is NULL, then the global parameters are looked for.  */
2552
2553 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2554 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2555 static void
2556 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2557 {
2558         Molecule *mol;
2559         Data_Get_Struct(val, Molecule, mol);
2560         if (mol == NULL)
2561                 rb_raise(rb_eMolbyError, "the molecule is empty");
2562         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2563                 /*  Do self.md_arena.prepare  */
2564                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2565                 if (val2 != Qnil)
2566                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2567         }
2568 }
2569
2570 static VALUE
2571 s_NewParameterValueFromValue(VALUE val)
2572 {
2573         Molecule *mol;
2574         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2575                 Data_Get_Struct(val, Molecule, mol);
2576                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2577                 MoleculeRetain(mol);
2578                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2579         } else {
2580                 mol = NULL;
2581                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2582         }
2583 }
2584
2585 static Molecule *
2586 s_MoleculeFromParameterValue(VALUE val)
2587 {
2588         Molecule *mol;
2589         Data_Get_Struct(val, Molecule, mol);
2590         return mol;
2591 }
2592
2593 static Parameter *
2594 s_ParameterFromParameterValue(VALUE val)
2595 {
2596         Molecule *mol;
2597         Data_Get_Struct(val, Molecule, mol);
2598         if (mol != NULL)
2599                 return mol->par;
2600         return gBuiltinParameters;
2601 }
2602
2603 /*  Forward declarations  */
2604 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2605 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2606
2607 static Molecule *
2608 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2609 {
2610         if (val == rb_cParameter) {
2611                 return NULL;  /*  Parameter class method: builtin parameters  */
2612         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2613                 return s_MoleculeFromParameterValue(val);
2614         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2615                 return s_MoleculeFromParEnumerableValue(val);
2616         } else return NULL;
2617 }
2618
2619 /*
2620  *  call-seq:
2621  *     builtin    -> Parameter
2622  *  
2623  *  Returns a parameter value that points to the global (builtin) parameters.
2624  *  Equivalent to Parameter::Builtin (constant).
2625  */
2626 static VALUE
2627 s_Parameter_Builtin(VALUE self)
2628 {
2629         static ID s_builtin_id = 0;
2630         if (s_builtin_id == 0)
2631                 s_builtin_id = rb_intern("Builtin");
2632         return rb_const_get(rb_cParameter, s_builtin_id);
2633 }
2634
2635 /*
2636  *  call-seq:
2637  *     bond(idx)          -> ParameterRef
2638  *  
2639  *  The index-th bond parameter record is returned.
2640  */
2641 static VALUE
2642 s_Parameter_Bond(VALUE self, VALUE ival)
2643 {
2644         Molecule *mol;
2645         int idx, n;
2646         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2647         idx = NUM2INT(rb_Integer(ival));
2648         if (mol == NULL)
2649                 n = gBuiltinParameters->nbondPars;
2650         else if (mol->par != NULL)
2651                 n = mol->par->nbondPars;
2652         else n = 0;
2653         if (idx < -n || idx >= n)
2654                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2655         if (idx < 0)
2656                 idx += n;
2657         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2658 }
2659
2660 /*
2661  *  call-seq:
2662  *     angle(idx)          -> ParameterRef
2663  *  
2664  *  The index-th angle parameter record is returned.
2665  */
2666 static VALUE
2667 s_Parameter_Angle(VALUE self, VALUE ival)
2668 {
2669         Molecule *mol;
2670         int idx, n;
2671         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2672         idx = NUM2INT(rb_Integer(ival));
2673         if (mol == NULL)
2674                 n = gBuiltinParameters->nanglePars;
2675         else if (mol->par != NULL)
2676                 n = mol->par->nanglePars;
2677         else n = 0;
2678         if (idx < -n || idx >= n)
2679                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2680         if (idx < 0)
2681                 idx += n;
2682         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2683 }
2684
2685 /*
2686  *  call-seq:
2687  *     dihedral(idx)          -> ParameterRef
2688  *  
2689  *  The index-th dihedral parameter record is returned.
2690  */
2691 static VALUE
2692 s_Parameter_Dihedral(VALUE self, VALUE ival)
2693 {
2694         Molecule *mol;
2695         int idx, n;
2696         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2697         idx = NUM2INT(rb_Integer(ival));
2698         if (mol == NULL)
2699                 n = gBuiltinParameters->ndihedralPars;
2700         else if (mol->par != NULL)
2701                 n = mol->par->ndihedralPars;
2702         else n = 0;
2703         if (idx < -n || idx >= n)
2704                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2705         if (idx < 0)
2706                 idx += n;
2707         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2708 }
2709
2710 /*
2711  *  call-seq:
2712  *     improper(idx)          -> ParameterRef
2713  *  
2714  *  The index-th improper parameter record is returned.
2715  */
2716 static VALUE
2717 s_Parameter_Improper(VALUE self, VALUE ival)
2718 {
2719         Molecule *mol;
2720         int idx, n;
2721         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2722         idx = NUM2INT(rb_Integer(ival));
2723         if (mol == NULL)
2724                 n = gBuiltinParameters->nimproperPars;
2725         else if (mol->par != NULL)
2726                 n = mol->par->nimproperPars;
2727         else n = 0;
2728         if (idx < -n || idx >= n)
2729                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2730         if (idx < 0)
2731                 idx += n;
2732         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2733 }
2734
2735 /*
2736  *  call-seq:
2737  *     vdw(idx)          -> ParameterRef
2738  *  
2739  *  The index-th vdw parameter record is returned.
2740  */
2741 static VALUE
2742 s_Parameter_Vdw(VALUE self, VALUE ival)
2743 {
2744         Molecule *mol;
2745         int idx, n;
2746         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2747         idx = NUM2INT(rb_Integer(ival));
2748         if (mol == NULL)
2749                 n = gBuiltinParameters->nvdwPars;
2750         else if (mol->par != NULL)
2751                 n = mol->par->nvdwPars;
2752         else n = 0;
2753         if (idx < -n || idx >= n)
2754                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2755         if (idx < 0)
2756                 idx += n;
2757         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2758 }
2759
2760 /*
2761  *  call-seq:
2762  *     vdw_pair(idx)          -> ParameterRef
2763  *  
2764  *  The index-th vdw pair parameter record is returned.
2765  */
2766 static VALUE
2767 s_Parameter_VdwPair(VALUE self, VALUE ival)
2768 {
2769         Molecule *mol;
2770         int idx, n;
2771         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2772         idx = NUM2INT(rb_Integer(ival));
2773         if (mol == NULL)
2774                 n = gBuiltinParameters->nvdwpPars;
2775         else if (mol->par != NULL)
2776                 n = mol->par->nvdwpPars;
2777         else n = 0;
2778         if (idx < -n || idx >= n)
2779                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2780         if (idx < 0)
2781                 idx += n;
2782         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2783 }
2784
2785 /*
2786  *  call-seq:
2787  *     vdw_cutoff(idx)          -> ParameterRef
2788  *  
2789  *  The index-th vdw cutoff parameter record is returned.
2790  */
2791 static VALUE
2792 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2793 {
2794         Molecule *mol;
2795         int idx, n;
2796         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2797         idx = NUM2INT(rb_Integer(ival));
2798         if (mol == NULL)
2799                 n = gBuiltinParameters->nvdwCutoffPars;
2800         else if (mol->par != NULL)
2801                 n = mol->par->nvdwCutoffPars;
2802         else n = 0;
2803         if (idx < -n || idx >= n)
2804                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2805         if (idx < 0)
2806                 idx += n;
2807         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2808 }
2809
2810 /*
2811  *  call-seq:
2812  *     element(idx)            -> ParameterRef
2813  *     element(t1)             -> ParameterRef
2814  *  
2815  *  In the first form, the index-th element parameter record is returned. In the second
2816  *  form, the element parameter for t1 is looked up (the last index first). t1
2817  *  is the element name string (up to 4 characters).
2818  *  Unlike other Parameter methods, this is used only for the global parameter.
2819  */
2820 static VALUE
2821 s_Parameter_Element(VALUE self, VALUE ival)
2822 {
2823         Int idx1;
2824         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2825                 int n = gCountElementParameters;
2826                 idx1 = NUM2INT(rb_Integer(ival));
2827                 if (idx1 < -n || idx1 >= n)
2828                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2829                 if (idx1 < 0)
2830                         idx1 += n;
2831                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2832         } else {
2833                 ElementPar *ep;
2834                 char name[6];
2835                 int i;
2836                 strncpy(name, StringValuePtr(ival), 4);
2837                 name[4] = 0;
2838                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2839                         if (strncmp(ep->name, name, 4) == 0)
2840                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2841                 }
2842                 return Qnil;
2843         }
2844 }
2845
2846 /*
2847  *  call-seq:
2848  *     nbonds          -> Integer
2849  *  
2850  *  Returns the number of bond parameters.
2851  */
2852 static VALUE
2853 s_Parameter_Nbonds(VALUE self)
2854 {
2855         Int n;
2856         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2857         if (mol == NULL)
2858                 n = gBuiltinParameters->nbondPars;
2859         else if (mol->par != NULL)
2860                 n = mol->par->nbondPars;
2861         else n = 0;
2862         return INT2NUM(n);
2863 }
2864
2865 /*
2866  *  call-seq:
2867  *     nangles          -> Integer
2868  *  
2869  *  Returns the number of angle parameters.
2870  */
2871 static VALUE
2872 s_Parameter_Nangles(VALUE self)
2873 {
2874         Int n;
2875         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2876         if (mol == NULL)
2877                 n = gBuiltinParameters->nanglePars;
2878         else if (mol->par != NULL)
2879                 n = mol->par->nanglePars;
2880         else n = 0;
2881         return INT2NUM(n);
2882 }
2883
2884 /*
2885  *  call-seq:
2886  *     ndihedrals          -> Integer
2887  *  
2888  *  Returns the number of dihedral parameters.
2889  */
2890 static VALUE
2891 s_Parameter_Ndihedrals(VALUE self)
2892 {
2893         Int n;
2894         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2895         if (mol == NULL)
2896                 n = gBuiltinParameters->ndihedralPars;
2897         else if (mol->par != NULL)
2898                 n = mol->par->ndihedralPars;
2899         else n = 0;
2900         return INT2NUM(n);
2901 }
2902
2903 /*
2904  *  call-seq:
2905  *     nimpropers          -> Integer
2906  *  
2907  *  Returns the number of improper parameters.
2908  */
2909 static VALUE
2910 s_Parameter_Nimpropers(VALUE self)
2911 {
2912         Int n;
2913         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2914         if (mol == NULL)
2915                 n = gBuiltinParameters->nimproperPars;
2916         else if (mol->par != NULL)
2917                 n = mol->par->nimproperPars;
2918         else n = 0;
2919         return INT2NUM(n);
2920 }
2921
2922 /*
2923  *  call-seq:
2924  *     nvdws          -> Integer
2925  *  
2926  *  Returns the number of vdw parameters.
2927  */
2928 static VALUE
2929 s_Parameter_Nvdws(VALUE self)
2930 {
2931         Int n;
2932         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2933         if (mol == NULL)
2934                 n = gBuiltinParameters->nvdwPars;
2935         else if (mol->par != NULL)
2936                 n = mol->par->nvdwPars;
2937         else n = 0;
2938         return INT2NUM(n);
2939 }
2940
2941 /*
2942  *  call-seq:
2943  *     nvdw_pairs          -> Integer
2944  *  
2945  *  Returns the number of vdw pair parameters.
2946  */
2947 static VALUE
2948 s_Parameter_NvdwPairs(VALUE self)
2949 {
2950         Int n;
2951         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2952         if (mol == NULL)
2953                 n = gBuiltinParameters->nvdwpPars;
2954         else if (mol->par != NULL)
2955                 n = mol->par->nvdwpPars;
2956         else n = 0;
2957         return INT2NUM(n);
2958 }
2959
2960 /*
2961  *  call-seq:
2962  *     nvdw_cutoffs          -> Integer
2963  *  
2964  *  Returns the number of vdw cutoff parameters.
2965  */
2966 static VALUE
2967 s_Parameter_NvdwCutoffs(VALUE self)
2968 {
2969         Int n;
2970         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2971         if (mol == NULL)
2972                 n = gBuiltinParameters->nvdwCutoffPars;
2973         else if (mol->par != NULL)
2974                 n = mol->par->nvdwCutoffPars;
2975         else n = 0;
2976         return INT2NUM(n);
2977 }
2978
2979 /*
2980  *  call-seq:
2981  *     nelements          -> Integer
2982  *  
2983  *  Returns the number of element parameters.
2984  */
2985 static VALUE
2986 s_Parameter_Nelements(VALUE self)
2987 {
2988         return INT2NUM(gCountElementParameters);
2989 }
2990
2991 /*
2992  *  call-seq:
2993  *     bonds          -> ParEnumerable
2994  *  
2995  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2996  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2997  *  useful when all accessible parameters should be examined by use of 'each' method.
2998  */
2999 static VALUE
3000 s_Parameter_Bonds(VALUE self)
3001 {
3002         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3003         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3004 }
3005
3006 /*
3007  *  call-seq:
3008  *     angles          -> ParEnumerable
3009  *  
3010  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3011  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3012  *  useful when all accessible parameters should be examined by use of 'each' method.
3013  */
3014 static VALUE
3015 s_Parameter_Angles(VALUE self)
3016 {
3017         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3018         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3019 }
3020
3021 /*
3022  *  call-seq:
3023  *     dihedrals          -> ParEnumerable
3024  *  
3025  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3026  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3027  *  useful when all accessible parameters should be examined by use of 'each' method.
3028  */
3029 static VALUE
3030 s_Parameter_Dihedrals(VALUE self)
3031 {
3032         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3033         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3034 }
3035
3036 /*
3037  *  call-seq:
3038  *     impropers          -> ParEnumerable
3039  *  
3040  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3041  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3042  *  useful when all accessible parameters should be examined by use of 'each' method.
3043  */
3044 static VALUE
3045 s_Parameter_Impropers(VALUE self)
3046 {
3047         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3048         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3049 }
3050
3051 /*
3052  *  call-seq:
3053  *     vdws          -> ParEnumerable
3054  *  
3055  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3056  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3057  *  useful when all accessible parameters should be examined by use of 'each' method.
3058  */
3059 static VALUE
3060 s_Parameter_Vdws(VALUE self)
3061 {
3062         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3063         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3064 }
3065
3066 /*
3067  *  call-seq:
3068  *     vdw_pairs          -> ParEnumerable
3069  *  
3070  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3071  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3072  *  useful when all accessible parameters should be examined by use of 'each' method.
3073  */
3074 static VALUE
3075 s_Parameter_VdwPairs(VALUE self)
3076 {
3077         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3078         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3079 }
3080
3081 /*
3082  *  call-seq:
3083  *     vdw_cutoffs          -> ParEnumerable
3084  *  
3085  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3086  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3087  *  useful when all accessible parameters should be examined by use of 'each' method.
3088  */
3089 static VALUE
3090 s_Parameter_VdwCutoffs(VALUE self)
3091 {
3092         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3093         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3094 }
3095
3096 /*
3097  *  call-seq:
3098  *     elements          -> ParEnumerable
3099  *  
3100  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3101  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3102  *  useful when all accessible parameters should be examined by use of 'each' method.
3103  */
3104 static VALUE
3105 s_Parameter_Elements(VALUE self)
3106 {
3107         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3108         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3109 }
3110
3111 static VALUE
3112 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3113 {
3114         VALUE atval, optval;
3115         UInt t[4];
3116         Int ii[4];
3117         int i, n, idx, flags, is_global;
3118
3119         rb_scan_args(argc, argv, "1*", &atval, &optval);
3120         
3121         /*  Get the atom types  */
3122         switch (parType) {
3123                 case kBondParType: n = 2; break;
3124                 case kAngleParType: n = 3; break;
3125                 case kDihedralParType: n = 4; break;
3126                 case kImproperParType: n = 4; break;
3127                 case kVdwParType: n = 1; break;
3128                 case kVdwPairParType: n = 2; break;
3129                 default: return Qnil;
3130         }
3131         s_ScanAtomTypes(atval, n, t);
3132         for (i = 0; i < n; i++) {
3133                 if (t[i] < kAtomTypeMinimum) {
3134                         /*  Explicit atom index  */
3135                         if (mol == NULL)
3136                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3137                         if (t[i] >= mol->natoms)
3138                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3139                         ii[i] = t[i];
3140                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3141                 } else ii[i] = -1;
3142         }
3143         
3144         /*  Analyze options  */
3145         flags = 0;
3146         n = RARRAY_LEN(optval);
3147         for (i = 0; i < n; i++) {
3148                 VALUE oval = RARRAY_PTR(optval)[i];
3149                 if (oval == ID2SYM(rb_intern("global")))
3150                         flags |= kParameterLookupGlobal;
3151                 else if (oval == ID2SYM(rb_intern("local")))
3152                         flags |= kParameterLookupLocal;
3153                 else if (oval == ID2SYM(rb_intern("missing")))
3154                         flags |= kParameterLookupMissing;
3155                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3156                         flags |= kParameterLookupNoWildcard;
3157                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3158                         flags |= kParameterLookupNoBaseAtomType;
3159                 else if (oval == ID2SYM(rb_intern("create")))
3160                         flags |= 256;
3161         }
3162         if (flags == 0)
3163                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3164         
3165         idx = -1;
3166         is_global = 0;
3167         switch (parType) {
3168                 case kBondParType: {
3169                         BondPar *bp;
3170                         if (mol != NULL) {
3171                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3172                                 if (bp != NULL) {
3173                                         idx = bp - mol->par->bondPars;
3174                                         break;
3175                                 }
3176                         }
3177                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3178                         if (bp != NULL) {
3179                                 idx = bp - gBuiltinParameters->bondPars;
3180                                 is_global = 1;
3181                         }
3182                         break;
3183                 }
3184                 case kAngleParType: {
3185                         AnglePar *ap;
3186                         if (mol != NULL) {
3187                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3188                                 if (ap != NULL) {
3189                                         idx = ap - mol->par->anglePars;
3190                                         break;
3191                                 }
3192                         }
3193                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3194                         if (ap != NULL) {
3195                                 idx = ap - gBuiltinParameters->anglePars;
3196                                 is_global = 1;
3197                         }
3198                         break;
3199                 }
3200                 case kDihedralParType: {
3201                         TorsionPar *tp;
3202                         if (mol != NULL) {
3203                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3204                                 if (tp != NULL) {
3205                                         idx = tp - mol->par->dihedralPars;
3206                                         break;
3207                                 }
3208                         }
3209                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3210                         if (tp != NULL) {
3211                                 idx = tp - gBuiltinParameters->dihedralPars;
3212                                 is_global = 1;
3213                         }
3214                         break;
3215                 }
3216                 case kImproperParType: {
3217                         TorsionPar *tp;
3218                         if (mol != NULL) {
3219                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3220                                 if (tp != NULL) {
3221                                         idx = tp - mol->par->improperPars;
3222                                         break;
3223                                 }
3224                         }
3225                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3226                         if (tp != NULL) {
3227                                 idx = tp - gBuiltinParameters->improperPars;
3228                                 is_global = 1;
3229                         }
3230                         break;
3231                 }       
3232                 case kVdwParType: {
3233                         VdwPar *vp;
3234                         if (mol != NULL) {
3235                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3236                                 if (vp != NULL) {
3237                                         idx = vp - mol->par->vdwPars;
3238                                         break;
3239                                 }
3240                         }
3241                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3242                         if (vp != NULL) {
3243                                 idx = vp - gBuiltinParameters->vdwPars;
3244                                 is_global = 1;
3245                         }
3246                         break;
3247                 }       
3248                 case kVdwPairParType: {
3249                         VdwPairPar *vp;
3250                         if (mol != NULL) {
3251                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3252                                 if (vp != NULL) {
3253                                         idx = vp - mol->par->vdwpPars;
3254                                         break;
3255                                 }
3256                         }
3257                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3258                         if (vp != NULL) {
3259                                 idx = vp - gBuiltinParameters->vdwpPars;
3260                                 is_global = 1;
3261                         }
3262                         break;
3263                 }
3264                 default:
3265                         return Qnil;
3266         }
3267         if (idx < 0) {
3268                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3269                         return Qnil;            
3270                 else {
3271                         /*  Insert a new parameter record  */
3272                         UnionPar *up;
3273                         Int count = ParameterGetCountForType(mol->par, parType);
3274                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3275                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3276                         IntGroupRelease(ig);
3277                         is_global = 0;
3278                         idx = count;
3279                         /*  Set atom types  */
3280                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3281                         if (up == NULL)
3282                                 return Qnil;
3283                         switch (parType) {
3284                                 case kBondParType:
3285                                         up->bond.type1 = t[0];
3286                                         up->bond.type2 = t[1];
3287                                         break;
3288                                 case kAngleParType:
3289                                         up->angle.type1 = t[0];
3290                                         up->angle.type2 = t[1];
3291                                         up->angle.type3 = t[2];
3292                                         break;
3293                                 case kDihedralParType:
3294                                 case kImproperParType:
3295                                         up->torsion.type1 = t[0];
3296                                         up->torsion.type2 = t[1];
3297                                         up->torsion.type3 = t[2];
3298                                         up->torsion.type4 = t[3];
3299                                         break;
3300                                 case kVdwParType:
3301                                         up->vdw.type1 = t[0];
3302                                         break;
3303                                 case kVdwPairParType:
3304                                         up->vdwp.type1 = t[0];
3305                                         up->vdwp.type2 = t[1];
3306                                         break;
3307                                 default:
3308                                         return Qnil;
3309                         }
3310                 }
3311         }
3312         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3313 }
3314
3315 /*
3316  *  call-seq:
3317  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3318  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3319  *
3320  *  Find the parameter record that matches the given atom types. The atom types are given
3321  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3322  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3323  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3324  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3325  */
3326 static VALUE
3327 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3328 {
3329         int parType;
3330         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3331         if (argc == 0)
3332                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3333         parType = s_ParTypeFromValue(argv[0]);
3334         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3335 }
3336
3337 /*
3338  *  call-seq:
3339  *     self == parameter -> boolean
3340  *  
3341  *  True if the parameters point to the same parameter table.
3342  */
3343 static VALUE
3344 s_Parameter_Equal(VALUE self, VALUE val)
3345 {
3346         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3347                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3348         } else return Qfalse;
3349 }
3350
3351 #pragma mark ====== ParEnumerable Class ======
3352
3353 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3354  and the parameter type. If the Molecule is NULL, then it refers to the
3355  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3356  the global parameters are always accessible. */
3357
3358 typedef struct ParEnumerable {
3359         Molecule *mol;
3360         Int parType;   /*  Same as parType in ParameterRef  */
3361 } ParEnumerable;
3362
3363 static ParEnumerable *
3364 s_ParEnumerableNew(Molecule *mol, Int parType)
3365 {
3366         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3367         if (pen != NULL) {
3368                 pen->mol = mol;
3369                 if (mol != NULL)
3370                         MoleculeRetain(mol);
3371                 pen->parType = parType;
3372         }
3373         return pen;
3374 }
3375
3376 static void
3377 s_ParEnumerableRelease(ParEnumerable *pen)
3378 {
3379         if (pen != NULL) {
3380                 if (pen->mol != NULL)
3381                         MoleculeRelease(pen->mol);
3382                 free(pen);
3383         }
3384 }
3385
3386 static Molecule *
3387 s_MoleculeFromParEnumerableValue(VALUE val)
3388 {
3389         ParEnumerable *pen;
3390     Data_Get_Struct(val, ParEnumerable, pen);
3391         return pen->mol;
3392 }
3393
3394 static VALUE
3395 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3396 {
3397         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3398         if (pen == NULL)
3399                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3400         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3401 }
3402
3403 /*
3404  *  call-seq:
3405  *     par_type -> String
3406  *
3407  *  Get the parameter type, like "bond", "angle", etc.
3408  */
3409 static VALUE
3410 s_ParEnumerable_ParType(VALUE self) {
3411         ParEnumerable *pen;
3412         Int tp;
3413     Data_Get_Struct(self, ParEnumerable, pen);
3414         tp = pen->parType;
3415         if (tp == kElementParType)
3416                 return Ruby_NewEncodedStringValue2("element");
3417         tp -= kFirstParType;
3418         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3419                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3420         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3421 }
3422
3423 /*
3424  *  call-seq:
3425  *     self[idx]          -> ParameterRef
3426  *  
3427  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3428  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3429  *  parent Parameter object of self.
3430  *
3431  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3432  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3433  */
3434 static VALUE
3435 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3436 {
3437         ParEnumerable *pen;
3438     Data_Get_Struct(self, ParEnumerable, pen);
3439         switch (pen->parType) {
3440                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3441                 case kBondParType:      return s_Parameter_Bond(self, ival);
3442                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3443                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3444                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3445                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3446                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3447                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3448                 case kElementParType:   return s_Parameter_Element(self, ival);
3449                 default:
3450                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3451         }
3452         return Qnil;  /*  Not reached  */
3453 }
3454
3455 /*
3456  *  call-seq:
3457  *     length          -> Integer
3458  *  
3459  *  Returns the number of parameters included in this enumerable.
3460  */
3461 static VALUE
3462 s_ParEnumerable_Length(VALUE self)
3463 {
3464         ParEnumerable *pen;
3465     Data_Get_Struct(self, ParEnumerable, pen);
3466         switch (pen->parType) {
3467                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3468                 case kBondParType:      return s_Parameter_Nbonds(self);
3469                 case kAngleParType:     return s_Parameter_Nangles(self); 
3470                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3471                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3472                 case kVdwParType:       return s_Parameter_Nvdws(self);
3473                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3474                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3475                 case kElementParType:   return s_Parameter_Nelements(self);
3476                 default:
3477                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3478         }
3479         return Qnil;  /*  Not reached  */
3480 }
3481
3482 /*
3483  *  call-seq:
3484  *     each {|pref| ...}
3485  *  
3486  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3487  */
3488 VALUE
3489 s_ParEnumerable_Each(VALUE self)
3490 {
3491         VALUE aval;
3492         ParEnumerable *pen;
3493         ParameterRef *pref;
3494         int i, ofs, n;
3495     Data_Get_Struct(self, ParEnumerable, pen);
3496         if (pen->parType == kElementParType)
3497                 n = gCountElementParameters;
3498         else {
3499                 switch (pen->parType) {
3500                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3501                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3502                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3503                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3504                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3505                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3506                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3507                         default:
3508                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3509                 }
3510                 if (pen->mol == NULL)
3511                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3512                 else if (pen->mol->par != NULL)
3513                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3514                 else return self;
3515         }               
3516         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3517         Data_Get_Struct(aval, ParameterRef, pref);
3518         for (i = 0; i < n; i++) {
3519                 pref->idx = i;
3520                 rb_yield(aval);
3521         }
3522     return self;
3523 }
3524
3525 /*
3526  *  call-seq:
3527  *     reverse_each {|pref| ...}
3528  *  
3529  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3530  */
3531 VALUE
3532 s_ParEnumerable_ReverseEach(VALUE self)
3533 {
3534         VALUE aval;
3535         ParEnumerable *pen;
3536         ParameterRef *pref;
3537         int i, ofs, n;
3538     Data_Get_Struct(self, ParEnumerable, pen);
3539         if (pen->parType == kElementParType)
3540                 n = gCountElementParameters;
3541         else {
3542                 switch (pen->parType) {
3543                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3544                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3545                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3546                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3547                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3548                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3549                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3550                         default:
3551                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3552                 }
3553                 if (pen->mol == NULL)
3554                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3555                 else if (pen->mol->par != NULL)
3556                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3557                 else return self;
3558         }               
3559         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3560         Data_Get_Struct(aval, ParameterRef, pref);
3561         for (i = n - 1; i >= 0; i--) {
3562                 pref->idx = i;
3563                 rb_yield(aval);
3564         }
3565     return self;
3566 }
3567
3568 /*
3569  *  call-seq:
3570  *     insert(idx = nil, pref = nil)       -> ParameterRef
3571  *  
3572  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3573  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3574  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3575  *  parameter is left undefined.
3576  *  Throws an exception if ParEnumerable points to the global parameter.
3577  */
3578 static VALUE
3579 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3580 {
3581         VALUE ival, pval;
3582         ParEnumerable *pen;
3583         int i, n;
3584         IntGroup *ig;
3585         UnionPar u;
3586         MolAction *act;
3587     Data_Get_Struct(self, ParEnumerable, pen);
3588         if (pen->mol == NULL)
3589                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3590         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3591         rb_scan_args(argc, argv, "02", &ival, &pval);
3592         if (ival != Qnil) {
3593                 i = NUM2INT(rb_Integer(ival));
3594                 if (i < 0 || i > n)
3595                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3596                 n = i;
3597         }
3598         if (pval != Qnil) {
3599                 Int type;
3600                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3601                 if (up == NULL || type != pen->parType)
3602                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3603                 ParameterCopyOneWithType(&u, up, pen->parType);
3604                 u.bond.src = 0;
3605         } else {
3606                 memset(&u, 0, sizeof(u));
3607                 u.bond.src = 0;
3608         }
3609         ig = IntGroupNewWithPoints(n, 1, -1);
3610         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3611
3612         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3613         MolActionCallback_registerUndo(pen->mol, act);
3614         MolActionRelease(act);
3615         
3616         IntGroupRelease(ig);
3617         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3618 }
3619
3620 /*
3621  *  call-seq:
3622  *     delete(Integer)
3623  *     delete(IntGroup)
3624  *  
3625  *  Delete the parameter(s) specified by the argument.
3626  */
3627 static VALUE
3628 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3629 {
3630         ParEnumerable *pen;
3631         int i, n;
3632         IntGroup *ig;
3633     Data_Get_Struct(self, ParEnumerable, pen);
3634         if (pen->mol == NULL)
3635                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3636         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3637         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3638                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3639                 i = 1;
3640         } else {
3641                 ig = IntGroupFromValue(ival);
3642                 if ((i = IntGroupGetCount(ig)) == 0) {
3643                         IntGroupRelease(ig);
3644                         return Qnil;
3645                 }
3646         }
3647         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3648                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3649         n = i;
3650
3651         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3652         IntGroupRelease(ig);
3653         return ival;
3654 }
3655
3656 /*
3657  *  call-seq:
3658  *     lookup(atom_types, options, ...) -> ParameterRef
3659  *     lookup(atom_type_string, options, ...) -> ParameterRef
3660  *
3661  *  Find the parameter record that matches the given atom types. The arguments are
3662  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3663  *  specified.
3664  */
3665 static VALUE
3666 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3667 {
3668         ParEnumerable *pen;
3669     Data_Get_Struct(self, ParEnumerable, pen);
3670         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3671 }
3672
3673 /*
3674  *  call-seq:
3675  *     self == parEnumerable -> boolean
3676  *  
3677  *  True if the arguments point to the same parameter table and type.
3678  */
3679 static VALUE
3680 s_ParEnumerable_Equal(VALUE self, VALUE val)
3681 {
3682         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3683                 ParEnumerable *pen1, *pen2;
3684                 Data_Get_Struct(self, ParEnumerable, pen1);
3685                 Data_Get_Struct(val, ParEnumerable, pen2);
3686                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3687         } else return Qfalse;
3688 }
3689
3690 #pragma mark ====== AtomRef Class ======
3691
3692 /*  Forward declaration for register undo  */
3693 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3694
3695 /*  Ruby string "set_atom_attr"  */
3696 static VALUE s_SetAtomAttrString;
3697
3698 static int
3699 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3700 {
3701         AtomRef *aref;
3702         int idx;
3703         Data_Get_Struct(self, AtomRef, aref);
3704         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3705         if (idx < 0 || idx >= aref->mol->natoms)
3706                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3707         if (app != NULL)
3708                 *app = aref->mol->atoms + idx;
3709         if (mpp != NULL)
3710                 *mpp = aref->mol;
3711         return idx;
3712 }
3713
3714 static Atom *
3715 s_AtomFromValue(VALUE self)
3716 {
3717         Atom *ap;
3718         s_AtomIndexFromValue(self, &ap, NULL);
3719         return ap;
3720 }
3721
3722 static Atom *
3723 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3724 {
3725         Atom *ap;
3726         s_AtomIndexFromValue(self, &ap, mpp);
3727         return ap;
3728 }
3729
3730 static void
3731 s_NotifyModificationForAtomRef(VALUE self)
3732 {
3733         AtomRef *aref;
3734         Data_Get_Struct(self, AtomRef, aref);
3735         MoleculeIncrementModifyCount(aref->mol);
3736 }
3737
3738 static void
3739 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3740 {
3741         AtomRef *aref;
3742         Data_Get_Struct(self, AtomRef, aref);
3743         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3744                 /*  Register undo  */
3745                 MolAction *act;
3746                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3747                 MolActionCallback_registerUndo(aref->mol, act);
3748                 MoleculeCallback_notifyModification(aref->mol, 0);
3749                 /*  Request MD rebuilt if necessary  */
3750                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3751                         aref->mol->needsMDRebuild = 1;
3752         }
3753 }
3754
3755 VALUE
3756 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3757 {
3758         AtomRef *aref;
3759         aref = AtomRefNew(mol, idx);
3760         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3761 }
3762
3763 static VALUE
3764 s_AtomRef_GetMolecule(VALUE self)
3765 {
3766         Molecule *mpp;
3767         s_AtomIndexFromValue(self, NULL, &mpp);
3768         return ValueFromMolecule(mpp);
3769 }
3770
3771 static VALUE s_AtomRef_GetIndex(VALUE self) {
3772         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3773 }
3774
3775 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3776         return INT2NUM(s_AtomFromValue(self)->segSeq);
3777 }
3778
3779 static VALUE s_AtomRef_GetSegName(VALUE self) {
3780         char *p = s_AtomFromValue(self)->segName;
3781         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3782 }
3783
3784 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3785         return INT2NUM(s_AtomFromValue(self)->resSeq);
3786 }
3787
3788 static VALUE s_AtomRef_GetResName(VALUE self) {
3789         char *p = s_AtomFromValue(self)->resName;
3790         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3791 }
3792
3793 static VALUE s_AtomRef_GetName(VALUE self) {
3794         char *p = s_AtomFromValue(self)->aname;
3795         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3796 }
3797
3798 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3799         int type = s_AtomFromValue(self)->type;
3800         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3801         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3802 }
3803
3804 static VALUE s_AtomRef_GetCharge(VALUE self) {
3805         return rb_float_new(s_AtomFromValue(self)->charge);
3806 }
3807
3808 static VALUE s_AtomRef_GetWeight(VALUE self) {
3809         return rb_float_new(s_AtomFromValue(self)->weight);
3810 }
3811
3812 static VALUE s_AtomRef_GetElement(VALUE self) {
3813         char *p = s_AtomFromValue(self)->element;
3814         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3815 }
3816
3817 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3818         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3819 }
3820
3821 static VALUE s_AtomRef_GetConnects(VALUE self) {
3822         VALUE retval;
3823         Int i, *cp;
3824         Atom *ap = s_AtomFromValue(self);
3825         retval = rb_ary_new();
3826         cp = AtomConnectData(&ap->connect);
3827         for (i = 0; i < ap->connect.count; i++)
3828                 rb_ary_push(retval, INT2NUM(cp[i]));
3829         return retval;
3830 }
3831
3832 static VALUE s_AtomRef_GetR(VALUE self) {
3833         return ValueFromVector(&(s_AtomFromValue(self)->r));
3834 }
3835
3836 static VALUE s_AtomRef_GetX(VALUE self) {
3837         return rb_float_new(s_AtomFromValue(self)->r.x);
3838 }
3839
3840 static VALUE s_AtomRef_GetY(VALUE self) {
3841         return rb_float_new(s_AtomFromValue(self)->r.y);
3842 }
3843
3844 static VALUE s_AtomRef_GetZ(VALUE self) {
3845         return rb_float_new(s_AtomFromValue(self)->r.z);
3846 }
3847
3848 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3849         Atom *ap;
3850         Molecule *mp;
3851         Vector r1;
3852         s_AtomIndexFromValue(self, &ap, &mp);
3853         r1 = ap->r;
3854         if (mp->cell != NULL)
3855                 TransformVec(&r1, mp->cell->rtr, &r1);
3856         return r1;
3857 }
3858
3859 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3860         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3861         return ValueFromVector(&r1);
3862 }
3863
3864 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3865         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3866 }
3867
3868 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3869         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3870 }
3871
3872 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3873         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3874 }
3875
3876 static VALUE s_AtomRef_GetSigma(VALUE self) {
3877         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3878 }
3879
3880 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3881         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3882 }
3883
3884 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3885         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3886 }
3887
3888 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3889         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3890 }
3891
3892 static VALUE s_AtomRef_GetV(VALUE self) {
3893         return ValueFromVector(&(s_AtomFromValue(self)->v));
3894 }
3895
3896 static VALUE s_AtomRef_GetF(VALUE self) {
3897         Vector v = s_AtomFromValue(self)->f;
3898         VecScaleSelf(v, INTERNAL2KCAL);
3899         return ValueFromVector(&v);
3900 }
3901
3902 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3903         return rb_float_new(s_AtomFromValue(self)->occupancy);
3904 }
3905
3906 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3907         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3908 }
3909
3910 static VALUE s_AtomRef_GetAniso(VALUE self) {
3911         VALUE retval;
3912         int i;
3913         Atom *ap = s_AtomFromValue(self);
3914         if (ap->aniso == NULL)
3915                 return Qnil;
3916         retval = rb_ary_new();
3917         for (i = 0; i < 6; i++)
3918                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3919         if (ap->aniso->has_bsig) {
3920                 rb_ary_push(retval, INT2NUM(0));
3921                 for (i = 0; i < 6; i++)
3922                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3923         }
3924         return retval;
3925 }
3926
3927 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
3928     VALUE retval;
3929     int i;
3930     Atom *ap = s_AtomFromValue(self);
3931     if (ap->aniso == NULL)
3932         return Qnil;
3933     retval = rb_ary_new();
3934     for (i = 0; i < 3; i++)
3935         rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
3936     return retval;
3937 }
3938
3939 static VALUE s_AtomRef_GetSymop(VALUE self) {
3940         VALUE retval;
3941         Atom *ap = s_AtomFromValue(self);
3942         if (!ap->symop.alive)
3943                 return Qnil;
3944         retval = rb_ary_new();
3945         rb_ary_push(retval, INT2NUM(ap->symop.sym));
3946         rb_ary_push(retval, INT2NUM(ap->symop.dx));
3947         rb_ary_push(retval, INT2NUM(ap->symop.dy));
3948         rb_ary_push(retval, INT2NUM(ap->symop.dz));
3949         rb_ary_push(retval, INT2NUM(ap->symbase));
3950         return retval;
3951 }
3952
3953 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3954         return INT2NUM(s_AtomFromValue(self)->intCharge);
3955 }
3956
3957 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3958         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3959 }
3960
3961 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3962         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3963 }
3964
3965 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3966         Molecule *mol;
3967         Atom *ap;
3968         int idx, i;
3969         MDExclusion *exinfo;
3970         Int *exlist;
3971         VALUE retval, aval;
3972         idx = s_AtomIndexFromValue(self, &ap, &mol);
3973         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3974                 VALUE mval = ValueFromMolecule(mol);
3975                 s_RebuildMDParameterIfNecessary(mval, Qnil);
3976         }
3977         if (mol->arena->exinfo == NULL)
3978                 return Qnil;
3979         exinfo = mol->arena->exinfo + idx;
3980         exlist = mol->arena->exlist;
3981         retval = rb_ary_new();
3982         aval = rb_ary_new();
3983         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
3984                 rb_ary_push(aval, INT2NUM(exlist[i]));
3985         rb_ary_push(retval, aval);
3986         aval = rb_ary_new();
3987         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
3988                 rb_ary_push(aval, INT2NUM(exlist[i]));
3989         rb_ary_push(retval, aval);
3990         aval = rb_ary_new();
3991         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
3992                 rb_ary_push(aval, INT2NUM(exlist[i]));
3993         rb_ary_push(retval, aval);
3994         return retval;
3995 }
3996
3997 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3998         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3999 }
4000
4001 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4002         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4003 }
4004
4005 static VALUE s_AtomRef_GetHidden(VALUE self) {
4006         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4007 }
4008
4009 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4010         VALUE retval;
4011         Int i, count, *cp;
4012         Atom *ap = s_AtomFromValue(self);
4013         if (ap->anchor == NULL)
4014                 return Qnil;
4015         count = ap->anchor->connect.count;
4016         retval = rb_ary_new2(count * 2);
4017         cp = AtomConnectData(&ap->anchor->connect);
4018         for (i = 0; i < count; i++) {
4019                 rb_ary_store(retval, i, INT2NUM(cp[i]));
4020                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4021         }
4022         return retval;
4023 }
4024
4025 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4026         char *p = s_AtomFromValue(self)->uff_type;
4027         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4028 }
4029
4030 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4031         rb_raise(rb_eMolbyError, "index cannot be directly set");
4032         return Qnil;
4033 }
4034
4035 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4036         VALUE oval = s_AtomRef_GetSegSeq(self);
4037         val = rb_Integer(val);
4038         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4039         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4040         return val;
4041 }
4042
4043 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4044         char *p = StringValuePtr(val);
4045         VALUE oval = s_AtomRef_GetSegName(self);
4046         strncpy(s_AtomFromValue(self)->segName, p, 4);
4047         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4048         return val;
4049 }
4050
4051 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4052         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4053         return val; /* Not reached */
4054 }
4055
4056 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4057         Atom *ap = s_AtomFromValue(self);
4058         char *p = StringValuePtr(val);
4059         VALUE oval = s_AtomRef_GetName(self);
4060         if (ap->anchor != NULL && p[0] == '_')
4061                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4062         strncpy(ap->aname, p, 4);
4063         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4064         return val;
4065 }
4066
4067 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4068         Molecule *mp;
4069         char *p = StringValuePtr(val);
4070         VALUE oval = s_AtomRef_GetAtomType(self);
4071         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4072         if (type != 0 && type < kAtomTypeMinimum)
4073                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4074         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4075         mp->needsMDRebuild = 1;
4076         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4077         return val;
4078 }
4079
4080 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4081         Molecule *mp;
4082         VALUE oval = s_AtomRef_GetCharge(self);
4083         val = rb_Float(val);
4084         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4085         mp->needsMDRebuild = 1;
4086         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4087         return val;
4088 }
4089
4090 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4091         Molecule *mp;
4092         VALUE oval = s_AtomRef_GetWeight(self);
4093         val = rb_Float(val);
4094         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4095         mp->needsMDRebuild = 1;
4096         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4097         return val;
4098 }
4099
4100 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4101         Double w;
4102         Molecule *mp;
4103         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4104         char *p = StringValuePtr(val);
4105         VALUE oval = s_AtomRef_GetElement(self);
4106         ap->atomicNumber = ElementToInt(p);
4107         ElementToString(ap->atomicNumber, ap->element);
4108         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4109                 ap->weight = w;
4110         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4111         mp->needsMDRebuild = 1;
4112         return val;
4113 }
4114
4115 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4116         Double w;
4117         Molecule *mp;
4118         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4119         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4120         val = rb_Integer(val);
4121         ap->atomicNumber = NUM2INT(val);
4122         ElementToString(ap->atomicNumber, ap->element);
4123         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4124                 ap->weight = w;
4125         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4126         mp->needsMDRebuild = 1;
4127         return val;
4128 }
4129
4130 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4131         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4132         return val; /* Not reached */
4133 }
4134
4135 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4136         Vector v;
4137         Molecule *mp;
4138         VALUE oval = s_AtomRef_GetR(self);
4139         VectorFromValue(val, &v);
4140         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4141         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4142         mp->needsMDCopyCoordinates = 1;
4143         return val;
4144 }
4145
4146 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4147         Double f;
4148         Molecule *mp;
4149         VALUE oval = s_AtomRef_GetX(self);
4150         val = rb_Float(val);
4151         f = NUM2DBL(val);
4152         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4153         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4154         mp->needsMDCopyCoordinates = 1;
4155         return val;
4156 }
4157
4158 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4159         Double f;
4160         Molecule *mp;
4161         VALUE oval = s_AtomRef_GetY(self);
4162         val = rb_Float(val);
4163         f = NUM2DBL(val);
4164         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4165         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4166         mp->needsMDCopyCoordinates = 1;
4167         return val;
4168 }
4169
4170 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4171         Double f;
4172         Molecule *mp;
4173         VALUE oval = s_AtomRef_GetZ(self);
4174         val = rb_Float(val);
4175         f = NUM2DBL(val);
4176         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4177         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4178         mp->needsMDCopyCoordinates = 1;
4179         return val;
4180 }
4181
4182 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4183         Vector v, ov;
4184         Atom *ap;
4185         Molecule *mp;
4186         s_AtomIndexFromValue(self, &ap, &mp);
4187         ov = ap->r;
4188         VectorFromValue(val, &v);
4189         if (mp->cell != NULL)
4190                 TransformVec(&v, mp->cell->tr, &v);
4191         ap->r = v;
4192         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4193         mp->needsMDCopyCoordinates = 1;
4194         return val;
4195 }
4196
4197 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4198         double f;
4199         Vector v, ov;
4200         Atom *ap;
4201         Molecule *mp;
4202         s_AtomIndexFromValue(self, &ap, &mp);
4203         ov = v = ap->r;
4204         val = rb_Float(val);
4205         f = NUM2DBL(val);
4206         if (mp->cell != NULL) {
4207                 TransformVec(&v, mp->cell->rtr, &v);
4208                 v.x = f;
4209                 TransformVec(&v, mp->cell->tr, &v);
4210         } else v.x = f;
4211         ap->r = v;
4212         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4213         mp->needsMDCopyCoordinates = 1;
4214         return val;
4215 }
4216
4217 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4218         double f;
4219         Vector v, ov;
4220         Atom *ap;
4221         Molecule *mp;
4222         s_AtomIndexFromValue(self, &ap, &mp);
4223         ov = v = ap->r;
4224         val = rb_Float(val);
4225         f = NUM2DBL(val);
4226         if (mp->cell != NULL) {
4227                 TransformVec(&v, mp->cell->rtr, &v);
4228                 v.y = f;
4229                 TransformVec(&v, mp->cell->tr, &v);
4230         } else v.y = f;
4231         ap->r = v;
4232         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4233         mp->needsMDCopyCoordinates = 1;
4234         return val;
4235 }
4236
4237 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4238         double f;
4239         Vector v, ov;
4240         Atom *ap;
4241         Molecule *mp;
4242         s_AtomIndexFromValue(self, &ap, &mp);
4243         ov = v = ap->r;
4244         val = rb_Float(val);
4245         f = NUM2DBL(val);
4246         if (mp->cell != NULL) {
4247                 TransformVec(&v, mp->cell->rtr, &v);
4248                 v.z = f;
4249                 TransformVec(&v, mp->cell->tr, &v);
4250         } else v.z = f;
4251         ap->r = v;
4252         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4253         mp->needsMDCopyCoordinates = 1;
4254         return val;
4255 }
4256
4257 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4258         Vector v;
4259         Molecule *mp;
4260         VALUE oval = s_AtomRef_GetSigma(self);
4261         VectorFromValue(val, &v);
4262         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4263         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4264         mp->needsMDCopyCoordinates = 1;
4265         return val;
4266 }
4267
4268 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4269         Double f;
4270         Molecule *mp;
4271         VALUE oval = s_AtomRef_GetSigmaX(self);
4272         val = rb_Float(val);
4273         f = NUM2DBL(val);
4274         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4275         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4276         mp->needsMDCopyCoordinates = 1;
4277         return val;
4278 }
4279
4280 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4281         Double f;
4282         Molecule *mp;
4283         VALUE oval = s_AtomRef_GetSigmaY(self);
4284         val = rb_Float(val);
4285         f = NUM2DBL(val);
4286         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4287         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4288         mp->needsMDCopyCoordinates = 1;
4289         return val;
4290 }
4291
4292 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4293         Double f;
4294         Molecule *mp;
4295         VALUE oval = s_AtomRef_GetSigmaZ(self);
4296         val = rb_Float(val);
4297         f = NUM2DBL(val);
4298         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4299         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4300         mp->needsMDCopyCoordinates = 1;
4301         return val;
4302 }
4303
4304 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4305         Vector v;
4306         Atom *ap;
4307         Molecule *mp;
4308         VALUE oval = s_AtomRef_GetV(self);
4309         VectorFromValue(val, &v);
4310         s_AtomIndexFromValue(self, &ap, &mp);
4311         ap->v = v;
4312         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4313         mp->needsMDCopyCoordinates = 1;
4314         return val;
4315 }
4316
4317 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4318         Vector v;
4319         Molecule *mp;
4320         VALUE oval = s_AtomRef_GetF(self);
4321         VectorFromValue(val, &v);
4322         VecScaleSelf(v, KCAL2INTERNAL);
4323         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4324         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4325         mp->needsMDCopyCoordinates = 1;
4326         return val;
4327 }
4328
4329 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4330         VALUE oval = s_AtomRef_GetOccupancy(self);
4331         Molecule *mp;
4332         val = rb_Float(val);
4333         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4334         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4335         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4336         return val;
4337 }
4338
4339 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4340         VALUE oval = s_AtomRef_GetTempFactor(self);
4341         val = rb_Float(val);
4342         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4343         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4344         return val;
4345 }
4346
4347 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4348         AtomRef *aref;
4349         int i, n, type;
4350         VALUE *valp;
4351         Double f[12];
4352         VALUE oval = s_AtomRef_GetAniso(self);
4353         Data_Get_Struct(self, AtomRef, aref);
4354         val = rb_funcall(val, rb_intern("to_a"), 0);
4355         n = RARRAY_LEN(val);
4356         valp = RARRAY_PTR(val);
4357         for (i = 0; i < 6; i++) {
4358                 if (i < n)
4359                         f[i] = NUM2DBL(rb_Float(valp[i]));
4360                 else f[i] = 0.0;
4361         }
4362         if (n >= 7)
4363                 type = NUM2INT(rb_Integer(valp[6]));
4364         else type = 0;
4365         if (n >= 13) {
4366                 for (i = 0; i < 6; i++)
4367                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4368         } else {
4369                 for (i = 0; i < 6; i++)
4370                         f[i + 6] = 0.0;
4371         }
4372         i = s_AtomIndexFromValue(self, NULL, NULL);
4373         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4374         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4375         return val;
4376 }
4377
4378 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4379     rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4380     return val; /* Not reached */
4381 }
4382
4383 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4384         Molecule *mol;
4385         Atom *ap;
4386         int i, n;
4387         VALUE *valp;
4388         Int ival[5];
4389         VALUE oval = s_AtomRef_GetSymop(self);
4390         i = s_AtomIndexFromValue(self, &ap, &mol);
4391         if (val == Qnil) {
4392                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4393         } else {
4394                 val = rb_funcall(val, rb_intern("to_a"), 0);
4395                 n = RARRAY_LEN(val);
4396                 valp = RARRAY_PTR(val);
4397                 for (i = 0; i < 5; i++) {
4398                         if (i < n) {
4399                                 if (valp[i] == Qnil)
4400                                         ival[i] = -100000;
4401                                 else 
4402                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4403                         } else ival[i] = -100000;
4404                 }
4405         }
4406         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4407                 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));
4408         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4409                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4410         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4411                 return val;  /*  No need to change  */
4412         if (ival[0] != -100000)
4413                 ap->symop.sym = ival[0];
4414         if (ival[1] != -100000)
4415                 ap->symop.dx = ival[1];
4416         if (ival[2] != -100000)
4417                 ap->symop.dy = ival[2];
4418         if (ival[3] != -100000)
4419                 ap->symop.dz = ival[3];
4420         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4421         if (ival[4] != -100000)
4422                 ap->symbase = ival[4];
4423         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4424                 /*  The anisotropic parameters should be recalculated  */
4425                 VALUE oaval = s_AtomRef_GetAniso(self);
4426                 MoleculeSetAnisoBySymop(mol, i);
4427                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4428         }
4429         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4430         return val;
4431 }
4432
4433 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4434         VALUE oval = s_AtomRef_GetIntCharge(self);
4435         val = rb_Integer(val);
4436         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4437         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4438         return val;
4439 }
4440
4441 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4442         Molecule *mp;
4443         VALUE oval = s_AtomRef_GetFixForce(self);
4444         val = rb_Float(val);
4445         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4446         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4447         mp->needsMDRebuild = 1;
4448         return val;
4449 }
4450
4451 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4452         Vector v;
4453         Molecule *mp;
4454         VALUE oval = s_AtomRef_GetFixPos(self);
4455         VectorFromValue(val, &v);
4456         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4457         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4458         mp->needsMDRebuild = 1;
4459         return val;
4460 }
4461
4462 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4463         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4464         return val; /* Not reached */
4465 }
4466
4467 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4468         VALUE oval = s_AtomRef_GetIntCharge(self);
4469         val = rb_Integer(val);
4470         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4471         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4472         return val;
4473 }
4474
4475 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4476         VALUE oval = s_AtomRef_GetIntCharge(self);
4477         val = rb_Integer(val);
4478         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4479         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4480         return val;
4481 }
4482
4483 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4484         Atom *ap = s_AtomFromValue(self);
4485         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4486         if (RTEST(val)) {
4487                 ap->exflags |= kAtomHiddenFlag;
4488         } else {
4489                 ap->exflags &= ~kAtomHiddenFlag;
4490         }
4491         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4492         return val;
4493 }
4494
4495 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4496         Int idx, i, j, k, n, *ip;
4497         Double *dp;
4498         Atom *ap;
4499         Molecule *mol;
4500         VALUE oval, v;
4501         AtomConnect ac;
4502         Int nUndoActions;
4503         MolAction **undoActions;
4504         memset(&ac, 0, sizeof(ac));
4505         idx = s_AtomIndexFromValue(self, &ap, &mol);
4506         oval = s_AtomRef_GetAnchorList(self);
4507         if (val != Qnil) {
4508                 val = rb_ary_to_ary(val);
4509                 n = RARRAY_LEN(val);
4510         } else n = 0;
4511         if (n == 0) {
4512                 if (ap->anchor != NULL) {
4513                         AtomConnectResize(&ap->anchor->connect, 0);
4514                         free(ap->anchor->coeffs);
4515                         free(ap->anchor);
4516                         ap->anchor = NULL;
4517                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4518                 }
4519                 return val;
4520         }
4521         if (n < 2)
4522                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4523         if (ap->aname[0] == '_')
4524                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4525         ip = (Int *)malloc(sizeof(Int) * n);
4526         dp = NULL;
4527         for (i = 0; i < n; i++) {
4528                 v = RARRAY_PTR(val)[i];
4529                 if (rb_obj_is_kind_of(v, rb_cFloat))
4530                         break;
4531                 j = NUM2INT(rb_Integer(v));
4532                 if (j < 0 || j >= mol->natoms)
4533                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4534                 for (k = 0; k < i; k++) {
4535                         if (ip[k] == j)
4536                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4537                 }
4538                 ip[i] = j;
4539         }
4540         if (i < n) {
4541                 if (i < 2)
4542                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4543                 else if (i * 2 != n)
4544                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4545                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4546                 for (i = 0; i < n / 2; i++) {
4547                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4548                         if (dp[i] <= 0.0)
4549                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4550                 }
4551                 n /= 2;
4552         }
4553         nUndoActions = 0;
4554         undoActions = NULL;
4555         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4556         free(dp);
4557         free(ip);
4558         if (i != 0)
4559                 rb_raise(rb_eMolbyError, "invalid argument");
4560         if (nUndoActions > 0) {
4561                 for (i = 0; i < nUndoActions; i++) {
4562                         MolActionCallback_registerUndo(mol, undoActions[i]);
4563                         MolActionRelease(undoActions[i]);
4564                 }
4565                 free(undoActions);
4566         }
4567         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4568         return val;
4569 }
4570
4571 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4572         Atom *ap = s_AtomFromValue(self);
4573         char *p = StringValuePtr(val);
4574         VALUE oval = s_AtomRef_GetUFFType(self);
4575         strncpy(ap->uff_type, p, 5);
4576         ap->uff_type[5] = 0;
4577         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4578         return val;
4579 }
4580
4581 static struct s_AtomAttrDef {
4582         char *name;
4583         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4584         ID id;                  /*  Will be set within InitMolby()  */
4585         VALUE (*getter)(VALUE);
4586         VALUE (*setter)(VALUE, VALUE);
4587 } s_AtomAttrDefTable[] = {
4588         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4589         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4590         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4591         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4592         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4593         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4594         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4595         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4596         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4597         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4598         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4599         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4600         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4601         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4602         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4603     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4604         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4605         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4606         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4607         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4608         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4609         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4610         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4611         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4612         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4613         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4614         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4615         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4616         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4617     {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues,        s_AtomRef_SetAnisoEigenValues},
4618         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4619         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4620         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4621         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4622         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4623         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4624         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4625         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4626         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4627         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4628         {NULL} /* Sentinel */
4629 };
4630
4631 static VALUE
4632 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4633 {
4634         int i;
4635         ID kid;
4636         if (TYPE(key) != T_SYMBOL) {
4637                 kid = rb_intern(StringValuePtr(key));
4638                 key = ID2SYM(kid);
4639         } else kid = SYM2ID(key);
4640         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4641                 if (s_AtomAttrDefTable[i].id == kid) {
4642                         if (value == Qundef)
4643                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4644                         else
4645                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4646                 }
4647         }
4648         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4649         return Qnil; /* not reached */
4650 }
4651
4652 static VALUE
4653 s_AtomRef_GetAttr(VALUE self, VALUE key)
4654 {
4655         return s_AtomRef_SetAttr(self, key, Qundef);
4656 }
4657
4658 /*
4659  *  call-seq:
4660  *     self == atomRef -> boolean
4661  *
4662  *  True if the two references point to the same atom.
4663  */
4664 static VALUE
4665 s_AtomRef_Equal(VALUE self, VALUE val)
4666 {
4667         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4668                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4669         } else return Qfalse;
4670 }
4671
4672 #pragma mark ====== MolEnumerable Class ======
4673
4674 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4675
4676 /*
4677  *  call-seq:
4678  *     self[idx] -> AtomRef or Array of Integers
4679  *  
4680  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4681  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4682  *  value is a String. Otherwise, the return value is an Array of Integers.
4683  */
4684 static VALUE
4685 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4686 {
4687         MolEnumerable *mseq;
4688         Molecule *mol;
4689         int idx1, idx2;
4690     Data_Get_Struct(self, MolEnumerable, mseq);
4691         mol = mseq->mol;
4692         if (mseq->kind == kAtomKind) {
4693                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4694         }
4695         idx1 = NUM2INT(arg1);
4696         switch (mseq->kind) {
4697                 case kBondKind: {
4698                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4699                         if (idx2 < 0 || idx2 >= mol->nbonds)
4700                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4701                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4702                 }
4703                 case kAngleKind: {
4704                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4705                         if (idx2 < 0 || idx2 >= mol->nangles)
4706                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4707                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4708                 }
4709                 case kDihedralKind: {
4710                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4711                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4712                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4713                         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]));
4714                 }
4715                 case kImproperKind: {
4716                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4717                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4718                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4719                         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]));
4720                 }
4721                 case kResidueKind: {
4722                         char *p;
4723                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4724                         if (idx2 < 0 || idx2 >= mol->nresidues)
4725                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4726                         p = mol->residues[idx2];
4727                         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4728                 }
4729         }
4730         return Qnil;
4731 }
4732
4733 /*
4734  *  call-seq:
4735  *     length          -> Integer
4736  *  
4737  *  Returns the number of objects included in this enumerable.
4738  */
4739 static VALUE
4740 s_MolEnumerable_Length(VALUE self)
4741 {
4742         MolEnumerable *mseq;
4743     Data_Get_Struct(self, MolEnumerable, mseq);
4744         switch (mseq->kind) {
4745                 case kAtomKind:
4746                         return INT2NUM(mseq->mol->natoms);
4747                 case kBondKind:
4748                         return INT2NUM(mseq->mol->nbonds);
4749                 case kAngleKind:
4750                         return INT2NUM(mseq->mol->nangles);
4751                 case kDihedralKind:
4752                         return INT2NUM(mseq->mol->ndihedrals);
4753                 case kImproperKind:
4754                         return INT2NUM(mseq->mol->nimpropers);
4755                 case kResidueKind:
4756                         return INT2NUM(mseq->mol->nresidues);
4757         }
4758         return INT2NUM(-1);
4759 }
4760
4761 /*
4762  *  call-seq:
4763  *     each {|obj| ...}
4764  *  
4765  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4766  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4767  *  For the atoms, a same AtomRef object is passed (with different internal information)
4768  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4769  *  for each iteration.
4770  */
4771 VALUE
4772 s_MolEnumerable_Each(VALUE self)
4773 {
4774         MolEnumerable *mseq;
4775         int i;
4776         int len = NUM2INT(s_MolEnumerable_Length(self));
4777     Data_Get_Struct(self, MolEnumerable, mseq);
4778         if (mseq->kind == kAtomKind) {
4779                 /*  The same AtomRef object will be used during the loop  */
4780                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4781                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4782                 for (i = 0; i < len; i++) {
4783                         aref->idx = i;
4784                         rb_yield(arval);
4785                 }
4786     } else {
4787                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4788                 for (i = 0; i < len; i++) {
4789                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4790                 }
4791         }
4792     return self;
4793 }
4794
4795 /*
4796  *  call-seq:
4797  *     self == molEnumerable -> boolean
4798  *
4799  *  True if the two arguments point to the same molecule and enumerable type.
4800  */
4801 static VALUE
4802 s_MolEnumerable_Equal(VALUE self, VALUE val)
4803 {
4804         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4805                 MolEnumerable *mseq1, *mseq2;
4806                 Data_Get_Struct(self, MolEnumerable, mseq1);
4807                 Data_Get_Struct(val, MolEnumerable, mseq2);
4808                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4809         } else return Qfalse;
4810 }
4811
4812
4813 #pragma mark ====== Molecule Class ======
4814
4815 #pragma mark ------ Allocate/Release/Accessor ------
4816
4817 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method.  */
4818 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4819 char *gLoadSaveErrorMessage = NULL;
4820
4821 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4822
4823 Molecule *
4824 MoleculeFromValue(VALUE val)
4825 {
4826         Molecule *mol;
4827         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4828                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4829     Data_Get_Struct(val, Molecule, mol);
4830         return mol;
4831 }
4832
4833 static VALUE sMoleculeRetainArray = Qnil;
4834
4835 /*  The function is called from MoleculeRelease()  */
4836 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4837 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4838 /*  object is always returned for the same Molecule.  */
4839 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4840 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4841 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4842 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4843 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4844
4845 /*  Register/unregister the exmolobj Ruby object  */
4846 void
4847 MoleculeReleaseExternalObj(Molecule *mol)
4848 {
4849         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4850                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4851                 mol->exmolobjProtected = 0;
4852         }
4853 }
4854
4855 void
4856 MoleculeRetainExternalObj(Molecule *mol)
4857 {
4858         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4859                 if (sMoleculeRetainArray == Qnil) {
4860                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4861                         sMoleculeRetainArray = rb_ary_new();
4862                 }
4863                 
4864                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4865                 mol->exmolobjProtected = 1;
4866         }
4867 }
4868
4869 /*  Release hook function for Ruby  */
4870 void
4871 MoleculeReleaseHook(Molecule *mol)
4872 {
4873         if (mol->exmolobj != NULL) {
4874                 /*  No need to remove from sMoleculeRetainArray  */
4875                 mol->exmolobj = NULL;
4876                 mol->exmolobjProtected = 0;
4877         }
4878         MoleculeRelease(mol);
4879 }
4880
4881 VALUE
4882 ValueFromMolecule(Molecule *mol)
4883 {
4884         if (mol == NULL)
4885                 return Qnil;
4886         if (mol->exmolobj != NULL)
4887                 return (VALUE)mol->exmolobj;
4888         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4889         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4890         return (VALUE)mol->exmolobj;
4891 }
4892
4893 /*  Allocator  */
4894 static VALUE
4895 s_Molecule_Alloc(VALUE klass)
4896 {
4897         VALUE val;
4898         Molecule *mol = MoleculeNew();
4899         val = ValueFromMolecule(mol);
4900         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4901         return val;
4902 }
4903
4904 static int
4905 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4906 {
4907         int n;
4908         char *p = "";
4909         if (FIXNUM_P(val)) {
4910                 n = FIX2INT(val);
4911                 if (n >= 0 && n < mol->natoms)
4912                         return n;
4913                 n = -1; /*  No such atom  */
4914                 val = rb_inspect(val);
4915         } else {
4916                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4917         }
4918         if (n >= 0 && n < mol->natoms)
4919                 return n;
4920         p = StringValuePtr(val);
4921         if (n == -1)
4922                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4923         else if (n == -2)
4924                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4925         else
4926                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4927         return 0; /* Not reached */
4928 }
4929
4930 static IntGroup *
4931 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4932 {
4933         IntGroup *ig;
4934     Molecule *mp1;
4935     Data_Get_Struct(self, Molecule, mp1);
4936         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4937         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4938                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4939         Data_Get_Struct(val, IntGroup, ig);
4940     IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
4941         IntGroupRetain(ig);
4942         return ig;
4943 }
4944
4945 /*
4946  *  call-seq:
4947  *     dup          -> Molecule
4948  *
4949  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
4950  *  created object does not affect the old object in any sense.
4951  */
4952 static VALUE
4953 s_Molecule_InitCopy(VALUE self, VALUE arg)
4954 {
4955         Molecule *mp1, *mp2;
4956         Data_Get_Struct(self, Molecule, mp1);
4957         mp2 = MoleculeFromValue(arg);
4958         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4959                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4960         return self;
4961 }
4962
4963 /*
4964  *  call-seq:
4965  *     atom_index(val)       -> Integer
4966  *
4967  *  Returns the atom index represented by val. val can be either a non-negative integer
4968  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4969  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
4970  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
4971  *  If val is a string and multiple atoms match the description, the atom with the lowest index
4972  *  is returned.
4973  */
4974 static VALUE
4975 s_Molecule_AtomIndex(VALUE self, VALUE val)
4976 {
4977     Molecule *mol;
4978     Data_Get_Struct(self, Molecule, mol);
4979         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4980 }
4981
4982 /*
4983  *  call-seq:
4984  *     self == Molecule -> boolean
4985  *
4986  *  True if the two arguments point to the same molecule.
4987  */
4988 static VALUE
4989 s_Molecule_Equal(VALUE self, VALUE val)
4990 {
4991         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4992                 Molecule *mol1, *mol2;
4993                 Data_Get_Struct(self, Molecule, mol1);
4994                 Data_Get_Struct(val, Molecule, mol2);
4995                 return (mol1 == mol2 ? Qtrue : Qfalse);
4996         } else return Qfalse;
4997 }
4998
4999 #pragma mark ------ Load/Save ------
5000
5001 static void
5002 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5003 {
5004         if (gLoadSaveErrorMessage != NULL) {
5005                 MyAppCallback_setConsoleColor(1);
5006                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5007                 MyAppCallback_setConsoleColor(0);
5008         }
5009         if (status != 0)
5010                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5011 }
5012
5013 /*
5014  *  call-seq:
5015  *     loadmbsf(file)       -> bool
5016  *
5017  *  Read a structure from a mbsf file.
5018  *  Return true if successful.
5019  */
5020 static VALUE
5021 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5022 {
5023         VALUE fname;
5024         char *fstr;
5025         Molecule *mol;
5026         int retval;
5027         MoleculeClearLoadSaveErrorMessage();
5028         Data_Get_Struct(self, Molecule, mol);
5029         rb_scan_args(argc, argv, "1", &fname);
5030         fstr = FileStringValuePtr(fname);
5031         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5032         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5033         return Qtrue;   
5034 }
5035
5036 /*
5037  *  call-seq:
5038  *     loadpsf(file, pdbfile = nil)       -> bool
5039  *
5040  *  Read a structure from a psf file. molecule must be empty. The psf may be
5041  *  an "extended" version, which also contains coordinates. If pdbfile 
5042  *  is given, then atomic coordinates are read from that file.
5043  *  Return true if successful.
5044  */
5045 static VALUE
5046 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5047 {
5048         VALUE fname, pdbname;
5049         char *fstr, *pdbstr;
5050         Molecule *mol;
5051         int retval;
5052         Data_Get_Struct(self, Molecule, mol);
5053         if (mol->natoms > 0)
5054                 return Qnil;  /*  Must be a new molecule  */
5055         MoleculeClearLoadSaveErrorMessage();
5056         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5057         fstr = FileStringValuePtr(fname);
5058         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5059         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5060         pdbstr = NULL;
5061         if (!NIL_P(pdbname)) {
5062                 pdbstr = strdup(FileStringValuePtr(pdbname));
5063                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5064                 free(pdbstr);
5065                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5066         }
5067         return Qtrue;
5068 }
5069
5070 /*
5071  *  call-seq:
5072  *     loadpdb(file)       -> bool
5073  *
5074  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5075  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5076  *  Return true if successful.
5077  */
5078 static VALUE
5079 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5080 {
5081         VALUE fname;
5082         char *fstr;
5083         Molecule *mol;
5084         int retval;
5085         Data_Get_Struct(self, Molecule, mol);
5086         rb_scan_args(argc, argv, "1", &fname);
5087         MoleculeClearLoadSaveErrorMessage();
5088         fstr = FileStringValuePtr(fname);
5089         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5090         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5091         return Qtrue;   
5092 }
5093
5094 /*
5095  *  call-seq:
5096  *     loaddcd(file)       -> bool
5097  *
5098  *  Read coordinates from a dcd file. The molecule should not empty.
5099  *  Return true if successful.
5100  */
5101 static VALUE
5102 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5103 {
5104         VALUE fname;
5105         char *fstr;
5106         Molecule *mol;
5107         int retval;
5108         Data_Get_Struct(self, Molecule, mol);
5109         rb_scan_args(argc, argv, "1", &fname);
5110         MoleculeClearLoadSaveErrorMessage();
5111         fstr = FileStringValuePtr(fname);
5112         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5113         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5114         return Qtrue;   
5115 }
5116
5117 /*
5118  *  call-seq:
5119  *     loadtep(file)       -> bool
5120  *
5121  *  Read coordinates from an ortep .tep file.
5122  *  Return true if successful.
5123  */
5124 static VALUE
5125 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5126 {
5127         VALUE fname;
5128         char *fstr;
5129         Molecule *mol;
5130         int retval;
5131         Data_Get_Struct(self, Molecule, mol);
5132         rb_scan_args(argc, argv, "1", &fname);
5133         MoleculeClearLoadSaveErrorMessage();
5134         fstr = FileStringValuePtr(fname);
5135         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5136         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5137         return Qtrue;   
5138 }
5139
5140 /*
5141  *  call-seq:
5142  *     loadres(file)       -> bool
5143  *
5144  *  Read coordinates from a shelx .res file.
5145  *  Return true if successful.
5146  */
5147 static VALUE
5148 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5149 {
5150         VALUE fname;
5151         char *fstr;
5152         Molecule *mol;
5153         int retval;
5154         Data_Get_Struct(self, Molecule, mol);
5155         rb_scan_args(argc, argv, "1", &fname);
5156         MoleculeClearLoadSaveErrorMessage();
5157         fstr = FileStringValuePtr(fname);
5158         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5159         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5160         return Qtrue;   
5161 }
5162
5163 /*
5164  *  call-seq:
5165  *     loadfchk(file)       -> bool
5166  *
5167  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5168  *  Return true if successful.
5169  */
5170 static VALUE
5171 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5172 {
5173         VALUE fname;
5174         char *fstr;
5175         Molecule *mol;
5176         int retval;
5177         Data_Get_Struct(self, Molecule, mol);
5178         rb_scan_args(argc, argv, "1", &fname);
5179         MoleculeClearLoadSaveErrorMessage();
5180         fstr = FileStringValuePtr(fname);
5181         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5182         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5183         return Qtrue;   
5184 }
5185
5186 /*
5187  *  call-seq:
5188  *     loaddat(file)       -> bool
5189  *
5190  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5191  *  Return true if successful.
5192  */
5193 static VALUE
5194 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5195 {
5196         VALUE fname;
5197         char *fstr;
5198         Molecule *mol;
5199         int retval;
5200         Data_Get_Struct(self, Molecule, mol);
5201         rb_scan_args(argc, argv, "1", &fname);
5202         MoleculeClearLoadSaveErrorMessage();
5203         fstr = FileStringValuePtr(fname);
5204         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5205         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5206         MyAppCallback_hideProgressPanel();
5207         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5208         return Qtrue;   
5209 }
5210
5211 /*
5212  *  call-seq:
5213  *     savembsf(file)       -> bool
5214  *
5215  *  Write structure as a mbsf file. Returns true if successful.
5216  */
5217 static VALUE
5218 s_Molecule_Savembsf(VALUE self, VALUE fname)
5219 {
5220         char *fstr;
5221     Molecule *mol;
5222         int retval;
5223     Data_Get_Struct(self, Molecule, mol);
5224         MoleculeClearLoadSaveErrorMessage();
5225         fstr = FileStringValuePtr(fname);
5226         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5227         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5228         return Qtrue;
5229 }
5230
5231 /*
5232  *  call-seq:
5233  *     savepsf(file)       -> bool
5234  *
5235  *  Write structure as a psf file. Returns true if successful.
5236  */
5237 static VALUE
5238 s_Molecule_Savepsf(VALUE self, VALUE fname)
5239 {
5240         char *fstr;
5241     Molecule *mol;
5242         int retval;
5243     Data_Get_Struct(self, Molecule, mol);
5244         MoleculeClearLoadSaveErrorMessage();
5245         fstr = FileStringValuePtr(fname);
5246         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5247         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5248         return Qtrue;
5249 }
5250
5251 /*
5252  *  call-seq:
5253  *     savepdb(file)       -> bool
5254  *
5255  *  Write coordinates as a pdb file. Returns true if successful.
5256  */
5257 static VALUE
5258 s_Molecule_Savepdb(VALUE self, VALUE fname)
5259 {
5260         char *fstr;
5261     Molecule *mol;
5262         int retval;
5263     Data_Get_Struct(self, Molecule, mol);
5264         MoleculeClearLoadSaveErrorMessage();
5265         fstr = FileStringValuePtr(fname);
5266         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5267         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5268         return Qtrue;
5269 }
5270
5271 /*
5272  *  call-seq:
5273  *     savedcd(file)       -> bool
5274  *
5275  *  Write coordinates as a dcd file. Returns true if successful.
5276  */
5277 static VALUE
5278 s_Molecule_Savedcd(VALUE self, VALUE fname)
5279 {
5280         char *fstr;
5281     Molecule *mol;
5282         int retval;
5283     Data_Get_Struct(self, Molecule, mol);
5284         MoleculeClearLoadSaveErrorMessage();
5285         fstr = FileStringValuePtr(fname);
5286         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5287         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5288         return Qtrue;
5289 }
5290
5291 /*  load([ftype, ] fname, ...)  */
5292 static VALUE
5293 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5294 {
5295         VALUE rval;
5296         char *argstr, *methname, *p, *type = "";
5297         ID mid = 0;
5298         int i;
5299         const char *ls = (loadFlag ? "load" : "save");
5300         int lslen = strlen(ls);
5301
5302         if (argc == 0)
5303                 return Qnil;
5304
5305         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5306                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5307         if (argstr[0] == ':') {
5308                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5309                 methname = ALLOC_N(char, lslen + strlen(argstr));
5310                 strcpy(methname, ls);
5311                 strcat(methname, argstr + 1);
5312                 type = argstr + 1;
5313                 for (i = lslen; methname[i] != 0; i++)
5314                         methname[i] = tolower(methname[i]);
5315                 mid = rb_intern(methname);
5316                 xfree(methname);
5317                 argc--;
5318                 argv++;
5319                 rval = rb_funcall2(self, mid, argc, argv);
5320                 if (rval == Qnil)
5321                         goto failure;
5322                 else
5323                         goto success;
5324         }
5325         /*  Guess file type from extension  */
5326         p = strrchr(argstr, '.');
5327         if (p != NULL) {
5328                 p++;
5329                 type = p;
5330                 for (methname = p; *methname != 0; methname++) {
5331                         if (!isalpha(*methname))
5332                                 break;
5333                 }
5334                 if (*methname == 0) {
5335                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5336                         if (methname == NULL)
5337                                 rb_raise(rb_eMolbyError, "Low memory");
5338                         strcpy(methname, ls);
5339                         strcat(methname, p);
5340                         for (i = lslen; methname[i] != 0; i++)
5341                                 methname[i] = tolower(methname[i]);
5342                         mid = rb_intern(methname);
5343                         xfree(methname);
5344                         if (loadFlag) {
5345                                 if (rb_respond_to(self, mid)) {
5346                                         /*  Load: try to call the load procedure only if it is available  */
5347                                         rval = rb_funcall2(self, mid, argc, argv);
5348                                         if (rval != Qnil)
5349                                                 goto success;
5350                                 }
5351                         } else {
5352                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5353                                 rval = rb_funcall2(self, mid, argc, argv);
5354                                 if (rval != Qnil)
5355                                         goto success;
5356                         }
5357                 }
5358         }
5359 failure:
5360         rval = rb_str_to_str(argv[0]);
5361         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5362         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5363         return Qnil;  /*  Does not reach here  */
5364
5365 success:
5366         {
5367                 /*  Register the path  */
5368                 Molecule *mol;
5369         /*      Atom *ap; */
5370                 Data_Get_Struct(self, Molecule, mol);
5371                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5372                 
5373                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5374         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5375                         if (ap->occupancy != 0.0)
5376                                 break;
5377                 }
5378                 if (i == mol->natoms) {
5379                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5380                                 ap->occupancy = 1.0;
5381                         }
5382                 } */
5383         }
5384         return rval;
5385 }
5386
5387 /*
5388  *  call-seq:
5389  *     molload(file, *args)       -> bool
5390  *
5391  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5392  *  file type given by the extension). If this method fails, then all defined (public)
5393  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5394  */
5395 static VALUE
5396 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5397 {
5398         return s_Molecule_LoadSave(argc, argv, self, 1);
5399 }
5400
5401 /*
5402  *  call-seq:
5403  *     molsave(file, *args)       -> bool
5404  *
5405  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5406  *  (XXX is the file type given by the extension).
5407  */
5408 static VALUE
5409 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5410 {
5411         return s_Molecule_LoadSave(argc, argv, self, 0);
5412 }
5413
5414 /*
5415  *  call-seq:
5416  *     open        -> Molecule
5417  *     open(file)  -> Molecule
5418  *
5419  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5420  */
5421 static VALUE
5422 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5423 {
5424         VALUE fname;
5425         const char *p;
5426         Molecule *mp;
5427         VALUE iflag;
5428     
5429     if (!gUseGUI) {
5430         rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5431     }
5432     
5433         rb_scan_args(argc, argv, "01", &fname);
5434         if (NIL_P(fname))
5435                 p = NULL;
5436         else
5437                 p = FileStringValuePtr(fname);
5438         iflag = Ruby_SetInterruptFlag(Qfalse);
5439         mp = MoleculeCallback_openNewMolecule(p);
5440         Ruby_SetInterruptFlag(iflag);
5441         if (mp == NULL) {
5442                 if (p == NULL)
5443                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5444                 else
5445                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5446         }
5447         return ValueFromMolecule(mp);
5448 }
5449
5450 /*
5451  *  call-seq:
5452  *     new  -> Molecule
5453  *     new(file, *args)  -> Molecule
5454  *
5455  *  Create a new molecule and call "load" method with the same arguments.
5456  */
5457 static VALUE
5458 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5459 {
5460         if (argc > 0)
5461                 return s_Molecule_Load(argc, argv, self);
5462         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5463 }
5464
5465 /*
5466  *  call-seq:
5467  *     error_message       -> String
5468  *
5469  *  Get the error_message from the last load/save method. If no error, returns nil.
5470  */
5471 static VALUE
5472 s_Molecule_ErrorMessage(VALUE klass)
5473 {
5474         if (gLoadSaveErrorMessage == NULL)
5475                 return Qnil;
5476         else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5477 }
5478
5479 /*
5480  *  call-seq:
5481  *     set_error_message(String)
5482  *     Molecule.error_message = String
5483  *
5484  *  Set the error_message for the present load/save method.
5485  */
5486 static VALUE
5487 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5488 {
5489         if (gLoadSaveErrorMessage != NULL) {
5490                 free(gLoadSaveErrorMessage);
5491                 gLoadSaveErrorMessage = NULL;
5492         }
5493         if (sval != Qnil) {
5494                 sval = rb_str_to_str(sval);
5495                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5496         }
5497         return sval;
5498 }
5499
5500 /*
5501  *  call-seq:
5502  *     set_molecule(Molecule)
5503  *
5504  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5505  *  This method is exclusively used for associating a new document with an existing molecule.
5506  */
5507 static VALUE
5508 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5509 {
5510         Molecule *mp1, *mp2;
5511         Data_Get_Struct(self, Molecule, mp1);
5512         mp2 = MoleculeFromValue(mval);
5513         MoleculeInitWithMolecule(mp1, mp2);
5514         MoleculeCallback_notifyModification(mp1, 1);
5515         return self;
5516 }
5517
5518 #pragma mark ------ Name attributes ------
5519
5520 /*
5521  *  call-seq:
5522  *     name       -> String
5523  *
5524  *  Returns the display name of the molecule. If the molecule has no associated
5525  *  document, then returns nil.
5526  */
5527 static VALUE
5528 s_Molecule_Name(VALUE self)
5529 {
5530     Molecule *mol;
5531         char buf[1024];
5532     Data_Get_Struct(self, Molecule, mol);
5533         MoleculeCallback_displayName(mol, buf, sizeof buf);
5534         if (buf[0] == 0)
5535                 return Qnil;
5536         else
5537                 return Ruby_NewEncodedStringValue2(buf);
5538 }
5539
5540 /*
5541  *  call-seq:
5542  *     set_name(string) -> self
5543  *
5544  *  Set the name of an untitled molecule. If the molecule is not associated with window
5545  *  or it already has an associated file, then exception is thrown.
5546  */
5547 static VALUE
5548 s_Molecule_SetName(VALUE self, VALUE nval)
5549 {
5550     Molecule *mol;
5551     Data_Get_Struct(self, Molecule, mol);
5552         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5553                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5554         return self;
5555 }
5556
5557
5558 /*
5559  *  call-seq:
5560  *     path       -> String
5561  *
5562  *  Returns the full path name of the molecule, if it is associated with a file.
5563  *  If the molecule has no associated file, then returns nil.
5564  */
5565 static VALUE
5566 s_Molecule_Path(VALUE self)
5567 {
5568     Molecule *mol;
5569         char buf[1024];
5570     Data_Get_Struct(self, Molecule, mol);
5571         MoleculeCallback_pathName(mol, buf, sizeof buf);
5572         if (buf[0] == 0)
5573                 return Qnil;
5574         else
5575                 return Ruby_NewFileStringValue(buf);
5576 }
5577
5578 /*
5579  *  call-seq:
5580  *     dir       -> String
5581  *
5582  *  Returns the full path name of the directory in which the file associated with the
5583  *  molecule is located. If the molecule has no associated file, then returns nil.
5584  */
5585 static VALUE
5586 s_Molecule_Dir(VALUE self)
5587 {
5588     Molecule *mol;
5589         char buf[1024], *p;
5590     Data_Get_Struct(self, Molecule, mol);
5591         MoleculeCallback_pathName(mol, buf, sizeof buf);
5592 #if __WXMSW__
5593         translate_char(buf, '\\', '/');
5594 #endif
5595         if (buf[0] == 0)
5596                 return Qnil;
5597         else {
5598                 p = strrchr(buf, '/');
5599                 if (p != NULL)
5600                         *p = 0;
5601                 return Ruby_NewEncodedStringValue2(buf);
5602         }
5603 }
5604
5605 /*
5606  *  call-seq:
5607  *     inspect       -> String
5608  *
5609  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5610  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5611  *  the Molecule structure) is returned.
5612  */
5613 static VALUE
5614 s_Molecule_Inspect(VALUE self)
5615 {
5616     Molecule *mol;
5617         char buf[256];
5618     Data_Get_Struct(self, Molecule, mol);
5619         MoleculeCallback_displayName(mol, buf, sizeof buf);
5620         if (buf[0] == 0) {
5621                 /*  No associated document  */
5622                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5623                 return Ruby_NewEncodedStringValue2(buf);
5624         } else {
5625                 /*  Check whether the document name is duplicate  */
5626                 char buf2[256];
5627                 int idx, k, k2;
5628                 Molecule *mol2;
5629                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5630                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5631                         if (strcmp(buf, buf2) == 0) {
5632                                 k++;
5633                                 if (mol == mol2)
5634                                         k2 = k;
5635                         }
5636                 }
5637                 if (k > 1) {
5638                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5639                 } else {
5640                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5641                 }
5642                 return Ruby_NewEncodedStringValue2(buf2);
5643         }
5644 }
5645
5646 #pragma mark ------ MolEnumerables ------
5647
5648 static VALUE
5649 s_Molecule_MolEnumerable(VALUE self, int kind)
5650 {
5651     Molecule *mol;
5652         MolEnumerable *mseq;
5653     Data_Get_Struct(self, Molecule, mol);
5654         mseq = MolEnumerableNew(mol, kind);
5655         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5656 }
5657
5658 /*
5659  *  call-seq:
5660  *     atoms       -> MolEnumerable
5661  *
5662  *  Returns a MolEnumerable object representing the array of atoms.
5663  */
5664 static VALUE
5665 s_Molecule_Atoms(VALUE self)
5666 {
5667         return s_Molecule_MolEnumerable(self, kAtomKind);
5668 }
5669
5670 /*
5671  *  call-seq:
5672  *     bonds       -> MolEnumerable
5673  *
5674  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5675  *  by an array of two atom indices.
5676  */
5677 static VALUE
5678 s_Molecule_Bonds(VALUE self)
5679 {
5680         return s_Molecule_MolEnumerable(self, kBondKind);
5681 }
5682
5683 /*
5684  *  call-seq:
5685  *     angles       -> MolEnumerable
5686  *
5687  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5688  *  by an array of three atom indices.
5689  */
5690 static VALUE
5691 s_Molecule_Angles(VALUE self)
5692 {
5693         return s_Molecule_MolEnumerable(self, kAngleKind);
5694 }
5695
5696 /*
5697  *  call-seq:
5698  *     dihedrals       -> MolEnumerable
5699  *
5700  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5701  *  by an array of four atom indices.
5702  */
5703 static VALUE
5704 s_Molecule_Dihedrals(VALUE self)
5705 {
5706         return s_Molecule_MolEnumerable(self, kDihedralKind);
5707 }
5708
5709 /*
5710  *  call-seq:
5711  *     impropers       -> MolEnumerable
5712  *
5713  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5714  *  by an array of four atom indices.
5715  */
5716 static VALUE
5717 s_Molecule_Impropers(VALUE self)
5718 {
5719         return s_Molecule_MolEnumerable(self, kImproperKind);
5720 }
5721
5722 /*
5723  *  call-seq:
5724  *     residues       -> MolEnumerable
5725  *
5726  *  Returns a MolEnumerable object representing the array of residue names.
5727  */
5728 static VALUE
5729 s_Molecule_Residues(VALUE self)
5730 {
5731         return s_Molecule_MolEnumerable(self, kResidueKind);
5732 }
5733
5734 /*
5735  *  call-seq:
5736  *     natoms       -> Integer
5737  *
5738  *  Returns the number of atoms.
5739  */
5740 static VALUE
5741 s_Molecule_Natoms(VALUE self)
5742 {
5743     Molecule *mol;
5744     Data_Get_Struct(self, Molecule, mol);
5745         return INT2NUM(mol->natoms);
5746 }
5747
5748 /*
5749  *  call-seq:
5750  *     nbonds       -> Integer
5751  *
5752  *  Returns the number of bonds.
5753  */
5754 static VALUE
5755 s_Molecule_Nbonds(VALUE self)
5756 {
5757     Molecule *mol;
5758     Data_Get_Struct(self, Molecule, mol);
5759         return INT2NUM(mol->nbonds);
5760 }
5761
5762 /*
5763  *  call-seq:
5764  *     nangles       -> Integer
5765  *
5766  *  Returns the number of angles.
5767  */
5768 static VALUE
5769 s_Molecule_Nangles(VALUE self)
5770 {
5771     Molecule *mol;
5772     Data_Get_Struct(self, Molecule, mol);
5773         return INT2NUM(mol->nangles);
5774 }
5775
5776 /*
5777  *  call-seq:
5778  *     ndihedrals       -> Integer
5779  *
5780  *  Returns the number of dihedrals.
5781  */
5782 static VALUE
5783 s_Molecule_Ndihedrals(VALUE self)
5784 {
5785     Molecule *mol;
5786     Data_Get_Struct(self, Molecule, mol);
5787         return INT2NUM(mol->ndihedrals);
5788 }
5789
5790 /*
5791  *  call-seq:
5792  *     nimpropers       -> Integer
5793  *
5794  *  Returns the number of impropers.
5795  */
5796 static VALUE
5797 s_Molecule_Nimpropers(VALUE self)
5798 {
5799     Molecule *mol;
5800     Data_Get_Struct(self, Molecule, mol);
5801         return INT2NUM(mol->nimpropers);
5802 }
5803
5804 /*
5805  *  call-seq:
5806  *     nresidues       -> Integer
5807  *
5808  *  Returns the number of residues.
5809  */
5810 static VALUE
5811 s_Molecule_Nresidues(VALUE self)
5812 {
5813     Molecule *mol;
5814     Data_Get_Struct(self, Molecule, mol);
5815         return INT2NUM(mol->nresidues);
5816 }
5817
5818 /*
5819  *  call-seq:
5820  *     nresidues = Integer
5821  *
5822  *  Change the number of residues.
5823  */
5824 static VALUE
5825 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5826 {
5827     Molecule *mol;
5828         int ival = NUM2INT(val);
5829     Data_Get_Struct(self, Molecule, mol);
5830         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5831         if (ival != mol->nresidues)
5832                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5833         return val;
5834 }
5835
5836 /*
5837  *  call-seq:
5838  *     max_residue_number(atom_group = nil)     -> Integer
5839  *
5840  *  Returns the maximum residue number actually used. If an atom group is given, only
5841  *  these atoms are examined. If no atom is present, nil is returned.
5842  */
5843 static VALUE
5844 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5845 {
5846     Molecule *mol;
5847         VALUE gval;
5848         int maxSeq;
5849         IntGroup *ig;
5850     Data_Get_Struct(self, Molecule, mol);
5851         rb_scan_args(argc, argv, "01", &gval);
5852         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5853         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5854         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5855 }
5856
5857 /*
5858  *  call-seq:
5859  *     min_residue_number(atom_group = nil)     -> Integer
5860  *
5861  *  Returns the minimum residue number actually used. If an atom group is given, only
5862  *  these atoms are examined. If no atom is present, nil is returned.
5863  */
5864 static VALUE
5865 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5866 {
5867     Molecule *mol;
5868         VALUE gval;
5869         int minSeq;
5870         IntGroup *ig;
5871     Data_Get_Struct(self, Molecule, mol);
5872         rb_scan_args(argc, argv, "01", &gval);
5873         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5874         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5875         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5876 }
5877
5878 /*
5879  *  call-seq:
5880  *     each_atom(atom_group = nil) {|aref| ...}
5881  *
5882  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5883  *  group is given, only these atoms are processed.
5884  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5885  *  is self (a Molecule object).
5886  */
5887 static VALUE
5888 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5889 {
5890         int i;
5891     Molecule *mol;
5892         AtomRef *aref;
5893         VALUE arval;
5894         VALUE gval;
5895         IntGroup *ig;
5896     Data_Get_Struct(self, Molecule, mol);
5897         rb_scan_args(argc, argv, "01", &gval);
5898         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5899         arval = ValueFromMoleculeAndIndex(mol, 0);
5900         Data_Get_Struct(arval, AtomRef, aref);
5901         for (i = 0; i < mol->natoms; i++) {
5902                 aref->idx = i;
5903                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5904                         rb_yield(arval);
5905         }
5906         if (ig != NULL)
5907                 IntGroupRelease(ig);
5908     return self;
5909 }
5910
5911 #pragma mark ------ Atom Group ------
5912
5913 static VALUE
5914 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5915 {
5916         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5917         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5918         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5919         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5920         return Qnil;
5921 }
5922
5923 /*
5924  *  call-seq:
5925  *     atom_group
5926  *     atom_group {|aref| ...}
5927  *     atom_group(arg1, arg2, ...)
5928  *     atom_group(arg1, arg2, ...) {|aref| ...}
5929  *
5930  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5931  *  If arguments are given, then the atoms reprensented by the arguments are added to the
5932  *  group. For a conversion of a string to an atom index, see the description
5933  *  of Molecule#atom_index.
5934  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
5935  *  representing each atom, and the atoms are removed from the result if the block returns false.
5936  *
5937  */
5938 static VALUE
5939 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5940 {
5941         IntGroup *ig1, *ig2;
5942     Molecule *mol;
5943         Int i, startPt, interval;
5944         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5945         Data_Get_Struct(retval, IntGroup, ig1);
5946     Data_Get_Struct(self, Molecule, mol);
5947         if (argc == 0) {
5948                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5949         } else {
5950                 while (argc > 0) {
5951                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5952                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5953                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5954                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5955                                 ig2 = IntGroupFromValue(*argv);
5956                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5957                                         interval = IntGroupGetInterval(ig2, i);
5958                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5959                                 }
5960                                 IntGroupRelease(ig2);
5961                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
5962                                 VALUE values[2];
5963                                 values[0] = (VALUE)mol;
5964                                 values[1] = (VALUE)ig1;
5965                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5966                         } else
5967                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5968                         argc--;
5969                         argv++;
5970                 }
5971         }
5972         if (rb_block_given_p()) {
5973                 /*  Evaluate the given block with an AtomRef as the argument, and delete
5974                         the index if the block returns false  */
5975                 AtomRef *aref = AtomRefNew(mol, 0);
5976                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5977                 ig2 = IntGroupNew();
5978                 IntGroupCopy(ig2, ig1);
5979                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5980                         VALUE resval;
5981                         if (startPt >= mol->natoms)
5982                                 break;
5983                         aref->idx = startPt;
5984                         resval = rb_yield(arval);
5985                         if (!RTEST(resval))
5986                                 IntGroupRemove(ig1, startPt, 1);
5987                 }
5988                 IntGroupRelease(ig2);
5989         }
5990         
5991         /*  Remove points that are out of bounds */
5992         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5993
5994         return retval;                  
5995 }
5996
5997 /*
5998  *  call-seq:
5999  *     selection       -> IntGroup
6000  *
6001  *  Returns the current selection.
6002  */
6003 static VALUE
6004 s_Molecule_Selection(VALUE self)
6005 {
6006     Molecule *mol;
6007         IntGroup *ig;
6008         VALUE val;
6009     Data_Get_Struct(self, Molecule, mol);
6010         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6011                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
6012                 val = ValueFromIntGroup(ig);
6013                 IntGroupRelease(ig);
6014         } else {
6015                 val = IntGroup_Alloc(rb_cIntGroup);
6016         }
6017         return val;
6018 }
6019
6020 static VALUE
6021 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6022 {
6023     Molecule *mol;
6024         IntGroup *ig;
6025     Data_Get_Struct(self, Molecule, mol);
6026         if (val == Qnil)
6027                 ig = NULL;
6028         else
6029                 ig = s_Molecule_AtomGroupFromValue(self, val);
6030         if (undoable)
6031                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6032         else
6033                 MoleculeSetSelection(mol, ig);
6034         if (ig != NULL)
6035                 IntGroupRelease(ig);
6036         return val;
6037 }
6038
6039 /*
6040  *  call-seq:
6041  *     selection = IntGroup
6042  *
6043  *  Set the current selection. The right-hand operand may be nil.
6044  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6045  */
6046 static VALUE
6047 s_Molecule_SetSelection(VALUE self, VALUE val)
6048 {
6049         return s_Molecule_SetSelectionSub(self, val, 0);
6050 }
6051
6052 /*
6053  *  call-seq:
6054  *     set_undoable_selection(IntGroup)
6055  *
6056  *  Set the current selection with undo registration. The right-hand operand may be nil.
6057  *  This operation is undoable.
6058  */
6059 static VALUE
6060 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6061 {
6062         return s_Molecule_SetSelectionSub(self, val, 1);
6063 }
6064
6065 #pragma mark ------ Editing ------
6066
6067 /*
6068  *  call-seq:
6069  *     extract(group, dummy_flag = nil)       -> Molecule
6070  *
6071  *  Extract the atoms given by group and return as a new molecule object.
6072  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6073  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6074  *  names beginning with an underscore) and included in the new molecule object.
6075  */
6076 static VALUE
6077 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6078 {
6079     Molecule *mol1, *mol2;
6080         IntGroup *ig;
6081         VALUE group, dummy_flag, retval;
6082     Data_Get_Struct(self, Molecule, mol1);
6083         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6084         ig = s_Molecule_AtomGroupFromValue(self, group);
6085         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6086                 retval = Qnil;
6087         } else {
6088                 retval = ValueFromMolecule(mol2);
6089         }
6090         IntGroupRelease(ig);
6091         return retval;
6092 }
6093
6094 /*
6095  *  call-seq:
6096  *     add(molecule2)       -> self
6097  *
6098  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6099     conflicts.
6100     This operation is undoable.
6101  */
6102 static VALUE
6103 s_Molecule_Add(VALUE self, VALUE val)
6104 {
6105     Molecule *mol1, *mol2;
6106     Data_Get_Struct(self, Molecule, mol1);
6107         mol2 = MoleculeFromValue(val);
6108         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6109         return self; 
6110 }
6111
6112 /*
6113  *  call-seq:
6114  *     remove(group)       -> Molecule
6115  *
6116  *  The atoms designated by the given group are removed from the molecule.
6117  *  This operation is undoable.
6118  */
6119 static VALUE
6120 s_Molecule_Remove(VALUE self, VALUE group)
6121 {
6122     Molecule *mol1;
6123         IntGroup *ig, *bg;
6124         Int i;
6125         IntGroupIterator iter;
6126
6127     ig = s_Molecule_AtomGroupFromValue(self, group);
6128 /*    Data_Get_Struct(self, Molecule, mol1);
6129         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6130         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6131                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6132         Data_Get_Struct(group, IntGroup, ig); */
6133     Data_Get_Struct(self, Molecule, mol1);
6134     
6135         /*  Remove the bonds between the two fragments  */
6136         /*  (This is necessary for undo to work correctly)  */
6137         IntGroupIteratorInit(ig, &iter);
6138         bg = NULL;
6139         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6140                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6141                 Int j, *cp;
6142                 cp = AtomConnectData(&ap->connect);
6143                 for (j = 0; j < ap->connect.count; j++) {
6144                         int n = cp[j];
6145                         if (!IntGroupLookup(ig, n, NULL)) {
6146                                 /*  bond i-n, i is in ig and n is not  */
6147                                 int k = MoleculeLookupBond(mol1, i, n);
6148                                 if (k >= 0) {
6149                                         if (bg == NULL)
6150                                                 bg = IntGroupNew();
6151                                         IntGroupAdd(bg, k, 1);
6152                                 }
6153                         }
6154                 }
6155         }
6156         IntGroupIteratorRelease(&iter);
6157         if (bg != NULL) {
6158                 /*  Remove bonds  */
6159                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6160                 IntGroupRelease(bg);
6161         }
6162         /*  Remove atoms  */
6163         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6164                 return Qnil;
6165         return self;
6166 }
6167
6168 /*
6169  *  call-seq:
6170  *     create_atom(name, pos = -1)  -> AtomRef
6171  *
6172  *  Create a new atom with the specified name (may contain residue 
6173  *  information) and position (if position is out of range, the atom is appended at
6174  *  the end). Returns the reference to the new atom.
6175  *  This operation is undoable.
6176  */
6177 static VALUE
6178 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6179 {
6180     Molecule *mol;
6181     Int i, pos;
6182         VALUE name, ival;
6183     Atom arec;
6184     AtomRef *aref;
6185         char *p, resName[6], atomName[6];
6186         int resSeq;
6187     Data_Get_Struct(self, Molecule, mol);
6188         rb_scan_args(argc, argv, "02", &name, &ival);
6189         if (ival != Qnil)
6190                 pos = NUM2INT(rb_Integer(ival));
6191         else pos = -1;
6192         if (name != Qnil) {
6193                 p = StringValuePtr(name);
6194                 if (p[0] != 0) {
6195                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6196                         if (atomName[0] == 0)
6197                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6198                 }
6199         } else p = NULL;
6200         if (p == NULL || p[0] == 0) {
6201                 memset(atomName, 0, 4);
6202                 resSeq = -1;
6203         }
6204     memset(&arec, 0, sizeof(arec));
6205     strncpy(arec.aname, atomName, 4);
6206     if (resSeq >= 0) {
6207       strncpy(arec.resName, resName, 4);
6208       arec.resSeq = resSeq;
6209     }
6210         arec.occupancy = 1.0;
6211 //    i = MoleculeCreateAnAtom(mol, &arec);
6212         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6213                 return Qnil;
6214     aref = AtomRefNew(mol, pos);
6215     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6216 }
6217
6218 /*
6219  *  call-seq:
6220  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6221  *
6222  *  Create a new atom with the same attributes (but no bonding information)
6223  *  with the specified atom. Returns the reference to the new atom.
6224  *  This operation is undoable.
6225  */
6226 static VALUE
6227 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6228 {
6229     Molecule *mol;
6230         const Atom *apsrc;
6231     Atom arec;
6232         AtomRef *aref;
6233         VALUE retval, aval, ival;
6234         Int pos;
6235     Data_Get_Struct(self, Molecule, mol);
6236         rb_scan_args(argc, argv, "11", &aval, &ival);
6237         if (FIXNUM_P(aval)) {
6238                 int idx = NUM2INT(aval);
6239                 if (idx < 0 || idx >= mol->natoms)
6240                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6241                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6242         } else {
6243                 apsrc = s_AtomFromValue(aval);
6244         }
6245         if (apsrc == NULL)
6246                 rb_raise(rb_eMolbyError, "bad atom specification");
6247         if (ival != Qnil)
6248                 pos = NUM2INT(rb_Integer(ival));
6249         else pos = -1;
6250         AtomDuplicate(&arec, apsrc);
6251         arec.connect.count = 0;
6252         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6253                 retval = Qnil;
6254         else {
6255                 aref = AtomRefNew(mol, pos);
6256                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6257         }
6258         AtomClean(&arec);
6259         return retval;
6260 }
6261
6262 /*
6263  *  call-seq:
6264  *     create_bond(n1, n2, ...)       -> Integer
6265  *
6266  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6267  *  do nothing for that pair. Returns the number of bonds actually created.
6268  *  This operation is undoable.
6269  */
6270 static VALUE
6271 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6272 {
6273     Molecule *mol;
6274         Int i, j, k, *ip, old_nbonds;
6275         if (argc == 0)
6276                 rb_raise(rb_eMolbyError, "missing arguments");
6277         if (argc % 2 != 0)
6278                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6279     Data_Get_Struct(self, Molecule, mol);
6280         ip = ALLOC_N(Int, argc + 1);
6281         for (i = j = 0; i < argc; i++, j++) {
6282                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6283                 if (i % 2 == 1) {
6284                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6285                                 j -= 2;  /*  This bond is already present: skip it  */
6286                         else {
6287                                 for (k = 0; k < j - 1; k += 2) {
6288                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6289                                                 j -= 2;   /*  The same entry is already in the argument  */
6290                                                 break;
6291                                         }
6292                                 }
6293                         }
6294                 }
6295         }
6296         old_nbonds = mol->nbonds;
6297         if (j > 0) {
6298                 ip[j] = kInvalidIndex;
6299                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6300         } else i = 0;
6301         xfree(ip);
6302         if (i == -1)
6303                 rb_raise(rb_eMolbyError, "atom index out of range");
6304         else if (i == -2)
6305                 rb_raise(rb_eMolbyError, "too many bonds");
6306         else if (i == -3)
6307                 rb_raise(rb_eMolbyError, "duplicate bonds");
6308         else if (i == -5)
6309                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6310         else if (i != 0)
6311                 rb_raise(rb_eMolbyError, "error in creating bonds");
6312         return INT2NUM(mol->nbonds - old_nbonds);
6313 }
6314
6315 /*
6316  *  call-seq:
6317  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6318  *
6319  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6320  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6321  *  This operation is undoable.
6322  */
6323 static VALUE
6324 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6325 {
6326     Molecule *mol;
6327         Int i, j, n[2];
6328         IntGroup *bg;
6329         if (argc == 0)
6330                 rb_raise(rb_eMolbyError, "missing arguments");
6331         if (argc % 2 != 0)
6332                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6333     Data_Get_Struct(self, Molecule, mol);
6334         bg = NULL;
6335         for (i = j = 0; i < argc; i++, j = 1 - j) {
6336                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6337                 if (j == 1) {
6338                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6339                         if (k >= 0) {
6340                                 if (bg == NULL)
6341                                         bg = IntGroupNew();
6342                                 IntGroupAdd(bg, k, 1);
6343                         }
6344                 }
6345         }
6346         if (bg != NULL) {
6347                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6348                 i = IntGroupGetCount(bg);
6349                 IntGroupRelease(bg);
6350         } else i = 0;
6351         return INT2NUM(i);
6352 }
6353
6354 /*
6355  *  call-seq:
6356  *     assign_bond_order(idx, d1)
6357  *     assign_bond_orders(group, [d1, d2, ...])
6358  *
6359  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6360  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6361  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6362  *  (This may change in the future)
6363  *  This operation is undoable.
6364  */
6365 static VALUE
6366 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6367 {
6368     Molecule *mol;
6369         IntGroup *ig;
6370     Data_Get_Struct(self, Molecule, mol);
6371         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6372                 /*  The first form  */
6373                 Int idx = NUM2INT(rb_Integer(idxval));
6374                 Double d1 = NUM2DBL(rb_Float(dval));
6375                 if (idx < 0 || idx >= mol->nbonds)
6376                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6377                 ig = IntGroupNewWithPoints(idx, 1, -1);
6378                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6379                 IntGroupRelease(ig);
6380         } else {
6381                 Int i, n;
6382                 Double *dp;
6383                 ig = IntGroupFromValue(idxval);
6384                 n = IntGroupGetCount(ig);
6385                 if (n == 0)
6386                         rb_raise(rb_eMolbyError, "the bond index is empty");
6387                 dval = rb_ary_to_ary(dval);
6388                 dp = (Double *)calloc(sizeof(Double), n);
6389                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6390                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6391                 }
6392                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6393                 free(dp);
6394                 IntGroupRelease(ig);
6395         }
6396         return self;
6397 }
6398
6399 /*
6400  *  call-seq:
6401  *     get_bond_order(idx) -> Float
6402  *     get_bond_orders(group) -> Array
6403  *
6404  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6405  *  In the second form, the bond orders at the indices in the group are returned as an array.
6406  *  If no bond order information have been assigned, returns nil (the first form)
6407  *  or an empty array (the second form).
6408  */
6409 static VALUE
6410 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6411 {
6412     Molecule *mol;
6413         IntGroup *ig;
6414         Double *dp;
6415         VALUE retval;
6416         Int i, n, numericArg;
6417     Data_Get_Struct(self, Molecule, mol);
6418         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6419                 /*  The first form  */
6420                 Int idx = NUM2INT(rb_Integer(idxval));
6421                 if (idx < 0 || idx >= mol->nbonds)
6422                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6423                 if (mol->bondOrders == NULL)
6424                         return Qnil;
6425                 ig = IntGroupNewWithPoints(idx, 1, -1);
6426                 n = 1;
6427                 numericArg = 1;
6428         } else {
6429                 if (mol->bondOrders == NULL)
6430                         return rb_ary_new();
6431                 ig = IntGroupFromValue(idxval);
6432                 n = IntGroupGetCount(ig);
6433                 if (n == 0)
6434                         rb_raise(rb_eMolbyError, "the bond index is empty");
6435                 numericArg = 0;
6436         }
6437         dp = (Double *)calloc(sizeof(Double), n);
6438         MoleculeGetBondOrders(mol, dp, ig);
6439         if (numericArg)
6440                 retval = rb_float_new(dp[0]);
6441         else {
6442                 retval = rb_ary_new();
6443                 for (i = 0; i < n; i++)
6444                         rb_ary_push(retval, rb_float_new(dp[i]));
6445         }
6446         free(dp);
6447         IntGroupRelease(ig);
6448         return retval;
6449 }
6450
6451 /*
6452  *  call-seq:
6453  *     bond_exist?(idx1, idx2) -> bool
6454  *
6455  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6456  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6457  */
6458 static VALUE
6459 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6460 {
6461         Molecule *mol;
6462         Int idx1, idx2, i;
6463         Atom *ap;
6464         Int *cp;
6465     Data_Get_Struct(self, Molecule, mol);
6466         idx1 = NUM2INT(rb_Integer(ival1));
6467         idx2 = NUM2INT(rb_Integer(ival2));
6468         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6469                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6470         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6471         cp = AtomConnectData(&ap->connect);
6472         for (i = 0; i < ap->connect.count; i++) {
6473                 if (cp[i] == idx2)
6474                         return Qtrue;
6475         }
6476         return Qfalse;
6477 }
6478
6479 /*
6480  *  call-seq:
6481  *     add_angle(n1, n2, n3)       -> Molecule
6482  *
6483  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6484  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6485  *  This operation is undoable.
6486  */
6487 static VALUE
6488 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6489 {
6490         Int n[4];
6491     Molecule *mol;
6492     Data_Get_Struct(self, Molecule, mol);
6493         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6494         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6495         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6496         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6497                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6498         n[3] = kInvalidIndex;
6499         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6500         return self;
6501 }
6502
6503 /*
6504  *  call-seq:
6505  *     remove_angle(n1, n2, n3)       -> Molecule
6506  *
6507  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6508  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6509  *  This operation is undoable.
6510  */
6511 static VALUE
6512 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6513 {
6514         Int n[4];
6515     Molecule *mol;
6516         IntGroup *ig;
6517     Data_Get_Struct(self, Molecule, mol);
6518         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6519         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6520         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6521         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6522                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6523         ig = IntGroupNewWithPoints(n[3], 1, -1);
6524         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6525         IntGroupRelease(ig);
6526         return self;
6527 }
6528
6529 /*
6530  *  call-seq:
6531  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6532  *
6533  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6534  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6535  *  This operation is undoable.
6536  */
6537 static VALUE
6538 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6539 {
6540         Int n[5];
6541     Molecule *mol;
6542     Data_Get_Struct(self, Molecule, mol);
6543         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6544         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6545         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6546         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6547         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6548                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6549         n[4] = kInvalidIndex;
6550         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6551         return self;
6552 }
6553
6554 /*
6555  *  call-seq:
6556  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6557  *
6558  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6559  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6560  *  This operation is undoable.
6561  */
6562 static VALUE
6563 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6564 {
6565         Int n[5];
6566     Molecule *mol;
6567         IntGroup *ig;
6568     Data_Get_Struct(self, Molecule, mol);
6569         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6570         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6571         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6572         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6573         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6574                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6575         ig = IntGroupNewWithPoints(n[4], 1, -1);
6576         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6577         IntGroupRelease(ig);
6578         return self;
6579 }
6580
6581 /*
6582  *  call-seq:
6583  *     add_improper(n1, n2, n3, n4)       -> Molecule
6584  *
6585  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6586  *  not automatically added when a new bond is created, so this method is more useful than
6587  *  the angle/dihedral counterpart.
6588  *  This operation is undoable.
6589  */
6590 static VALUE
6591 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6592 {
6593         Int n[5];
6594     Molecule *mol;
6595     Data_Get_Struct(self, Molecule, mol);
6596         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6597         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6598         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6599         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6600         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6601                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6602         n[4] = kInvalidIndex;
6603         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6604         return self;
6605 }
6606
6607 /*
6608  *  call-seq:
6609  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6610  *     remove_improper(intgroup)             -> Molecule
6611  *
6612  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6613  *  Returns self. Unlike angles and dihedrals, impropers are
6614  *  not automatically added when a new bond is created, so this method is more useful than
6615  *  the angle/dihedral counterpart.
6616  *  This operation is undoable.
6617  */
6618 static VALUE
6619 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6620 {
6621         Int n[5];
6622         VALUE v1, v2, v3, v4;
6623     Molecule *mol;
6624         IntGroup *ig;
6625     Data_Get_Struct(self, Molecule, mol);
6626         if (argc == 1) {
6627                 ig = IntGroupFromValue(argv[0]);
6628         } else {
6629                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6630                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6631                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6632                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6633                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6634                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6635                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6636                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6637         }
6638         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6639         IntGroupRelease(ig);
6640         return self;
6641 }
6642
6643 /*
6644  *  call-seq:
6645  *     assign_residue(group, res)       -> Molecule
6646  *
6647  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6648  *  or "resname.resno". When the residue number is not specified, the residue number of
6649  *  the first atom in the group is used.
6650  *  This operation is undoable.
6651  */
6652 static VALUE
6653 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6654 {
6655     Molecule *mol;
6656         IntGroup *ig;
6657         char *p, *pp, buf[16];
6658         Int resid, n;
6659         Atom *ap;
6660     Data_Get_Struct(self, Molecule, mol);
6661         
6662         /*  Parse the argument res  */
6663         if (FIXNUM_P(res)) {
6664                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6665                 resid = NUM2INT(res);
6666                 buf[0] = 0;
6667         } else {
6668                 p = StringValuePtr(res);
6669                 pp = strchr(p, '.');
6670                 if (pp != NULL) {
6671                         resid = atoi(pp + 1);
6672                         n = pp - p;
6673                 } else {
6674                         resid = -1;
6675                         n = strlen(p);
6676                 }
6677                 if (n > sizeof buf - 1)
6678                         n = sizeof buf - 1;
6679                 strncpy(buf, p, n);
6680                 buf[n] = 0;
6681         }
6682         ig = s_Molecule_AtomGroupFromValue(self, range);
6683         if (ig == NULL || IntGroupGetCount(ig) == 0)
6684                 return Qnil;
6685
6686         if (resid < 0) {
6687                 /*  Use the residue number of the first specified atom  */
6688                 n = IntGroupGetNthPoint(ig, 0);
6689                 if (n >= mol->natoms)
6690                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6691                 ap = ATOM_AT_INDEX(mol->atoms, n);
6692                 resid = ap->resSeq;
6693         }
6694         /*  Change the residue number  */
6695         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6696         /*  Change the residue name if necessary  */
6697         if (buf[0] != 0) {
6698         /*      Int seqs[2];
6699                 seqs[0] = resid;
6700                 seqs[1] = kInvalidIndex; */
6701                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6702         }
6703         IntGroupRelease(ig);
6704         return self;
6705 }
6706
6707 /*
6708  *  call-seq:
6709  *     offset_residue(group, offset)       -> Molecule
6710  *
6711  *  Offset the residue number of the specified atoms. If any of the residue number gets
6712  *  negative, then exception is thrown.
6713  *  This operation is undoable.
6714  */
6715 static VALUE
6716 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6717 {
6718     Molecule *mol;
6719         IntGroup *ig;
6720         int ofs, result;
6721     Data_Get_Struct(self, Molecule, mol);
6722         ig = s_Molecule_AtomGroupFromValue(self, range);
6723         ofs = NUM2INT(offset);
6724         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6725         if (result > 0)
6726                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6727         IntGroupRelease(ig);
6728         return self;
6729 }
6730
6731 /*
6732  *  call-seq:
6733  *     renumber_atoms(array)       -> IntGroup
6734  *
6735  *  Change the order of atoms so that the atoms specified in the array argument appear
6736  *  in this order from the top of the molecule. The atoms that are not included in array
6737  *  are placed after these atoms, and these atoms are returned as an intGroup.
6738  *  This operation is undoable.
6739  */
6740 static VALUE
6741 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6742 {
6743     Molecule *mol;
6744         Int *new2old;
6745         IntGroup *ig;
6746         int i, n;
6747         VALUE *valp, retval;
6748     Data_Get_Struct(self, Molecule, mol);
6749         if (TYPE(array) != T_ARRAY)
6750                 array = rb_funcall(array, rb_intern("to_a"), 0);
6751         n = RARRAY_LEN(array);
6752         valp = RARRAY_PTR(array);
6753         new2old = ALLOC_N(Int, n + 1);
6754         for (i = 0; i < n; i++)
6755                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6756         new2old[i] = kInvalidIndex;
6757         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6758         if (i == 1)
6759                 rb_raise(rb_eMolbyError, "Atom index out of range");
6760         else if (i == 2)
6761                 rb_raise(rb_eMolbyError, "Duplicate entry");
6762         else if (i == 3)
6763                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6764         retval = IntGroup_Alloc(rb_cIntGroup);
6765         Data_Get_Struct(retval, IntGroup, ig);
6766         if (mol->natoms > n)
6767                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6768         xfree(new2old);
6769         return retval;
6770 }
6771
6772 /*
6773  *  call-seq:
6774  *     set_atom_attr(index, key, value)
6775  *
6776  *  Set the atom attribute for the specified atom.
6777  *  This operation is undoable.
6778  */
6779 static VALUE
6780 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6781 {
6782         Molecule *mol;
6783         VALUE aref, oldval;
6784     Data_Get_Struct(self, Molecule, mol);
6785         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6786         oldval = s_AtomRef_GetAttr(aref, key);
6787         if (val == Qundef)
6788                 return oldval;
6789         s_AtomRef_SetAttr(aref, key, val);
6790         return val;
6791 }
6792
6793 /*
6794  *  call-seq:
6795  *     get_atom_attr(index, key)
6796  *
6797  *  Get the atom attribute for the specified atom.
6798  */
6799 static VALUE
6800 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6801 {
6802         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6803 }
6804
6805 #pragma mark ------ Undo Support ------
6806
6807 /*
6808  *  call-seq:
6809  *     register_undo(script, *args)
6810  *
6811  *  Register an undo operation with the current molecule.
6812  */
6813 static VALUE
6814 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6815 {
6816         Molecule *mol;
6817         VALUE script, args;
6818         MolAction *act;
6819     Data_Get_Struct(self, Molecule, mol);
6820         rb_scan_args(argc, argv, "1*", &script, &args);
6821         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6822         MolActionCallback_registerUndo(mol, act);
6823         return script;
6824 }
6825
6826 /*
6827  *  call-seq:
6828  *     undo_enabled? -> bool
6829  *
6830  *  Returns true if undo is enabled for this molecule; otherwise no.
6831  */
6832 static VALUE
6833 s_Molecule_UndoEnabled(VALUE self)
6834 {
6835     Molecule *mol;
6836     Data_Get_Struct(self, Molecule, mol);
6837         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6838                 return Qtrue;
6839         else return Qfalse;
6840 }
6841
6842 /*
6843  *  call-seq:
6844  *     undo_enabled = bool
6845  *
6846  *  Enable or disable undo.
6847  */
6848 static VALUE
6849 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6850 {
6851     Molecule *mol;
6852     Data_Get_Struct(self, Molecule, mol);
6853         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6854         return val;
6855 }
6856
6857 #pragma mark ------ Measure ------
6858
6859 static void
6860 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6861 {
6862         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6863                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6864                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6865                 case 0: break;
6866                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6867         }
6868 }
6869
6870 /*
6871  *  call-seq:
6872  *     center_of_mass(group = nil)       -> Vector3D
6873  *
6874  *  Calculate the center of mass for the given set of atoms. The argument
6875  *  group is null, then all atoms are considered.
6876  */
6877 static VALUE
6878 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6879 {
6880     Molecule *mol;
6881         VALUE group;
6882         IntGroup *ig;
6883         Vector v;
6884     Data_Get_Struct(self, Molecule, mol);
6885         rb_scan_args(argc, argv, "01", &group);
6886         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6887         s_Molecule_DoCenterOfMass(mol, &v, ig);
6888         if (ig != NULL)
6889                 IntGroupRelease(ig);
6890         return ValueFromVector(&v);
6891 }
6892
6893 /*
6894  *  call-seq:
6895  *     centralize(group = nil)       -> self
6896  *
6897  *  Translate the molecule so that the center of mass of the given group is located
6898  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6899  */
6900 static VALUE
6901 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6902 {
6903     Molecule *mol;
6904         VALUE group;
6905         IntGroup *ig;
6906         Vector v;
6907     Data_Get_Struct(self, Molecule, mol);
6908         rb_scan_args(argc, argv, "01", &group);
6909         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6910         s_Molecule_DoCenterOfMass(mol, &v, ig);
6911         if (ig != NULL)
6912                 IntGroupRelease(ig);
6913         v.x = -v.x;
6914         v.y = -v.y;
6915         v.z = -v.z;
6916         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6917         return self;
6918 }
6919
6920 /*
6921  *  call-seq:
6922  *     bounds(group = nil)       -> [min, max]
6923  *
6924  *  Calculate the boundary. The return value is an array of two Vector3D objects.
6925  */
6926 static VALUE
6927 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6928 {
6929     Molecule *mol;
6930         VALUE group;
6931         IntGroup *ig;
6932         Vector vmin, vmax;
6933         int n;
6934         Atom *ap;
6935     Data_Get_Struct(self, Molecule, mol);
6936         rb_scan_args(argc, argv, "01", &group);
6937         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6938         if (ig != NULL && IntGroupGetCount(ig) == 0)
6939                 rb_raise(rb_eMolbyError, "atom group is empty");
6940         vmin.x = vmin.y = vmin.z = 1e30;
6941         vmax.x = vmax.y = vmax.z = -1e30;
6942         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6943                 Vector r;
6944                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6945                         continue;
6946                 r = ap->r;
6947                 if (r.x < vmin.x)
6948                         vmin.x = r.x;
6949                 if (r.y < vmin.y)
6950                         vmin.y = r.y;
6951                 if (r.z < vmin.z)
6952                         vmin.z = r.z;
6953                 if (r.x > vmax.x)
6954                         vmax.x = r.x;
6955                 if (r.y > vmax.y)
6956                         vmax.y = r.y;
6957                 if (r.z > vmax.z)
6958                         vmax.z = r.z;
6959         }
6960         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6961 }
6962
6963 /*  Get atom position or a vector  */
6964 static void
6965 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6966 {
6967         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6968                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6969                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6970         } else {
6971                 VectorFromValue(val, vp);
6972         }
6973 }
6974
6975 /*
6976  *  call-seq:
6977  *     measure_bond(n1, n2)       -> Float
6978  *
6979  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
6980  *  or Vector3D values.
6981  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6982  */
6983 static VALUE
6984 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6985 {
6986     Molecule *mol;
6987         Vector v1, v2;
6988     Data_Get_Struct(self, Molecule, mol);
6989         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6990         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6991         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6992 }
6993
6994 /*
6995  *  call-seq:
6996  *     measure_angle(n1, n2, n3)       -> Float
6997  *
6998  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
6999  *  or Vector3D values. The return value is in degree.
7000  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7001  */
7002 static VALUE
7003 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7004 {
7005     Molecule *mol;
7006         Vector v1, v2, v3;
7007         Double d;
7008     Data_Get_Struct(self, Molecule, mol);
7009         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7010         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7011         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7012         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7013         if (isnan(d))
7014                 return Qnil;  /*  Cannot define  */
7015         else return rb_float_new(d);
7016 }
7017
7018 /*
7019  *  call-seq:
7020  *     measure_dihedral(n1, n2, n3, n4)       -> Float
7021  *
7022  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
7023  *  or Vector3D values. The return value is in degree.
7024  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7025  */
7026 static VALUE
7027 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7028 {
7029     Molecule *mol;
7030         Vector v1, v2, v3, v4;
7031         Double d;
7032     Data_Get_Struct(self, Molecule, mol);
7033         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7034         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7035         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7036         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
7037         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7038         if (isnan(d))
7039                 return Qnil;  /*  Cannot define  */
7040         else return rb_float_new(d);
7041 }
7042
7043 /*
7044  *  call-seq:
7045  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7046  *
7047  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7048  *  first and second atom in the pair should belong to group1 and group2, respectively.
7049  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7050  */
7051 static VALUE
7052 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7053 {
7054     Molecule *mol;
7055         VALUE limval, gval1, gval2, rval, igval;
7056         IntGroup *ig1, *ig2;
7057         IntGroupIterator iter1, iter2;
7058         Int npairs, *pairs;
7059         Int n[2], i;
7060         Double lim;
7061         Vector r1;
7062         Atom *ap1, *ap2;
7063         MDExclusion *exinfo;
7064         Int *exlist;
7065         
7066     Data_Get_Struct(self, Molecule, mol);
7067         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7068         lim = NUM2DBL(rb_Float(limval));
7069         if (lim <= 0.0)
7070                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7071         if (gval1 != Qnil)
7072                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7073         else
7074                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7075         if (gval2 != Qnil)
7076                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7077         else
7078                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7079         
7080         if (!RTEST(igval)) {
7081                 /*  Use the exclusion table in MDArena  */
7082                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7083                         VALUE mval = ValueFromMolecule(mol);
7084                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7085                 }
7086                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7087                 exlist = mol->arena->exlist;    
7088         } else {
7089                 exinfo = NULL;
7090                 exlist = NULL;
7091         }
7092         IntGroupIteratorInit(ig1, &iter1);
7093         IntGroupIteratorInit(ig2, &iter2);
7094         npairs = 0;
7095         pairs = NULL;
7096         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7097                 Int exn1, exn2;
7098                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7099                 r1 = ap1->r;
7100                 if (exinfo != NULL) {
7101                         exn1 = exinfo[n[0]].index1;
7102                         exn2 = exinfo[n[0] + 1].index1;
7103                 } else exn1 = exn2 = -1;
7104                 IntGroupIteratorReset(&iter2);
7105                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7106                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7107                         if (n[0] == n[1])
7108                                 continue;  /*  Same atom  */
7109                         if (exinfo != NULL) {
7110                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7111                                 for (i = exn1; i < exn2; i++) {
7112                                         if (exlist[i] == n[1])
7113                                                 break;
7114                                 }
7115                                 if (i < exn2)
7116                                         continue;  /*  Should be excluded  */
7117                         }
7118                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7119                                 /*  Is this pair already registered?  */
7120                                 Int *ip;
7121                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7122                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7123                                                 break;
7124                                 }
7125                                 if (i >= npairs) {
7126                                         /*  Not registered yet  */
7127                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7128                                 }
7129                         }
7130                 }
7131         }
7132         IntGroupIteratorRelease(&iter2);
7133         IntGroupIteratorRelease(&iter1);
7134         IntGroupRelease(ig2);
7135         IntGroupRelease(ig1);
7136         rval = rb_ary_new2(npairs);
7137         if (pairs != NULL) {
7138                 for (i = 0; i < npairs; i++) {
7139                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7140                 }
7141                 free(pairs);
7142         }
7143         return rval;
7144 }
7145
7146 /*
7147  *  call-seq:
7148  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7149  *
7150  *  Find atoms that are within the threshold distance from the given atom.
7151  *  (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.)
7152  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7153  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7154  *  If limit is not given, a default value of 1.2 is used.
7155  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7156  */
7157 static VALUE
7158 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7159 {
7160     Molecule *mol;
7161         VALUE aval, limval, radval;
7162         double limit, radius;
7163         Int n1, nbonds, *bonds, an;
7164         Vector v;
7165     Data_Get_Struct(self, Molecule, mol);
7166         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7167         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)) {
7168                 VectorFromValue(aval, &v);
7169                 if (radval == Qnil)
7170                         radius = gElementParameters[6].radius;
7171                 else
7172                         radius = NUM2DBL(rb_Float(radval));
7173                 n1 = mol->natoms;
7174         } else {
7175                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7176                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7177                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7178                 if (an >= 0 && an < gCountElementParameters)
7179                         radius = gElementParameters[an].radius;
7180                 else radius = gElementParameters[6].radius;
7181         }
7182         if (limval == Qnil)
7183                 limit = 1.2;
7184         else
7185                 limit = NUM2DBL(rb_Float(limval));
7186         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7187         bonds = NULL;
7188         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7189         aval = rb_ary_new();
7190         if (nbonds > 0) {
7191                 for (n1 = 0; n1 < nbonds; n1++)
7192                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7193                 free(bonds);
7194         }
7195         return aval;
7196 }
7197
7198 /*
7199  *  call-seq:
7200  *     guess_bonds(limit = 1.2)       -> Integer
7201  *
7202  *  Create bonds between atoms that are within the threshold distance.
7203  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7204  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7205  *  If limit is not given, a default value of 1.2 is used.
7206  *  The number of the newly created bonds is returned.
7207  *  This operation is undoable.
7208  */
7209 static VALUE
7210 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7211 {
7212     Molecule *mol;
7213         VALUE limval;
7214         double limit;
7215         Int nbonds, *bonds;
7216     Data_Get_Struct(self, Molecule, mol);
7217         rb_scan_args(argc, argv, "01", &limval);
7218         if (limval == Qnil)
7219                 limit = 1.2;
7220         else
7221                 limit = NUM2DBL(rb_Float(limval));
7222         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7223         if (nbonds > 0) {
7224                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7225                 free(bonds);
7226         }
7227         return INT2NUM(nbonds);
7228 }
7229
7230 #pragma mark ------ Cell and Symmetry ------
7231
7232 /*
7233  *  call-seq:
7234  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7235  *
7236  *  Returns the unit cell parameters. If cell is not set, returns nil.
7237  */
7238 static VALUE
7239 s_Molecule_Cell(VALUE self)
7240 {
7241     Molecule *mol;
7242         int i;
7243         VALUE val;
7244     Data_Get_Struct(self, Molecule, mol);
7245         if (mol->cell == NULL)
7246                 return Qnil;
7247         val = rb_ary_new2(6);
7248         for (i = 0; i < 6; i++)
7249                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7250         if (mol->cell->has_sigma) {
7251                 for (i = 0; i < 6; i++) {
7252                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7253                 }
7254         }
7255         return val;
7256 }
7257
7258 /*
7259  *  call-seq:
7260  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7261  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7262  *
7263  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7264  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7265  This operation is undoable.
7266  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7267  */
7268 static VALUE
7269 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7270 {
7271     Molecule *mol;
7272         VALUE val, cval;
7273         int i, convert_coord, n;
7274         double d[12];
7275     Data_Get_Struct(self, Molecule, mol);
7276         rb_scan_args(argc, argv, "11", &val, &cval);
7277         if (val == Qnil) {
7278                 n = 0;
7279         } else {
7280                 int len;
7281                 val = rb_ary_to_ary(val);
7282                 len = RARRAY_LEN(val);
7283                 if (len >= 12) {
7284                         n = 12;
7285                 } else if (len >= 6) {
7286                         n = 6;
7287                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7288                 for (i = 0; i < n; i++)
7289                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7290         }
7291         convert_coord = (RTEST(cval) ? 1 : 0);
7292         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7293         return val;
7294 }
7295
7296 /*
7297  *  call-seq:
7298  *     box -> [avec, bvec, cvec, origin, flags]
7299  *
7300  *  Get the unit cell information in the form of a periodic bounding box.
7301  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7302  *  Integers which define whether the system is periodic along the axis.
7303  *  If no unit cell is defined, nil is returned.
7304  */
7305 static VALUE
7306 s_Molecule_Box(VALUE self)
7307 {
7308     Molecule *mol;
7309         VALUE v[5], val;
7310     Data_Get_Struct(self, Molecule, mol);
7311         if (mol == NULL || mol->cell == NULL)
7312                 return Qnil;
7313         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7314         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7315         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7316         v[3] = ValueFromVector(&(mol->cell->origin));
7317         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7318         val = rb_ary_new4(5, v);
7319         return val;
7320 }
7321
7322 /*
7323  *  call-seq:
7324  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7325  *     set_box(d, origin = [0, 0, 0])
7326  *     set_box
7327  *
7328  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7329  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7330  as the box vector.
7331  Flags, if present, is a 3-member array of Integers defining whether the system is
7332  periodic along the axis.
7333  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7334  In the second form, an isotropic box with cell-length d is set.
7335  In the third form, the existing box is cleared.
7336  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7337  */
7338 static VALUE
7339 s_Molecule_SetBox(VALUE self, VALUE aval)
7340 {
7341     Molecule *mol;
7342         VALUE v[6];
7343         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7344         Vector vv[3];
7345         Vector origin = {0, 0, 0};
7346         char flags[3];
7347         Double d;
7348         int i, convertCoordinates = 0;
7349     Data_Get_Struct(self, Molecule, mol);
7350         if (aval == Qnil) {
7351                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7352                 return self;
7353         }
7354         aval = rb_ary_to_ary(aval);
7355         for (i = 0; i < 6; i++) {
7356                 if (i < RARRAY_LEN(aval))
7357                         v[i] = (RARRAY_PTR(aval))[i];
7358                 else v[i] = Qnil;
7359         }
7360         if (v[0] == Qnil) {
7361                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7362                 return self;
7363         }
7364         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7365                 d = NUM2DBL(rb_Float(v[0]));
7366                 for (i = 0; i < 3; i++)
7367                         VecScale(vv[i], ax[i], d);
7368                 if (v[1] != Qnil)
7369                         VectorFromValue(v[1], &origin);
7370                 flags[0] = flags[1] = flags[2] = 1;
7371         } else {
7372                 for (i = 0; i < 3; i++) {
7373                         if (v[i] == Qnil) {
7374                                 VecZero(vv[i]);
7375                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7376                                 d = NUM2DBL(rb_Float(v[i]));
7377                                 VecScale(vv[i], ax[i], d);
7378                         } else {
7379                                 VectorFromValue(v[i], &vv[i]);
7380                         }
7381                         flags[i] = (VecLength2(vv[i]) > 0.0);
7382                 }
7383                 if (v[3] != Qnil)
7384                         VectorFromValue(v[3], &origin);
7385                 if (v[4] != Qnil) {
7386                         for (i = 0; i < 3; i++) {
7387                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7388                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7389                         }
7390                 }
7391                 if (RTEST(v[5]))
7392                         convertCoordinates = 1;
7393         }
7394         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7395         return self;
7396 }
7397
7398 /*
7399  *  call-seq:
7400  *     cell_periodicity -> [n1, n2, n3]
7401  *
7402  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7403  *  nil is returned.
7404  */
7405 static VALUE
7406 s_Molecule_CellPeriodicity(VALUE self)
7407 {
7408     Molecule *mol;
7409     Data_Get_Struct(self, Molecule, mol);
7410         if (mol->cell == NULL)
7411                 return Qnil;
7412         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7413 }
7414
7415 /*
7416  *  call-seq:
7417  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7418  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7419  *
7420  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7421  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7422  *  If cell is not defined, exception is raised.
7423  *  This operation is undoable.
7424  */
7425 static VALUE
7426 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7427 {
7428     Molecule *mol;
7429         Int flag;
7430     Data_Get_Struct(self, Molecule, mol);
7431         if (mol->cell == NULL)
7432                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7433         if (arg == Qnil)
7434                 flag = 0;
7435         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7436                 flag = NUM2INT(rb_Integer(arg));
7437         else {
7438                 Int i;
7439                 VALUE arg0;
7440                 arg = rb_ary_to_ary(arg);
7441                 flag = 0;
7442                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7443                         arg0 = RARRAY_PTR(arg)[i];
7444                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7445                                 flag |= (1 << (2 - i));
7446                 }
7447         }
7448         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7449         return arg;
7450 }
7451
7452 /*
7453  *  call-seq:
7454  *     cell_flexibility -> bool
7455  *
7456  *  Returns the unit cell is flexible or not
7457  */
7458 static VALUE
7459 s_Molecule_CellFlexibility(VALUE self)
7460 {
7461         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7462         return Qtrue;
7463         /*    Molecule *mol;
7464          Data_Get_Struct(self, Molecule, mol);
7465          if (mol->cell == NULL)
7466          return Qfalse;
7467          if (mol->useFlexibleCell)
7468          return Qtrue;
7469          else return Qfalse; */
7470 }
7471
7472 /*
7473  *  call-seq:
7474  *     self.cell_flexibility = bool
7475  *     set_cell_flexibility(bool)
7476  *
7477  *  Change the unit cell is flexible or not
7478  */
7479 static VALUE
7480 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7481 {
7482         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7483         return self;
7484         /*    Molecule *mol;
7485          Data_Get_Struct(self, Molecule, mol);
7486          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7487          return self; */
7488 }
7489
7490 /*
7491  *  call-seq:
7492  *     cell_transform -> Transform
7493  *
7494  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7495  *  If cell is not defined, nil is returned.
7496  */
7497 static VALUE
7498 s_Molecule_CellTransform(VALUE self)
7499 {
7500     Molecule *mol;
7501     Data_Get_Struct(self, Molecule, mol);
7502         if (mol == NULL || mol->cell == NULL)
7503                 return Qnil;
7504         return ValueFromTransform(&(mol->cell->tr));
7505 }
7506
7507 /*
7508  *  call-seq:
7509  *     symmetry -> Array of Transforms
7510  *     symmetries -> Array of Transforms
7511  *
7512  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7513  *  returns an empty array.
7514  */
7515 static VALUE
7516 s_Molecule_Symmetry(VALUE self)
7517 {
7518     Molecule *mol;
7519         VALUE val;
7520         int i;
7521     Data_Get_Struct(self, Molecule, mol);
7522         if (mol->nsyms <= 0)
7523                 return rb_ary_new();
7524         val = rb_ary_new2(mol->nsyms);
7525         for (i = 0; i < mol->nsyms; i++) {
7526                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7527         }
7528         return val;
7529 }
7530
7531 /*
7532  *  call-seq:
7533  *     nsymmetries -> Integer
7534  *
7535  *  Get the number of currently defined symmetry operations.
7536  */
7537 static VALUE
7538 s_Molecule_Nsymmetries(VALUE self)
7539 {
7540     Molecule *mol;
7541     Data_Get_Struct(self, Molecule, mol);
7542         return INT2NUM(mol->nsyms);
7543 }
7544
7545 /*
7546  *  call-seq:
7547  *     add_symmetry(Transform) -> Integer
7548  *
7549  *  Add a new symmetry operation. If no symmetry operation is defined and the
7550  *  given argument is not an identity transform, then also add an identity
7551  *  transform at the index 0.
7552  *  Returns the total number of symmetries after operation.
7553  */
7554 static VALUE
7555 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7556 {
7557     Molecule *mol;
7558         Transform tr;
7559     Data_Get_Struct(self, Molecule, mol);
7560         TransformFromValue(trans, &tr);
7561         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7562         return INT2NUM(mol->nsyms);
7563 }
7564
7565 /*
7566  *  call-seq:
7567  *     remove_symmetry(count = nil) -> Integer
7568  *     remove_symmetries(count = nil) -> Integer
7569  *
7570  *  Remove the specified number of symmetry operations. The last added ones are removed
7571  *  first. If count is nil, then all symmetry operations are removed. Returns the
7572  *  number of leftover symmetries.
7573  */
7574 static VALUE
7575 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7576 {
7577     Molecule *mol;
7578         VALUE cval;
7579         int i, n;
7580     Data_Get_Struct(self, Molecule, mol);
7581         rb_scan_args(argc, argv, "01", &cval);
7582         if (cval == Qnil)
7583                 n = mol->nsyms - 1;
7584         else {
7585                 n = NUM2INT(rb_Integer(cval));
7586                 if (n < 0 || n > mol->nsyms)
7587                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7588                 if (n == mol->nsyms)
7589                         n = mol->nsyms - 1;
7590         }
7591         for (i = 0; i < n; i++)
7592                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7593         return INT2NUM(mol->nsyms);
7594 }
7595
7596 /*
7597  *  call-seq:
7598  *     wrap_unit_cell(group) -> Vector3D
7599  *
7600  *  Move the specified group so that the center of mass of the group is within the
7601  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7602  *  exception is raised.
7603  */
7604 static VALUE
7605 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7606 {
7607     Molecule *mol;
7608         IntGroup *ig;
7609         Vector v, cv, dv;
7610     Data_Get_Struct(self, Molecule, mol);
7611         if (mol->cell == NULL)
7612                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7613         ig = s_Molecule_AtomGroupFromValue(self, gval);
7614         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7615         TransformVec(&v, mol->cell->rtr, &cv);
7616         if (mol->cell->flags[0])
7617                 v.x -= floor(v.x);
7618         if (mol->cell->flags[1])
7619                 v.y -= floor(v.y);
7620         if (mol->cell->flags[2])
7621                 v.z -= floor(v.z);
7622         TransformVec(&dv, mol->cell->tr, &v);
7623         VecDec(dv, cv);
7624         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7625         IntGroupRelease(ig);
7626         return ValueFromVector(&dv);
7627 }
7628
7629 /*
7630  *  call-seq:
7631  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7632  *
7633  *  Expand the specified part of the molecule by the given symmetry operation.
7634  *  Returns the array of atom indices corresponding to the expanded atoms.
7635  *  If allow_overlap is true, then new atoms are created even when the
7636  *  coordinates coincide with the some other atom (special position) of the
7637  *  same element; otherwise, such atom will not be created and the index of the
7638  *  existing atom is given in the returned array.
7639  */
7640 static VALUE
7641 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7642 {
7643     Molecule *mol;
7644         VALUE gval, sval, xval, yval, zval, rval, oval;
7645         IntGroup *ig;
7646         Int n[4], allow_overlap;
7647         Int natoms;
7648         Int nidx, *idx;
7649         
7650     Data_Get_Struct(self, Molecule, mol);
7651         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7652         n[0] = NUM2INT(rb_Integer(sval));
7653         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7654         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7655         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7656         allow_overlap = (RTEST(oval) ? 1 : 0);
7657         ig = s_Molecule_AtomGroupFromValue(self, gval);
7658         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7659                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7660         natoms = mol->natoms;
7661         
7662         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7663         
7664         rval = rb_ary_new2(nidx);
7665         while (--nidx >= 0) {
7666                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7667         }
7668         /*      if (natoms == mol->natoms)
7669          rval = Qnil;
7670          else {
7671          rval = IntGroup_Alloc(rb_cIntGroup);
7672          Data_Get_Struct(rval, IntGroup, ig);
7673          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7674          } */
7675         return rval;
7676 }
7677
7678 /*
7679  *  call-seq:
7680  *     amend_by_symmetry(group = nil) -> IntGroup
7681  *
7682  *  Expand the specified part of the molecule by the given symmetry operation.
7683  *  Returns an IntGroup containing the added atoms.
7684  */
7685 static VALUE
7686 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7687 {
7688     Molecule *mol;
7689         IntGroup *ig, *ig2;
7690         VALUE rval, gval;
7691     Data_Get_Struct(self, Molecule, mol);
7692         rb_scan_args(argc, argv, "01", &gval);
7693         if (gval != Qnil)
7694                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7695         else ig = NULL;
7696         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7697         rval = ValueFromIntGroup(ig2);
7698         IntGroupRelease(ig2);
7699         return rval;
7700 }
7701
7702 #pragma mark ------ Transforms ------
7703
7704 /*
7705  *  call-seq:
7706  *     translate(vec, group = nil)       -> Molecule
7707  *
7708  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7709  *  This operation is undoable.
7710  */
7711 static VALUE
7712 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7713 {
7714     Molecule *mol;
7715         VALUE vec, group;
7716         Vector v;
7717         IntGroup *ig;
7718     Data_Get_Struct(self, Molecule, mol);
7719         rb_scan_args(argc, argv, "11", &vec, &group);
7720         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7721         VectorFromValue(vec, &v);
7722         //      MoleculeTranslate(mol, &v, ig);
7723         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7724         if (ig != NULL)
7725                 IntGroupRelease(ig);
7726         return self;
7727 }
7728
7729 /*
7730  *  call-seq:
7731  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7732  *
7733  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7734  *  If group is given, only atoms in the group are moved.
7735  *  This operation is undoable.
7736  */
7737 static VALUE
7738 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7739 {
7740     Molecule *mol;
7741         volatile VALUE aval, anval, cval, gval;
7742         Double angle;
7743         Vector av, cv;
7744         Transform tr;
7745         IntGroup *ig;
7746     Data_Get_Struct(self, Molecule, mol);
7747         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7748         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7749         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7750         VectorFromValue(aval, &av);
7751         if (NIL_P(cval))
7752                 cv.x = cv.y = cv.z = 0.0;
7753         else
7754                 VectorFromValue(cval, &cv);
7755         if (TransformForRotation(tr, &av, angle, &cv))
7756                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7757         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7758         if (ig != NULL)
7759                 IntGroupRelease(ig);
7760         return self;
7761 }
7762
7763 /*
7764  *  call-seq:
7765  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7766  *
7767  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7768  *  axis must not be a zero vector.
7769  *  If group is given, only atoms in the group are moved.
7770  *  This operation is undoable.
7771  */
7772 static VALUE
7773 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7774 {
7775     Molecule *mol;
7776         volatile VALUE aval, cval, gval;
7777         Vector av, cv;
7778         Transform tr;
7779         IntGroup *ig;
7780     Data_Get_Struct(self, Molecule, mol);
7781         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7782         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7783         VectorFromValue(aval, &av);
7784         if (NIL_P(cval))
7785                 cv.x = cv.y = cv.z = 0.0;
7786         else
7787                 VectorFromValue(cval, &cv);
7788         if (TransformForReflection(tr, &av, &cv))
7789                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7790         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7791         if (ig != NULL)
7792                 IntGroupRelease(ig);
7793         return self;
7794 }
7795
7796 /*
7797  *  call-seq:
7798  *     invert(center = [0,0,0], group = nil)       -> Molecule
7799  *
7800  *  Invert the molecule with the given center.
7801  *  If group is given, only atoms in the group are moved.
7802  *  This operation is undoable.
7803  */
7804 static VALUE
7805 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7806 {
7807         Molecule *mol;
7808         volatile VALUE cval, gval;
7809         Vector cv;
7810         Transform tr;
7811         IntGroup *ig;
7812     Data_Get_Struct(self, Molecule, mol);
7813         rb_scan_args(argc, argv, "02", &cval, &gval);
7814         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7815         if (NIL_P(cval))
7816                 cv.x = cv.y = cv.z = 0.0;
7817         else
7818                 VectorFromValue(cval, &cv);
7819         TransformForInversion(tr, &cv);
7820         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7821         if (ig != NULL)
7822                 IntGroupRelease(ig);
7823         return self;
7824 }
7825
7826 /*
7827  *  call-seq:
7828  *     transform(transform, group = nil)       -> Molecule
7829  *
7830  *  Transform the molecule by the given Transform object.
7831  *  If group is given, only atoms in the group are moved.
7832  *  This operation is undoable.
7833  */
7834 static VALUE
7835 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7836 {
7837     Molecule *mol;
7838         VALUE trans, group;
7839         Transform tr;
7840         IntGroup *ig;
7841     Data_Get_Struct(self, Molecule, mol);
7842         rb_scan_args(argc, argv, "11", &trans, &group);
7843         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7844         TransformFromValue(trans, &tr);
7845         /*      MoleculeTransform(mol, tr, ig); */
7846         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7847         if (ig != NULL)
7848                 IntGroupRelease(ig);
7849         return self;
7850 }
7851
7852 /*
7853  *  call-seq:
7854  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7855  *
7856  *  Get the transform corresponding to the symmetry operation. The symop can either be
7857  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7858  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7859  *  Otherwise, the returned transform is for fractional coordinates.
7860  *  Raises exception when no cell or no transform are defined.
7861  */
7862 static VALUE
7863 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7864 {
7865     Molecule *mol;
7866         VALUE sval, fval;
7867         Symop symop;
7868         Transform tr;
7869     Data_Get_Struct(self, Molecule, mol);
7870         if (mol->cell == NULL)
7871                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7872         if (mol->nsyms == 0)
7873                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7874         rb_scan_args(argc, argv, "11", &sval, &fval);
7875         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7876                 symop.sym = NUM2INT(rb_Integer(sval));
7877                 symop.dx = symop.dy = symop.dz = 0;
7878         } else {
7879                 sval = rb_ary_to_ary(sval);
7880                 if (RARRAY_LEN(sval) < 4)
7881                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7882                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7883                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7884                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7885                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7886         }
7887         if (symop.sym >= mol->nsyms)
7888                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7889         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7890         return ValueFromTransform(&tr);
7891 }
7892
7893 /*
7894  *  call-seq:
7895  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7896  *
7897  *  Get the symmetry operation corresponding to the given transform.
7898  *  If is_cartesian is true, the given transform is for cartesian coordinates.
7899  *  Otherwise, the given transform is for fractional coordinates.
7900  *  Raises exception when no cell or no transform are defined.
7901  */
7902 static VALUE
7903 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7904 {
7905     Molecule *mol;
7906         VALUE tval, fval;
7907         Symop symop;
7908         Transform tr;
7909         int n;
7910     Data_Get_Struct(self, Molecule, mol);
7911         if (mol->cell == NULL)
7912                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7913         if (mol->nsyms == 0)
7914                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7915         rb_scan_args(argc, argv, "11", &tval, &fval);
7916         TransformFromValue(tval, &tr);
7917         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7918         if (n == 0) {
7919                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7920         } else {
7921                 return Qnil;  /*  Not found  */
7922         }
7923 }
7924
7925 #pragma mark ------ Frames ------
7926
7927 /*
7928  *  call-seq:
7929  *     select_frame(index)
7930  *     frame = index
7931  *
7932  *  Select the specified frame. If successful, returns true, otherwise returns false.
7933  */
7934 static VALUE
7935 s_Molecule_SelectFrame(VALUE self, VALUE val)
7936 {
7937     Molecule *mol;
7938         int ival = NUM2INT(val);
7939     Data_Get_Struct(self, Molecule, mol);
7940         ival = MoleculeSelectFrame(mol, ival, 1);
7941         if (ival >= 0)
7942                 return Qtrue;
7943         else return Qfalse;
7944 }
7945
7946 /*
7947  *  call-seq:
7948  *     frame -> Integer
7949  *
7950  *  Get the current frame.
7951  */
7952 static VALUE
7953 s_Molecule_Frame(VALUE self)
7954 {
7955     Molecule *mol;
7956     Data_Get_Struct(self, Molecule, mol);
7957         return INT2NUM(mol->cframe);
7958 }
7959
7960 /*
7961  *  call-seq:
7962  *     nframes -> Integer
7963  *
7964  *  Get the number of frames.
7965  */
7966 static VALUE
7967 s_Molecule_Nframes(VALUE self)
7968 {
7969     Molecule *mol;
7970     Data_Get_Struct(self, Molecule, mol);
7971         return INT2NUM(MoleculeGetNumberOfFrames(mol));
7972 }
7973
7974 /*
7975  *  call-seq:
7976  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7977  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7978  *
7979  *  Insert new frames at the indices specified by the intGroup. If the first argument is
7980  *  an integer, a single new frame is inserted at that index. If the first argument is 
7981  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7982  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
7983  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
7984  *  to the new frame.
7985  *  Returns an intGroup representing the inserted frames if successful, nil if not.
7986  */
7987 static VALUE
7988 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7989 {
7990         VALUE val, coords, cells;
7991     Molecule *mol;
7992         IntGroup *ig;
7993         int count, ival, i, j, len, len_c, len2, nframes;
7994         VALUE *ptr, *ptr2;
7995         Vector *vp, *vp2;
7996     Data_Get_Struct(self, Molecule, mol);
7997         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7998         if (coords != Qnil) {
7999                 if (TYPE(coords) != T_ARRAY)
8000                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8001                 len = RARRAY_LEN(coords);
8002         } else len = 0;
8003         if (cells != Qnil) {
8004                 if (mol->cell == NULL)
8005                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8006                 if (TYPE(cells) != T_ARRAY)
8007                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8008                 len_c = RARRAY_LEN(cells);
8009         } else len_c = 0;
8010         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
8011         nframes = MoleculeGetNumberOfFrames(mol);
8012         if (val == Qnil) {
8013                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8014                 val = ValueFromIntGroup(ig);
8015         } else {
8016                 ig = IntGroupFromValue(val);
8017         }
8018         count = IntGroupGetCount(ig);  /*  Count is updated here  */
8019         vp = ALLOC_N(Vector, mol->natoms * count);
8020         if (cells != Qnil)
8021                 vp2 = ALLOC_N(Vector, 4 * count);
8022         else vp2 = NULL;
8023         if (len > 0) {
8024                 if (len < count)
8025                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8026                 ptr = RARRAY_PTR(coords);
8027                 for (i = 0; i < count; i++) {
8028                         if (TYPE(ptr[i]) != T_ARRAY)
8029                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8030                         len2 = RARRAY_LEN(ptr[i]);
8031                         if (len2 < mol->natoms)
8032                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8033                         ptr2 = RARRAY_PTR(ptr[i]);
8034                         for (j = 0; j < mol->natoms; j++)
8035                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8036                 }
8037         } else {
8038                 Atom *ap;
8039                 for (i = 0; i < count; i++) {
8040                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8041                                 vp[i * mol->natoms + j] = ap->r;
8042                         }
8043                 }
8044         }
8045         if (len_c > 0) {
8046                 if (len_c < count)
8047                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8048                 ptr = RARRAY_PTR(cells);
8049                 for (i = 0; i < count; i++) {
8050                         if (TYPE(ptr[i]) != T_ARRAY)
8051                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8052                         len2 = RARRAY_LEN(ptr[i]);
8053                         if (len2 < 4)
8054                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8055                         ptr2 = RARRAY_PTR(ptr[i]);
8056                         for (j = 0; j < 4; j++)
8057                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8058                 }
8059         }
8060         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8061         IntGroupRelease(ig);
8062         xfree(vp);
8063         if (vp2 != NULL)
8064                 xfree(vp2);
8065         return (ival >= 0 ? val : Qnil);
8066 }
8067
8068 /*
8069  *  call-seq:
8070  *     create_frame(coordinates = nil) -> Integer
8071  *     create_frames(coordinates = nil) -> Integer
8072  *
8073  *  Same as molecule.insert_frames(nil, coordinates).
8074  */
8075 static VALUE
8076 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8077 {
8078         VALUE vals[3];
8079         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8080         vals[0] = Qnil;
8081         return s_Molecule_InsertFrames(3, vals, self);
8082 }
8083
8084 /*
8085  *  call-seq:
8086  *     remove_frames(IntGroup, wantCoordinates = false)
8087  *
8088  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8089  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8090  *  removed frames is returned if operation is successful.
8091  */
8092 static VALUE
8093 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8094 {
8095         VALUE val, flag;
8096         VALUE retval;
8097     Molecule *mol;
8098         IntGroup *ig;
8099         int count;
8100     Data_Get_Struct(self, Molecule, mol);
8101         rb_scan_args(argc, argv, "11", &val, &flag);
8102         ig = IntGroupFromValue(val);
8103         count = IntGroupGetCount(ig);
8104         if (RTEST(flag)) {
8105                 /*  Create return value before removing frames  */
8106                 VALUE coords;
8107                 int i, j, n;
8108                 Atom *ap;
8109                 Vector v;
8110                 retval = rb_ary_new2(count);
8111                 for (i = 0; i < count; i++) {
8112                         n = IntGroupGetNthPoint(ig, i);
8113                         coords = rb_ary_new2(mol->natoms);
8114                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8115                                 if (n < ap->nframes && n != mol->cframe)
8116                                         v = ap->frames[n];
8117                                 else v = ap->r;
8118                                 rb_ary_push(coords, ValueFromVector(&v));
8119                         }
8120                         rb_ary_push(retval, coords);
8121                 }
8122         } else retval = Qtrue;
8123         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8124                 return retval;
8125         else return Qnil;
8126 }
8127
8128 /*
8129  *  call-seq:
8130  *     each_frame {|n| ...}
8131  *
8132  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8133  *  the frame number. After completion, the original frame number is restored.
8134  */
8135 static VALUE
8136 s_Molecule_EachFrame(VALUE self)
8137 {
8138         int i, cframe, nframes;
8139     Molecule *mol;
8140     Data_Get_Struct(self, Molecule, mol);
8141         cframe = mol->cframe;
8142         nframes = MoleculeGetNumberOfFrames(mol);
8143         if (nframes > 0) {
8144                 for (i = 0; i < nframes; i++) {
8145                         MoleculeSelectFrame(mol, i, 1);
8146                         rb_yield(INT2NUM(i));
8147                 }
8148                 MoleculeSelectFrame(mol, cframe, 1);
8149         }
8150     return self;
8151 }
8152
8153 /*
8154  *  call-seq:
8155  *     get_coord_from_frame(index, group = nil)
8156  *
8157  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8158  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8159  *  copied; now they are always copied)
8160  */
8161 static VALUE
8162 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8163 {
8164         Molecule *mol;
8165         VALUE ival, gval, cval;
8166         Int index, i, j, n, nn;
8167         IntGroup *ig;
8168         IntGroupIterator iter;
8169         Atom *ap;
8170         Vector *vp;
8171     Data_Get_Struct(self, Molecule, mol);
8172         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8173         if (argc == 3)
8174                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8175         index = NUM2INT(rb_Integer(ival));
8176         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8177                 if (n == 0)
8178                         rb_raise(rb_eMolbyError, "No frame is present");
8179                 else
8180                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8181         }
8182         if (gval == Qnil) {
8183                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8184         } else {
8185                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8186         }
8187         n = IntGroupGetCount(ig);
8188         if (n > 0) {
8189                 vp = (Vector *)calloc(sizeof(Vector), n);
8190                 IntGroupIteratorInit(ig, &iter);
8191                 j = 0;
8192                 nn = 0;
8193                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8194                         ap = ATOM_AT_INDEX(mol->atoms, i);
8195                         if (index < ap->nframes) {
8196                                 vp[j] = ap->frames[index];
8197                                 nn++;
8198                         } else {
8199                                 vp[j] = ap->r;
8200                         }
8201                         j++;
8202                 }
8203                 if (nn > 0)
8204                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8205                 free(vp);
8206                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8207                         vp = mol->frame_cells + index * 4;
8208                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8209                 }
8210                 IntGroupIteratorRelease(&iter);
8211         }
8212         /*  Copy the extra properties  */
8213         IntGroupRelease(ig);
8214         for (i = 0; i < mol->nmolprops; i++) {
8215                 Double *dp = (Double *)malloc(sizeof(Double));
8216                 ig = IntGroupNew();
8217                 IntGroupAdd(ig, mol->cframe, 1);
8218                 *dp = mol->molprops[i].propvals[index];
8219                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8220                 free(dp);
8221                 IntGroupRelease(ig);
8222         }
8223         
8224         return self;
8225 }
8226
8227 /*
8228  *  call-seq:
8229  *     reorder_frames(old_indices)
8230  *
8231  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8232  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8233  *  same as the old frames 2/0/1, respectively.
8234  *  The argument must have the same number of integers as the number of frames.
8235  */
8236 static VALUE
8237 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8238 {
8239         Molecule *mol;
8240         Int *ip, *ip2, i, n, nframes;
8241     Data_Get_Struct(self, Molecule, mol);
8242         aval = rb_ary_to_ary(aval);
8243         nframes = MoleculeGetNumberOfFrames(mol);
8244         if (RARRAY_LEN(aval) != nframes)
8245                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8246         ip2 = (Int *)calloc(sizeof(Int), nframes);
8247         ip = (Int *)calloc(sizeof(Int), nframes);
8248         for (i = 0; i < nframes; i++) {
8249                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8250                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8251                         free(ip2);
8252                         free(ip);
8253                         if (n < 0 || n >= nframes)
8254                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8255                         else
8256                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8257                 }
8258                 ip2[n] = 1;
8259                 ip[i] = n;
8260         }
8261         free(ip2);
8262         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8263         free(ip);
8264         return self;
8265 }
8266
8267 #pragma mark ------ Fragments ------
8268
8269 /*
8270  *  call-seq:
8271  *     fragment(n1, *exatoms)  -> IntGroup
8272  *     fragment(group, *exatoms)  -> IntGroup
8273  *
8274  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8275  *  those atoms will not be counted during the search.
8276  */
8277 static VALUE
8278 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8279 {
8280     Molecule *mol;
8281         IntGroup *baseg, *ig, *exatoms;
8282         int n;
8283         volatile VALUE nval, exval;
8284     Data_Get_Struct(self, Molecule, mol);
8285         rb_scan_args(argc, argv, "1*", &nval, &exval);
8286         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8287                 baseg = NULL;
8288                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8289         } else {
8290                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8291         }
8292         if (RARRAY_LEN(exval) == 0) {
8293                 exatoms = NULL;
8294         } else {
8295                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8296                 Data_Get_Struct(exval, IntGroup, exatoms);
8297         }
8298         if (baseg == NULL) {
8299                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8300         } else {
8301                 IntGroupIterator iter;
8302                 IntGroupIteratorInit(baseg, &iter);
8303                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8304                         ig = IntGroupNew();
8305                 } else {
8306                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8307                         if (ig != NULL) {
8308                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8309                                         IntGroup *subg;
8310                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8311                                         if (subg != NULL) {
8312                                                 IntGroupAddIntGroup(ig, subg);
8313                                                 IntGroupRelease(subg);
8314                                         }
8315                                 }
8316                         }
8317                 }
8318                 IntGroupIteratorRelease(&iter);
8319                 IntGroupRelease(baseg);
8320         }
8321         if (ig == NULL)
8322                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8323         nval = ValueFromIntGroup(ig);
8324         IntGroupRelease(ig);
8325         return nval;
8326 }
8327
8328 /*
8329  *  call-seq:
8330  *     fragments(exclude = nil)
8331  *
8332  *  Returns the fragments as an array of IntGroups. 
8333  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8334  *  in defining the fragment.
8335  */
8336 static VALUE
8337 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8338 {
8339     Molecule *mol;
8340         IntGroup *ag, *fg, *eg;
8341         VALUE gval, exval, retval;
8342     Data_Get_Struct(self, Molecule, mol);
8343         if (mol == NULL)
8344                 return Qnil;
8345         if (mol->natoms == 0)
8346                 return rb_ary_new();
8347         rb_scan_args(argc, argv, "01", &exval);
8348         if (exval == Qnil)
8349                 eg = NULL;
8350         else
8351                 eg = IntGroupFromValue(exval);
8352         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8353         if (eg != NULL)
8354                 IntGroupRemoveIntGroup(ag, eg);
8355         retval = rb_ary_new();
8356         while (IntGroupGetCount(ag) > 0) {
8357                 int n = IntGroupGetNthPoint(ag, 0);
8358                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8359                 if (fg == NULL)
8360                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8361                 gval = ValueFromIntGroup(fg);
8362                 rb_ary_push(retval, gval);
8363                 IntGroupRemoveIntGroup(ag, fg);
8364                 IntGroupRelease(fg);
8365         }
8366         IntGroupRelease(ag);
8367         if (eg != NULL)
8368                 IntGroupRelease(eg);
8369         return retval;
8370 }
8371
8372 /*
8373  *  call-seq:
8374  *     each_fragment(exclude = nil) {|group| ...}
8375  *
8376  *  Execute the block, with the IntGroup object for each fragment as the argument.
8377  *  Atoms or bonds should not be added or removed during the execution of the block.
8378  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8379  *  in defining the fragment.
8380  */
8381 static VALUE
8382 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8383 {
8384     Molecule *mol;
8385         IntGroup *ag, *fg, *eg;
8386         VALUE gval, exval;
8387     Data_Get_Struct(self, Molecule, mol);
8388         if (mol == NULL || mol->natoms == 0)
8389                 return self;
8390         rb_scan_args(argc, argv, "01", &exval);
8391         if (exval == Qnil)
8392                 eg = NULL;
8393         else
8394                 eg = IntGroupFromValue(exval);
8395         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8396         if (eg != NULL)
8397                 IntGroupRemoveIntGroup(ag, eg);
8398         while (IntGroupGetCount(ag) > 0) {
8399                 int n = IntGroupGetNthPoint(ag, 0);
8400                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8401                 if (fg == NULL)
8402                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8403                 gval = ValueFromIntGroup(fg);
8404                 rb_yield(gval);
8405                 IntGroupRemoveIntGroup(ag, fg);
8406                 IntGroupRelease(fg);
8407         }
8408         IntGroupRelease(ag);
8409         if (eg != NULL)
8410                 IntGroupRelease(eg);
8411         return self;
8412 }
8413
8414 /*
8415  *  call-seq:
8416  *     detachable?(group)  -> [n1, n2]
8417  *
8418  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8419  *  of the molecule via only one bond. If it is, then the indices of the atoms
8420  *  belonging to the bond is returned, the first element being the atom included
8421  *  in the fragment. Otherwise, Qnil is returned.
8422  */
8423 static VALUE
8424 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8425 {
8426         Molecule *mol;
8427         IntGroup *ig;
8428         int n1, n2;
8429         VALUE retval;
8430     Data_Get_Struct(self, Molecule, mol);
8431         ig = s_Molecule_AtomGroupFromValue(self, gval);
8432         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8433                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8434         } else retval = Qnil;
8435         IntGroupRelease(ig);
8436         return retval;
8437 }
8438
8439 /*
8440  *  call-seq:
8441  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8442  *
8443  *  Returns an array of bonds that connect an atom in the group and an atom out
8444  *  of the group. The first atom in the bond always belongs to the group. If no
8445  *  such bonds are present, an empty array is returned.
8446  */
8447 static VALUE
8448 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8449 {
8450         Molecule *mol;
8451         IntGroup *ig, *bg;
8452         VALUE gval, retval;
8453     Data_Get_Struct(self, Molecule, mol);
8454         rb_scan_args(argc, argv, "01", &gval);
8455         if (gval == Qnil) {
8456                 ig = MoleculeGetSelection(mol);
8457                 if (ig != NULL)
8458                         IntGroupRetain(ig);
8459         } else {
8460                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8461         }
8462         retval = rb_ary_new();
8463         if (ig == NULL)
8464                 return retval;
8465         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8466         if (bg != NULL) {
8467                 IntGroupIterator iter;
8468                 Int i;
8469                 IntGroupIteratorInit(bg, &iter);
8470                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8471                         /*  The atoms at the border  */
8472                         Int n1, n2;
8473                         n1 = mol->bonds[i * 2];
8474                         n2 = mol->bonds[i * 2 + 1];
8475                         if (IntGroupLookupPoint(ig, n1) < 0) {
8476                                 int w = n1;
8477                                 n1 = n2;
8478                                 n2 = w;
8479                                 if (IntGroupLookupPoint(ig, n1) < 0)
8480                                         continue;  /*  Actually this is an internal error  */
8481                         }
8482                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8483                 }
8484                 IntGroupIteratorRelease(&iter);
8485         }
8486         IntGroupRelease(bg);
8487         IntGroupRelease(ig);
8488         return retval;
8489 }
8490
8491 /*  Calculate the transform that moves the current coordinates to the reference
8492  coordinates with least displacements.   */
8493 static Double
8494 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8495 {
8496         Atom *ap, *ap1;
8497         Int natoms, nn;
8498         Vector org1, org2;
8499         Int i, in, j, k;
8500         Double w, w1;
8501         Mat33 r, q, u;
8502         Double eigen_val[3];
8503         Vector eigen_vec[3];
8504         Vector s[3];
8505         IntGroupIterator iter;
8506
8507         natoms = mol->natoms;
8508         ap = mol->atoms;
8509         IntGroupIteratorInit(ig, &iter);
8510         
8511         /*  Calculate the weighted center  */
8512         VecZero(org1);
8513         VecZero(org2);
8514         w = 0.0;
8515         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8516                 ap1 = ATOM_AT_INDEX(ap, in);
8517                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8518                 VecScaleInc(org1, ap1->r, w1);
8519                 VecScaleInc(org2, ref[i], w1);
8520                 w += w1;
8521         }
8522         w = 1.0 / w;
8523         VecScaleSelf(org1, w);
8524         VecScaleSelf(org2, w);
8525
8526     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8527     /*  Matrix to diagonalize = R * tR    */
8528         memset(r, 0, sizeof(Mat33));
8529         memset(q, 0, sizeof(Mat33));
8530         memset(u, 0, sizeof(Mat33));
8531         nn = 0;
8532         IntGroupIteratorReset(&iter);
8533         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8534                 Vector v1, v2;
8535                 ap1 = ATOM_AT_INDEX(ap, in);
8536                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8537                 w1 *= w1;
8538                 VecSub(v1, ap1->r, org1);
8539                 VecSub(v2, ref[i], org2);
8540                 r[0] += w1 * v1.x * v2.x;
8541                 r[1] += w1 * v1.y * v2.x;
8542                 r[2] += w1 * v1.z * v2.x;
8543                 r[3] += w1 * v1.x * v2.y;
8544                 r[4] += w1 * v1.y * v2.y;
8545                 r[5] += w1 * v1.z * v2.y;
8546                 r[6] += w1 * v1.x * v2.z;
8547                 r[7] += w1 * v1.y * v2.z;
8548                 r[8] += w1 * v1.z * v2.z;
8549                 nn++;
8550         }
8551         for (i = 0; i < 9; i++)
8552                 r[i] /= (nn * nn);
8553         for (i = 0; i < 3; i++) {
8554                 for (j = 0; j < 3; j++) {
8555                         for (k = 0; k < 3; k++) {
8556                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8557                         }
8558                 }
8559         }
8560         
8561         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8562                 IntGroupIteratorRelease(&iter);
8563                 return -1.0;  /*  Cannot determine the eigenvector  */
8564         }
8565
8566     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8567     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8568         MatrixTranspose(r, r);
8569         for (i = 0; i < 3; i++) {
8570                 MatrixVec(&s[i], r, &eigen_vec[i]);
8571                 w1 = 1.0 / sqrt(eigen_val[i]);
8572                 VecScaleSelf(s[i], w1);
8573         }
8574         for (k = 0; k < 3; k++) {
8575                 u[0] += s[k].x * eigen_vec[k].x;
8576                 u[1] += s[k].y * eigen_vec[k].x;
8577                 u[2] += s[k].z * eigen_vec[k].x;
8578                 u[3] += s[k].x * eigen_vec[k].y;
8579                 u[4] += s[k].y * eigen_vec[k].y;
8580                 u[5] += s[k].z * eigen_vec[k].y;
8581                 u[6] += s[k].x * eigen_vec[k].z;
8582                 u[7] += s[k].y * eigen_vec[k].z;
8583                 u[8] += s[k].z * eigen_vec[k].z;
8584         }
8585         
8586         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8587         MatrixVec(&org1, u, &org1);
8588         VecDec(org2, org1);
8589         for (i = 0; i < 9; i++)
8590                 trans[i] = u[i];
8591         trans[9] = org2.x;
8592         trans[10] = org2.y;
8593         trans[11] = org2.z;
8594         
8595         /*  Calculate rmsd  */
8596         IntGroupIteratorReset(&iter);
8597         w = 0.0;
8598         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8599                 Vector tv;
8600                 ap1 = ATOM_AT_INDEX(ap, in);
8601                 TransformVec(&tv, trans, &ap1->r);
8602                 VecDec(tv, ref[i]);
8603                 w += VecLength2(tv);
8604         }
8605         w = sqrt(w / nn);
8606         IntGroupIteratorRelease(&iter);
8607         return w;
8608 }
8609
8610 /*
8611  *  call-seq:
8612  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8613  *
8614  *  Calculate the transform to fit the given group to the set of reference coordinates.
8615  *  The reference coordinates ref is given as either a frame number, an array of
8616  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8617  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8618  *  Return values are the transform (that converts the present coordinates to the
8619  *  target coordinates) and root mean square deviation (without weight).
8620  */
8621 static VALUE
8622 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8623 {
8624         Molecule *mol;
8625         Atom *ap;
8626         VALUE gval, rval, wval;
8627         IntGroup *ig;
8628         IntGroupIterator iter;
8629         int nn, errno, i, j, in, status;
8630         Vector *ref;
8631         Double *weights, dval[3];
8632         Transform tr;
8633
8634         Data_Get_Struct(self, Molecule, mol);
8635         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8636         if (gval == Qnil)
8637                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8638         else
8639                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8640         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8641                 IntGroupRelease(ig);
8642                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8643         }
8644         ref = (Vector *)calloc(sizeof(Vector), nn);
8645         weights = (Double *)calloc(sizeof(Double), nn);
8646         IntGroupIteratorInit(ig, &iter);
8647         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8648                 int fn = NUM2INT(rb_Integer(rval));
8649                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8650                         errno = 1;
8651                         status = fn;
8652                         goto err;
8653                 }
8654                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8655                         ap = ATOM_AT_INDEX(mol->atoms, in);
8656                         if (fn < ap->nframes)
8657                                 ref[i] = ap->frames[fn];
8658                         else ref[i] = ap->r;
8659                 }
8660         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8661                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8662                 if (m->row * m->column < nn * 3) {
8663                         errno = 2;
8664                         goto err;
8665                 }
8666                 for (i = 0; i < nn; i++) {
8667                         ref[i].x = m->data[i * 3];
8668                         ref[i].y = m->data[i * 3 + 1];
8669                         ref[i].z = m->data[i * 3 + 2];
8670                 }
8671         } else {
8672                 VALUE aval;
8673                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8674                 if (status != 0) {
8675                         errno = 3;
8676                         goto err;
8677                 }
8678                 if (RARRAY_LEN(rval) < nn) {
8679                         errno = 2;
8680                         goto err;
8681                 }
8682                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8683                         /*  Array of 3*nn numbers  */
8684                         if (RARRAY_LEN(rval) < nn * 3) {
8685                                 errno = 2;
8686                                 goto err;
8687                         }
8688                         for (i = 0; i < nn; i++) {
8689                                 for (j = 0; j < 3; j++) {
8690                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8691                                         if (status != 0) {
8692                                                 errno = 3;
8693                                                 goto err;
8694                                         }
8695                                         dval[j] = NUM2DBL(aval);
8696                                 }
8697                                 ref[i].x = dval[0];
8698                                 ref[i].y = dval[1];
8699                                 ref[i].z = dval[2];
8700                         }
8701                 } else {
8702                         /*  Array of nn Vector3Ds or Arrays  */
8703                         for (i = 0; i < nn; i++) {
8704                                 aval = (RARRAY_PTR(rval))[i];
8705                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8706                                         VectorFromValue(aval, &ref[i]);
8707                                 } else {
8708                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8709                                         if (status != 0) {
8710                                                 errno = 3;
8711                                                 goto err;
8712                                         }
8713                                         if (RARRAY_LEN(aval) < 3) {
8714                                                 errno = 4;
8715                                                 status = i;
8716                                                 goto err;
8717                                         }
8718                                         for (j = 0; j < 3; j++) {
8719                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8720                                                 if (status != 0) {
8721                                                         errno = 3;
8722                                                         goto err;
8723                                                 }
8724                                                 dval[j] = NUM2DBL(aaval);
8725                                         }
8726                                         ref[i].x = dval[0];
8727                                         ref[i].y = dval[1];
8728                                         ref[i].z = dval[2];
8729                                 }
8730                         }
8731                 }
8732         }
8733         if (wval == Qnil) {
8734                 /*  Use atomic weights  */
8735                 IntGroupIteratorReset(&iter);
8736                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8737                         ap = ATOM_AT_INDEX(mol->atoms, in);
8738                         weights[i] = ap->weight;
8739                 }
8740         } else {
8741                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8742                 if (status != 0) {
8743                         errno = 3;
8744                         goto err;
8745                 }
8746                 if (RARRAY_LEN(wval) < nn) {
8747                         errno = 5;
8748                         goto err;
8749                 }
8750                 for (i = 0; i < nn; i++) {
8751                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8752                         if (status != 0) {
8753                                 errno = 3;
8754                                 goto err;
8755                         }
8756                         weights[i] = NUM2DBL(wwval);
8757                 }
8758         }
8759         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8760         if (dval[0] < 0) {
8761                 errno = 6;
8762                 goto err;
8763         }
8764         errno = 0;
8765 err:
8766         IntGroupIteratorRelease(&iter);
8767         free(ref);
8768         free(weights);
8769         if (errno == 0) {
8770                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8771         } else if (errno == 1) {
8772                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8773         } else if (errno == 2) {
8774                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8775         } else if (errno == 3) {
8776                 rb_jump_tag(status);
8777         } else if (errno == 4) {
8778                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8779         } else if (errno == 5) {
8780                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8781         } else if (errno == 6) {
8782                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8783         }
8784         return Qnil;  /*  Not reached  */
8785 }
8786
8787 #pragma mark ------ Screen Display ------
8788
8789 /*
8790  *  call-seq:
8791  *     display
8792  *
8793  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8794  */
8795 static VALUE
8796 s_Molecule_Display(VALUE self)
8797 {
8798     Molecule *mol;
8799     Data_Get_Struct(self, Molecule, mol);
8800         if (mol->mview != NULL)
8801                 MainViewCallback_display(mol->mview);
8802         return Qnil;
8803 }
8804
8805 /*
8806  *  call-seq:
8807  *     make_front
8808  *
8809  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8810  */
8811 static VALUE
8812 s_Molecule_MakeFront(VALUE self)
8813 {
8814     Molecule *mol;
8815     Data_Get_Struct(self, Molecule, mol);
8816         if (mol->mview != NULL)
8817                 MainViewCallback_makeFront(mol->mview);
8818         return Qnil;
8819 }
8820
8821 /*
8822  *  call-seq:
8823  *     update_enabled? -> bool
8824  *
8825  *  Returns true if screen update is enabled; otherwise no.
8826  */
8827 static VALUE
8828 s_Molecule_UpdateEnabled(VALUE self)
8829 {
8830     Molecule *mol;
8831     Data_Get_Struct(self, Molecule, mol);
8832         if (mol->mview != NULL && !mol->mview->freezeScreen)
8833                 return Qtrue;
8834         else return Qfalse;
8835 }
8836
8837 /*
8838  *  call-seq:
8839  *     update_enabled = bool
8840  *
8841  *  Enable or disable screen update. This is effective for automatic update on modification.
8842  *  Explicit call to molecule.display() always updates the screen.
8843  */
8844 static VALUE
8845 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8846 {
8847     Molecule *mol;
8848     Data_Get_Struct(self, Molecule, mol);
8849         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8850         if (mol->mview != NULL)
8851                 mol->mview->freezeScreen = (val == Qfalse);
8852         else val = Qfalse;
8853         return val;
8854 }
8855
8856 /*
8857  *  call-seq:
8858  *     show_unitcell
8859  *     show_unitcell(bool)
8860  *     show_unitcell = bool
8861  *
8862  *  Set the flag whether to show the unit cell. If no argument is given, the
8863  *  current flag is returned.
8864  */
8865 static VALUE
8866 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8867 {
8868     Molecule *mol;
8869     Data_Get_Struct(self, Molecule, mol);
8870         if (mol->mview == NULL)
8871                 return Qnil;
8872         if (argc > 0) {
8873                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8874                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8875         }
8876         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8877 }
8878
8879 /*
8880  *  call-seq:
8881  *     show_hydrogens
8882  *     show_hydrogens(bool)
8883  *     show_hydrogens = bool
8884  *
8885  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8886  *  current flag is returned.
8887  */
8888 static VALUE
8889 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8890 {
8891     Molecule *mol;
8892     Data_Get_Struct(self, Molecule, mol);
8893         if (mol->mview == NULL)
8894                 return Qnil;
8895         if (argc > 0) {
8896                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8897                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8898         }
8899         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8900 }
8901
8902 /*
8903  *  call-seq:
8904  *     show_dummy_atoms
8905  *     show_dummy_atoms(bool)
8906  *     show_dummy_atoms = bool
8907  *
8908  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8909  *  current flag is returned.
8910  */
8911 static VALUE
8912 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8913 {
8914     Molecule *mol;
8915     Data_Get_Struct(self, Molecule, mol);
8916         if (mol->mview == NULL)
8917                 return Qnil;
8918         if (argc > 0) {
8919                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8920                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8921         }
8922         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8923 }
8924
8925 /*
8926  *  call-seq:
8927  *     show_expanded
8928  *     show_expanded(bool)
8929  *     show_expanded = bool
8930  *
8931  *  Set the flag whether to show the expanded atoms. If no argument is given, the
8932  *  current flag is returned.
8933  */
8934 static VALUE
8935 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8936 {
8937     Molecule *mol;
8938     Data_Get_Struct(self, Molecule, mol);
8939         if (mol->mview == NULL)
8940                 return Qnil;
8941         if (argc > 0) {
8942                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8943                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8944         }
8945         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8946 }
8947
8948 /*
8949  *  call-seq:
8950  *     show_ellipsoids
8951  *     show_ellipsoids(bool)
8952  *     show_ellipsoids = bool
8953  *
8954  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8955  *  current flag is returned.
8956  */
8957 static VALUE
8958 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8959 {
8960     Molecule *mol;
8961     Data_Get_Struct(self, Molecule, mol);
8962         if (mol->mview == NULL)
8963                 return Qnil;
8964         if (argc > 0) {
8965                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8966                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8967         }
8968         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8969 }
8970
8971 /*
8972  *  call-seq:
8973  *     is_atom_visible(index)  -> Boolean
8974  *
8975  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8976  *  as well as the molecule attributes (showHydrogens, etc.)
8977  */
8978 static VALUE
8979 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8980 {
8981         Molecule *mol;
8982         Int idx;
8983         Atom *ap;
8984     Data_Get_Struct(self, Molecule, mol);
8985         idx = s_Molecule_AtomIndexFromValue(mol, ival);
8986         if (idx < 0 || idx >= mol->natoms)
8987                 return Qnil;
8988         ap = ATOM_AT_INDEX(mol->atoms, idx);
8989         if (mol->mview != NULL) {
8990                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8991                         return Qfalse;
8992                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8993                         return Qfalse;
8994                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8995                         return Qfalse;
8996         }
8997         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8998 }
8999
9000 /*
9001  *  call-seq:
9002  *     hidden_atoms       -> IntGroup
9003  *
9004  *  Returns the currently hidden atoms.
9005  */
9006 static VALUE
9007 s_Molecule_HiddenAtoms(VALUE self)
9008 {
9009         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9010         return Qnil;  /*  Not reached  */
9011 }
9012
9013 /*
9014  *  call-seq:
9015  *     set_hidden_atoms(IntGroup)
9016  *     self.hidden_atoms = IntGroup
9017  *
9018  *  Hide the specified atoms. This operation is _not_ undoable.
9019  */
9020 static VALUE
9021 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9022 {
9023         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9024         return Qnil;  /*  Not reached  */
9025 }
9026
9027 /*
9028  *  call-seq:
9029  *     show_graphite -> Integer
9030  *     show_graphite = Integer
9031  *     show_graphite = boolean
9032  *
9033  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9034  *  number of rings to display for each direction.
9035  *  If the argument is boolean, only the show/hide flag is set.
9036  */
9037 static VALUE
9038 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9039 {
9040     Molecule *mol;
9041     Data_Get_Struct(self, Molecule, mol);
9042         if (mol->mview == NULL)
9043                 return Qnil;
9044         if (argc > 0) {
9045                 if (argv[0] == Qnil || argv[0] == Qfalse)
9046                         mol->mview->showGraphiteFlag = 0;
9047                 else if (argv[0] == Qtrue)
9048                         mol->mview->showGraphiteFlag = 1;
9049                 else {
9050                         int n = NUM2INT(rb_Integer(argv[0]));
9051                         if (n < 0)
9052                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9053                         mol->mview->showGraphite = n;
9054                 }
9055                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9056         }
9057         return INT2NUM(mol->mview->showGraphite);
9058 }
9059
9060 /*
9061  *  call-seq:
9062  *     show_graphite? -> boolean
9063  *
9064  *  Return whether the graphite is set visible or not.
9065 */
9066 static VALUE
9067 s_Molecule_ShowGraphiteFlag(VALUE self)
9068 {
9069     Molecule *mol;
9070     Data_Get_Struct(self, Molecule, mol);
9071         if (mol->mview == NULL)
9072                 return Qnil;
9073         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9074 }
9075         
9076 /*
9077  *  call-seq:
9078  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9079  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9080  *     show_periodic_image = boolean
9081  *
9082  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9083  *  set but no visual effects are observed.
9084  *  If the argument is boolean, only the show/hide flag is modified.
9085  */
9086 static VALUE
9087 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9088 {
9089     Molecule *mol;
9090         VALUE val;
9091         int ival[6];
9092         int i;
9093     Data_Get_Struct(self, Molecule, mol);
9094         if (mol->mview == NULL)
9095                 return Qnil;
9096         rb_scan_args(argc, argv, "01", &val);
9097         if (argc > 0) {
9098                 /*  Change current settings  */
9099                 if (val == Qnil || val == Qfalse)
9100                         mol->mview->showPeriodicImageFlag = 0;
9101                 else if (val == Qtrue)
9102                         mol->mview->showPeriodicImageFlag = 1;
9103                 else {
9104                         val = rb_ary_to_ary(val);
9105                         for (i = 0; i < 6; i++) {
9106                                 if (i < RARRAY_LEN(val))
9107                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9108                         }
9109                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9110                                 rb_raise(rb_eMolbyError, "bad arguments");
9111                         for (i = 0; i < 6; i++)
9112                                 mol->mview->showPeriodicImage[i] = ival[i];
9113                 }
9114                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9115         }
9116         val = rb_ary_new();
9117         for (i = 0; i < 6; i++)
9118                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9119         return val;
9120 }
9121
9122 /*
9123  *  call-seq:
9124  *     show_periodic_image? -> boolean
9125  *
9126  *  Return whether the periodic images are set to visible or not. This flag is
9127  *  independent from the show_periodic_image settings.
9128  */
9129 static VALUE
9130 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9131 {
9132     Molecule *mol;
9133     Data_Get_Struct(self, Molecule, mol);
9134         if (mol->mview == NULL)
9135                 return Qnil;
9136         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9137 }
9138
9139 /*
9140  *  call-seq:
9141  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9142  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9143  *     show_rotation_center = boolean
9144  *
9145  *  Set to show the rotation center of the screen.
9146  *  If the argument is boolean, only the show/hide flag is modified.
9147  */
9148 static VALUE
9149 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9150 {
9151     Molecule *mol;
9152     Data_Get_Struct(self, Molecule, mol);
9153         if (mol->mview == NULL)
9154                 return Qnil;
9155         if (argc > 0) {
9156                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9157                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9158         }
9159         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9160 }
9161
9162 /*
9163  *  call-seq:
9164  *     line_mode
9165  *     line_mode(bool)
9166  *     line_mode = bool
9167  *
9168  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9169  *  current flag is returned.
9170  */
9171 static VALUE
9172 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9173 {
9174     Molecule *mol;
9175     Data_Get_Struct(self, Molecule, mol);
9176         if (mol->mview == NULL)
9177                 return Qnil;
9178         if (argc > 0) {
9179                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9180                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9181         }
9182         return (mol->mview->lineMode ? Qtrue : Qfalse);
9183 }
9184
9185 /*
9186  *  call-seq:
9187  *     atom_radius = float
9188  *     atom_radius
9189  *
9190  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9191  *  (Default = 0.4)
9192  *  If no argument is given, the current value is returned.
9193  */
9194 static VALUE
9195 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9196 {
9197     Molecule *mol;
9198     Data_Get_Struct(self, Molecule, mol);
9199         if (mol->mview == NULL)
9200                 return Qnil;
9201         if (argc > 0) {
9202                 double rad = NUM2DBL(rb_Float(argv[0]));
9203                 if (rad > 0.0) {
9204                         mol->mview->atomRadius = rad;
9205                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9206                 }
9207                 return argv[0];
9208         }
9209         return rb_float_new(mol->mview->atomRadius);
9210 }
9211
9212 /*
9213  *  call-seq:
9214  *     bond_radius = float
9215  *     bond_radius
9216  *
9217  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9218  *  If no argument is given, the current value is returned.
9219  */
9220 static VALUE
9221 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9222 {
9223     Molecule *mol;
9224     Data_Get_Struct(self, Molecule, mol);
9225         if (mol->mview == NULL)
9226                 return Qnil;
9227         if (argc > 0) {
9228                 double rad = NUM2DBL(rb_Float(argv[0]));
9229                 if (rad > 0.0) {
9230                         mol->mview->bondRadius = rad;
9231                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9232                 }
9233                 return argv[0];
9234         }
9235         return rb_float_new(mol->mview->bondRadius);
9236 }
9237
9238 /*
9239  *  call-seq:
9240  *     atom_resolution = integer
9241  *     atom_resolution
9242  *
9243  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9244  *  (Default = 12; minimum = 6)
9245  *  If no argument is given, the current value is returned.
9246  */
9247 static VALUE
9248 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9249 {
9250     Molecule *mol;
9251     Data_Get_Struct(self, Molecule, mol);
9252         if (mol->mview == NULL)
9253                 return Qnil;
9254         if (argc > 0) {
9255                 int res = NUM2INT(rb_Integer(argv[0]));
9256                 if (res < 6)
9257                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9258                 mol->mview->atomResolution = res;
9259                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9260                 return INT2NUM(res);
9261         }
9262         return INT2NUM(mol->mview->atomResolution);
9263 }
9264
9265 /*
9266  *  call-seq:
9267  *     bond_resolution = integer
9268  *     bond_resolution
9269  *
9270  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9271  *  (Default = 8; minimum = 4)
9272  *  If no argument is given, the current value is returned.
9273  */
9274 static VALUE
9275 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9276 {
9277     Molecule *mol;
9278     Data_Get_Struct(self, Molecule, mol);
9279         if (mol->mview == NULL)
9280                 return Qnil;
9281         if (argc > 0) {
9282                 int res = NUM2INT(rb_Integer(argv[0]));
9283                 if (res < 4)
9284                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9285                 mol->mview->bondResolution = res;
9286                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9287                 return INT2NUM(res);
9288         }
9289         return INT2NUM(mol->mview->bondResolution);
9290 }
9291
9292 /*
9293  *  call-seq:
9294  *     resize_to_fit
9295  *
9296  *  Resize the model drawing to fit in the window.
9297  */
9298 static VALUE
9299 s_Molecule_ResizeToFit(VALUE self)
9300 {
9301     Molecule *mol;
9302     Data_Get_Struct(self, Molecule, mol);
9303         if (mol->mview != NULL)
9304                 MainView_resizeToFit(mol->mview);
9305         return self;    
9306 }
9307
9308 /*
9309  *  call-seq:
9310  *     get_view_rotation -> [[ax, ay, az], angle]
9311  *
9312  *  Get the current rotation for the view. Angle is in degree, not radian.
9313  */
9314 static VALUE
9315 s_Molecule_GetViewRotation(VALUE self)
9316 {
9317     Molecule *mol;
9318         double f[4];
9319         Vector v;
9320     Data_Get_Struct(self, Molecule, mol);
9321         if (mol->mview == NULL)
9322                 return Qnil;
9323         TrackballGetRotate(mol->mview->track, f);
9324         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9325         v.x = f[1];
9326         v.y = f[2];
9327         v.z = f[3];
9328         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9329 }
9330
9331 /*
9332  *  call-seq:
9333  *     get_view_scale -> float
9334  *
9335  *  Get the current scale for the view.
9336  */
9337 static VALUE
9338 s_Molecule_GetViewScale(VALUE self)
9339 {
9340     Molecule *mol;
9341     Data_Get_Struct(self, Molecule, mol);
9342         if (mol->mview == NULL)
9343                 return Qnil;
9344         return rb_float_new(TrackballGetScale(mol->mview->track));
9345 }
9346
9347 /*
9348  *  call-seq:
9349  *     get_view_center -> Vector
9350  *
9351  *  Get the current center point of the view.
9352  */
9353 static VALUE
9354 s_Molecule_GetViewCenter(VALUE self)
9355 {
9356     Molecule *mol;
9357         double f[4];
9358         Vector v;
9359     Data_Get_Struct(self, Molecule, mol);
9360         if (mol->mview == NULL)
9361                 return Qnil;
9362         TrackballGetTranslate(mol->mview->track, f);
9363         v.x = -f[0] * mol->mview->dimension;
9364         v.y = -f[1] * mol->mview->dimension;
9365         v.z = -f[2] * mol->mview->dimension;
9366         return ValueFromVector(&v);
9367 }
9368
9369 /*
9370  *  call-seq:
9371  *     set_view_rotation([ax, ay, az], angle) -> self
9372  *
9373  *  Set the current rotation for the view. Angle is in degree, not radian.
9374  */
9375 static VALUE
9376 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9377 {
9378     Molecule *mol;
9379         double f[4];
9380         Vector v;
9381     Data_Get_Struct(self, Molecule, mol);
9382         if (mol->mview == NULL)
9383                 return Qnil;
9384         VectorFromValue(aval, &v);
9385         if (NormalizeVec(&v, &v))
9386                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9387         f[1] = v.x;
9388         f[2] = v.y;
9389         f[3] = v.z;
9390         f[0] = -NUM2DBL(rb_Float(angval));
9391         TrackballSetRotate(mol->mview->track, f);
9392         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9393         return self;
9394 }
9395
9396 /*
9397  *  call-seq:
9398  *     set_view_scale(scale) -> self
9399  *
9400  *  Set the current scale for the view.
9401  */
9402 static VALUE
9403 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9404 {
9405     Molecule *mol;
9406     Data_Get_Struct(self, Molecule, mol);
9407         if (mol->mview == NULL)
9408                 return Qnil;
9409         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9410         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9411         return self;
9412 }
9413
9414 /*
9415  *  call-seq:
9416  *     set_view_center(vec) -> self
9417  *
9418  *  Set the current center point of the view.
9419  */
9420 static VALUE
9421 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9422 {
9423     Molecule *mol;
9424         Vector v;
9425         double f[4];
9426     Data_Get_Struct(self, Molecule, mol);
9427         if (mol->mview == NULL)
9428                 return Qnil;
9429         VectorFromValue(aval, &v);
9430         f[0] = -v.x / mol->mview->dimension;
9431         f[1] = -v.y / mol->mview->dimension;
9432         f[2] = -v.z / mol->mview->dimension;
9433         TrackballSetTranslate(mol->mview->track, f);
9434         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9435         return self;
9436 }
9437
9438 /*
9439  *  call-seq:
9440  *     set_background_color(red, green, blue)
9441  *
9442  *  Set the background color of the model window.
9443  */
9444 static VALUE
9445 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9446 {
9447     Molecule *mol;
9448     Data_Get_Struct(self, Molecule, mol);
9449         if (mol->mview != NULL) {
9450                 VALUE rval, gval, bval;
9451                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9452                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9453         }
9454         return self;    
9455 }
9456
9457 /*
9458  *  call-seq:
9459  *     export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9460  *
9461  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9462  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9463  *  If either width or height is not specified, then the screen width/height is used instead.
9464  */
9465 static VALUE
9466 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9467 {
9468         Molecule *mol;
9469         VALUE fval, sval, bval, wval, hval;
9470         char *fname;
9471         float scale;
9472         int bg_color, width, height;
9473     Data_Get_Struct(self, Molecule, mol);
9474         if (mol->mview == NULL)
9475                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9476         rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9477         fname = FileStringValuePtr(fval);
9478         if (sval == Qnil)
9479                 scale = 1.0;
9480         else scale = NUM2DBL(rb_Float(sval));
9481         if (bval == Qnil)
9482                 bg_color = -1;
9483         else bg_color = NUM2INT(rb_Integer(bval));
9484         if (wval == Qnil)
9485                 width = 0;
9486         else width = NUM2INT(rb_Integer(wval));
9487         if (hval == Qnil)
9488                 height = 0;
9489         else height = NUM2INT(rb_Integer(hval));
9490         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9491                 return fval;
9492         else return Qnil;
9493 }
9494
9495 #pragma mark ------ Graphics ------
9496
9497 static void
9498 s_CalculateGraphicNormals(MainViewGraphic *gp)
9499 {
9500         int i;
9501         Vector v1, v2, v3;
9502         if (gp == NULL || gp->npoints < 3)
9503                 return;
9504         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9505         v1.x = gp->points[3] - gp->points[0];
9506         v1.y = gp->points[4] - gp->points[1];
9507         v1.z = gp->points[5] - gp->points[2];
9508         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9509         for (i = 2; i < gp->npoints; i++) {
9510                 v2.x = gp->points[i * 3] - gp->points[0];
9511                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9512                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9513                 VecCross(v3, v1, v2);
9514                 NormalizeVec(&v3, &v3);
9515                 gp->normals[i * 3] = v3.x;
9516                 gp->normals[i * 3 + 1] = v3.y;
9517                 gp->normals[i * 3 + 2] = v3.z;
9518                 v1 = v2;
9519         }
9520         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9521         VecZero(v1);
9522         for (i = 2; i < gp->npoints; i++) {
9523                 v1.x += gp->normals[i * 3];
9524                 v1.y += gp->normals[i * 3 + 1];
9525                 v1.z += gp->normals[i * 3 + 2];
9526         }
9527         NormalizeVec(&v1, &v1);
9528         gp->normals[0] = v1.x;
9529         gp->normals[1] = v1.y;
9530         gp->normals[2] = v1.z;
9531         /*  normals[1] = nv[2].normalize  */
9532         v2.x = gp->normals[6];
9533         v2.y = gp->normals[7];
9534         v2.z = gp->normals[8];
9535         NormalizeVec(&v1, &v2);
9536         gp->normals[3] = v1.x;
9537         gp->normals[4] = v1.y;
9538         gp->normals[5] = v1.z;
9539         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9540         for (i = 2; i < gp->npoints; i++) {
9541                 if (i == gp->npoints - 1)
9542                         VecZero(v3);
9543                 else {
9544                         v3.x = gp->normals[i * 3 + 3];
9545                         v3.y = gp->normals[i * 3 + 4];
9546                         v3.z = gp->normals[i * 3 + 5];
9547                 }
9548                 VecInc(v2, v3);
9549                 NormalizeVec(&v1, &v2);
9550                 gp->normals[i * 3] = v1.x;
9551                 gp->normals[i * 3 + 1] = v1.y;
9552                 gp->normals[i * 3 + 2] = v1.z;
9553                 v2 = v3;
9554         }
9555 }
9556
9557 /*
9558  *  call-seq:
9559  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9560  *
9561  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9562  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9563  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9564  *   points: an array of Vectors
9565  *   
9566  */
9567 static VALUE
9568 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9569 {
9570     Molecule *mol;
9571         MainViewGraphic g;
9572         int i, n, ni, idx;
9573         const char *p;
9574         VALUE kval, cval, pval, fval, ival;
9575     Data_Get_Struct(self, Molecule, mol);
9576         if (mol->mview == NULL)
9577                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9578         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9579         idx = NUM2INT(rb_Integer(ival));
9580         if (idx == -1)
9581                 idx = mol->mview->ngraphics;
9582         else if (idx < 0 || idx > mol->mview->ngraphics)
9583                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9584         memset(&g, 0, sizeof(g));
9585         g.visible = 1;
9586         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9587                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9588         } else {
9589                 kval = rb_obj_as_string(kval);
9590                 p = StringValuePtr(kval);
9591                 if (strcmp(p, "line") == 0)
9592                         g.kind = kMainViewGraphicLine;
9593                 else if (strcmp(p, "poly") == 0)
9594                         g.kind = kMainViewGraphicPoly;
9595                 else if (strcmp(p, "cylinder") == 0)
9596                         g.kind = kMainViewGraphicCylinder;
9597                 else if (strcmp(p, "cone") == 0)
9598                         g.kind = kMainViewGraphicCone;
9599                 else if (strcmp(p, "ellipsoid") == 0)
9600                         g.kind = kMainViewGraphicEllipsoid;
9601                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9602         }
9603         g.closed = (RTEST(fval) ? 1 : 0);
9604         cval = rb_ary_to_ary(cval);
9605         n = RARRAY_LEN(cval);
9606         if (n < 3 || n >= 5)
9607                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9608         if (n == 3)
9609                 g.rgba[3] = 1.0;
9610         for (i = 0; i < n; i++)
9611                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9612         pval = rb_ary_to_ary(pval);
9613         n = RARRAY_LEN(pval);
9614         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9615         if (n <= 0)
9616                 rb_raise(rb_eArgError, "no control points are given");
9617         switch (g.kind) {
9618                 case kMainViewGraphicLine:
9619                         if (n < 2)
9620                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9621                         break;
9622                 case kMainViewGraphicPoly:
9623                         if (n < 3)
9624                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9625                         break;
9626                 case kMainViewGraphicCylinder:
9627                 case kMainViewGraphicCone:
9628                         if (n != 3)
9629                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9630                         ni = 2;
9631                         break;
9632                 case kMainViewGraphicEllipsoid:
9633                         if (n == 2) {
9634                                 ni = 1;
9635                         } else if (n != 4)
9636                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9637                         break;
9638         }
9639         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9640         for (i = 0; i < n; i++) {
9641                 Vector v;
9642                 VALUE rval = RARRAY_PTR(pval)[i];
9643                 if (i == ni) {
9644                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9645                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9646                                 VectorFromValue(rval, &v);
9647                         } else {
9648                                 v.x = NUM2DBL(rb_Float(rval));
9649                         }
9650                         v.y = v.z = 0;
9651                 } else {
9652                         VectorFromValue(rval, &v);
9653                 }
9654                 g.points[i * 3] = v.x;
9655                 g.points[i * 3 + 1] = v.y;
9656                 g.points[i * 3 + 2] = v.z;
9657         }
9658         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9659                 /*  Sphere  */
9660                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9661                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9662                 g.points[7] = g.points[11] = g.points[3];
9663         }
9664         if (g.kind == kMainViewGraphicPoly) {
9665                 /*  Calculate normals  */
9666                 s_CalculateGraphicNormals(&g);
9667         }
9668         MainView_insertGraphic(mol->mview, idx, &g);
9669         
9670         {
9671                 /*  Register undo  */
9672                 MolAction *act;
9673                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9674                 MolActionCallback_registerUndo(mol, act);
9675                 MolActionRelease(act);
9676         }
9677
9678         return INT2NUM(idx);    
9679 }
9680
9681 /*
9682  *  call-seq:
9683  *     create_graphic(kind, color, points, fill = nil) -> integer
9684  *
9685  *  Create a new graphic object. The arguments are similar as insert_graphic.
9686  */
9687 static VALUE
9688 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9689 {
9690         VALUE args[5];
9691         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9692         args[0] = INT2NUM(-1);
9693         return s_Molecule_InsertGraphic(argc + 1, args, self);
9694 }
9695
9696 /*
9697  *  call-seq:
9698  *     remove_graphic(index) -> integer
9699  *
9700  *  Remove a graphic object.
9701  */
9702 static VALUE
9703 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9704 {
9705     Molecule *mol;
9706         int i;
9707     Data_Get_Struct(self, Molecule, mol);
9708         if (mol->mview == NULL)
9709                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9710         i = NUM2INT(rb_Integer(ival));
9711         if (i < 0 || i >= mol->mview->ngraphics)
9712                 rb_raise(rb_eArgError, "graphic index is out of range");
9713         {
9714                 /*  Prepare data for undo  */
9715                 MainViewGraphic *gp;
9716                 Vector *vp;
9717                 MolAction *act;
9718                 double col[4];
9719                 int n;
9720                 gp = mol->mview->graphics + i;
9721                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9722                 for (n = 0; n < gp->npoints; n++) {
9723                         vp[n].x = gp->points[n * 3];
9724                         vp[n].y = gp->points[n * 3 + 1];
9725                         vp[n].z = gp->points[n * 3 + 2];
9726                 }
9727                 col[0] = gp->rgba[0];
9728                 col[1] = gp->rgba[1];
9729                 col[2] = gp->rgba[2];
9730                 col[3] = gp->rgba[3];
9731                 if (gp->visible == 0) {
9732                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9733                         MolActionCallback_registerUndo(mol, act);
9734                         MolActionRelease(act);
9735                 }
9736                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9737                 MolActionCallback_registerUndo(mol, act);
9738                 free(vp);
9739                 MolActionRelease(act);
9740         }
9741         MainView_removeGraphic(mol->mview, i);
9742         return ival;
9743 }
9744
9745 /*
9746  *  call-seq:
9747  *     ngraphics -> integer
9748  *
9749  *  Get the number of graphic objects.
9750  */
9751 static VALUE
9752 s_Molecule_NGraphics(VALUE self)
9753 {
9754     Molecule *mol;
9755     Data_Get_Struct(self, Molecule, mol);
9756         if (mol->mview == NULL)
9757                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9758         return INT2NUM(mol->mview->ngraphics);
9759 }
9760
9761 /*
9762  *  call-seq:
9763  *     get_graphic_point(graphic_index, point_index) -> value
9764  *     get_graphic_points(graphic_index) -> values
9765  *
9766  *  Get the point_index-th control point of graphic_index-th graphic object.
9767  *  Get an array of all control points with the given values.
9768  *   
9769  */
9770 static VALUE
9771 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9772 {
9773         MainViewGraphic *gp;
9774     Molecule *mol;
9775         int index, pindex;
9776         Vector v;
9777         VALUE gval, pval;
9778     Data_Get_Struct(self, Molecule, mol);
9779         if (mol->mview == NULL)
9780                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9781         rb_scan_args(argc, argv, "11", &gval, &pval);
9782         index = NUM2INT(rb_Integer(gval));
9783         if (index < 0 || index >= mol->mview->ngraphics)
9784                 rb_raise(rb_eArgError, "the graphic index is out of range");
9785         gp = mol->mview->graphics + index;
9786         if (pval != Qnil) {
9787                 pindex = NUM2INT(rb_Integer(pval));
9788                 if (pindex < 0 || pindex >= gp->npoints)
9789                         rb_raise(rb_eArgError, "the point index is out of range");
9790                 v.x = gp->points[pindex * 3];
9791                 v.y = gp->points[pindex * 3 + 1];
9792                 v.z = gp->points[pindex * 3 + 2];
9793                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9794                         return rb_float_new(v.x);
9795                 } else {
9796                         return ValueFromVector(&v);
9797                 }
9798         } else {
9799                 pval = rb_ary_new();
9800                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9801                         v.x = gp->points[pindex * 3];
9802                         v.y = gp->points[pindex * 3 + 1];
9803                         v.z = gp->points[pindex * 3 + 2];
9804                         rb_ary_push(pval, ValueFromVector(&v));
9805                 }
9806                 return pval;
9807         }
9808 }
9809
9810 /*
9811  *  call-seq:
9812  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9813  *     set_graphic_points(graphic_index, new_values) -> new_values
9814  *
9815  *  Change the point_index-th control point of graphic_index-th graphic object.
9816  *  Replace the control points with the given values.
9817  *   
9818  */
9819 static VALUE
9820 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9821 {
9822         MainViewGraphic *gp;
9823     Molecule *mol;
9824         int index, pindex;
9825         Vector v, v0;
9826         VALUE gval, pval, nval;
9827         MolAction *act;
9828     Data_Get_Struct(self, Molecule, mol);
9829         if (mol->mview == NULL)
9830                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9831         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9832         index = NUM2INT(rb_Integer(gval));
9833         if (index < 0 || index >= mol->mview->ngraphics)
9834                 rb_raise(rb_eArgError, "the graphic index is out of range");
9835         gp = mol->mview->graphics + index;
9836         if (nval != Qnil) {
9837                 pindex = NUM2INT(rb_Integer(pval));
9838                 if (pindex < 0 || pindex >= gp->npoints)
9839                         rb_raise(rb_eArgError, "the point index is out of range");
9840                 v0.x = gp->points[pindex * 3];
9841                 v0.y = gp->points[pindex * 3 + 1];
9842                 v0.z = gp->points[pindex * 3 + 2];
9843                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9844                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9845                                 v.x = NUM2DBL(rb_Float(nval));
9846                                 v.y = v.z = 0;
9847                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9848                                 v.x = NUM2DBL(rb_Float(nval));
9849                                 v.y = v.z = 0;
9850                                 gp->points[7] = gp->points[11] = v.x;
9851                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9852                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9853                 } else {
9854                         if (nval == Qnil) {
9855                                 v.x = kInvalidFloat;
9856                                 v.y = v.z = 0.0;
9857                         } else VectorFromValue(nval, &v);
9858                 }
9859                 gp->points[pindex * 3] = v.x;
9860                 gp->points[pindex * 3 + 1] = v.y;
9861                 gp->points[pindex * 3 + 2] = v.z;
9862                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9863         } else {
9864                 VALUE aval;
9865                 int len;
9866                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9867                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9868                         vp[pindex].x = gp->points[pindex * 3];
9869                         vp[pindex].y = gp->points[pindex * 3 + 1];
9870                         vp[pindex].z = gp->points[pindex * 3 + 2];
9871                 }
9872                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9873                 free(vp);
9874                 pval = rb_ary_to_ary(pval);
9875                 len = RARRAY_LEN(pval);
9876                 if (gp->npoints < len) {
9877                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9878                         gp->npoints = len;
9879                 } else if (gp->npoints > len) {
9880                         int len2 = 3;
9881                         switch (gp->kind) {
9882                                 case kMainViewGraphicLine: len2 = 2; break;
9883                                 case kMainViewGraphicPoly: len2 = 3; break;
9884                                 case kMainViewGraphicCylinder: len2 = 3; break;
9885                                 case kMainViewGraphicCone: len2 = 3; break;
9886                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9887                         }
9888                         if (len2 < len)
9889                                 len2 = len;
9890                         gp->npoints = len2;
9891                 }
9892                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9893                         aval = RARRAY_PTR(pval)[pindex];
9894                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9895                                 v.x = NUM2DBL(rb_Float(aval));
9896                                 v.y = v.z = 0;
9897                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9898                                 v.x = NUM2DBL(rb_Float(aval));
9899                                 v.y = v.z = 0;
9900                                 gp->points[7] = gp->points[11] = v.x;
9901                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9902                                 break;
9903                         } else VectorFromValue(aval, &v);
9904                         gp->points[pindex * 3] = v.x;
9905                         gp->points[pindex * 3 + 1] = v.y;
9906                         gp->points[pindex * 3 + 2] = v.z;
9907                 }
9908         }
9909         if (gp->kind == kMainViewGraphicPoly) {
9910                 /*  Calculate normals  */
9911                 s_CalculateGraphicNormals(gp);
9912         }
9913         MolActionCallback_registerUndo(mol, act);
9914         MolActionRelease(act);          
9915         MoleculeCallback_notifyModification(mol, 0);
9916         return nval;
9917 }
9918
9919 /*
9920  *  call-seq:
9921  *     get_graphic_color(graphic_index) -> value
9922  *
9923  *  Get the color of graphic_index-th graphic object
9924  */
9925 static VALUE
9926 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9927 {
9928         MainViewGraphic *gp;
9929     Molecule *mol;
9930         int index;
9931     Data_Get_Struct(self, Molecule, mol);
9932         if (mol->mview == NULL)
9933                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9934         index = NUM2INT(rb_Integer(gval));
9935         if (index < 0 || index >= mol->mview->ngraphics)
9936                 rb_raise(rb_eArgError, "the graphic index is out of range");
9937         gp = mol->mview->graphics + index;
9938         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]));
9939 }
9940
9941 /*
9942  *  call-seq:
9943  *     set_graphic_color(graphic_index, new_value) -> new_value
9944  *
9945  *  Change the color of graphic_index-th graphic object
9946  *   
9947  */
9948 static VALUE
9949 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9950 {
9951         MainViewGraphic *gp;
9952     Molecule *mol;
9953         MolAction *act;
9954         double c[4];
9955         int index, i, n;
9956     Data_Get_Struct(self, Molecule, mol);
9957         if (mol->mview == NULL)
9958                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9959         index = NUM2INT(rb_Integer(gval));
9960         if (index < 0 || index >= mol->mview->ngraphics)
9961                 rb_raise(rb_eArgError, "the graphic index is out of range");
9962         gp = mol->mview->graphics + index;
9963         for (i = 0; i < 4; i++)
9964                 c[i] = gp->rgba[i];
9965         cval = rb_ary_to_ary(cval);
9966         n = RARRAY_LEN(cval);
9967         if (n != 3 && n != 4)
9968                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9969
9970         for (i = 0; i < n; i++) {
9971                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9972         }
9973         if (n == 3)
9974                 gp->rgba[3] = 1.0;
9975         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9976         MolActionCallback_registerUndo(mol, act);
9977         MolActionRelease(act);          
9978         MoleculeCallback_notifyModification(mol, 0);
9979         return cval;
9980 }
9981
9982 /*
9983  *  call-seq:
9984  *     show_graphic(graphic_index) -> self
9985  *
9986  *  Enable the visible flag of the graphic_index-th graphic object
9987  *   
9988  */
9989 static VALUE
9990 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9991 {
9992         MainViewGraphic *gp;
9993     Molecule *mol;
9994         int index;
9995     Data_Get_Struct(self, Molecule, mol);
9996         if (mol->mview == NULL)
9997                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9998         index = NUM2INT(rb_Integer(gval));
9999         if (index < 0 || index >= mol->mview->ngraphics)
10000                 rb_raise(rb_eArgError, "the graphic index is out of range");
10001         gp = mol->mview->graphics + index;
10002         gp->visible = 1;
10003         MoleculeCallback_notifyModification(mol, 0);
10004         return self;
10005 }
10006
10007 /*
10008  *  call-seq:
10009  *     hide_graphic(graphic_index) -> self
10010  *
10011  *  Disable the visible flag of the graphic_index-th graphic object
10012  *   
10013  */
10014 static VALUE
10015 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10016 {
10017         MainViewGraphic *gp;
10018     Molecule *mol;
10019         int index;
10020     Data_Get_Struct(self, Molecule, mol);
10021         if (mol->mview == NULL)
10022                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10023         index = NUM2INT(rb_Integer(gval));
10024         if (index < 0 || index >= mol->mview->ngraphics)
10025                 rb_raise(rb_eArgError, "the graphic index is out of range");
10026         gp = mol->mview->graphics + index;
10027         gp->visible = 0;
10028         MoleculeCallback_notifyModification(mol, 0);
10029         return self;
10030 }
10031
10032 /*
10033  *  call-seq:
10034  *     show_text(string)
10035  *
10036  *  Show the string in the info text box.
10037  */
10038 static VALUE
10039 s_Molecule_ShowText(VALUE self, VALUE arg)
10040 {
10041     Molecule *mol;
10042     Data_Get_Struct(self, Molecule, mol);
10043         if (mol->mview != NULL)
10044                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10045         return Qnil;
10046 }
10047
10048 #pragma mark ------ MD Support ------
10049
10050 /*
10051  *  call-seq:
10052  *     md_arena -> MDArena
10053  *
10054  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10055  *  this molecule, a new arena is created.
10056  */
10057 static VALUE
10058 s_Molecule_MDArena(VALUE self)
10059 {
10060     Molecule *mol;
10061         VALUE retval;
10062     Data_Get_Struct(self, Molecule, mol);
10063         if (mol->arena == NULL)
10064                 md_arena_new(mol);
10065         retval = ValueFromMDArena(mol->arena);
10066         return retval;
10067 }
10068
10069 /*
10070  *  call-seq:
10071  *     set_parameter_attr(type, index, key, value, src) -> value
10072  *
10073  *  This method is used only internally.
10074  */
10075 static VALUE
10076 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10077 {
10078         /*  This method is called from MolAction to change a MM parameter attribute.  */
10079     Molecule *mol;
10080         VALUE pval;
10081         ParameterRef *pref;
10082         UnionPar *up;
10083     Data_Get_Struct(self, Molecule, mol);
10084         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10085         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10086         
10087         /*  This is the special part of this method; it allows modification of the src field. */
10088         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10089         Data_Get_Struct(pval, ParameterRef, pref);
10090         up = ParameterRefGetPar(pref);
10091         up->bond.src = FIX2INT(sval);
10092         
10093         return vval;
10094 }
10095
10096 /*
10097  *  call-seq:
10098  *     parameter -> Parameter
10099  *
10100  *  Get the local parameter of this molecule. If not defined, returns nil.
10101  */
10102 static VALUE
10103 s_Molecule_Parameter(VALUE self)
10104 {
10105     Molecule *mol;
10106     Data_Get_Struct(self, Molecule, mol);
10107 /*      if (mol->par == NULL)
10108                 return Qnil; */
10109         return s_NewParameterValueFromValue(self);
10110 }
10111
10112 /*
10113  *  call-seq:
10114  *     start_step       -> Integer
10115  *
10116  *  Returns the start step (defined by dcd format).
10117  */
10118 static VALUE
10119 s_Molecule_StartStep(VALUE self)
10120 {
10121     Molecule *mol;
10122     Data_Get_Struct(self, Molecule, mol);
10123         return INT2NUM(mol->startStep);
10124 }
10125
10126 /*
10127  *  call-seq:
10128  *     start_step = Integer
10129  *
10130  *  Set the start step (defined by dcd format).
10131  */
10132 static VALUE
10133 s_Molecule_SetStartStep(VALUE self, VALUE val)
10134 {
10135     Molecule *mol;
10136     Data_Get_Struct(self, Molecule, mol);
10137         mol->startStep = NUM2INT(rb_Integer(val));
10138         return val;
10139 }
10140
10141 /*
10142  *  call-seq:
10143  *     steps_per_frame       -> Integer
10144  *
10145  *  Returns the number of steps between frames (defined by dcd format).
10146  */
10147 static VALUE
10148 s_Molecule_StepsPerFrame(VALUE self)
10149 {
10150     Molecule *mol;
10151     Data_Get_Struct(self, Molecule, mol);
10152         return INT2NUM(mol->stepsPerFrame);
10153 }
10154
10155 /*
10156  *  call-seq:
10157  *     steps_per_frame = Integer
10158  *
10159  *  Set the number of steps between frames (defined by dcd format).
10160  */
10161 static VALUE
10162 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10163 {
10164     Molecule *mol;
10165     Data_Get_Struct(self, Molecule, mol);
10166         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10167         return val;
10168 }
10169
10170 /*
10171  *  call-seq:
10172  *     ps_per_step       -> Float
10173  *
10174  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10175  */
10176 static VALUE
10177 s_Molecule_PsPerStep(VALUE self)
10178 {
10179     Molecule *mol;
10180     Data_Get_Struct(self, Molecule, mol);
10181         return rb_float_new(mol->psPerStep);
10182 }
10183
10184 /*
10185  *  call-seq:
10186  *     ps_per_step = Float
10187  *
10188  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10189  */
10190 static VALUE
10191 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10192 {
10193     Molecule *mol;
10194     Data_Get_Struct(self, Molecule, mol);
10195         mol->psPerStep = NUM2DBL(rb_Float(val));
10196         return val;
10197 }
10198
10199 static VALUE
10200 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10201 {
10202         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.");
10203 }
10204
10205 #pragma mark ------ MO Handling ------
10206
10207 /*
10208  *  call-seq:
10209  *     selectedMO -> IntGroup
10210  *
10211  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10212  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10213  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10214  */
10215 static VALUE
10216 s_Molecule_SelectedMO(VALUE self)
10217 {
10218     Molecule *mol;
10219         IntGroup *ig;
10220         VALUE val;
10221     Data_Get_Struct(self, Molecule, mol);
10222         if (mol->mview == NULL)
10223                 return Qnil;
10224         ig = MainView_selectedMO(mol->mview);
10225         if (ig == NULL)
10226                 return Qnil;
10227         IntGroupOffset(ig, 1);
10228         val = ValueFromIntGroup(ig);
10229         IntGroupRelease(ig);
10230         return val;
10231 }
10232
10233 /*
10234  *  call-seq:
10235  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10236  *
10237  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10238  *  If the molecule does not contain a basis set information, then returns nil.
10239  */
10240 static VALUE
10241 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10242 {
10243     Molecule *mol;
10244         Vector o, dx, dy, dz;
10245         Int nx, ny, nz;
10246         VALUE nval;
10247         Int npoints = 80 * 80 * 80;
10248     Data_Get_Struct(self, Molecule, mol);
10249         if (mol->bset == NULL)
10250                 return Qnil;
10251         rb_scan_args(argc, argv, "01", &nval);
10252         if (nval != Qnil)
10253                 npoints = NUM2INT(rb_Integer(nval));
10254         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10255                 return Qnil;
10256         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));
10257 }
10258
10259 static int
10260 s_Cubegen_callback(double progress, void *ref)
10261 {
10262         MyAppCallback_setProgressValue(progress);
10263         if (MyAppCallback_checkInterrupt())
10264                 return 1;
10265         else return 0;
10266 }
10267
10268 /*
10269  *  call-seq:
10270  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10271  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10272  *
10273  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10274  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10275  *  second form, the cube dimension is explicitly given.
10276  *  Returns fname when successful, nil otherwise.
10277  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10278  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10279  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10280  */
10281 static VALUE
10282 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10283 {
10284         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10285     Molecule *mol;
10286         Int mono, nx, ny, nz, npoints;
10287         Vector o, dx, dy, dz;
10288         int index, n;
10289         char buf[1024];
10290     Data_Get_Struct(self, Molecule, mol);
10291         if (mol->bset == NULL)
10292                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10293         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10294         
10295         /*  Set up parameters  */
10296         mono = NUM2INT(rb_Integer(mval));
10297         if (mono < 0 || mono > mol->bset->ncomps)
10298                 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d, or 0 as 'arbitrary vector')", mono, mol->bset->ncomps);
10299         if (RTEST(bval)) {
10300                 if (mol->bset->rflag != 0)
10301                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10302                 mono += mol->bset->ncomps;
10303         }
10304                 
10305         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10306                 /*  Automatic grid formation  */
10307                 if (oval != Qnil)
10308                         npoints = NUM2INT(rb_Integer(oval));
10309                 else npoints = 0;
10310                 if (npoints == 0)
10311                         npoints = 1000000;
10312                 else if (npoints < 8)
10313                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10314                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10315                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10316                 ival = dxval;
10317                 bval = dyval;
10318         } else {
10319                 VectorFromValue(oval, &o);
10320                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10321                         VectorFromValue(dxval, &dx);
10322                 else {
10323                         dx.x = NUM2DBL(rb_Float(dxval));
10324                         dx.y = dx.z = 0.0;
10325                 }
10326                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10327                         VectorFromValue(dyval, &dy);
10328                 else {
10329                         dy.y = NUM2DBL(rb_Float(dyval));
10330                         dy.x = dy.z = 0.0;
10331                 }
10332                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10333                         VectorFromValue(dzval, &dz);
10334                 else {
10335                         dz.z = NUM2DBL(rb_Float(dzval));
10336                         dz.x = dz.y = 0.0;
10337                 }
10338                 nx = NUM2INT(rb_Integer(nxval));
10339                 ny = NUM2INT(rb_Integer(nyval));
10340                 nz = NUM2INT(rb_Integer(nzval));
10341                 if (nx <= 0 || ny <= 0 || nz <= 0)
10342                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10343                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10344                         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);
10345         }
10346         
10347         /*  Calc MO  */
10348         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10349         if (index == -2)
10350                 rb_interrupt();
10351         else if (index < 0)
10352                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10353         
10354         /*  Output to file  */
10355         MoleculeCallback_displayName(mol, buf, sizeof buf);
10356         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10357         if (n != 0)
10358                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10359         
10360         /*  Discard the cube  */
10361         MoleculeClearCubeAtIndex(mol, index);
10362         return fval;
10363 }
10364
10365 /*
10366  *  call-seq:
10367  *     clear_surface
10368  *
10369  *  Clear the MO surface if present.
10370  */
10371 static VALUE
10372 s_Molecule_ClearSurface(VALUE self)
10373 {
10374     Molecule *mol;
10375     Data_Get_Struct(self, Molecule, mol);
10376         if (mol->mcube != NULL)
10377                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10378         return self;
10379 }
10380
10381 /*
10382  *  call-seq:
10383  *     hide_surface
10384  *
10385  *  Hide the MO surface if present.
10386  */
10387 static VALUE
10388 s_Molecule_HideSurface(VALUE self)
10389 {
10390     Molecule *mol;
10391     Data_Get_Struct(self, Molecule, mol);
10392         if (mol->mcube != NULL) {
10393                 mol->mcube->hidden = 1;
10394                 MoleculeCallback_notifyModification(mol, 0);
10395         }
10396         return self;
10397 }
10398
10399 /*
10400  *  call-seq:
10401  *     show_surface
10402  *
10403  *  Show the MO surface if present.
10404  */
10405 static VALUE
10406 s_Molecule_ShowSurface(VALUE self)
10407 {
10408     Molecule *mol;
10409     Data_Get_Struct(self, Molecule, mol);
10410         if (mol->mcube != NULL) {
10411                 mol->mcube->hidden = 0;
10412                 MoleculeCallback_notifyModification(mol, 0);
10413         }
10414         return self;
10415 }
10416
10417 /*
10418  *  call-seq:
10419  *     create_surface(mo, attr = nil)
10420  *
10421  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10422  *  then it denotes the beta orbital.
10423  *  If mo is nil, then the attributes of the current surface are modified.
10424  *  Attributes:
10425  *    :npoints : the approximate number of grid points
10426  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10427  *    :thres   : the threshold for the isovalue surface
10428  *  If the molecule does not contain MO information, raises exception.
10429  */
10430 static VALUE
10431 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10432 {
10433     Molecule *mol;
10434         Vector o, dx, dy, dz;
10435         Int nmo, nx, ny, nz, i;
10436         Int need_recalc = 0;
10437         VALUE nval, hval, aval;
10438         Int npoints;
10439         Double expand;
10440         Double thres;
10441         Double d[4];
10442     Data_Get_Struct(self, Molecule, mol);
10443         rb_scan_args(argc, argv, "11", &nval, &hval);
10444         if (mol->bset == NULL)
10445                 rb_raise(rb_eMolbyError, "No MO information is given");
10446         if (nval == Qnil) {
10447                 nmo = -1;
10448         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10449                 nmo = mol->bset->nmos + 1;
10450         } else {
10451                 nmo = NUM2INT(rb_Integer(nval));
10452                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10453                         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);
10454                 if (nmo < 0)
10455                         nmo = -nmo + mol->bset->ncomps;
10456         }
10457         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10458                 npoints = NUM2INT(rb_Integer(aval));
10459                 need_recalc = 1;
10460         } else if (mol->mcube != NULL) {
10461                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10462         } else npoints = 80 * 80 * 80;
10463         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10464                 expand = NUM2DBL(rb_Float(aval));
10465         } else if (mol->mcube != NULL) {
10466                 expand = mol->mcube->expand;
10467         } else expand = 1.0;
10468         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10469                 thres = NUM2DBL(rb_Float(aval));
10470         } else if (mol->mcube != NULL) {
10471                 thres = mol->mcube->thres;
10472         } else thres = 0.05;
10473         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10474                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10475                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10476                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10477                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10478         }
10479         for (nx = 0; nx < 2; nx++) {
10480                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10481                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10482                         aval = rb_ary_to_ary(aval);
10483                         if (RARRAY_LEN(aval) < 3) {
10484                         raise:
10485                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10486                         }
10487                         for (i = 0; i < 4; i++)
10488                                 d[i] = mol->mcube->c[nx].rgba[i];
10489                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10490                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10491                                 if (d[i] < 0.0 && d[i] > 1.0)
10492                                         goto raise;
10493                         }
10494                         for (i = 0; i < 4; i++)
10495                                 mol->mcube->c[nx].rgba[i] = d[i];
10496                 }
10497         }
10498         if (mol->mcube->expand != expand)
10499                 need_recalc = 1;
10500         mol->mcube->thres = thres;
10501         mol->mcube->expand = expand;
10502         if (nmo < 0) {
10503                 if (mol->mcube->idn < 0)
10504                         return self;  /*  Only set attributes for now  */
10505                 if (need_recalc)
10506                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10507         }
10508         if (MoleculeUpdateMCube(mol, nmo) != 0)
10509                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10510         return self;
10511 }
10512
10513 /*
10514  *  call-seq:
10515  *     set_surface_attr(attr = nil)
10516  *
10517  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10518  */
10519 static VALUE
10520 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10521 {
10522         VALUE args[2];
10523         args[0] = Qnil;
10524         args[1] = hval;
10525         return s_Molecule_CreateSurface(2, args, self);
10526 }
10527
10528 /*
10529  *  call-seq:
10530  *     nelpots
10531  *
10532  *  Get the number of electrostatic potential info.
10533  */
10534 static VALUE
10535 s_Molecule_NElpots(VALUE self)
10536 {
10537         Molecule *mol;
10538     Data_Get_Struct(self, Molecule, mol);
10539         return INT2NUM(mol->nelpots);
10540 }
10541
10542 /*
10543  *  call-seq:
10544  *     elpot(idx)
10545  *
10546  *  Get the electrostatic potential info at the given index. If present, then the
10547  *  return value is [Vector, Float] (position and potential). If not present, then
10548  *  returns nil.
10549  */
10550 static VALUE
10551 s_Molecule_Elpot(VALUE self, VALUE ival)
10552 {
10553         Molecule *mol;
10554         int idx;
10555     Data_Get_Struct(self, Molecule, mol);
10556         idx = NUM2INT(rb_Integer(ival));
10557         if (idx < 0 || idx >= mol->nelpots)
10558                 return Qnil;
10559         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10560 }
10561
10562 /*
10563  *  call-seq:
10564  *     clear_basis_set
10565  *
10566  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10567  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10568  */
10569 static VALUE
10570 s_Molecule_ClearBasisSet(VALUE self)
10571 {
10572         Molecule *mol;
10573     Data_Get_Struct(self, Molecule, mol);
10574         if (mol != NULL) {
10575                 if (mol->bset != NULL) {
10576                         BasisSetRelease(mol->bset);
10577                         mol->bset = NULL;
10578                 }
10579                 if (mol->mcube != NULL) {
10580                         MoleculeDeallocateMCube(mol->mcube);
10581                         mol->mcube = NULL;
10582                 }
10583         }
10584         return self;
10585 }
10586
10587 /*
10588  *  call-seq:
10589  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10590  *
10591  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10592  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10593  *  -2, D5-type.
10594  */
10595 static VALUE
10596 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10597 {
10598         Molecule *mol;
10599         int sym, nprims, a_idx, n;
10600     Data_Get_Struct(self, Molecule, mol);
10601         a_idx = NUM2INT(rb_Integer(aval));
10602         sym = NUM2INT(rb_Integer(symval));
10603         nprims = NUM2INT(rb_Integer(npval));
10604         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10605         if (n == -1)
10606                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10607         else if (n == -2)
10608                 rb_raise(rb_eMolbyError, "Low memory");
10609         else if (n == -3)
10610                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10611         else if (n != 0)
10612                 rb_raise(rb_eMolbyError, "Unknown error");
10613         return self;
10614 }
10615
10616 /*
10617  *  call-seq:
10618  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10619  *
10620  *  To be used internally. Add a gaussian primitive coefficients.
10621  */
10622 static VALUE
10623 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10624 {
10625         Molecule *mol;
10626         Int n;
10627         Double exponent, contraction, contraction_sp;
10628     Data_Get_Struct(self, Molecule, mol);
10629         exponent = NUM2DBL(rb_Float(expval));
10630         contraction = NUM2DBL(rb_Float(cval));
10631         contraction_sp = NUM2DBL(rb_Float(cspval));
10632         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10633         if (n == -1)
10634                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10635         else if (n == -2)
10636                 rb_raise(rb_eMolbyError, "Low memory");
10637         else if (n != 0)
10638                 rb_raise(rb_eMolbyError, "Unknown error");
10639         return self;
10640 }
10641
10642 /*
10643  *  call-seq:
10644  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10645  *
10646  *  Get the Gaussian shell information for the given MO coefficient index.
10647  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10648  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10649  *  is the number of MO component belonging to this shell.
10650  */
10651 static VALUE
10652 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10653 {
10654         Molecule *mol;
10655         ShellInfo *sp;
10656         int s_idx, sym;
10657     Data_Get_Struct(self, Molecule, mol);
10658         if (mol->bset == NULL)
10659                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10660         s_idx = NUM2INT(rb_Integer(sval));
10661         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10662                 return Qnil;
10663         sp = mol->bset->shells + s_idx;
10664         sym = sp->sym;
10665         switch (sym) {
10666                 case kGTOType_S:  sym = 0;  break;
10667                 case kGTOType_SP: sym = -1; break;
10668                 case kGTOType_P:  sym = 1;  break;
10669                 case kGTOType_D:  sym = 2;  break;
10670                 case kGTOType_D5: sym = -2; break;
10671                 case kGTOType_F:  sym = 3;  break;
10672                 case kGTOType_F7: sym = -3; break;
10673                 case kGTOType_G:  sym = 4;  break;
10674                 case kGTOType_G9: sym = -4; break;
10675                 default:
10676                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10677         }
10678         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10679 }
10680
10681 /*
10682  *  call-seq:
10683  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10684  *
10685  *  Get the Gaussian primitive coefficients for the given MO component.
10686  */
10687 static VALUE
10688 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10689 {
10690         Molecule *mol;
10691         ShellInfo *sp;
10692         PrimInfo *pp;
10693         int s_idx, i;
10694         VALUE retval, aval;
10695     Data_Get_Struct(self, Molecule, mol);
10696         if (mol->bset == NULL)
10697                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10698         s_idx = NUM2INT(rb_Integer(sval));
10699         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10700                 return Qnil;
10701         sp = mol->bset->shells + s_idx;
10702         pp = mol->bset->priminfos + sp->p_idx;
10703         retval = rb_ary_new2(sp->nprim);
10704         for (i = 0; i < sp->nprim; i++) {
10705                 if (sp->sym == kGTOType_SP) {
10706                         /*  With P contraction coefficient  */
10707                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10708                 } else {
10709                         /*  Without P contraction coefficient  */
10710                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10711                 }
10712                 rb_ary_store(retval, i, aval);
10713         }
10714         return retval;
10715 }
10716
10717 /*
10718  *  call-seq:
10719  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10720  *
10721  *  Get the Gaussian shell information for the given MO coefficient index.
10722  */
10723 static VALUE
10724 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10725 {
10726         Molecule *mol;
10727         Int n, c, atom_idx, shell_idx;
10728         char label[32];
10729     Data_Get_Struct(self, Molecule, mol);
10730         if (mol->bset == NULL)
10731                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10732         c = NUM2INT(rb_Integer(cval));
10733         if (c < 0 || c >= mol->bset->ncomps)
10734                 return Qnil;
10735         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10736         if (n != 0)
10737                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10738         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10739 }
10740
10741 /*
10742  *  call-seq:
10743  *     clear_mo_coefficients
10744  *
10745  *  Clear the existing MO coefficients.
10746  */
10747 static VALUE
10748 s_Molecule_ClearMOCoefficients(VALUE self)
10749 {
10750         Molecule *mol;
10751         Data_Get_Struct(self, Molecule, mol);
10752         if (mol->bset != NULL) {
10753                 if (mol->bset->moenergies != NULL) {
10754                         free(mol->bset->moenergies);
10755                         mol->bset->moenergies = NULL;
10756                 }
10757                 if (mol->bset->mo != NULL) {
10758                         free(mol->bset->mo);
10759                         mol->bset->mo = NULL;
10760                 }
10761                 mol->bset->nmos = 0;
10762         }
10763         return self;
10764 }
10765
10766 /*
10767  *  call-seq:
10768  *     set_mo_coefficients(idx, energy, coefficients)
10769  *
10770  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10771  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10772  *  Energy is the MO energy, and coefficients is an array
10773  *  of MO coefficients.
10774  */
10775 static VALUE
10776 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10777 {
10778         Molecule *mol;
10779         Int idx, ncomps, i;
10780         Double energy;
10781         Double *coeffs;
10782     Data_Get_Struct(self, Molecule, mol);
10783         idx = NUM2INT(rb_Integer(ival));
10784         energy = NUM2DBL(rb_Float(eval));
10785         aval = rb_ary_to_ary(aval);
10786         ncomps = RARRAY_LEN(aval);
10787         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10788         if (coeffs == NULL) {
10789                 i = -2;
10790                 goto end;
10791         }
10792         for (i = 0; i < ncomps; i++)
10793                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10794         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10795 end:
10796         if (i == -1)
10797                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10798         else if (i == -2)
10799                 rb_raise(rb_eMolbyError, "Low memory");
10800         else if (i == -3)
10801                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10802         else if (i == -4)
10803                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10804         else if (i == -5)
10805                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10806         else if (i != 0)
10807                 rb_raise(rb_eMolbyError, "Unknown error");
10808         return self;
10809 }
10810
10811 /*
10812  *  call-seq:
10813  *     get_mo_coefficients(idx)
10814  *
10815  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10816  */
10817 static VALUE
10818 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10819 {
10820         Molecule *mol;
10821         Int idx, ncomps, n;
10822         Double energy;
10823         Double *coeffs;
10824         VALUE retval;
10825     Data_Get_Struct(self, Molecule, mol);
10826         idx = NUM2INT(rb_Integer(ival));
10827         ncomps = 0;
10828         coeffs = NULL;
10829         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10830         if (n == -1)
10831                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10832         else if (n == -2)
10833                 rb_raise(rb_eMolbyError, "No basis set information is present");
10834         else if (n == -3)
10835                 return Qnil;  /*  Silently returns nil  */
10836         retval = rb_ary_new2(ncomps);
10837         for (n = 0; n < ncomps; n++)
10838                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10839         free(coeffs);
10840         return retval;
10841 }
10842
10843 /*
10844  *  call-seq:
10845  *     get_mo_energy(idx)
10846  *
10847  *  To be used internally. Get the MO energy for the given MO index (1-based).
10848  */
10849 static VALUE
10850 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10851 {
10852         Molecule *mol;
10853         Int idx, n;
10854         Double energy;
10855     Data_Get_Struct(self, Molecule, mol);
10856         idx = NUM2INT(rb_Integer(ival));
10857         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10858         if (n == -1)
10859                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10860         else if (n == -2)
10861                 rb_raise(rb_eMolbyError, "No basis set information is present");
10862         else if (n == -3)
10863                 return Qnil;
10864         return rb_float_new(energy);
10865 }
10866
10867 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10868
10869 static inline void
10870 s_InitMOInfoKeys(void)
10871 {
10872         if (sTypeSym == 0) {
10873                 sTypeSym = ID2SYM(rb_intern("type"));
10874                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10875                 sBetaSym = ID2SYM(rb_intern("beta"));
10876                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10877                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10878         }
10879 }
10880
10881 /*
10882  *  call-seq:
10883  *     set_mo_info(hash)
10884  *
10885  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10886  *  :alpha=>integer, :beta=>integer
10887  */
10888 static VALUE
10889 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10890 {
10891         Molecule *mol;
10892         VALUE aval;
10893         Int rflag, na, nb, n;
10894         char *s;
10895     Data_Get_Struct(self, Molecule, mol);
10896         if (mol->bset != NULL) {
10897                 rflag = mol->bset->rflag;
10898                 na = mol->bset->ne_alpha;
10899                 nb = mol->bset->ne_beta;
10900         } else {
10901                 rflag = 1;
10902                 na = 0;
10903                 nb = 0;
10904         }
10905         if (hval != Qnil) {
10906                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10907                         s = StringValuePtr(aval);
10908                         if (strcasecmp(s, "RHF") == 0)
10909                                 rflag = 1;
10910                         else if (strcasecmp(s, "UHF") == 0)
10911                                 rflag = 0;
10912                         else if (strcasecmp(s, "ROHF") == 0)
10913                                 rflag = 2;
10914                 }
10915                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10916                         n = NUM2INT(rb_Integer(aval));
10917                         if (n >= 0)
10918                                 na = n;
10919                 }
10920                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10921                         n = NUM2INT(rb_Integer(aval));
10922                         if (n >= 0)
10923                                 nb = n;
10924                 }
10925                 MoleculeSetMOInfo(mol, rflag, na, nb);
10926         }
10927         return self;
10928 }
10929
10930 /*
10931  *  call-seq:
10932  *     get_mo_info(key)
10933  *
10934  *  Get the MO info. The key is as described in set_mo_info.
10935  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10936  */
10937 static VALUE
10938 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10939 {
10940         Molecule *mol;
10941     Data_Get_Struct(self, Molecule, mol);
10942         if (mol->bset == NULL)
10943                 return Qnil;
10944         if (kval == sTypeSym) {
10945                 switch (mol->bset->rflag) {
10946                         case 0: return Ruby_NewEncodedStringValue2("UHF");
10947                         case 1: return Ruby_NewEncodedStringValue2("RHF");
10948                         case 2: return Ruby_NewEncodedStringValue2("ROHF");
10949                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10950                 }
10951         } else if (kval == sAlphaSym) {
10952                 return INT2NUM(mol->bset->ne_alpha);
10953         } else if (kval == sBetaSym) {
10954                 return INT2NUM(mol->bset->ne_beta);
10955         } else if (kval == sNcompsSym) {
10956                 return INT2NUM(mol->bset->ncomps);
10957         } else if (kval == sNshellsSym) {
10958                 return INT2NUM(mol->bset->nshells);
10959         } else {
10960                 kval = rb_inspect(kval);
10961                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10962                 return Qnil;  /*  Does not reach here  */
10963         }
10964 }
10965
10966 /*
10967  *  call-seq:
10968  *     mo_type
10969  *
10970  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10971  */
10972 static VALUE
10973 s_Molecule_MOType(VALUE self)
10974 {
10975         return s_Molecule_GetMOInfo(self, sTypeSym);
10976 }
10977
10978 #pragma mark ------ Molecular Topology ------
10979
10980 /*
10981  *  call-seq:
10982  *     search_equivalent_atoms(ig = nil)
10983  *
10984  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
10985  */
10986 static VALUE
10987 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10988 {
10989         Molecule *mol;
10990         Int *result, i;
10991         VALUE val;
10992         IntGroup *ig;
10993     Data_Get_Struct(self, Molecule, mol);
10994         if (mol->natoms == 0)
10995                 return Qnil;
10996         rb_scan_args(argc, argv, "01", &val);
10997         if (val != Qnil)
10998                 ig = s_Molecule_AtomGroupFromValue(self, val);
10999         else ig = NULL;
11000         result = MoleculeSearchEquivalentAtoms(mol, ig);
11001         if (result == NULL)
11002                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11003         if (ig != NULL)
11004                 IntGroupRelease(ig);
11005         val = rb_ary_new2(mol->natoms);
11006         for (i = 0; i < mol->natoms; i++)
11007                 rb_ary_push(val, INT2NUM(result[i]));
11008         free(result);
11009         return val;
11010 }
11011
11012 /*
11013  *  call-seq:
11014  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11015  *
11016  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11017  *  Name is the name of the new pi anchor, and group is the atoms that define
11018  *  the pi system. Type (a String) is an atom type for MM implementation.
11019  *  Weights represent the relative significance of the component atoms; if omitted, then
11020  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
11021  *  The weight values will be normalized so that the sum of the weights is 1.0.
11022  *  The weight values must be positive.
11023  *  Index is the atom index where the created pi-anchor is inserted in the 
11024  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
11025  *  having the largest index.
11026  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
11027  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11028  */
11029 static VALUE
11030 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11031 {
11032         Molecule *mol;
11033         VALUE nval, gval;
11034         IntGroup *ig;
11035         Int i, n, idx, last_component;
11036         Atom a, *ap;
11037         PiAnchor an;
11038         AtomRef *aref;
11039         if (argc < 2 || argc >= 6)
11040                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11041         nval = *argv++;
11042         gval = *argv++;
11043         argc -= 2;
11044     Data_Get_Struct(self, Molecule, mol);
11045     if (gval == Qnil)
11046         ig = NULL;
11047     else
11048         ig = s_Molecule_AtomGroupFromValue(self, gval);
11049     if (ig == NULL || IntGroupGetCount(ig) == 0)
11050     rb_raise(rb_eMolbyError, "atom group is not given correctly");
11051         memset(&a, 0, sizeof(a));
11052         memset(&an, 0, sizeof(an));
11053         strncpy(a.aname, StringValuePtr(nval), 4);
11054         if (a.aname[0] == '_')
11055                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11056         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11057         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11058                 if (n >= mol->natoms) {
11059                         AtomConnectResize(&an.connect, 0);
11060                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11061                 }
11062                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11063                 last_component = n;
11064         }
11065         if (an.connect.count == 0)
11066                 rb_raise(rb_eMolbyError, "no atoms are specified");
11067         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11068         for (i = 0; i < an.connect.count; i++) {
11069                 an.coeffs[i] = 1.0 / an.connect.count;
11070         }
11071         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11072                 /*  Atom type  */
11073                 if (argv[0] != Qnil)
11074                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11075                 argc--;
11076                 argv++;
11077         }
11078         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11079                 if (argv[0] != Qnil) {
11080                         VALUE aval = rb_ary_to_ary(argv[0]);
11081                         Double d, sum;
11082                         if (RARRAY_LEN(aval) != an.connect.count)
11083                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11084                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11085                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11086                                 if (d <= 0.0)
11087                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11088                                 sum += d;
11089                                 an.coeffs[i] = d;
11090                         }
11091                         for (i = 0; i < an.connect.count; i++)
11092                                 an.coeffs[i] /= sum;
11093                 }
11094                 argc--;
11095                 argv++;
11096         }
11097         if (argc > 0 && argv[0] != Qnil) {
11098                 /*  Index  */
11099                 idx = NUM2INT(rb_Integer(argv[0]));
11100         } else idx = -1;
11101         if (idx < 0 || idx > mol->natoms) {
11102                 /*  Immediately after the last specified atom  */
11103                 idx = last_component + 1;
11104         }
11105         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11106         memmove(a.anchor, &an, sizeof(PiAnchor));
11107         /*  Use residue information of the last specified atom  */
11108         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11109         a.resSeq = ap->resSeq;
11110         strncpy(a.resName, ap->resName, 4);
11111         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11112                 return Qnil;
11113         MoleculeCalculatePiAnchorPosition(mol, idx);
11114     aref = AtomRefNew(mol, idx);
11115     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11116 }
11117
11118 #pragma mark ------ Molecular Properties ------
11119
11120 /*
11121  *  call-seq:
11122  *     set_property(name, value[, index]) -> value
11123  *     set_property(name, values, group) -> values
11124  *
11125  *  Set molecular property. A property is a floating-point number with a specified name,
11126  *  and can be set for each frame separately. The name of the property is given as a String.
11127  *  The value can be a single floating point number, which is set to the current frame.
11128  *  
11129  */
11130 static VALUE
11131 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11132 {
11133         Molecule *mol;
11134         VALUE nval, vval, ival;
11135         char *name;
11136         IntGroup *ig;
11137         Int i, n, idx, fidx;
11138         Double *dp;
11139         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11140     Data_Get_Struct(self, Molecule, mol);
11141         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11142                 idx = NUM2INT(rb_Integer(nval));
11143                 if (idx < 0 || idx >= mol->nmolprops)
11144                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11145         } else {
11146                 name = StringValuePtr(nval);
11147                 idx = MoleculeLookUpProperty(mol, name);
11148                 if (idx < 0) {
11149                         idx = MoleculeCreateProperty(mol, name);
11150                         if (idx < 0)
11151                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11152                 }
11153         }
11154         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11155                 if (ival == Qnil)
11156                         fidx = mol->cframe;
11157                 else {
11158                         fidx = NUM2INT(rb_Integer(ival));
11159                         n = MoleculeGetNumberOfFrames(mol);
11160                         if (fidx < 0 || fidx >= n)
11161                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11162                 }
11163                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11164                 dp = (Double *)malloc(sizeof(Double));
11165                 *dp = NUM2DBL(rb_Float(vval));
11166                 n = 1;
11167         } else {
11168                 vval = rb_ary_to_ary(vval);
11169                 ig = IntGroupFromValue(ival);
11170                 n = IntGroupGetCount(ig);
11171                 if (n == 0)
11172                         rb_raise(rb_eMolbyError, "No frames are specified");
11173                 if (RARRAY_LEN(vval) < n)
11174                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11175                 dp = (Double *)calloc(sizeof(Double), n);
11176                 for (i = 0; i < n; i++)
11177                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11178         }
11179         
11180         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11181         free(dp);
11182         IntGroupRelease(ig);
11183         return self;
11184 }
11185
11186 /*
11187  *  call-seq:
11188  *     get_property(name[, index]) -> value
11189  *     get_property(name, group) -> values
11190  *
11191  *  Get molecular property. In the first form, a property value for a single frame is returned.
11192  *  (If index is omitted, then the value for the current frame is given)
11193  *  In the second form, an array of property values for the given frames is returned.
11194  *  If name is not one of known properties or a valid index integer, exception is raised.
11195  */
11196 static VALUE
11197 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11198 {
11199         Molecule *mol;
11200         VALUE nval, ival;
11201         char *name;
11202         IntGroup *ig;
11203         Int i, n, idx, fidx;
11204         Double *dp;
11205         rb_scan_args(argc, argv, "11", &nval, &ival);
11206     Data_Get_Struct(self, Molecule, mol);
11207         if (mol->nmolprops == 0)
11208                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11209         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11210                 idx = NUM2INT(rb_Integer(nval));
11211                 if (idx < 0 || idx >= mol->nmolprops)
11212                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11213         } else {
11214                 name = StringValuePtr(nval);
11215                 idx = MoleculeLookUpProperty(mol, name);
11216                 if (idx < 0)
11217                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11218         }
11219         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11220                 if (ival == Qnil)
11221                         fidx = mol->cframe;
11222                 else {
11223                         fidx = NUM2INT(rb_Integer(ival));
11224                         n = MoleculeGetNumberOfFrames(mol);
11225                         if (fidx < 0 || fidx >= n)
11226                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11227                 }
11228                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11229                 ival = INT2FIX(fidx);
11230                 n = 1;
11231         } else {
11232                 ig = IntGroupFromValue(ival);
11233                 n = IntGroupGetCount(ig);
11234                 if (n == 0)
11235                         return rb_ary_new();
11236         }
11237         dp = (Double *)calloc(sizeof(Double), n);
11238         MoleculeGetProperty(mol, idx, ig, dp);  
11239         if (FIXNUM_P(ival))
11240                 ival = rb_float_new(dp[0]);
11241         else {
11242                 ival = rb_ary_new();
11243                 for (i = n - 1; i >= 0; i--) {
11244                         nval = rb_float_new(dp[i]);
11245                         rb_ary_store(ival, i, nval);
11246                 }
11247         }
11248         free(dp);
11249         IntGroupRelease(ig);
11250         return ival;
11251 }
11252
11253 /*
11254  *  call-seq:
11255  *     property_names -> Array
11256  *
11257  *  Get an array of property names.
11258  */
11259 static VALUE
11260 s_Molecule_PropertyNames(VALUE self)
11261 {
11262         Molecule *mol;
11263         VALUE rval, nval;
11264         int i;
11265     Data_Get_Struct(self, Molecule, mol);
11266         rval = rb_ary_new();
11267         for (i = mol->nmolprops - 1; i >= 0; i--) {
11268                 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11269                 rb_ary_store(rval, i, nval);
11270         }
11271         return rval;
11272 }
11273
11274 #pragma mark ------ Class methods ------
11275
11276 /*
11277  *  call-seq:
11278  *     current       -> Molecule
11279  *
11280  *  Get the currently "active" molecule.
11281  */
11282 static VALUE
11283 s_Molecule_Current(VALUE klass)
11284 {
11285         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11286 }
11287
11288 /*
11289  *  call-seq:
11290  *     Molecule[]          -> Molecule
11291  *     Molecule[n]         -> Molecule
11292  *     Molecule[name]      -> Molecule
11293  *     Molecule[name, k]   -> Molecule
11294  *     Molecule[regex]     -> Molecule
11295  *     Molecule[regex, k]  -> Molecule
11296  *
11297  *  Molecule[] is equivalent to Molecule.current.
11298  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11299  *  Molecule[name] gives the first document (in the order of creation time) that has
11300  *  the given name. If a second argument (k) is given, the k-th document that has the
11301  *  given name is returned.
11302  *  Molecule[regex] gives the first document (in the order of creation time) that
11303  *  has a name matching the regular expression. If a second argument (k) is given, 
11304  *  the k-th document that has a name matching the re is returned.
11305  */
11306 static VALUE
11307 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11308 {
11309         VALUE val, kval;
11310         int idx, k;
11311         Molecule *mol;
11312         char buf[1024];
11313         rb_scan_args(argc, argv, "02", &val, &kval);
11314         if (val == Qnil)
11315                 return s_Molecule_Current(klass);
11316         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11317                 idx = NUM2INT(val);
11318                 mol = MoleculeCallback_moleculeAtIndex(idx);
11319         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11320                 char *p = StringValuePtr(val);
11321                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11322                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11323                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11324                         if (strcmp(buf, p) == 0 && --k == 0)
11325                                 break;
11326                 }
11327         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11328                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11329                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11330                         VALUE name;
11331                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11332                         name = Ruby_NewEncodedStringValue2(buf);
11333                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11334                                 break;
11335                 }       
11336         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11337         
11338         if (mol == NULL)
11339                 return Qnil;
11340         else return ValueFromMolecule(mol);
11341 }
11342
11343 /*
11344  *  call-seq:
11345  *     list         -> array of Molecules
11346  *
11347  *  Get the list of molecules associated to the documents, in the order of creation
11348  *  time of the document. If no document is open, returns an empry array.
11349  */
11350 static VALUE
11351 s_Molecule_List(VALUE klass)
11352 {
11353         Molecule *mol;
11354         int i;
11355         VALUE ary;
11356         i = 0;
11357         ary = rb_ary_new();
11358         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11359                 rb_ary_push(ary, ValueFromMolecule(mol));
11360                 i++;
11361         }
11362         return ary;
11363 }
11364
11365 /*
11366  *  call-seq:
11367  *     ordered_list         -> array of Molecules
11368  *
11369  *  Get the list of molecules associated to the documents, in the order of front-to-back
11370  *  ordering of the associated window. If no document is open, returns an empry array.
11371  */
11372 static VALUE
11373 s_Molecule_OrderedList(VALUE klass)
11374 {
11375         Molecule *mol;
11376         int i;
11377         VALUE ary;
11378         i = 0;
11379         ary = rb_ary_new();
11380         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11381                 rb_ary_push(ary, ValueFromMolecule(mol));
11382                 i++;
11383         }
11384         return ary;
11385 }
11386
11387 #pragma mark ------ Call Subprocess ------
11388
11389 /*  The callback functions for call_subprocess_async  */
11390 static int
11391 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11392 {
11393         int ruby_status;
11394         VALUE procval, retval, args[2];
11395         args[0] = ValueFromMolecule(mol);
11396         args[1] = INT2NUM(status);
11397         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11398         if (procval != Qnil) {
11399                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11400                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11401                         return 1;
11402         }
11403         return 0;
11404 }
11405
11406 static int
11407 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11408 {
11409         int ruby_status;
11410         VALUE procval, retval, args[2];
11411         args[0] = ValueFromMolecule(mol);
11412         args[1] = INT2NUM(tcount);
11413         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11414         if (procval != Qnil) {
11415                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11416                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11417                         return 1;
11418         }
11419         return 0;
11420 }
11421
11422 /*
11423  *  call-seq:
11424  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11425  *
11426  *  Call subprocess asynchronically.
11427  *  If end_callback is given, it will be called (with two arguments self and termination status)
11428  *  when the subprocess terminated.
11429  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11430  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11431  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11432  *  filename begins with ">>", then the message will be appended to the file.
11433  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11434  *  If the argument is nil, then the message will be sent to the Ruby console.
11435  *  Returns the process ID as an integer.
11436  */
11437 static VALUE
11438 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11439 {
11440         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11441         Molecule *mol;
11442         char *sout, *serr;
11443         int n;
11444         FILE *fpout, *fperr;
11445         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11446         Data_Get_Struct(self, Molecule, mol);
11447
11448         if (stdout_val == Qnil) {
11449                 fpout = (FILE *)1;
11450         } else {
11451                 sout = StringValuePtr(stdout_val);
11452                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11453                         fpout = NULL;
11454                 else {
11455                         if (strncmp(sout, ">>", 2) == 0) {
11456                                 sout += 2;
11457                                 fpout = fopen(sout, "a");
11458                         } else {
11459                                 if (*sout == '>')
11460                                         sout++;
11461                                 fpout = fopen(sout, "w");
11462                         }
11463                         if (fpout == NULL)
11464                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11465                 }
11466         }
11467         if (stderr_val == Qnil) {
11468                 fperr = (FILE *)1;
11469         } else {
11470                 serr = StringValuePtr(stderr_val);
11471                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11472                         fperr = NULL;
11473                 else {
11474                         if (strncmp(serr, ">>", 2) == 0) {
11475                                 serr += 2;
11476                                 fpout = fopen(serr, "a");
11477                         } else {
11478                                 if (*serr == '>')
11479                                         serr++;
11480                                 fperr = fopen(serr, "w");
11481                         }
11482                         if (fperr == NULL)
11483                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11484                 }
11485         }
11486         
11487         /*  Register procs as instance variables  */
11488         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11489         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11490         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11491         if (fpout != NULL && fpout != (FILE *)1)
11492                 fclose(fpout);
11493         if (fperr != NULL && fperr != (FILE *)1)
11494                 fclose(fperr);
11495         return INT2NUM(n);
11496 }
11497
11498 #pragma mark ====== Define Molby Classes ======
11499
11500 void
11501 Init_Molby(void)
11502 {
11503         int i;
11504         
11505         /*  Define module Molby  */
11506         rb_mMolby = rb_define_module("Molby");
11507         
11508         /*  Define Vector3D, Transform, IntGroup  */
11509         Init_MolbyTypes();
11510         
11511         /*  Define MDArena  */
11512         Init_MolbyMDTypes();
11513
11514         /*  class Molecule  */
11515         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11516
11517         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11518     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11519     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11520         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11521         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11522
11523     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11524     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11525     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11526     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11527     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11528     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11529     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11530     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11531     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11532     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11533         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11534     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11535     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11536     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11537     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11538     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11539     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11540     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11541         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11542         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11543         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11544         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11545         
11546     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11547         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11548     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11549     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11550     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11551
11552     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11553     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11554     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11555     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11556     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11557     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11558         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11559         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11560         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11561         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11562         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11563         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11564         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11565         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11566         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11567         
11568         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11569         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11570         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11571         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11572         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11573         
11574         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11575     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11576         rb_define_alias(rb_cMolecule, "+", "add");
11577     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11578         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11579         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11580         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11581         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11582         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11583         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11584         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11585         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11586         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11587         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11588         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11589         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11590         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11591         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11592         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11593         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11594         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11595         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11596         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11597         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11598
11599         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11600         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11601         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11602         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11603         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11604
11605         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11606         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11607         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11608         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11609         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11610         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11611         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11612         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11613         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11614
11615         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11616         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11617         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11618         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11619         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11620         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11621         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11622         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11623         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11624         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11625         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11626         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11627         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11628         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11629         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11630         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11631         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11632         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11633         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11634         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11635         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11636         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11637
11638         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11639         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11640         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11641         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11642         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11643         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11644         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11645
11646         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11647         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11648         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11649         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11650         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11651         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11652         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11653         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11654         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11655         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11656         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11657         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11658         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11659
11660         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11661         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11662         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11663         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11664         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11665         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11666
11667         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11668         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11669         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11670         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11671         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11672         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11673         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11674         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11675         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11676         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11677         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11678         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11679         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11680         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11681         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11682         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11683         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11684         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11685         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11686         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11687         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11688         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11689         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11690         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11691         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11692         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11693         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11694         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11695         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11696         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11697         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11698         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11699         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11700         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11701         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11702         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11703         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11704         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11705         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11706         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11707         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11708         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11709         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11710         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11711         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11712         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11713         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11714         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11715         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11716         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11717         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11718         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11719         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11720         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11721         
11722         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11723         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11724         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11725         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11726         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11727         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11728         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11729         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11730         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11731         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11732         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11733         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11734         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11735
11736         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11737         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11738         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11739         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11740         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11741         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11742         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11743         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11744         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11745         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11746         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11747         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11748         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11749         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11750                 
11751         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11752         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11753         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11754         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11755         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11756         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11757         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11758         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11759         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11760         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11761         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11762         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11763         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11764         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11765         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11766         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11767         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11768         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11769         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11770         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11771         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11772         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11773         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11774
11775         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11776         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11777         
11778         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11779         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11780         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11781                 
11782         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11783         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11784         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11785         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11786         
11787         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11788         
11789         /*  class MolEnumerable  */
11790         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11791     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11792         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11793         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11794     rb_define_alias(rb_cMolEnumerable, "size", "length");
11795         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11796         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11797
11798         /*  class AtomRef  */
11799         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11800         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11801                 char buf[64];
11802                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11803                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11804                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11805                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11806                 strcat(buf, "=");
11807                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11808         }
11809         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11810         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11811         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11812         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11813         s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11814         rb_global_variable(&s_SetAtomAttrString);
11815         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11816         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11817
11818         /*  class Parameter  */
11819         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11820         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11821         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11822         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11823         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11824         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11825         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11826         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11827         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11828         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11829         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11830         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11831         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11832         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11833         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11834         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11835         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11836         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11837         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11838         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11839         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11840         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11841         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11842         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11843         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11844         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11845         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11846         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11847         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11848         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11849         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11850         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11851         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11852         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11853         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11854         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11855         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11856         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11857         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11858         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11859         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11860         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11861         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11862         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11863         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11864         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11865         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11866         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11867         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11868         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11869         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11870         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11871         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11872         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11873
11874         /*  class ParEnumerable  */
11875         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11876     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11877         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11878         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11879         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11880         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11881         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11882         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11883         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11884         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11885         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11886         
11887         /*  class ParameterRef  */
11888         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11889         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11890                 char buf[64];
11891                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11892                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11893                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11894                 if (s_ParameterAttrDefTable[i].symref != NULL)
11895                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11896                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11897                         strcat(buf, "=");
11898                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11899                 }
11900         }
11901         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11902         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11903         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11904         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11905         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11906         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11907         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11908         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11909
11910         /*  class MolbyError  */
11911         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11912
11913         /*  module Kernel  */
11914         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11915         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11916         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11917         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11918         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11919         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11920         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11921         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11922         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11923         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11924         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11925         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11926         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11927         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11928         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11929         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11930         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11931         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11932         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11933         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11934         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11935         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11936         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11937         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11938
11939         /*  class IO  */
11940         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11941         
11942         s_ID_equal = rb_intern("==");
11943         g_RubyID_call = rb_intern("call");
11944         
11945         s_InitMOInfoKeys();
11946         
11947         /*  Symbols for graphics  */
11948         s_LineSym = ID2SYM(rb_intern("line"));
11949         s_PolySym = ID2SYM(rb_intern("poly"));
11950         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11951         s_ConeSym = ID2SYM(rb_intern("cone"));
11952         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11953 }
11954
11955 #pragma mark ====== Interface with RubyDialog class ======
11956
11957 RubyValue
11958 RubyDialogCallback_parentModule(void)
11959 {
11960         return (RubyValue)rb_mMolby;
11961 }
11962
11963 #pragma mark ====== External functions ======
11964
11965 static VALUE s_ruby_top_self = Qfalse;
11966 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11967 static VALUE s_ruby_export_local_variables = Qfalse;
11968
11969 static VALUE
11970 s_evalRubyScriptOnMoleculeSub(VALUE val)
11971 {
11972         void **ptr = (void **)val;
11973         Molecule *mol = (Molecule *)ptr[1];
11974         VALUE sval, fnval, lnval, retval;
11975         VALUE binding;
11976
11977         /*  Clear the error information (store in the history array if necessary)  */
11978         sval = rb_errinfo();
11979         if (sval != Qnil) {
11980                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11981                 rb_set_errinfo(Qnil);
11982         }
11983
11984         if (s_ruby_top_self == Qfalse) {
11985                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11986         }
11987         if (s_ruby_get_binding_for_molecule == Qfalse) {
11988                 const char *s1 =
11989                  "lambda { |_mol_, _bind_| \n"
11990                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11991                  "  _proc_.call(_mol_) } ";
11992                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11993                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11994         }
11995         if (s_ruby_export_local_variables == Qfalse) {
11996                 const char *s2 =
11997                 "lambda { |_bind_| \n"
11998                 "   # find local variables newly defined in _bind_ \n"
11999                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12000                 " _a_.each { |_vsym_| \n"
12001                 "   _vname_ = _vsym_.to_s \n"
12002                 "   _vval_ = _bind_.eval(_vname_) \n"
12003                 "   #  Define local variable \n"
12004                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12005                 "   #  Then set value  \n"
12006                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12007                 " } \n"
12008                 "}";
12009                 s_ruby_export_local_variables = rb_eval_string(s2);
12010                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12011         }
12012         if (ptr[2] == NULL) {
12013                 char *scr;
12014                 /*  String literal: we need to specify string encoding  */
12015 #if defined(__WXMSW__)
12016                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12017 #else
12018                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12019 #endif
12020                 sval = Ruby_NewEncodedStringValue2(scr);
12021                 free(scr);
12022                 fnval = Ruby_NewEncodedStringValue2("(eval)");
12023                 lnval = INT2FIX(0);
12024         } else {
12025                 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12026                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12027                 lnval = INT2FIX(1);
12028         }
12029         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12030         if (mol != NULL) {
12031                 VALUE mval = ValueFromMolecule(mol);
12032                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12033         }
12034         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12035         if (mol != NULL) {
12036                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12037         }
12038         return retval;
12039 }
12040
12041 RubyValue
12042 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12043 {
12044         RubyValue retval;
12045         void *args[3];
12046         VALUE save_interrupt_flag;
12047 /*      char *save_ruby_sourcefile;
12048         int save_ruby_sourceline; */
12049         if (gMolbyIsCheckingInterrupt) {
12050                 MolActionAlertRubyIsRunning();
12051                 *status = -1;
12052                 return (RubyValue)Qnil;
12053         }
12054         gMolbyRunLevel++;
12055         args[0] = (void *)script;
12056         args[1] = (void *)mol;
12057         args[2] = (void *)fname;
12058         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12059 /*      save_ruby_sourcefile = ruby_sourcefile;
12060         save_ruby_sourceline = ruby_sourceline; */
12061         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12062         if (*status != 0) {
12063                 /*  Is this 'exit' exception?  */
12064                 VALUE last_exception = rb_gv_get("$!");
12065                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12066                         /*  Capture exit and return the status value  */
12067                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12068                         *status = 0;
12069                         rb_set_errinfo(Qnil);
12070                 }
12071         }
12072         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12073 /*      ruby_sourcefile = save_ruby_sourcefile;
12074         ruby_sourceline = save_ruby_sourceline; */
12075         gMolbyRunLevel--;
12076         return retval;
12077 }
12078
12079 /*  For debug  */
12080 char *
12081 Ruby_inspectValue(RubyValue value)
12082 {
12083     int status;
12084     static char buf[256];
12085     VALUE val = (VALUE)value;
12086     gMolbyRunLevel++;
12087     val = rb_protect(rb_inspect, val, &status);
12088     gMolbyRunLevel--;
12089     if (status == 0) {
12090         char *str = StringValuePtr(val);
12091         strncpy(buf, str, sizeof(buf) - 1);
12092         buf[sizeof(buf) - 1] = 0;
12093     } else {
12094         snprintf(buf, sizeof(buf), "Error status = %d", status);
12095     }
12096     return buf;
12097 }
12098
12099 int
12100 Ruby_showValue(RubyValue value, char **outValueString)
12101 {
12102         VALUE val = (VALUE)value;
12103         if (gMolbyIsCheckingInterrupt) {
12104                 MolActionAlertRubyIsRunning();
12105                 return 0;
12106         }
12107         if (val != Qnil) {
12108                 int status;
12109                 char *str;
12110                 gMolbyRunLevel++;
12111                 val = rb_protect(rb_inspect, val, &status);
12112                 gMolbyRunLevel--;
12113                 if (status != 0)
12114                         return status;
12115                 str = StringValuePtr(val);
12116                 if (outValueString != NULL)
12117                         *outValueString = strdup(str);
12118                 MyAppCallback_showScriptMessage("%s", str);
12119         } else {
12120                 if (outValueString != NULL)
12121                         *outValueString = NULL;
12122         }
12123         return 0;
12124 }
12125
12126 void
12127 Ruby_showError(int status)
12128 {
12129         static const int tag_raise = 6;
12130     char *main_message = "Molby script error";
12131         char *msg = NULL, *msg2;
12132         VALUE val, backtrace;
12133         int interrupted = 0;
12134     int exit_status = -1;
12135         if (status == tag_raise) {
12136                 VALUE errinfo = rb_errinfo();
12137                 VALUE eclass = CLASS_OF(errinfo);
12138                 if (eclass == rb_eInterrupt) {
12139             main_message = "Molby script interrupted";
12140             msg = "Interrupt";
12141                         interrupted = 1;
12142         } else if (eclass == rb_eSystemExit) {
12143             main_message = "Molby script exit";
12144             interrupted = 2;
12145             val = rb_eval_string_protect("$!.status", &status);
12146             if (status == 0) {
12147                 exit_status = NUM2INT(rb_Integer(val));
12148                 asprintf(&msg, "Molby script exit with status %d", exit_status);
12149             } else {
12150                 asprintf(&msg, "Molby script exit with unknown status");
12151             }
12152         }
12153         }
12154         gMolbyRunLevel++;
12155     if (exit_status != 0) {
12156         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12157         if (msg == NULL) {
12158             val = rb_eval_string_protect("$!.to_s", &status);
12159             if (status == 0)
12160                 msg = RSTRING_PTR(val);
12161             else
12162                 msg = "(message not available)";
12163         }
12164         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12165     } else {
12166         msg2 = strdup(msg);
12167     }
12168         MyAppCallback_messageBox(msg2, main_message, 0, 3);
12169         free(msg2);
12170     if (interrupted == 2) {
12171         free(msg);
12172         if (!gUseGUI && exit_status == 0)
12173             exit(0);  // Capture exit(0) here and force exit
12174     }
12175         gMolbyRunLevel--;
12176 }
12177
12178 /*  Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode.  */
12179 int
12180 Molby_loadScript(const char *script, int from_file)
12181 {
12182     int status;
12183     gMolbyRunLevel++;
12184     if (from_file)
12185         rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12186     else
12187         rb_eval_string_protect(script, &status);
12188     gMolbyRunLevel--;
12189     return status;
12190 }
12191
12192 void
12193 Molby_getDescription(char **versionString, char **auxString)
12194 {
12195         extern const char *gVersionString, *gCopyrightString;
12196         extern int gRevisionNumber;
12197         extern char *gLastBuildString;
12198     char *s1, *s2;
12199         char *revisionString;
12200         if (gRevisionNumber > 0) {
12201                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12202         } else revisionString = "";
12203
12204     asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12205 #if defined(__WXMSW__)
12206     #if TARGET_ARCH == 64
12207              "Molby (64 bit)",
12208     #else
12209              "Molby (32 bit)",
12210     #endif
12211 #else
12212              "Molby",
12213 #endif
12214              gVersionString, revisionString, gCopyrightString, gLastBuildString);
12215     if (gUseGUI) {
12216         asprintf(&s2,
12217                  "\nIncluding:\n"
12218                  "%s"
12219                  "ruby %s, http://www.ruby-lang.org/\n"
12220                  "%s\n"
12221                  "FFTW 3.3.2, http://www.fftw.org/\n"
12222                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12223                  "  and Massachusetts Institute of Technology",
12224                  MyAppCallback_getGUIDescriptionString(),
12225                  gRubyVersion, gRubyCopyright);
12226     } else {
12227         asprintf(&s2,
12228                  "Including "
12229                  "ruby %s, http://www.ruby-lang.org/\n"
12230                  "%s\n"
12231                  "FFTW 3.3.2, http://www.fftw.org/\n"
12232                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12233                  "  and Massachusetts Institute of Technology",
12234                  gRubyVersion, gRubyCopyright);
12235
12236     }
12237         if (revisionString[0] != 0)
12238                 free(revisionString);
12239         if (versionString != NULL)
12240         *versionString = s1;
12241     if (auxString != NULL)
12242         *auxString = s2;
12243 }
12244
12245 void
12246 Molby_startup(const char *script, const char *dir)
12247 {
12248         VALUE val;
12249         int status;
12250         char *libpath;
12251         char *respath, *p, *wbuf;
12252
12253         /*  Get version/copyright string from Ruby interpreter  */
12254         {
12255                 gRubyVersion = strdup(ruby_version);
12256                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12257                                  "  ",  /*  Indent for displaying in About dialog  */
12258                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12259         }
12260         
12261         /*  Read build and revision information for Molby  */
12262 /*      {
12263                 char buf[200];
12264                 extern int gRevisionNumber;
12265                 extern char *gLastBuildString;
12266                 FILE *fp = fopen("../buildInfo.txt", "r");
12267                 gLastBuildString = "";
12268                 if (fp != NULL) {
12269                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12270                                 char *p1 = strchr(buf, '\"');
12271                                 char *p2 = strrchr(buf, '\"');
12272                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12273                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12274                                         buf[p2 - p1 - 1] = 0;
12275                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12276                                 }
12277                         }
12278                         fclose(fp);
12279                 }
12280                 fp = fopen("../revisionInfo.txt", "r");
12281                 gRevisionNumber = 0;
12282                 if (fp != NULL) {
12283                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12284                                 gRevisionNumber = strtol(buf, NULL, 0);
12285                         }
12286                         fclose(fp);
12287                 }
12288     } */
12289
12290     if (!gUseGUI) {
12291         char *wbuf2;
12292         Molby_getDescription(&wbuf, &wbuf2);
12293         MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12294         free(wbuf);
12295         free(wbuf2);
12296     }
12297         
12298         /*  Read atom display parameters  */
12299         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12300         MyAppCallback_setConsoleColor(1);
12301         MyAppCallback_showScriptMessage("%s", wbuf);
12302         MyAppCallback_setConsoleColor(0);
12303                 free(wbuf);
12304         }
12305         
12306         /*  Read default parameters  */
12307         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12308         if (wbuf != NULL) {
12309         MyAppCallback_setConsoleColor(1);
12310         MyAppCallback_showScriptMessage("%s", wbuf);
12311         MyAppCallback_setConsoleColor(0);
12312                 free(wbuf);
12313         }
12314                 
12315         /*  Initialize Ruby interpreter  */
12316 #if __WXMSW__
12317         if (gUseGUI) {
12318                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12319                     it causes rb_bug() (= fatal error) during ruby_init().
12320                     As a workaround, these standard streams are reopend as
12321                     NUL stream.  */
12322                 freopen("NUL", "r", stdin);
12323                 freopen("NUL", "w", stdout);
12324                 freopen("NUL", "w", stderr);
12325         }
12326 #endif
12327         ruby_init();
12328
12329         {
12330         /*  Initialize CP932/Windows-31J encodings  */
12331                 extern void Init_shift_jis(void), Init_windows_31j(void),  Init_trans_japanese_sjis(void);
12332         extern int rb_enc_alias(const char *, const char *);
12333         Init_shift_jis();
12334         Init_windows_31j();
12335         Init_trans_japanese_sjis();
12336         rb_enc_alias("CP932", "Windows-31J");
12337     }
12338     
12339 #if defined(__WXMSW__)
12340     {
12341         /*  Set default external encoding  */
12342         /*  The following snippet is taken from encoding.c  */
12343         extern void rb_enc_set_default_external(VALUE encoding);
12344         char cp[sizeof(int) * 8 / 3 + 22];
12345         int status;
12346         VALUE enc;
12347         snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12348         enc = rb_eval_string_protect(cp, &status);
12349         if (status == 0 && !NIL_P(enc)) {
12350             rb_enc_set_default_external(enc);
12351         }
12352         }
12353 #endif
12354
12355         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12356         ruby_incpush(".");
12357         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12358         ruby_incpush(libpath);
12359         free(libpath);
12360         ruby_incpush(dir);
12361
12362         ruby_script("Molby");
12363         
12364         /*  Find the resource path (the parent directory of the given directory)  */
12365         respath = strdup(dir);
12366         p = strrchr(respath, '/');
12367         if (p == NULL && PATH_SEPARATOR != '/')
12368                 p = strrchr(respath, PATH_SEPARATOR);
12369         if (p != NULL)
12370                 *p = 0;
12371         val = Ruby_NewFileStringValue(respath);
12372         rb_define_global_const("MolbyResourcePath", val);
12373         free(respath);
12374
12375         /*  Define Molby classes  */
12376         Init_Molby();
12377     if (gUseGUI)
12378         RubyDialogInitClass();
12379
12380         rb_define_const(rb_mMolby, "ResourcePath", val);
12381         val = Ruby_NewFileStringValue(dir);
12382         rb_define_const(rb_mMolby, "ScriptPath", val);
12383         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12384         val = Ruby_NewFileStringValue(p);
12385         rb_define_const(rb_mMolby, "MbsfPath", val);    
12386         free(p);
12387         
12388         p = MyAppCallback_getHomeDir();
12389         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12390         rb_define_const(rb_mMolby, "HomeDirectory", val);
12391         free(p);
12392         p = MyAppCallback_getDocumentHomeDir();
12393         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12394         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12395         free(p);
12396         
12397     if (gUseGUI)
12398         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12399     else
12400         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12401
12402     {
12403         /*  Create objects for stdout and stderr  */
12404         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12405         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12406         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12407         rb_gv_set("$stdout", val);
12408         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12409         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12410         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12411         rb_gv_set("$stderr", val);
12412
12413         /*  Create objects for stdin  */
12414         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12415         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12416         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12417         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12418         rb_gv_set("$stdin", val);
12419     }
12420         
12421         /*  Global variable to hold error information  */
12422         rb_define_variable("$backtrace", &gMolbyBacktrace);
12423         rb_define_variable("$error_history", &gMolbyErrorHistory);
12424         gMolbyErrorHistory = rb_ary_new();
12425         
12426         /*  Global variables for script menus  */
12427         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12428         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12429         gScriptMenuCommands = rb_ary_new();
12430         gScriptMenuEnablers = rb_ary_new();
12431         
12432     if (gUseGUI) {
12433         /*  Register interrupt check code  */
12434         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12435         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12436         s_SetIntervalTimer(0, 50);
12437     }
12438         
12439         /*  Read the startup script  */
12440         if (script != NULL && script[0] != 0) {
12441                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12442                 gMolbyRunLevel++;
12443                 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12444                 gMolbyRunLevel--;
12445                 if (status != 0)
12446                         Ruby_showError(status);
12447                 else
12448                         MyAppCallback_showScriptMessage("Done.\n");
12449         }
12450 }
12451
12452 void
12453 Molby_buildARGV(int argc, const char **argv)
12454 {
12455         int i;
12456     rb_ary_clear(rb_argv);
12457     for (i = 0; i < argc; i++) {
12458                 VALUE arg = rb_tainted_str_new2(argv[i]);
12459                 OBJ_FREEZE(arg);
12460                 rb_ary_push(rb_argv, arg);
12461     }
12462 }