OSDN Git Service

Start implementing JANPA integration with Psi4
[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  *     hartree_to_kcal(val)
392  *
393  *  Convert hartree to kcal/mol
394  */
395 static VALUE
396 s_Kernel_HartreeToKcal(VALUE self, VALUE fval)
397 {
398     double d = NUM2DBL(rb_Float(fval));
399     return rb_float_new(d * 627.5094740630557);
400 }
401
402 /*
403  *  call-seq:
404  *     kcal_to_hartree(val)
405  *
406  *  Convert kcal/mol to hartree
407  */
408 static VALUE
409 s_Kernel_KcalToHartree(VALUE self, VALUE fval)
410 {
411     double d = NUM2DBL(rb_Float(fval));
412     return rb_float_new(d / 627.5094740630557);
413 }
414
415 /*
416  *  call-seq:
417  *     hartree_to_kj(val)
418  *
419  *  Convert hartree to kJ/mol
420  */
421 static VALUE
422 s_Kernel_HartreeToKJ(VALUE self, VALUE fval)
423 {
424     double d = NUM2DBL(rb_Float(fval));
425     return rb_float_new(d * 2625.4996394798253);
426 }
427
428 /*
429  *  call-seq:
430  *     kj_to_hartree(val)
431  *
432  *  Convert kJ/mol to hartree
433  */
434 static VALUE
435 s_Kernel_KJToHartree(VALUE self, VALUE fval)
436 {
437     double d = NUM2DBL(rb_Float(fval));
438     return rb_float_new(d / 2625.4996394798253);
439 }
440
441 /*
442  *  call-seq:
443  *     bohr_to_angstrom(val)
444  *
445  *  Convert bohr to angstrom
446  */
447 static VALUE
448 s_Kernel_BohrToAngstrom(VALUE self, VALUE fval)
449 {
450     double d = NUM2DBL(rb_Float(fval));
451     return rb_float_new(d * 0.529177210903);
452 }
453
454 /*
455  *  call-seq:
456  *     angstrom_to_bohr(val)
457  *
458  *  Convert angstrom to bohr
459  */
460 static VALUE
461 s_Kernel_AngstromToBohr(VALUE self, VALUE fval)
462 {
463     double d = NUM2DBL(rb_Float(fval));
464     return rb_float_new(d / 0.529177210903);
465 }
466
467 /*
468  *  call-seq:
469  *     stdout.write(str)
470  *
471  *  Put the message in the main text view in black color.
472  */
473 static VALUE
474 s_StandardOutput(VALUE self, VALUE str)
475 {
476         int n;
477         MyAppCallback_setConsoleColor(0);
478         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
479         return INT2NUM(n);
480 }
481
482 /*
483  *  call-seq:
484  *     stderr.write(str)
485  *
486  *  Put the message in the main text view in red color.
487  */
488 static VALUE
489 s_StandardErrorOutput(VALUE self, VALUE str)
490 {
491         int n;
492         MyAppCallback_setConsoleColor(1);
493         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
494         MyAppCallback_setConsoleColor(0);
495         return INT2NUM(n);
496 }
497
498 /*
499  *  call-seq:
500  *     stdout.flush
501  *     stderr.flush
502  *
503  *  Flush the standard (error) output. Actually do nothing.
504  */
505 static VALUE
506 s_FlushConsoleOutput(VALUE self)
507 {
508         return self;
509 }
510
511 /*
512  *  call-seq:
513  *     stdin.gets(rs = $/)
514  *
515  *  Read one line message via dialog box.
516  */
517 static VALUE
518 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
519 {
520         VALUE pval, rval;
521         pval = Ruby_NewEncodedStringValue2("Enter a line:");
522         rval = s_Kernel_Ask(1, &pval, self);
523         if (rval == Qnil)
524                 rb_interrupt();
525         rb_str_cat2(rval, "\n");
526         return rval;
527 }
528
529 /*
530  *  call-seq:
531  *     stdin.method_missing(name, args, ...)
532  *
533  *  Throw an exception, noting only gets and readline are defined.
534  */
535 static VALUE
536 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
537 {
538         VALUE nval;
539         rb_scan_args(argc, argv, "10", &nval);
540         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
541         return Qnil;  /*  Not reached  */
542 }
543
544 #pragma mark ====== Track key events ======
545
546 /*  User interrupt handling
547  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
548  *  key events. This polling should only be enabled during "normal" execution
549  *  of scripts and must be disabled when the rest of the application (or Ruby
550  *  script itself) is handling GUI. This is ensured by appropriate calls to
551  *  enable_interrupt and disable_interrupt.  */
552
553 static VALUE s_interrupt_flag = Qfalse;
554
555 static VALUE
556 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
557 {
558         volatile VALUE message;
559         const char *p;
560         if (Ruby_GetInterruptFlag() == Qtrue) {
561                 rb_scan_args(argc, argv, "01", &message);
562                 if (message != Qnil)
563                         p = StringValuePtr(message);
564                 else
565                         p = NULL;
566                 MyAppCallback_showProgressPanel(p);
567         }
568         return Qnil;
569 }
570
571 static VALUE
572 s_HideProgressPanel(VALUE self)
573 {
574         MyAppCallback_hideProgressPanel();
575         return Qnil;
576 }
577
578 static VALUE
579 s_SetProgressValue(VALUE self, VALUE val)
580 {
581         double dval = NUM2DBL(rb_Float(val));
582         MyAppCallback_setProgressValue(dval);
583         return Qnil;
584 }
585
586 static VALUE
587 s_SetProgressMessage(VALUE self, VALUE msg)
588 {
589         const char *p;
590         if (msg == Qnil)
591                 p = NULL;
592         else p = StringValuePtr(msg);
593         MyAppCallback_setProgressMessage(p);
594         return Qnil;
595 }
596
597 static VALUE
598 s_SetInterruptFlag(VALUE self, VALUE val)
599 {
600         VALUE oldval;
601         if (val != Qundef) {
602                 if (val == Qfalse || val == Qnil)
603                         val = Qfalse;
604                 else val = Qtrue;
605         }
606         oldval = s_interrupt_flag;
607         if (val != Qundef) {
608                 s_interrupt_flag = val;
609         }
610         return oldval;
611 }
612
613 static VALUE
614 s_GetInterruptFlag(VALUE self)
615 {
616         return s_SetInterruptFlag(self, Qundef);
617 }
618
619 VALUE
620 Ruby_SetInterruptFlag(VALUE val)
621 {
622         return s_SetInterruptFlag(Qnil, val);
623 }
624
625 VALUE
626 Ruby_GetInterruptFlag(void)
627 {
628         return s_SetInterruptFlag(Qnil, Qundef);
629 }
630
631 /*
632  *  call-seq:
633  *     check_interrupt -> integer
634  *
635  *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
636  */
637 static VALUE
638 s_Kernel_CheckInterrupt(VALUE self)
639 {
640         if (Ruby_GetInterruptFlag() == Qfalse)
641                 return INT2NUM(-1);
642         else if (MyAppCallback_checkInterrupt())
643                 return INT2NUM(1);
644         else return INT2NUM(0);
645 }
646
647 static volatile unsigned long sITimerCount = 0;
648
649 #if __WXMSW__
650 static HANDLE sITimerEvent;
651 static HANDLE sITimerThread;
652 static int sITimerInterval;
653
654 static __stdcall unsigned
655 s_ITimerThreadFunc(void *p)
656 {
657         while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
658                 sITimerCount++;
659         }
660         return 0;
661 }
662
663 #elif USE_PTHREAD_FOR_TIMER
664
665 /*  Timer thread  */
666 static pthread_t sTimerThread;
667
668 /*  -1: uninitiated; 0: active, 1: inactive, -2: request to terminate  */
669 static volatile signed char sTimerFlag = -1;
670 static volatile int sTimerIntervalMicrosec = 0;
671
672 static void *
673 s_TimerThreadEntry(void *param)
674 {
675         while (1) {
676                 usleep(sTimerIntervalMicrosec);
677                 if (sTimerFlag == 0)
678                         sITimerCount++;
679                 else if (sTimerFlag == -2)
680                         break;
681         }
682         return NULL;    
683 }
684
685 #endif
686
687 static void
688 s_SignalAction(int n)
689 {
690         sITimerCount++;
691 }
692
693 static void
694 s_SetIntervalTimer(int n, int msec)
695 {
696 #if __WXMSW__
697         if (n == 0) {
698                 /*  Start interval timer  */
699                 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
700                 sITimerInterval = msec;
701                 if (sITimerEvent) {
702                         sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
703                 }
704         } else {
705                 /*  Stop interval timer  */
706                 if (sITimerEvent)
707                         SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
708                 if (sITimerThread) {
709                         WaitForSingleObject(sITimerThread, 1000);
710                         CloseHandle(sITimerThread);
711                 }
712                 if (sITimerEvent)
713                         CloseHandle(sITimerEvent);
714                 sITimerEvent = NULL;
715                 sITimerThread = NULL;
716         }
717 #elif USE_PTHREAD_FOR_TIMER
718         if (n == 0) {
719                 if (sTimerFlag == -1) {
720                         int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
721                         if (status != 0) {
722                                 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
723                         }
724                 }
725                 sTimerFlag = 0;  /*  Active  */
726                 sTimerIntervalMicrosec = msec * 1000;
727         } else if (sTimerFlag != -1)
728                 sTimerFlag = 1;  /*  Inactive  */       
729 #else
730         static struct itimerval sOldValue;
731         static struct sigaction sOldAction;
732         struct itimerval val;
733         struct sigaction act;
734         if (n == 0) {
735                 sITimerCount = 0;
736                 act.sa_handler = s_SignalAction;
737                 act.sa_mask = 0;
738                 act.sa_flags = 0;
739                 sigaction(SIGALRM, &act, &sOldAction);
740                 val.it_value.tv_sec = 0;
741                 val.it_value.tv_usec = msec * 1000;
742                 val.it_interval.tv_sec = 0;
743                 val.it_interval.tv_usec = msec * 1000;
744                 setitimer(ITIMER_REAL, &val, &sOldValue);
745         } else {
746                 setitimer(ITIMER_REAL, &sOldValue, &val);
747                 sigaction(SIGALRM, &sOldAction, &act);
748         }
749 #endif
750 }
751
752 static unsigned long
753 s_GetTimerCount(void)
754 {
755         return sITimerCount;
756 }
757
758 static void
759 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
760 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
761 {
762         if (s_interrupt_flag != Qfalse) {
763                 static unsigned long sLastTime = 0;
764                 unsigned long currentTime;
765                 int flag;
766                 currentTime = s_GetTimerCount();
767                 if (currentTime != sLastTime) {
768                         sLastTime = currentTime;
769                         gMolbyIsCheckingInterrupt = 1;
770                         flag = MyAppCallback_checkInterrupt();
771                         gMolbyIsCheckingInterrupt = 0;
772                         if (flag) {
773                                 s_SetInterruptFlag(Qnil, Qfalse);
774                                 rb_interrupt();
775                         }
776                 }
777         }
778 }
779
780 #pragma mark ====== Menu handling ======
781
782 /*
783  *  call-seq:
784  *     register_menu(title, method, enable_proc = nil)
785  *
786  *  Register the method (specified as a symbol) in the script menu.
787  *  The method must be either an instance method of Molecule with no argument,
788  *  or a class method of Molecule with one argument (the current molecule),
789  *  or a proc object with one argument (the current molecule).
790  *  The menu associated with the class method can be invoked even when no document
791  *  is open (the argument is set to Qnil in this case). On the other hand, the
792  *  menu associated with the instance method can only be invoked when at least one 
793  *  document is active.
794  *  If enable_proc is non-nil, then it is called whenever the availability of
795  *  the menu command is tested. It is usually a proc object with one argument
796  *  (the current molecule or nil). As a special case, the following symbols can
797  *  be given; :mol (enabled when any molecule is open), :non_empty (enabled when
798  *  the top-level molecule has at least one atom), :selection (enabled when
799  *  the top-level molecule has one or more selected atoms).
800  */
801 static VALUE
802 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
803 {
804         int n, mtype = 0;
805         VALUE tval, mval, pval;
806         static VALUE sMolSym, sNonEmptySym, sSelectionSym;
807         static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
808         rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
809         tval = rb_str_to_str(tval);
810         n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
811         if (n < 0)
812                 return Qnil;
813         if (TYPE(mval) == T_SYMBOL) {
814                 /*  Create an appropriate proc object  */
815                 const char *name = rb_id2name(SYM2ID(mval));
816                 char *s;
817                 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
818                         /*  Defined as a Molecule method  */
819                         asprintf(&s, "lambda { |m| m.%s }", name);
820                         mtype = 1;
821                 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
822                         /*  Defined as a Molecule class method  */
823                         asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
824                         mtype = 2;
825                 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
826                 mval = rb_eval_string(s);
827                 free(s);
828         }
829         if (sMolSym == Qfalse) {
830                 sMolSym = ID2SYM(rb_intern("mol"));
831                 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
832                 sSelectionSym = ID2SYM(rb_intern("selection"));
833                 sMolProc = rb_eval_string("lambda { |m| m != nil }");
834         rb_define_variable("$is_a_molecule_proc", &sMolProc);
835                 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
836         rb_define_variable("$is_molecule_not_empty_proc", &sNonEmptyProc);
837                 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
838         rb_define_variable("$has_molecule_selection_proc", &sSelectionProc);
839                 sTrueProc = rb_eval_string("lambda { |m| true }");
840         rb_define_variable("$always_true_proc", &sTrueProc);
841         }
842         
843         if (pval == Qnil) {
844                 if (mtype == 1)
845                         pval = sMolProc;
846                 else
847                         pval = sTrueProc;
848         } else if (pval == sMolSym)
849                 pval = sMolProc;
850         else if (pval == sNonEmptySym)
851                 pval = sNonEmptyProc;
852         else if (pval == sSelectionSym)
853                 pval = sSelectionProc;
854         rb_ary_store(gScriptMenuCommands, n, mval);
855         rb_ary_store(gScriptMenuEnablers, n, pval);
856         return INT2NUM(n);
857 }
858
859 static VALUE
860 s_Kernel_LookupMenu(VALUE self, VALUE title)
861 {
862         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
863         return INT2NUM(n);
864 }
865
866 static VALUE
867 s_Ruby_UpdateUI_handler(VALUE data)
868 {
869         void **p = (void **)data;
870         int index = (intptr_t)p[0];
871         Molecule *mol = (Molecule *)p[1];
872         int *outChecked = (int *)p[2];
873         char **outTitle = (char **)p[3];
874         VALUE mval = ValueFromMolecule(mol);
875         VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
876         static ID call_id = 0;
877         if (call_id == 0)
878                 call_id = rb_intern("call");
879         if (pval == Qnil)
880                 return Qnil;
881         pval = rb_funcall(pval, call_id, 1, mval);
882         if (rb_obj_is_kind_of(pval, rb_cArray)) {
883                 VALUE val;
884                 if (outChecked != NULL) {
885                         val = rb_ary_entry(pval, 1);  /*  Checked or not  */
886                         *outChecked = (RTEST(val) ? 1 : 0);
887                 }
888                 if (outTitle != NULL) {
889                         val = rb_ary_entry(pval, 2);  /*  Text  */
890                         if (TYPE(val) == T_STRING) {
891                                 *outTitle = strdup(StringValuePtr(val));
892                         }
893                 }
894                 pval = rb_ary_entry(pval, 0);
895         }
896         return pval;
897 }
898
899 int
900 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
901 {
902         int status;
903         void *p[4];
904         VALUE retval;
905         p[0] = (void *)(intptr_t)index;
906         p[1] = mol;
907         p[2] = outChecked;
908         p[3] = outTitle;
909         retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
910         return (RTEST(retval) ? 1 : 0);
911 }
912
913 /*
914 static VALUE
915 s_Ruby_methodType_sub(VALUE data)
916 {
917         const char **p = (const char **)data;
918         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
919         ID mid = rb_intern(p[1]);
920         int ival;
921         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
922                 ival = 1;
923         else if (rb_respond_to(klass, mid))
924                 ival = 2;
925         else ival = 0;
926         return INT2FIX(ival);
927 }
928 */      
929 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
930     has the singleton method (class method) with the given name, 0 otherwise.  */
931 /*int
932 Ruby_methodType(const char *className, const char *methodName)
933 {
934         int status;
935         VALUE retval;
936         const char *p[2];
937         p[0] = className;
938         p[1] = methodName;
939         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
940         if (status == 0)
941                 return FIX2INT(retval);
942         else return 0;
943 }
944 */
945
946 /*
947  *  call-seq:
948  *     execute_script_file(fname)
949  *
950  *  Execute the script in the given file. If a molecule is active, then
951  *  the script is evaluated as Molecule.current.instance_eval(script).
952  *  Before entering the script, the current directory is set to the parent
953  *  directory of the script.
954  */
955 static VALUE
956 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
957 {
958         int status;
959         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
960         if (retval == (VALUE)6 && status == -1)
961                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
962         if (status != 0)
963                 rb_jump_tag(status);
964         return retval;
965 }
966
967 /*
968  *  call-seq:
969  *     document_home
970  *
971  *  Get the directory suitable for storing user documents. On Windows
972  *  it is the home directory + "My Documents". On other platforms
973  *  it is the home directory.
974  */
975 static VALUE
976 s_Kernel_DocumentHome(VALUE self)
977 {
978         char *s = MyAppCallback_getDocumentHomeDir();
979         VALUE retval = Ruby_NewFileStringValue(s);
980         free(s);
981         return retval;
982 }
983
984 /*  The callback function for call_subprocess  */
985 static int
986 s_Kernel_CallSubProcess_Callback(void *data)
987 {
988         int status;
989         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
990         if (status != 0 || retval == Qnil || retval == Qfalse)
991                 return 1;
992         else return 0;
993 }
994
995 /*
996  *  call-seq:
997  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
998  *
999  *  Call subprocess. A progress dialog window is displayed, with a message
1000  *  "Running #{process_name}...".
1001  *  A callback proc can be given, which is called periodically during execution. If the proc returns
1002  *  nil or false, then the execution will be interrupted.
1003  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
1004  *  filename begins with ">>", then the message will be appended to the file.
1005  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
1006  *  If the argument is nil, then the message will be sent to the Ruby console.
1007  */
1008 static VALUE
1009 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
1010 {
1011         VALUE cmd, procname, cproc, stdout_val, stderr_val;
1012     VALUE save_interruptFlag;
1013         int n, exitstatus, pid;
1014         char *sout, *serr;
1015     const char *pnamestr;
1016         FILE *fpout, *fperr;
1017
1018         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
1019
1020         if (stdout_val == Qnil) {
1021                 fpout = (FILE *)1;
1022         } else {
1023                 sout = StringValuePtr(stdout_val);
1024                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
1025                         fpout = NULL;
1026                 else {
1027                         if (strncmp(sout, ">>", 2) == 0) {
1028                                 sout += 2;
1029                                 fpout = fopen(sout, "a");
1030                         } else {
1031                                 if (*sout == '>')
1032                                         sout++;
1033                                 fpout = fopen(sout, "w");
1034                         }
1035                         if (fpout == NULL)
1036                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
1037                 }
1038         }
1039         if (stderr_val == Qnil) {
1040                 fperr = (FILE *)1;
1041         } else {
1042                 serr = StringValuePtr(stderr_val);
1043                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
1044                         fperr = NULL;
1045                 else {
1046                         if (strncmp(serr, ">>", 2) == 0) {
1047                                 serr += 2;
1048                                 fpout = fopen(serr, "a");
1049                         } else {
1050                                 if (*serr == '>')
1051                                         serr++;
1052                                 fperr = fopen(serr, "w");
1053                         }
1054                         if (fperr == NULL)
1055                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
1056                 }
1057         }
1058     
1059     save_interruptFlag = s_SetInterruptFlag(self, Qnil);
1060     if (procname != Qnil)
1061         pnamestr = StringValuePtr(procname);
1062     else pnamestr = NULL;
1063         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), pnamestr, (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
1064     s_SetInterruptFlag(self, save_interruptFlag);
1065     
1066         if (fpout != NULL && fpout != (FILE *)1)
1067                 fclose(fpout);
1068         if (fperr != NULL && fperr != (FILE *)1)
1069                 fclose(fperr);
1070
1071         return INT2NUM(n);
1072
1073         
1074 }
1075
1076 /*
1077  *  call-seq:
1078  *     backquote(cmd)
1079  *
1080  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
1081  */
1082 static VALUE
1083 s_Kernel_Backquote(VALUE self, VALUE cmd)
1084 {
1085         char *buf;
1086         int n, exitstatus, pid;
1087         VALUE val;
1088         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1089 /*      fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1090         if (n >= 0 && buf != NULL) {
1091                 val = Ruby_NewEncodedStringValue(buf, 0);
1092                 free(buf);
1093         } else {
1094                 val = Ruby_NewEncodedStringValue("", 0);
1095         }
1096         rb_last_status_set(exitstatus, pid);
1097         return val;
1098 }
1099
1100 #pragma mark ====== User defaults ======
1101
1102 /*
1103  *  call-seq:
1104  *     get_global_settings(key)
1105  *
1106  *  Get a setting data for key from the application preferences.
1107  */
1108 static VALUE
1109 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1110 {
1111         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1112         if (p != NULL) {
1113                 VALUE retval = rb_eval_string(p);
1114                 free(p);
1115                 return retval;
1116         } else return Qnil;
1117 }
1118
1119 /*
1120  *  call-seq:
1121  *     set_global_settings(key, value)
1122  *
1123  *  Set a setting data for key to the application preferences.
1124  */
1125 static VALUE
1126 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1127 {
1128         VALUE sval = rb_inspect(value);
1129         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1130         return value;
1131 }
1132
1133 #pragma mark ====== IO extension ======
1134
1135 static VALUE
1136 s_Ruby_str_encode_protected(VALUE val)
1137 {
1138         return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1139                                   ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1140 }
1141
1142 /*
1143  *  call-seq:
1144  *     gets_any_eol
1145  *
1146  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1147  */
1148 static VALUE
1149 s_IO_gets_any_eol(VALUE self)
1150 {
1151         VALUE val, val2, cval;
1152         char buf[1024];
1153         int i, c, status;
1154         static ID id_getbyte = 0, id_ungetbyte;
1155         if (id_getbyte == 0) {
1156                 id_getbyte = rb_intern("getbyte");
1157                 id_ungetbyte = rb_intern("ungetbyte");
1158         }
1159         i = 0;
1160         val = Qnil;
1161         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1162                 c = NUM2INT(rb_Integer(cval));
1163                 if (c == 0x0d) {
1164                         cval = rb_funcall(self, id_getbyte, 0);
1165                         if (cval != Qnil) {
1166                                 c = NUM2INT(rb_Integer(cval));
1167                                 if (c != 0x0a)
1168                                         rb_funcall(self, id_ungetbyte, 1, cval);
1169                         }
1170                         break;
1171                 } else if (c != 0x0a) {
1172                         buf[i++] = c;
1173                         if (i >= 1020) {
1174                                 buf[i] = 0;
1175                                 if (val == Qnil)
1176                                         val = rb_str_new(buf, i);
1177                                 else
1178                                         rb_str_append(val, rb_str_new(buf, i));
1179                                 i = 0;
1180                         }
1181                 } else break;
1182         }
1183         if (cval == Qnil && i == 0 && val == Qnil)
1184                 return Qnil;  /*  End of file  */
1185         buf[i] = 0;
1186         if (val == Qnil)
1187                 val = rb_str_new(buf, i);
1188         else if (i > 0)
1189                 rb_str_append(val, rb_str_new(buf, i));
1190         val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /*  Ignore exception  */
1191         if (status == 0)
1192                 val = val2;
1193         if (cval != Qnil) {
1194                 /*  Needs a end-of-line mark  */
1195                 cval = rb_gv_get("$/");
1196                 rb_str_append(val, cval);
1197         }
1198         rb_gv_set("$_", val);
1199         return val;
1200 }
1201
1202 #pragma mark ====== Utility functions (protected funcall) ======
1203
1204 struct Ruby_funcall2_record {
1205         VALUE recv;
1206         ID mid;
1207         int argc;
1208         VALUE *argv;
1209 };
1210
1211 static VALUE
1212 s_Ruby_funcall2_sub(VALUE data)
1213 {
1214         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1215         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1216 }
1217
1218 VALUE
1219 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1220 {
1221         struct Ruby_funcall2_record rec;
1222         rec.recv = recv;
1223         rec.mid = mid;
1224         rec.argc = argc;
1225         rec.argv = argv;
1226         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1227 }
1228
1229 RubyValue
1230 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1231 {
1232         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1233 }
1234
1235 #pragma mark ====== ParameterRef Class ======
1236
1237 static UnionPar *
1238 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1239 {
1240         UnionPar *up;
1241         ParameterRef *pref;
1242         Data_Get_Struct(self, ParameterRef, pref);
1243         if (typep != NULL)
1244                 *typep = pref->parType;
1245         if (pref->parType == kElementParType) {
1246                 up = (UnionPar *)&gElementParameters[pref->idx];
1247         } else {
1248                 up = ParameterRefGetPar(pref);
1249                 if (checkEditable) {
1250                         if (pref->idx < 0)
1251                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1252                         if (up->bond.src != 0 && up->bond.src != -1)
1253                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1254                 }
1255         }
1256         return up;
1257 }
1258
1259 static void
1260 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1261 {
1262         UnionPar *up;
1263         ParameterRef *pref;
1264         Data_Get_Struct(self, ParameterRef, pref);
1265         if (pref->mol == NULL)
1266                 return;
1267         up = ParameterRefGetPar(pref);
1268         if (key != s_SourceSym)
1269                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1270         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1271                 /*  Register undo  */
1272                 MolAction *act;
1273                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1274                 MolActionCallback_registerUndo(pref->mol, act);
1275                 MoleculeCallback_notifyModification(pref->mol, 0);
1276                 pref->mol->needsMDRebuild = 1;
1277         }
1278 }
1279
1280 VALUE
1281 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1282 {
1283         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1284         if (pref != NULL)
1285                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1286         else
1287                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1288 }
1289
1290 static int
1291 s_AtomTypeIndexFromValue(VALUE val)
1292 {
1293         if (rb_obj_is_kind_of(val, rb_cNumeric))
1294                 return NUM2INT(val);
1295         else
1296                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1297 }
1298
1299 static const char *s_ParameterTypeNames[] = {
1300         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1301 };
1302 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1303
1304 static int
1305 s_ParTypeFromValue(VALUE val)
1306 {
1307         int i, n;
1308         ID valid;
1309         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1310         if (s_ParameterTypeIDs[0] == 0) {
1311                 for (i = 0; i < n; i++)
1312                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1313         }
1314         valid = rb_to_id(val);
1315         for (i = 0; i < n; i++) {
1316                 if (valid == s_ParameterTypeIDs[i]) {
1317                         if (i == 7)
1318                                 return kElementParType;
1319                         else return kFirstParType + i;
1320                 }
1321         }
1322         return kInvalidParType;
1323 }
1324
1325 /*
1326  *  call-seq:
1327  *     index -> Integer
1328  *
1329  *  Get the index in the parameter list.
1330  */
1331 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1332         ParameterRef *pref;
1333         Data_Get_Struct(self, ParameterRef, pref);
1334         return INT2NUM(pref->idx);
1335 }
1336
1337 /*
1338  *  call-seq:
1339  *     par_type -> String
1340  *
1341  *  Get the parameter type, like "bond", "angle", etc.
1342  */
1343 static VALUE s_ParameterRef_GetParType(VALUE self) {
1344         Int tp;
1345         s_UnionParFromValue(self, &tp, 0);
1346         if (tp == kElementParType)
1347                 return Ruby_NewEncodedStringValue2("element");
1348         tp -= kFirstParType;
1349         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1350                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1351         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1352 }
1353
1354 /*
1355  *  call-seq:
1356  *     atom_type -> String or Array of String
1357  *     atom_types -> String or Array of String
1358  *
1359  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1360  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1361  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1362  *  The atom type may be "X", which is a wildcard that matches any atom type.
1363  */
1364 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1365         UnionPar *up;
1366         Int tp, i, n;
1367         UInt types[4];
1368         VALUE vals[4];
1369         up = s_UnionParFromValue(self, &tp, 0);
1370         n = ParameterGetAtomTypes(tp, up, types);
1371         if (n == 0)
1372                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1373         for (i = 0; i < n; i++) {
1374                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1375                         vals[i] = INT2NUM(types[i]);
1376                 else
1377                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1378         }
1379         if (n == 1)
1380                 return vals[0];
1381         else
1382                 return rb_ary_new4(n, vals);
1383 }
1384
1385 /*
1386  *  call-seq:
1387  *     k -> Float
1388  *
1389  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1390  */
1391 static VALUE s_ParameterRef_GetK(VALUE self) {
1392         UnionPar *up;
1393         Int tp, i, n;
1394         VALUE vals[3];
1395         up = s_UnionParFromValue(self, &tp, 0);
1396         switch (tp) {
1397                 case kBondParType:
1398                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1399                 case kAngleParType:
1400                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1401                 case kDihedralParType:
1402                 case kImproperParType:
1403                         if (up->torsion.mult == 1)
1404                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1405                         n = up->torsion.mult;
1406                         if (n > 3)
1407                                 n = 3;
1408                         for (i = 0; i < n; i++)
1409                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1410                         return rb_ary_new4(n, vals);
1411                 default:
1412                         rb_raise(rb_eMolbyError, "invalid member k");
1413         }
1414 }
1415
1416 /*
1417  *  call-seq:
1418  *     r0 -> Float
1419  *
1420  *  Get the equilibrium bond length. Only available for bond parameters.
1421  */
1422 static VALUE s_ParameterRef_GetR0(VALUE self) {
1423         UnionPar *up;
1424         Int tp;
1425         up = s_UnionParFromValue(self, &tp, 0);
1426         if (tp == kBondParType)
1427                 return rb_float_new(up->bond.r0);
1428         else rb_raise(rb_eMolbyError, "invalid member r0");
1429 }
1430
1431 /*
1432  *  call-seq:
1433  *     a0 -> Float
1434  *
1435  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1436  */
1437 static VALUE s_ParameterRef_GetA0(VALUE self) {
1438         UnionPar *up;
1439         Int tp;
1440         up = s_UnionParFromValue(self, &tp, 0);
1441         if (tp == kAngleParType)
1442                 return rb_float_new(up->angle.a0 * kRad2Deg);
1443         else rb_raise(rb_eMolbyError, "invalid member a0");
1444 }
1445
1446 /*
1447  *  call-seq:
1448  *     mult -> Float
1449  *
1450  *  Get the multiplicity. Only available for dihedral and improper parameters.
1451  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1452  */
1453 static VALUE s_ParameterRef_GetMult(VALUE self) {
1454         UnionPar *up;
1455         Int tp;
1456         up = s_UnionParFromValue(self, &tp, 0);
1457         if (tp == kDihedralParType || tp == kImproperParType)
1458                 return rb_float_new(up->torsion.mult);
1459         else rb_raise(rb_eMolbyError, "invalid member mult");
1460 }
1461
1462 /*
1463  *  call-seq:
1464  *     period -> Integer or Array of Integers
1465  *
1466  *  Get the periodicity. Only available for dihedral and improper parameters.
1467  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1468  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1469  */
1470 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1471         UnionPar *up;
1472         Int tp, i, n;
1473         VALUE vals[3];
1474         up = s_UnionParFromValue(self, &tp, 0);
1475         if (tp == kDihedralParType || tp == kImproperParType) {
1476                 if (up->torsion.mult == 1)
1477                         return INT2NUM(up->torsion.period[0]);
1478                 n = up->torsion.mult;
1479                 if (n > 3)
1480                         n = 3;
1481                 for (i = 0; i < n; i++)
1482                         vals[i] = INT2NUM(up->torsion.period[i]);
1483                 return rb_ary_new4(n, vals);
1484         } else rb_raise(rb_eMolbyError, "invalid member period");
1485 }
1486
1487 /*
1488  *  call-seq:
1489  *     phi0 -> Float or Array of Floats
1490  *
1491  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1492  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1493  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1494  */
1495 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1496         UnionPar *up;
1497         Int tp, i, n;
1498         VALUE vals[3];
1499         up = s_UnionParFromValue(self, &tp, 0);
1500         if (tp == kDihedralParType || tp == kImproperParType) {
1501                 if (up->torsion.mult == 1)
1502                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1503                 n = up->torsion.mult;
1504                 if (n > 3)
1505                         n = 3;
1506                 for (i = 0; i < n; i++)
1507                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1508                 return rb_ary_new4(n, vals);
1509         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1510 }
1511
1512 /*
1513  *  call-seq:
1514  *     A -> Float
1515  *
1516  *  Get the "A" value for the van der Waals parameter.
1517  */
1518 /*
1519  static VALUE s_ParameterRef_GetA(VALUE self) {
1520         UnionPar *up;
1521         Int tp;
1522         up = s_UnionParFromValue(self, &tp, 0);
1523         if (tp == kVdwParType)
1524                 return rb_float_new(up->vdw.A);
1525         else if (tp == kVdwPairParType)
1526                 return rb_float_new(up->vdwp.A);
1527         else rb_raise(rb_eMolbyError, "invalid member A");
1528 }
1529 */
1530
1531 /*
1532  *  call-seq:
1533  *     B -> Float
1534  *
1535  *  Get the "B" value for the van der Waals parameter.
1536  */
1537 /*
1538 static VALUE s_ParameterRef_GetB(VALUE self) {
1539         UnionPar *up;
1540         Int tp;
1541         up = s_UnionParFromValue(self, &tp, 0);
1542         if (tp == kVdwParType)
1543                 return rb_float_new(up->vdw.B);
1544         else if (tp == kVdwPairParType)
1545                 return rb_float_new(up->vdwp.B);
1546         else rb_raise(rb_eMolbyError, "invalid member B");
1547 }
1548 */
1549
1550 /*
1551  *  call-seq:
1552  *     r_eq -> Float
1553  *
1554  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1555  */
1556 static VALUE s_ParameterRef_GetReq(VALUE self) {
1557         UnionPar *up;
1558         Int tp;
1559 /*      Double a, b, r; */
1560         Double r;
1561         up = s_UnionParFromValue(self, &tp, 0);
1562         if (tp == kVdwParType) {
1563         /*      a = up->vdw.A;
1564                 b = up->vdw.B;  */
1565                 r = up->vdw.r_eq;
1566         } else if (tp == kVdwPairParType) {
1567         /*      a = up->vdwp.A;
1568                 b = up->vdwp.B;  */
1569                 r = up->vdwp.r_eq;
1570         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1571 /*      if (a == 0.0 || b == 0.0) */
1572         return rb_float_new(r);
1573 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1574 }
1575
1576 /*
1577  *  call-seq:
1578  *     eps -> Float
1579  *
1580  *  Get the minimum energy for the van der Waals parameter.
1581  */
1582 static VALUE s_ParameterRef_GetEps(VALUE self) {
1583         UnionPar *up;
1584         Int tp;
1585 /*      Double a, b; */
1586         Double eps;
1587         up = s_UnionParFromValue(self, &tp, 0);
1588         if (tp == kVdwParType) {
1589         /*      a = up->vdw.A;
1590                 b = up->vdw.B;  */
1591                 eps = up->vdw.eps;
1592         } else if (tp == kVdwPairParType) {
1593         /*      a = up->vdwp.A;
1594                 b = up->vdwp.B; */
1595                 eps = up->vdwp.eps;
1596         } else rb_raise(rb_eMolbyError, "invalid member eps");
1597 /*      if (a == 0.0 || b == 0.0)  */
1598                 return rb_float_new(eps * INTERNAL2KCAL);
1599 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1600 }
1601
1602 /*
1603  *  call-seq:
1604  *     A14 -> Float
1605  *
1606  *  Get the "A" value for the 1-4 van der Waals parameter.
1607  */
1608 /*
1609 static VALUE s_ParameterRef_GetA14(VALUE self) {
1610         UnionPar *up;
1611         Int tp;
1612         up = s_UnionParFromValue(self, &tp, 0);
1613         if (tp == kVdwParType)
1614                 return rb_float_new(up->vdw.A14);
1615         else if (tp == kVdwPairParType)
1616                 return rb_float_new(up->vdwp.A14);
1617         else rb_raise(rb_eMolbyError, "invalid member A14");
1618 }
1619 */
1620
1621 /*
1622  *  call-seq:
1623  *     B14 -> Float
1624  *
1625  *  Get the "B" value for the 1-4 van der Waals parameter.
1626  */
1627 /*
1628 static VALUE s_ParameterRef_GetB14(VALUE self) {
1629         UnionPar *up;
1630         Int tp;
1631         up = s_UnionParFromValue(self, &tp, 0);
1632         if (tp == kVdwParType)
1633                 return rb_float_new(up->vdw.B14);
1634         else if (tp == kVdwPairParType)
1635                 return rb_float_new(up->vdwp.B14);
1636         else rb_raise(rb_eMolbyError, "invalid member B14");
1637 }
1638 */
1639
1640 /*
1641  *  call-seq:
1642  *     r_eq14 -> Float
1643  *
1644  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1645  */
1646 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1647         UnionPar *up;
1648         Int tp;
1649 /*      Double a, b, r; */
1650         Double r;
1651         up = s_UnionParFromValue(self, &tp, 0);
1652         if (tp == kVdwParType) {
1653         /*      a = up->vdw.A14;
1654                 b = up->vdw.B14; */
1655                 r = up->vdw.r_eq14;
1656         } else if (tp == kVdwPairParType) {
1657         /*      a = up->vdwp.A14;
1658                 b = up->vdwp.B14;  */
1659                 r = up->vdwp.r_eq14;
1660         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1661 /*      if (a == 0.0 || b == 0.0)  */
1662         return rb_float_new(r);
1663 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1664 }
1665
1666 /*
1667  *  call-seq:
1668  *     eps14 -> Float
1669  *
1670  *  Get the minimum energy for the 1-4 van der Waals parameter.
1671  */
1672 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1673         UnionPar *up;
1674         Int tp;
1675 /*      Double a, b;  */
1676         Double eps;
1677         up = s_UnionParFromValue(self, &tp, 0);
1678         if (tp == kVdwParType) {
1679         /*      a = up->vdw.A14;
1680                 b = up->vdw.B14;  */
1681                 eps = up->vdw.eps14;
1682         } else if (tp == kVdwPairParType) {
1683         /*      a = up->vdwp.A14;
1684                 b = up->vdwp.B14; */
1685                 eps = up->vdwp.eps14;
1686         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1687 /*      if (a == 0.0 || b == 0.0) */
1688         return rb_float_new(eps * INTERNAL2KCAL);
1689 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1690 }
1691
1692 /*
1693  *  call-seq:
1694  *     cutoff -> Float
1695  *
1696  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1697  */
1698 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1699         UnionPar *up;
1700         Int tp;
1701         up = s_UnionParFromValue(self, &tp, 0);
1702         if (tp == kVdwCutoffParType)
1703                 return rb_float_new(up->vdwcutoff.cutoff);
1704         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1705 }
1706
1707 /*
1708  *  call-seq:
1709  *     radius -> Float
1710  *
1711  *  Get the atomic (covalent) radius for the element parameter.
1712  */
1713 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1714         UnionPar *up;
1715         Int tp;
1716         up = s_UnionParFromValue(self, &tp, 0);
1717         if (tp == kElementParType)
1718                 return rb_float_new(up->atom.radius);
1719         else rb_raise(rb_eMolbyError, "invalid member radius");
1720 }
1721
1722 /*
1723  *  call-seq:
1724  *     vdw_radius -> Float
1725  *
1726  *  Get the van der Waals radius for the element parameter. (0 if not given)
1727  */
1728 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1729         UnionPar *up;
1730         Int tp;
1731         up = s_UnionParFromValue(self, &tp, 0);
1732         if (tp == kElementParType)
1733                 return rb_float_new(up->atom.vdw_radius);
1734         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1735 }
1736
1737 /*
1738  *  call-seq:
1739  *     color -> [Float, Float, Float]
1740  *
1741  *  Get the rgb color for the element parameter.
1742  */
1743 static VALUE s_ParameterRef_GetColor(VALUE self) {
1744         UnionPar *up;
1745         Int tp;
1746         up = s_UnionParFromValue(self, &tp, 0);
1747         if (tp == kElementParType)
1748                 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));
1749         else rb_raise(rb_eMolbyError, "invalid member color");
1750 }
1751
1752 /*
1753  *  call-seq:
1754  *     atomic_number -> Integer
1755  *
1756  *  Get the atomic number for the vdw or element parameter.
1757  */
1758 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1759         UnionPar *up;
1760         Int tp;
1761         up = s_UnionParFromValue(self, &tp, 0);
1762         if (tp == kElementParType)
1763                 return INT2NUM(up->atom.number);
1764         else if (tp == kVdwParType)
1765                 return INT2NUM(up->vdw.atomicNumber);
1766         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1767 }
1768
1769 /*
1770  *  call-seq:
1771  *     name -> String
1772  *
1773  *  Get the name for the element parameter.
1774  */
1775 static VALUE s_ParameterRef_GetName(VALUE self) {
1776         UnionPar *up;
1777         Int tp;
1778         up = s_UnionParFromValue(self, &tp, 0);
1779         if (tp == kElementParType) {
1780                 char name[5];
1781                 strncpy(name, up->atom.name, 4);
1782                 name[4] = 0;
1783                 return Ruby_NewEncodedStringValue2(name);
1784         } else rb_raise(rb_eMolbyError, "invalid member name");
1785 }
1786
1787 /*
1788  *  call-seq:
1789  *     weight -> Float
1790  *
1791  *  Get the atomic weight for the element parameter.
1792  */
1793 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1794         UnionPar *up;
1795         Int tp;
1796         up = s_UnionParFromValue(self, &tp, 0);
1797         if (tp == kElementParType)
1798                 return rb_float_new(up->atom.weight);
1799         else if (tp == kVdwParType)
1800                 return rb_float_new(up->vdw.weight);
1801         else rb_raise(rb_eMolbyError, "invalid member weight");
1802 }
1803
1804 /*
1805  *  call-seq:
1806  *     fullname -> String
1807  *
1808  *  Get the full name for the element parameter.
1809  */
1810 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1811         UnionPar *up;
1812         Int tp;
1813         up = s_UnionParFromValue(self, &tp, 0);
1814         if (tp == kElementParType) {
1815                 char fullname[16];
1816                 strncpy(fullname, up->atom.fullname, 15);
1817                 fullname[15] = 0;
1818                 return Ruby_NewEncodedStringValue2(fullname);
1819         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1820 }
1821
1822 /*
1823  *  call-seq:
1824  *     comment -> String
1825  *
1826  *  Get the comment for the parameter.
1827  */
1828 static VALUE s_ParameterRef_GetComment(VALUE self) {
1829         UnionPar *up;
1830         Int tp, com;
1831         up = s_UnionParFromValue(self, &tp, 0);
1832         com = up->bond.com;
1833         if (com == 0)
1834                 return Qnil;
1835         else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1836 }
1837
1838 /*
1839  *  call-seq:
1840  *     source -> String
1841  *
1842  *  Get the source string for the parameter. Returns false for undefined parameter,
1843  *  and nil for "local" parameter that is specific for the molecule.
1844  */
1845 static VALUE s_ParameterRef_GetSource(VALUE self) {
1846         UnionPar *up;
1847         Int tp, src;
1848         up = s_UnionParFromValue(self, &tp, 0);
1849         src = up->bond.src;
1850         if (src < 0)
1851                 return Qfalse;  /* undefined */
1852         else if (src == 0)
1853                 return Qnil;  /*  local  */
1854         else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1855 }
1856
1857 static void
1858 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1859 {
1860         VALUE *valp;
1861         int i;
1862         if (n == 1)
1863                 valp = &val;
1864         else {
1865                 if (rb_obj_is_kind_of(val, rb_cString)) {
1866                         char *s = StringValuePtr(val);
1867                         char *p;
1868                         for (i = 0; i < n; i++) {
1869                                 char buf[40];
1870                                 int len;
1871                                 /*  Skip leading separaters  */
1872                                 while (*s == '-' || *s == ' ' || *s == '\t')
1873                                         s++;
1874                                 for (p = s; *p != 0; p++) {
1875                                         if (*p == '-' || *p == ' ' || *p == '\t')
1876                                                 break;
1877                                 }
1878                                 len = p - s;
1879                                 if (len >= sizeof(buf))
1880                                         len = sizeof(buf) - 1;
1881                                 strncpy(buf, s, len);
1882                                 buf[len] = 0;
1883                                 /*  Skip trailing blanks  */
1884                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1885                                         buf[len] = 0;
1886                                 if (buf[0] == 0)
1887                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1888                                 if (buf[0] >= '0' && buf[0] <= '9')
1889                                         types[i] = atoi(buf);
1890                                 else
1891                                         types[i] = AtomTypeEncodeToUInt(buf);
1892                                 if (p == NULL || *p == 0) {
1893                                         i++;
1894                                         break;
1895                                 } else s = p + 1;
1896                         }
1897                         if (i < n)
1898                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1899                         return;
1900                 }
1901                 val = rb_ary_to_ary(val);
1902                 if (RARRAY_LEN(val) != n)
1903                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1904                 valp = RARRAY_PTR(val);
1905         }
1906         for (i = 0; i < n; i++) {
1907                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1908                         types[i] = NUM2INT(rb_Integer(valp[i]));
1909                 else {
1910                         VALUE sval = valp[i];
1911                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1912                 }
1913         }
1914 }
1915
1916 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1917         UnionPar *up;
1918         VALUE oldval;
1919         Int oldsrc, tp;
1920         UInt types[4];
1921         up = s_UnionParFromValue(self, &tp, 1);
1922         oldval = s_ParameterRef_GetAtomTypes(self);
1923         oldsrc = up->bond.src;
1924         switch (tp) {
1925                 case kBondParType:
1926                         s_ScanAtomTypes(val, 2, types);
1927                         up->bond.type1 = types[0];
1928                         up->bond.type2 = types[1];
1929                         break;
1930                 case kAngleParType:
1931                         s_ScanAtomTypes(val, 3, types);
1932                         up->angle.type1 = types[0];
1933                         up->angle.type2 = types[1];
1934                         up->angle.type3 = types[2];
1935                         break;
1936                 case kDihedralParType:
1937                 case kImproperParType:
1938                         s_ScanAtomTypes(val, 4, types);
1939                         up->torsion.type1 = types[0];
1940                         up->torsion.type2 = types[1];
1941                         up->torsion.type3 = types[2];
1942                         up->torsion.type4 = types[3];
1943                         break;
1944                 case kVdwParType:
1945                         s_ScanAtomTypes(val, 1, types);
1946                         up->vdw.type1 = types[0];
1947                         break;
1948                 case kVdwPairParType:
1949                         s_ScanAtomTypes(val, 2, types);
1950                         up->vdwp.type1 = types[0];
1951                         up->vdwp.type2 = types[1];
1952                         break;
1953                 case kVdwCutoffParType:
1954                         s_ScanAtomTypes(val, 2, types);
1955                         up->vdwcutoff.type1 = types[0];
1956                         up->vdwcutoff.type2 = types[1];
1957                         break;
1958                 default:
1959                         return Qnil;
1960         }
1961         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1962         return val;
1963 }
1964
1965 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1966         UnionPar *up;
1967         Int tp, i, n, oldsrc;
1968         VALUE *valp, oldval;
1969         up = s_UnionParFromValue(self, &tp, 1);
1970         oldval = s_ParameterRef_GetK(self);
1971         oldsrc = up->bond.src;
1972         switch (tp) {
1973                 case kBondParType:
1974                         val = rb_Float(val);
1975                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1976                         break;
1977                 case kAngleParType:
1978                         val = rb_Float(val);
1979                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1980                         break;
1981                 case kDihedralParType:
1982                 case kImproperParType:
1983                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1984                                 up->torsion.mult = 1;
1985                                 val = rb_Float(val);
1986                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1987                                 break;
1988                         }
1989                         n = up->torsion.mult;
1990                         if (n > 3)
1991                                 n = 3;
1992                         val = rb_ary_to_ary(val);
1993                         if (RARRAY_LEN(val) != n)
1994                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1995                         valp = RARRAY_PTR(val);
1996                         for (i = 0; i < n; i++) {
1997                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1998                         }
1999                         break;
2000                 default:
2001                         rb_raise(rb_eMolbyError, "invalid member k");
2002         }
2003         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
2004         return val;
2005 }
2006
2007 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
2008         UnionPar *up;
2009         Int tp, oldsrc;
2010         VALUE oldval;
2011         up = s_UnionParFromValue(self, &tp, 1);
2012         oldval = s_ParameterRef_GetR0(self);
2013         oldsrc = up->bond.src;
2014         if (tp == kBondParType) {
2015                 val = rb_Float(val);
2016                 up->bond.r0 = NUM2DBL(val);
2017         } else rb_raise(rb_eMolbyError, "invalid member r0");
2018         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
2019         return val;
2020 }
2021
2022 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
2023         UnionPar *up;
2024         Int tp, oldsrc;
2025         VALUE oldval;
2026         up = s_UnionParFromValue(self, &tp, 1);
2027         oldval = s_ParameterRef_GetA0(self);
2028         oldsrc = up->bond.src;
2029         if (tp == kAngleParType) {
2030                 val = rb_Float(val);
2031                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
2032         } else rb_raise(rb_eMolbyError, "invalid member a0");
2033         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
2034         return val;
2035 }
2036
2037 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
2038         UnionPar *up;
2039         Int tp, oldsrc;
2040         VALUE oldval;
2041         up = s_UnionParFromValue(self, &tp, 1);
2042         oldval = s_ParameterRef_GetMult(self);
2043         oldsrc = up->bond.src;
2044         if (tp == kDihedralParType || tp == kImproperParType) {
2045                 int i;
2046                 val = rb_Integer(val);
2047                 i = NUM2INT(val);
2048                 if (i < 0 || i > 3)
2049                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
2050                 up->torsion.mult = i;
2051         } else rb_raise(rb_eMolbyError, "invalid member mult");
2052         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
2053         return val;
2054 }
2055
2056 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
2057         UnionPar *up;
2058         Int tp, i, n, oldsrc;
2059         VALUE *valp, oldval;
2060         up = s_UnionParFromValue(self, &tp, 1);
2061         oldval = s_ParameterRef_GetPeriod(self);
2062         oldsrc = up->bond.src;
2063         if (tp == kDihedralParType || tp == kImproperParType) {
2064                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2065                         up->torsion.mult = 1;
2066                         val = rb_Integer(val);
2067                         up->torsion.period[0] = NUM2INT(val);
2068                 } else {
2069                         n = up->torsion.mult;
2070                         if (n > 3)
2071                                 n = 3;
2072                         val = rb_ary_to_ary(val);
2073                         if (RARRAY_LEN(val) != n)
2074                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
2075                         valp = RARRAY_PTR(val);
2076                         for (i = 0; i < n; i++) {
2077                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
2078                         }
2079                 }
2080         } else rb_raise(rb_eMolbyError, "invalid member period");
2081         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2082         return val;
2083 }
2084
2085 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2086         UnionPar *up;
2087         Int tp, i, n, oldsrc;
2088         VALUE *valp, oldval;
2089         up = s_UnionParFromValue(self, &tp, 1);
2090         oldval = s_ParameterRef_GetPhi0(self);
2091         oldsrc = up->bond.src;
2092         if (tp == kDihedralParType || tp == kImproperParType) {
2093                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2094                         up->torsion.mult = 1;
2095                         val = rb_Float(val);
2096                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2097                 } else {
2098                         n = up->torsion.mult;
2099                         if (n > 3)
2100                                 n = 3;
2101                         val = rb_ary_to_ary(val);
2102                         if (RARRAY_LEN(val) != n)
2103                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2104                         valp = RARRAY_PTR(val);
2105                         for (i = 0; i < n; i++)
2106                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2107                 }
2108         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2109         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2110         return val;
2111 }
2112
2113 /*
2114 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2115         UnionPar *up;
2116         Int tp, oldsrc;
2117         double d;
2118         VALUE oldval;
2119         up = s_UnionParFromValue(self, &tp, 1);
2120         oldval = s_ParameterRef_GetA(self);
2121         oldsrc = up->bond.src;
2122         val = rb_Float(val);
2123         d = NUM2DBL(val);
2124         if (tp == kVdwParType)
2125                 up->vdw.A = d;
2126         else if (tp == kVdwPairParType)
2127                 up->vdwp.A = d;
2128         else rb_raise(rb_eMolbyError, "invalid member A");
2129         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2130         return val;
2131 }
2132
2133 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2134         UnionPar *up;
2135         Int tp, oldsrc;
2136         double d;
2137         VALUE oldval;
2138         up = s_UnionParFromValue(self, &tp, 1);
2139         oldval = s_ParameterRef_GetB(self);
2140         oldsrc = up->bond.src;
2141         val = rb_Float(val);
2142         d = NUM2DBL(val);
2143         if (tp == kVdwParType)
2144                 up->vdw.B = d;
2145         else if (tp == kVdwPairParType)
2146                 up->vdwp.B = d;
2147         else rb_raise(rb_eMolbyError, "invalid member B");
2148         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2149         return val;
2150 }
2151 */
2152
2153 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2154         UnionPar *up;
2155         Int tp, oldsrc;
2156         Double r;
2157         VALUE oldval;
2158         up = s_UnionParFromValue(self, &tp, 1);
2159         oldval = s_ParameterRef_GetReq(self);
2160         oldsrc = up->bond.src;
2161         val = rb_Float(val);
2162         r = NUM2DBL(val);
2163         if (tp == kVdwParType) {
2164                 up->vdw.r_eq = r;
2165                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2166                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2167         } else if (tp == kVdwPairParType) {
2168                 up->vdwp.r_eq = r;
2169                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2170                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2171         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2172         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2173         return val;
2174 }
2175
2176 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2177         UnionPar *up;
2178         Int tp, oldsrc;
2179         Double e;
2180         VALUE oldval;
2181         up = s_UnionParFromValue(self, &tp, 1);
2182         oldval = s_ParameterRef_GetEps(self);
2183         oldsrc = up->bond.src;
2184         val = rb_Float(val);
2185         e = NUM2DBL(val) * KCAL2INTERNAL;
2186         if (tp == kVdwParType) {
2187                 up->vdw.eps = e;
2188                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2189                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2190         } else if (tp == kVdwPairParType) {
2191                 up->vdwp.eps = e;
2192                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2193                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2194         } else rb_raise(rb_eMolbyError, "invalid member eps");
2195         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2196         return val;
2197 }
2198
2199 /*
2200 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2201         UnionPar *up;
2202         Int tp, oldsrc;
2203         double d;
2204         VALUE oldval;
2205         up = s_UnionParFromValue(self, &tp, 1);
2206         oldval = s_ParameterRef_GetA14(self);
2207         oldsrc = up->bond.src;
2208         val = rb_Float(val);
2209         d = NUM2DBL(val);
2210         if (tp == kVdwParType)
2211                 up->vdw.A14 = d;
2212         else if (tp == kVdwPairParType)
2213                 up->vdwp.A14 = d;
2214         else rb_raise(rb_eMolbyError, "invalid member A14");
2215         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2216         return val;
2217 }
2218
2219 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2220         UnionPar *up;
2221         Int tp, oldsrc;
2222         double d;
2223         VALUE oldval;
2224         up = s_UnionParFromValue(self, &tp, 1);
2225         oldval = s_ParameterRef_GetB14(self);
2226         oldsrc = up->bond.src;
2227         val = rb_Float(val);
2228         d = NUM2DBL(val);
2229         if (tp == kVdwParType)
2230                 up->vdw.B14 = d;
2231         else if (tp == kVdwPairParType)
2232                 up->vdwp.B14 = d;
2233         else rb_raise(rb_eMolbyError, "invalid member B14");
2234         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2235         return val;
2236 }
2237 */
2238
2239 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2240         UnionPar *up;
2241         Int tp, oldsrc;
2242         Double r;
2243         VALUE oldval;
2244         up = s_UnionParFromValue(self, &tp, 1);
2245         oldval = s_ParameterRef_GetReq14(self);
2246         oldsrc = up->bond.src;
2247         val = rb_Float(val);
2248         r = NUM2DBL(val);
2249         if (tp == kVdwParType) {
2250                 up->vdw.r_eq14 = r;
2251                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2252                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2253         } else if (tp == kVdwPairParType) {
2254                 up->vdwp.r_eq14 = r;
2255                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2256                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2257         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2258         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2259         return val;
2260 }
2261
2262 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2263         UnionPar *up;
2264         Int tp, oldsrc;
2265         Double e;
2266         VALUE oldval;
2267         up = s_UnionParFromValue(self, &tp, 1);
2268         oldval = s_ParameterRef_GetEps14(self);
2269         oldsrc = up->bond.src;
2270         val = rb_Float(val);
2271         e = NUM2DBL(val) * KCAL2INTERNAL;
2272         if (tp == kVdwParType) {
2273                 up->vdw.eps14 = e;
2274                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2275                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2276         } else if (tp == kVdwPairParType) {
2277                 up->vdwp.eps14 = e;
2278                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2279                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2280         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2281         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2282         return val;
2283 }
2284
2285 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2286         UnionPar *up;
2287         Int tp, oldsrc;
2288         VALUE oldval;
2289         oldval = s_ParameterRef_GetCutoff(self);
2290         oldsrc = up->bond.src;
2291         up = s_UnionParFromValue(self, &tp, 1);
2292         val = rb_Float(val);
2293         if (tp == kVdwCutoffParType) {
2294                 up->vdwcutoff.cutoff = NUM2DBL(val);
2295         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2296         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2297         return val;
2298 }
2299
2300 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2301         UnionPar *up;
2302         Int tp, oldsrc;
2303         VALUE oldval;
2304         up = s_UnionParFromValue(self, &tp, 1);
2305         oldval = s_ParameterRef_GetRadius(self);
2306         oldsrc = up->bond.src;
2307         val = rb_Float(val);
2308         if (tp == kElementParType) {
2309                 up->atom.radius = NUM2DBL(val);
2310         } else rb_raise(rb_eMolbyError, "invalid member radius");
2311         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2312         return val;
2313 }
2314
2315 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2316         UnionPar *up;
2317         Int tp, oldsrc;
2318         VALUE oldval;
2319         up = s_UnionParFromValue(self, &tp, 1);
2320         oldval = s_ParameterRef_GetVdwRadius(self);
2321         oldsrc = up->bond.src;
2322         val = rb_Float(val);
2323         if (tp == kElementParType) {
2324                 up->atom.vdw_radius = NUM2DBL(val);
2325         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2326         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2327         return val;
2328 }
2329
2330 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2331         UnionPar *up;
2332         Int tp, oldsrc;
2333         VALUE *valp, oldval;
2334         up = s_UnionParFromValue(self, &tp, 1);
2335         oldval = s_ParameterRef_GetColor(self);
2336         oldsrc = up->bond.src;
2337         val = rb_ary_to_ary(val);
2338         if (RARRAY_LEN(val) != 3)
2339                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2340         valp = RARRAY_PTR(val);
2341         if (tp == kElementParType) {
2342                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2343                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2344                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2345         } else rb_raise(rb_eMolbyError, "invalid member color");
2346         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2347         return val;
2348 }
2349
2350 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2351         UnionPar *up;
2352         Int tp, oldsrc;
2353         VALUE oldval;
2354         up = s_UnionParFromValue(self, &tp, 1);
2355         oldval = s_ParameterRef_GetAtomicNumber(self);
2356         oldsrc = up->bond.src;
2357         val = rb_Integer(val);
2358         if (tp == kElementParType)
2359                 up->atom.number = NUM2INT(val);
2360         else if (tp == kVdwParType) {
2361                 up->vdw.atomicNumber = NUM2INT(val);
2362                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2363         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2364         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2365         return val;
2366 }
2367
2368 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2369         UnionPar *up;
2370         Int tp, oldsrc;
2371         VALUE oldval;
2372         up = s_UnionParFromValue(self, &tp, 1);
2373         oldval = s_ParameterRef_GetName(self);
2374         oldsrc = up->bond.src;
2375         if (tp == kElementParType) {
2376                 strncpy(up->atom.name, StringValuePtr(val), 4);
2377         } else rb_raise(rb_eMolbyError, "invalid member name");
2378         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2379         return val;
2380 }
2381
2382 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2383         UnionPar *up;
2384         Int tp, oldsrc;
2385         VALUE oldval;
2386         val = rb_Float(val);
2387         oldval = s_ParameterRef_GetWeight(self);
2388         up = s_UnionParFromValue(self, &tp, 1);
2389         oldsrc = up->bond.src;
2390         if (tp == kElementParType)
2391                 up->atom.weight = NUM2DBL(val);
2392         else if (tp == kVdwParType)
2393                 up->vdw.weight = NUM2DBL(val);
2394         else rb_raise(rb_eMolbyError, "invalid member weight");
2395         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2396         return val;
2397 }
2398
2399 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2400         UnionPar *up;
2401         Int tp, oldsrc;
2402         VALUE oldval;
2403         up = s_UnionParFromValue(self, &tp, 1);
2404         oldval = s_ParameterRef_GetFullName(self);
2405         oldsrc = up->bond.src;
2406         if (tp == kElementParType) {
2407                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2408                 up->atom.fullname[15] = 0;
2409         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2410         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2411         return val;
2412 }
2413
2414 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2415         UnionPar *up;
2416         Int tp, com, oldsrc;
2417         VALUE oldval;
2418         up = s_UnionParFromValue(self, &tp, 1);
2419         oldval = s_ParameterRef_GetComment(self);
2420         oldsrc = up->bond.src;
2421         if (val == Qnil)
2422                 up->bond.com = 0;
2423         else {
2424                 com = ParameterCommentIndex(StringValuePtr(val));
2425                 up->bond.com = com;
2426         }
2427         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2428         return val;     
2429 }
2430
2431 /*  Only false (undefined) and nil (local) can be set  */
2432 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2433         UnionPar *up;
2434         Int tp, oldsrc;
2435         VALUE oldval;
2436         up = s_UnionParFromValue(self, &tp, 1);
2437         if (val != Qfalse && val != Qnil)
2438                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2439         oldval = s_ParameterRef_GetSource(self);
2440         oldsrc = up->bond.src;
2441         if (oldsrc != 0 && oldsrc != -1)
2442                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2443         up->bond.src = (val == Qfalse ? -1 : 0);
2444         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2445         return val;     
2446 }
2447
2448 static struct s_ParameterAttrDef {
2449         char *name;
2450         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2451         ID id;                  /*  Will be set within InitMolby()  */
2452         VALUE (*getter)(VALUE);
2453         VALUE (*setter)(VALUE, VALUE);
2454 } s_ParameterAttrDefTable[] = {
2455         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2456         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2457         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2458         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2459         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2460         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2461         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2462         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2463         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2464         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2465 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2466         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2467         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2468         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2469 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2470         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2471         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2472         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2473         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2474         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2475         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2476         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2477         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2478         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2479         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2480         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2481         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2482         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2483         {NULL} /* Sentinel */
2484 };
2485
2486 static VALUE
2487 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2488 {
2489         int i;
2490         ID kid;
2491         if (TYPE(key) != T_SYMBOL) {
2492                 kid = rb_intern(StringValuePtr(key));
2493                 key = ID2SYM(kid);
2494         } else kid = SYM2ID(key);
2495         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2496                 if (s_ParameterAttrDefTable[i].id == kid) {
2497                         if (value == Qundef)
2498                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2499                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2500                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2501                         else
2502                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2503                 }
2504         }
2505         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2506         return Qnil; /* not reached */
2507 }
2508
2509 static VALUE
2510 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2511 {
2512         return s_ParameterRef_SetAttr(self, key, Qundef);
2513 }
2514
2515 /*
2516  *  call-seq:
2517  *     keys(idx)          -> array of valid parameter attributes
2518  *  
2519  *  Returns an array of valid parameter attributes (as Symbols).
2520  */
2521 static VALUE
2522 s_ParameterRef_Keys(VALUE self)
2523 {
2524         ParameterRef *pref;
2525         Data_Get_Struct(self, ParameterRef, pref);
2526         switch (pref->parType) {
2527                 case kBondParType:
2528                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2529                 case kAngleParType:
2530                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2531                 case kDihedralParType:
2532                 case kImproperParType:
2533                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2534                 case kVdwParType:
2535                         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);
2536                 case kVdwPairParType:
2537                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2538                 case kVdwCutoffParType:
2539                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2540                 case kElementParType:
2541                         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);
2542                 default:
2543                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2544         }
2545         return Qnil;  /*  Not reached  */
2546 }
2547
2548 /*
2549  *  call-seq:
2550  *     to_hash(idx)          -> Hash
2551  *  
2552  *  Returns a hash containing valid parameter names and values
2553  */
2554 static VALUE
2555 s_ParameterRef_ToHash(VALUE self)
2556 {
2557         VALUE keys = s_ParameterRef_Keys(self);
2558         VALUE retval;
2559         int i;
2560         if (keys == Qnil)
2561                 return Qnil;
2562         retval = rb_hash_new();
2563         for (i = 0; i < RARRAY_LEN(keys); i++) {
2564                 VALUE key = RARRAY_PTR(keys)[i];
2565                 VALUE val = s_ParameterRef_GetAttr(self, key);
2566                 rb_hash_aset(retval, key, val);
2567         }
2568         return retval;
2569 }
2570
2571 /*
2572  *  call-seq:
2573  *     parameter.to_s(idx)          -> String
2574  *  
2575  *  Returns a string representation of the given parameter
2576  */
2577 static VALUE
2578 s_ParameterRef_ToString(VALUE self)
2579 {
2580         Int tp, i, n;
2581         char buf[1024], types[4][8];
2582         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2583         switch (tp) {
2584                 case kBondParType:
2585                         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);
2586                         break;
2587                 case kAngleParType:
2588                         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);
2589                         break;
2590                 case kDihedralParType:
2591                 case kImproperParType:
2592                         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]));
2593                         n = strlen(buf);
2594                         for (i = 0; i < up->torsion.mult; i++) {
2595                                 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);
2596                                 n = strlen(buf);
2597                         }
2598                         break;
2599                 case kVdwParType:
2600                         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);
2601                         break;
2602                 case kVdwPairParType:
2603                         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);
2604                         break;
2605                 case kVdwCutoffParType:
2606                         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);
2607                         break;
2608                 case kElementParType:
2609                         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);
2610                         break;
2611         }
2612         return Ruby_NewEncodedStringValue2(buf);
2613 }
2614
2615 /*
2616  *  call-seq:
2617  *     self == parameterRef -> boolean
2618  *  
2619  *  True if the parameters point to the same parameter record.
2620  */
2621 static VALUE
2622 s_ParameterRef_Equal(VALUE self, VALUE val)
2623 {
2624         Int tp1, tp2;
2625         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2626                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2627         } else return Qfalse;
2628 }
2629         
2630 #pragma mark ====== Parameter Class ======
2631
2632 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2633  *  is NULL, then the global parameters are looked for.  */
2634
2635 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2636 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2637 static void
2638 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2639 {
2640         Molecule *mol;
2641         Data_Get_Struct(val, Molecule, mol);
2642         if (mol == NULL)
2643                 rb_raise(rb_eMolbyError, "the molecule is empty");
2644         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2645                 /*  Do self.md_arena.prepare  */
2646                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2647                 if (val2 != Qnil)
2648                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2649         }
2650 }
2651
2652 static VALUE
2653 s_NewParameterValueFromValue(VALUE val)
2654 {
2655         Molecule *mol;
2656         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2657                 Data_Get_Struct(val, Molecule, mol);
2658                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2659                 MoleculeRetain(mol);
2660                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2661         } else {
2662                 mol = NULL;
2663                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2664         }
2665 }
2666
2667 static Molecule *
2668 s_MoleculeFromParameterValue(VALUE val)
2669 {
2670         Molecule *mol;
2671         Data_Get_Struct(val, Molecule, mol);
2672         return mol;
2673 }
2674
2675 static Parameter *
2676 s_ParameterFromParameterValue(VALUE val)
2677 {
2678         Molecule *mol;
2679         Data_Get_Struct(val, Molecule, mol);
2680         if (mol != NULL)
2681                 return mol->par;
2682         return gBuiltinParameters;
2683 }
2684
2685 /*  Forward declarations  */
2686 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2687 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2688
2689 static Molecule *
2690 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2691 {
2692         if (val == rb_cParameter) {
2693                 return NULL;  /*  Parameter class method: builtin parameters  */
2694         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2695                 return s_MoleculeFromParameterValue(val);
2696         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2697                 return s_MoleculeFromParEnumerableValue(val);
2698         } else return NULL;
2699 }
2700
2701 /*
2702  *  call-seq:
2703  *     builtin    -> Parameter
2704  *  
2705  *  Returns a parameter value that points to the global (builtin) parameters.
2706  *  Equivalent to Parameter::Builtin (constant).
2707  */
2708 static VALUE
2709 s_Parameter_Builtin(VALUE self)
2710 {
2711         static ID s_builtin_id = 0;
2712         if (s_builtin_id == 0)
2713                 s_builtin_id = rb_intern("Builtin");
2714         return rb_const_get(rb_cParameter, s_builtin_id);
2715 }
2716
2717 /*
2718  *  call-seq:
2719  *     bond(idx)          -> ParameterRef
2720  *  
2721  *  The index-th bond parameter record is returned.
2722  */
2723 static VALUE
2724 s_Parameter_Bond(VALUE self, VALUE ival)
2725 {
2726         Molecule *mol;
2727         int idx, n;
2728         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2729         idx = NUM2INT(rb_Integer(ival));
2730         if (mol == NULL)
2731                 n = gBuiltinParameters->nbondPars;
2732         else if (mol->par != NULL)
2733                 n = mol->par->nbondPars;
2734         else n = 0;
2735         if (idx < -n || idx >= n)
2736                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2737         if (idx < 0)
2738                 idx += n;
2739         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2740 }
2741
2742 /*
2743  *  call-seq:
2744  *     angle(idx)          -> ParameterRef
2745  *  
2746  *  The index-th angle parameter record is returned.
2747  */
2748 static VALUE
2749 s_Parameter_Angle(VALUE self, VALUE ival)
2750 {
2751         Molecule *mol;
2752         int idx, n;
2753         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2754         idx = NUM2INT(rb_Integer(ival));
2755         if (mol == NULL)
2756                 n = gBuiltinParameters->nanglePars;
2757         else if (mol->par != NULL)
2758                 n = mol->par->nanglePars;
2759         else n = 0;
2760         if (idx < -n || idx >= n)
2761                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2762         if (idx < 0)
2763                 idx += n;
2764         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2765 }
2766
2767 /*
2768  *  call-seq:
2769  *     dihedral(idx)          -> ParameterRef
2770  *  
2771  *  The index-th dihedral parameter record is returned.
2772  */
2773 static VALUE
2774 s_Parameter_Dihedral(VALUE self, VALUE ival)
2775 {
2776         Molecule *mol;
2777         int idx, n;
2778         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2779         idx = NUM2INT(rb_Integer(ival));
2780         if (mol == NULL)
2781                 n = gBuiltinParameters->ndihedralPars;
2782         else if (mol->par != NULL)
2783                 n = mol->par->ndihedralPars;
2784         else n = 0;
2785         if (idx < -n || idx >= n)
2786                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2787         if (idx < 0)
2788                 idx += n;
2789         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2790 }
2791
2792 /*
2793  *  call-seq:
2794  *     improper(idx)          -> ParameterRef
2795  *  
2796  *  The index-th improper parameter record is returned.
2797  */
2798 static VALUE
2799 s_Parameter_Improper(VALUE self, VALUE ival)
2800 {
2801         Molecule *mol;
2802         int idx, n;
2803         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2804         idx = NUM2INT(rb_Integer(ival));
2805         if (mol == NULL)
2806                 n = gBuiltinParameters->nimproperPars;
2807         else if (mol->par != NULL)
2808                 n = mol->par->nimproperPars;
2809         else n = 0;
2810         if (idx < -n || idx >= n)
2811                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2812         if (idx < 0)
2813                 idx += n;
2814         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2815 }
2816
2817 /*
2818  *  call-seq:
2819  *     vdw(idx)          -> ParameterRef
2820  *  
2821  *  The index-th vdw parameter record is returned.
2822  */
2823 static VALUE
2824 s_Parameter_Vdw(VALUE self, VALUE ival)
2825 {
2826         Molecule *mol;
2827         int idx, n;
2828         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2829         idx = NUM2INT(rb_Integer(ival));
2830         if (mol == NULL)
2831                 n = gBuiltinParameters->nvdwPars;
2832         else if (mol->par != NULL)
2833                 n = mol->par->nvdwPars;
2834         else n = 0;
2835         if (idx < -n || idx >= n)
2836                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2837         if (idx < 0)
2838                 idx += n;
2839         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2840 }
2841
2842 /*
2843  *  call-seq:
2844  *     vdw_pair(idx)          -> ParameterRef
2845  *  
2846  *  The index-th vdw pair parameter record is returned.
2847  */
2848 static VALUE
2849 s_Parameter_VdwPair(VALUE self, VALUE ival)
2850 {
2851         Molecule *mol;
2852         int idx, n;
2853         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2854         idx = NUM2INT(rb_Integer(ival));
2855         if (mol == NULL)
2856                 n = gBuiltinParameters->nvdwpPars;
2857         else if (mol->par != NULL)
2858                 n = mol->par->nvdwpPars;
2859         else n = 0;
2860         if (idx < -n || idx >= n)
2861                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2862         if (idx < 0)
2863                 idx += n;
2864         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2865 }
2866
2867 /*
2868  *  call-seq:
2869  *     vdw_cutoff(idx)          -> ParameterRef
2870  *  
2871  *  The index-th vdw cutoff parameter record is returned.
2872  */
2873 static VALUE
2874 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2875 {
2876         Molecule *mol;
2877         int idx, n;
2878         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2879         idx = NUM2INT(rb_Integer(ival));
2880         if (mol == NULL)
2881                 n = gBuiltinParameters->nvdwCutoffPars;
2882         else if (mol->par != NULL)
2883                 n = mol->par->nvdwCutoffPars;
2884         else n = 0;
2885         if (idx < -n || idx >= n)
2886                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2887         if (idx < 0)
2888                 idx += n;
2889         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2890 }
2891
2892 /*
2893  *  call-seq:
2894  *     element(idx)            -> ParameterRef
2895  *     element(t1)             -> ParameterRef
2896  *  
2897  *  In the first form, the index-th element parameter record is returned. In the second
2898  *  form, the element parameter for t1 is looked up (the last index first). t1
2899  *  is the element name string (up to 4 characters).
2900  *  Unlike other Parameter methods, this is used only for the global parameter.
2901  */
2902 static VALUE
2903 s_Parameter_Element(VALUE self, VALUE ival)
2904 {
2905         Int idx1;
2906         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2907                 int n = gCountElementParameters;
2908                 idx1 = NUM2INT(rb_Integer(ival));
2909                 if (idx1 < -n || idx1 >= n)
2910                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2911                 if (idx1 < 0)
2912                         idx1 += n;
2913                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2914         } else {
2915                 ElementPar *ep;
2916                 char name[6];
2917                 int i;
2918                 strncpy(name, StringValuePtr(ival), 4);
2919                 name[4] = 0;
2920                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2921                         if (strncmp(ep->name, name, 4) == 0)
2922                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2923                 }
2924                 return Qnil;
2925         }
2926 }
2927
2928 /*
2929  *  call-seq:
2930  *     nbonds          -> Integer
2931  *  
2932  *  Returns the number of bond parameters.
2933  */
2934 static VALUE
2935 s_Parameter_Nbonds(VALUE self)
2936 {
2937         Int n;
2938         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2939         if (mol == NULL)
2940                 n = gBuiltinParameters->nbondPars;
2941         else if (mol->par != NULL)
2942                 n = mol->par->nbondPars;
2943         else n = 0;
2944         return INT2NUM(n);
2945 }
2946
2947 /*
2948  *  call-seq:
2949  *     nangles          -> Integer
2950  *  
2951  *  Returns the number of angle parameters.
2952  */
2953 static VALUE
2954 s_Parameter_Nangles(VALUE self)
2955 {
2956         Int n;
2957         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2958         if (mol == NULL)
2959                 n = gBuiltinParameters->nanglePars;
2960         else if (mol->par != NULL)
2961                 n = mol->par->nanglePars;
2962         else n = 0;
2963         return INT2NUM(n);
2964 }
2965
2966 /*
2967  *  call-seq:
2968  *     ndihedrals          -> Integer
2969  *  
2970  *  Returns the number of dihedral parameters.
2971  */
2972 static VALUE
2973 s_Parameter_Ndihedrals(VALUE self)
2974 {
2975         Int n;
2976         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2977         if (mol == NULL)
2978                 n = gBuiltinParameters->ndihedralPars;
2979         else if (mol->par != NULL)
2980                 n = mol->par->ndihedralPars;
2981         else n = 0;
2982         return INT2NUM(n);
2983 }
2984
2985 /*
2986  *  call-seq:
2987  *     nimpropers          -> Integer
2988  *  
2989  *  Returns the number of improper parameters.
2990  */
2991 static VALUE
2992 s_Parameter_Nimpropers(VALUE self)
2993 {
2994         Int n;
2995         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2996         if (mol == NULL)
2997                 n = gBuiltinParameters->nimproperPars;
2998         else if (mol->par != NULL)
2999                 n = mol->par->nimproperPars;
3000         else n = 0;
3001         return INT2NUM(n);
3002 }
3003
3004 /*
3005  *  call-seq:
3006  *     nvdws          -> Integer
3007  *  
3008  *  Returns the number of vdw parameters.
3009  */
3010 static VALUE
3011 s_Parameter_Nvdws(VALUE self)
3012 {
3013         Int n;
3014         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3015         if (mol == NULL)
3016                 n = gBuiltinParameters->nvdwPars;
3017         else if (mol->par != NULL)
3018                 n = mol->par->nvdwPars;
3019         else n = 0;
3020         return INT2NUM(n);
3021 }
3022
3023 /*
3024  *  call-seq:
3025  *     nvdw_pairs          -> Integer
3026  *  
3027  *  Returns the number of vdw pair parameters.
3028  */
3029 static VALUE
3030 s_Parameter_NvdwPairs(VALUE self)
3031 {
3032         Int n;
3033         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3034         if (mol == NULL)
3035                 n = gBuiltinParameters->nvdwpPars;
3036         else if (mol->par != NULL)
3037                 n = mol->par->nvdwpPars;
3038         else n = 0;
3039         return INT2NUM(n);
3040 }
3041
3042 /*
3043  *  call-seq:
3044  *     nvdw_cutoffs          -> Integer
3045  *  
3046  *  Returns the number of vdw cutoff parameters.
3047  */
3048 static VALUE
3049 s_Parameter_NvdwCutoffs(VALUE self)
3050 {
3051         Int n;
3052         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3053         if (mol == NULL)
3054                 n = gBuiltinParameters->nvdwCutoffPars;
3055         else if (mol->par != NULL)
3056                 n = mol->par->nvdwCutoffPars;
3057         else n = 0;
3058         return INT2NUM(n);
3059 }
3060
3061 /*
3062  *  call-seq:
3063  *     nelements          -> Integer
3064  *  
3065  *  Returns the number of element parameters.
3066  */
3067 static VALUE
3068 s_Parameter_Nelements(VALUE self)
3069 {
3070         return INT2NUM(gCountElementParameters);
3071 }
3072
3073 /*
3074  *  call-seq:
3075  *     bonds          -> ParEnumerable
3076  *  
3077  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
3078  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
3079  *  useful when all accessible parameters should be examined by use of 'each' method.
3080  */
3081 static VALUE
3082 s_Parameter_Bonds(VALUE self)
3083 {
3084         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3085         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3086 }
3087
3088 /*
3089  *  call-seq:
3090  *     angles          -> ParEnumerable
3091  *  
3092  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3093  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3094  *  useful when all accessible parameters should be examined by use of 'each' method.
3095  */
3096 static VALUE
3097 s_Parameter_Angles(VALUE self)
3098 {
3099         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3100         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3101 }
3102
3103 /*
3104  *  call-seq:
3105  *     dihedrals          -> ParEnumerable
3106  *  
3107  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3108  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3109  *  useful when all accessible parameters should be examined by use of 'each' method.
3110  */
3111 static VALUE
3112 s_Parameter_Dihedrals(VALUE self)
3113 {
3114         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3115         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3116 }
3117
3118 /*
3119  *  call-seq:
3120  *     impropers          -> ParEnumerable
3121  *  
3122  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3123  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3124  *  useful when all accessible parameters should be examined by use of 'each' method.
3125  */
3126 static VALUE
3127 s_Parameter_Impropers(VALUE self)
3128 {
3129         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3130         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3131 }
3132
3133 /*
3134  *  call-seq:
3135  *     vdws          -> ParEnumerable
3136  *  
3137  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3138  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3139  *  useful when all accessible parameters should be examined by use of 'each' method.
3140  */
3141 static VALUE
3142 s_Parameter_Vdws(VALUE self)
3143 {
3144         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3145         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3146 }
3147
3148 /*
3149  *  call-seq:
3150  *     vdw_pairs          -> ParEnumerable
3151  *  
3152  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3153  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3154  *  useful when all accessible parameters should be examined by use of 'each' method.
3155  */
3156 static VALUE
3157 s_Parameter_VdwPairs(VALUE self)
3158 {
3159         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3160         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3161 }
3162
3163 /*
3164  *  call-seq:
3165  *     vdw_cutoffs          -> ParEnumerable
3166  *  
3167  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3168  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3169  *  useful when all accessible parameters should be examined by use of 'each' method.
3170  */
3171 static VALUE
3172 s_Parameter_VdwCutoffs(VALUE self)
3173 {
3174         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3175         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3176 }
3177
3178 /*
3179  *  call-seq:
3180  *     elements          -> ParEnumerable
3181  *  
3182  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3183  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3184  *  useful when all accessible parameters should be examined by use of 'each' method.
3185  */
3186 static VALUE
3187 s_Parameter_Elements(VALUE self)
3188 {
3189         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3190         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3191 }
3192
3193 static VALUE
3194 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3195 {
3196         VALUE atval, optval;
3197         UInt t[4];
3198         Int ii[4];
3199         int i, n, idx, flags, is_global;
3200
3201         rb_scan_args(argc, argv, "1*", &atval, &optval);
3202         
3203         /*  Get the atom types  */
3204         switch (parType) {
3205                 case kBondParType: n = 2; break;
3206                 case kAngleParType: n = 3; break;
3207                 case kDihedralParType: n = 4; break;
3208                 case kImproperParType: n = 4; break;
3209                 case kVdwParType: n = 1; break;
3210                 case kVdwPairParType: n = 2; break;
3211                 default: return Qnil;
3212         }
3213         s_ScanAtomTypes(atval, n, t);
3214         for (i = 0; i < n; i++) {
3215                 if (t[i] < kAtomTypeMinimum) {
3216                         /*  Explicit atom index  */
3217                         if (mol == NULL)
3218                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3219                         if (t[i] >= mol->natoms)
3220                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3221                         ii[i] = t[i];
3222                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3223                 } else ii[i] = -1;
3224         }
3225         
3226         /*  Analyze options  */
3227         flags = 0;
3228         n = RARRAY_LEN(optval);
3229         for (i = 0; i < n; i++) {
3230                 VALUE oval = RARRAY_PTR(optval)[i];
3231                 if (oval == ID2SYM(rb_intern("global")))
3232                         flags |= kParameterLookupGlobal;
3233                 else if (oval == ID2SYM(rb_intern("local")))
3234                         flags |= kParameterLookupLocal;
3235                 else if (oval == ID2SYM(rb_intern("missing")))
3236                         flags |= kParameterLookupMissing;
3237                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3238                         flags |= kParameterLookupNoWildcard;
3239                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3240                         flags |= kParameterLookupNoBaseAtomType;
3241                 else if (oval == ID2SYM(rb_intern("create")))
3242                         flags |= 256;
3243         }
3244         if (flags == 0)
3245                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3246         
3247         idx = -1;
3248         is_global = 0;
3249         switch (parType) {
3250                 case kBondParType: {
3251                         BondPar *bp;
3252                         if (mol != NULL) {
3253                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3254                                 if (bp != NULL) {
3255                                         idx = bp - mol->par->bondPars;
3256                                         break;
3257                                 }
3258                         }
3259                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3260                         if (bp != NULL) {
3261                                 idx = bp - gBuiltinParameters->bondPars;
3262                                 is_global = 1;
3263                         }
3264                         break;
3265                 }
3266                 case kAngleParType: {
3267                         AnglePar *ap;
3268                         if (mol != NULL) {
3269                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3270                                 if (ap != NULL) {
3271                                         idx = ap - mol->par->anglePars;
3272                                         break;
3273                                 }
3274                         }
3275                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3276                         if (ap != NULL) {
3277                                 idx = ap - gBuiltinParameters->anglePars;
3278                                 is_global = 1;
3279                         }
3280                         break;
3281                 }
3282                 case kDihedralParType: {
3283                         TorsionPar *tp;
3284                         if (mol != NULL) {
3285                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3286                                 if (tp != NULL) {
3287                                         idx = tp - mol->par->dihedralPars;
3288                                         break;
3289                                 }
3290                         }
3291                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3292                         if (tp != NULL) {
3293                                 idx = tp - gBuiltinParameters->dihedralPars;
3294                                 is_global = 1;
3295                         }
3296                         break;
3297                 }
3298                 case kImproperParType: {
3299                         TorsionPar *tp;
3300                         if (mol != NULL) {
3301                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3302                                 if (tp != NULL) {
3303                                         idx = tp - mol->par->improperPars;
3304                                         break;
3305                                 }
3306                         }
3307                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3308                         if (tp != NULL) {
3309                                 idx = tp - gBuiltinParameters->improperPars;
3310                                 is_global = 1;
3311                         }
3312                         break;
3313                 }       
3314                 case kVdwParType: {
3315                         VdwPar *vp;
3316                         if (mol != NULL) {
3317                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3318                                 if (vp != NULL) {
3319                                         idx = vp - mol->par->vdwPars;
3320                                         break;
3321                                 }
3322                         }
3323                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3324                         if (vp != NULL) {
3325                                 idx = vp - gBuiltinParameters->vdwPars;
3326                                 is_global = 1;
3327                         }
3328                         break;
3329                 }       
3330                 case kVdwPairParType: {
3331                         VdwPairPar *vp;
3332                         if (mol != NULL) {
3333                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3334                                 if (vp != NULL) {
3335                                         idx = vp - mol->par->vdwpPars;
3336                                         break;
3337                                 }
3338                         }
3339                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3340                         if (vp != NULL) {
3341                                 idx = vp - gBuiltinParameters->vdwpPars;
3342                                 is_global = 1;
3343                         }
3344                         break;
3345                 }
3346                 default:
3347                         return Qnil;
3348         }
3349         if (idx < 0) {
3350                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3351                         return Qnil;            
3352                 else {
3353                         /*  Insert a new parameter record  */
3354                         UnionPar *up;
3355                         Int count = ParameterGetCountForType(mol->par, parType);
3356                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3357                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3358                         IntGroupRelease(ig);
3359                         is_global = 0;
3360                         idx = count;
3361                         /*  Set atom types  */
3362                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3363                         if (up == NULL)
3364                                 return Qnil;
3365                         switch (parType) {
3366                                 case kBondParType:
3367                                         up->bond.type1 = t[0];
3368                                         up->bond.type2 = t[1];
3369                                         break;
3370                                 case kAngleParType:
3371                                         up->angle.type1 = t[0];
3372                                         up->angle.type2 = t[1];
3373                                         up->angle.type3 = t[2];
3374                                         break;
3375                                 case kDihedralParType:
3376                                 case kImproperParType:
3377                                         up->torsion.type1 = t[0];
3378                                         up->torsion.type2 = t[1];
3379                                         up->torsion.type3 = t[2];
3380                                         up->torsion.type4 = t[3];
3381                                         break;
3382                                 case kVdwParType:
3383                                         up->vdw.type1 = t[0];
3384                                         break;
3385                                 case kVdwPairParType:
3386                                         up->vdwp.type1 = t[0];
3387                                         up->vdwp.type2 = t[1];
3388                                         break;
3389                                 default:
3390                                         return Qnil;
3391                         }
3392                 }
3393         }
3394         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3395 }
3396
3397 /*
3398  *  call-seq:
3399  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3400  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3401  *
3402  *  Find the parameter record that matches the given atom types. The atom types are given
3403  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3404  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3405  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3406  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3407  */
3408 static VALUE
3409 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3410 {
3411         int parType;
3412         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3413         if (argc == 0)
3414                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3415         parType = s_ParTypeFromValue(argv[0]);
3416         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3417 }
3418
3419 /*
3420  *  call-seq:
3421  *     self == parameter -> boolean
3422  *  
3423  *  True if the parameters point to the same parameter table.
3424  */
3425 static VALUE
3426 s_Parameter_Equal(VALUE self, VALUE val)
3427 {
3428         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3429                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3430         } else return Qfalse;
3431 }
3432
3433 #pragma mark ====== ParEnumerable Class ======
3434
3435 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3436  and the parameter type. If the Molecule is NULL, then it refers to the
3437  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3438  the global parameters are always accessible. */
3439
3440 typedef struct ParEnumerable {
3441         Molecule *mol;
3442         Int parType;   /*  Same as parType in ParameterRef  */
3443 } ParEnumerable;
3444
3445 static ParEnumerable *
3446 s_ParEnumerableNew(Molecule *mol, Int parType)
3447 {
3448         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3449         if (pen != NULL) {
3450                 pen->mol = mol;
3451                 if (mol != NULL)
3452                         MoleculeRetain(mol);
3453                 pen->parType = parType;
3454         }
3455         return pen;
3456 }
3457
3458 static void
3459 s_ParEnumerableRelease(ParEnumerable *pen)
3460 {
3461         if (pen != NULL) {
3462                 if (pen->mol != NULL)
3463                         MoleculeRelease(pen->mol);
3464                 free(pen);
3465         }
3466 }
3467
3468 static Molecule *
3469 s_MoleculeFromParEnumerableValue(VALUE val)
3470 {
3471         ParEnumerable *pen;
3472     Data_Get_Struct(val, ParEnumerable, pen);
3473         return pen->mol;
3474 }
3475
3476 static VALUE
3477 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3478 {
3479         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3480         if (pen == NULL)
3481                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3482         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3483 }
3484
3485 /*
3486  *  call-seq:
3487  *     par_type -> String
3488  *
3489  *  Get the parameter type, like "bond", "angle", etc.
3490  */
3491 static VALUE
3492 s_ParEnumerable_ParType(VALUE self) {
3493         ParEnumerable *pen;
3494         Int tp;
3495     Data_Get_Struct(self, ParEnumerable, pen);
3496         tp = pen->parType;
3497         if (tp == kElementParType)
3498                 return Ruby_NewEncodedStringValue2("element");
3499         tp -= kFirstParType;
3500         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3501                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3502         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3503 }
3504
3505 /*
3506  *  call-seq:
3507  *     self[idx]          -> ParameterRef
3508  *  
3509  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3510  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3511  *  parent Parameter object of self.
3512  *
3513  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3514  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3515  */
3516 static VALUE
3517 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3518 {
3519         ParEnumerable *pen;
3520     Data_Get_Struct(self, ParEnumerable, pen);
3521         switch (pen->parType) {
3522                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3523                 case kBondParType:      return s_Parameter_Bond(self, ival);
3524                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3525                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3526                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3527                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3528                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3529                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3530                 case kElementParType:   return s_Parameter_Element(self, ival);
3531                 default:
3532                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3533         }
3534         return Qnil;  /*  Not reached  */
3535 }
3536
3537 /*
3538  *  call-seq:
3539  *     length          -> Integer
3540  *  
3541  *  Returns the number of parameters included in this enumerable.
3542  */
3543 static VALUE
3544 s_ParEnumerable_Length(VALUE self)
3545 {
3546         ParEnumerable *pen;
3547     Data_Get_Struct(self, ParEnumerable, pen);
3548         switch (pen->parType) {
3549                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3550                 case kBondParType:      return s_Parameter_Nbonds(self);
3551                 case kAngleParType:     return s_Parameter_Nangles(self); 
3552                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3553                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3554                 case kVdwParType:       return s_Parameter_Nvdws(self);
3555                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3556                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3557                 case kElementParType:   return s_Parameter_Nelements(self);
3558                 default:
3559                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3560         }
3561         return Qnil;  /*  Not reached  */
3562 }
3563
3564 /*
3565  *  call-seq:
3566  *     each {|pref| ...}
3567  *  
3568  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3569  */
3570 VALUE
3571 s_ParEnumerable_Each(VALUE self)
3572 {
3573         VALUE aval;
3574         ParEnumerable *pen;
3575         ParameterRef *pref;
3576         int i, ofs, n;
3577     Data_Get_Struct(self, ParEnumerable, pen);
3578         if (pen->parType == kElementParType)
3579                 n = gCountElementParameters;
3580         else {
3581                 switch (pen->parType) {
3582                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3583                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3584                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3585                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3586                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3587                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3588                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3589                         default:
3590                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3591                 }
3592                 if (pen->mol == NULL)
3593                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3594                 else if (pen->mol->par != NULL)
3595                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3596                 else return self;
3597         }               
3598         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3599         Data_Get_Struct(aval, ParameterRef, pref);
3600         for (i = 0; i < n; i++) {
3601                 pref->idx = i;
3602                 rb_yield(aval);
3603         }
3604     return self;
3605 }
3606
3607 /*
3608  *  call-seq:
3609  *     reverse_each {|pref| ...}
3610  *  
3611  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3612  */
3613 VALUE
3614 s_ParEnumerable_ReverseEach(VALUE self)
3615 {
3616         VALUE aval;
3617         ParEnumerable *pen;
3618         ParameterRef *pref;
3619         int i, ofs, n;
3620     Data_Get_Struct(self, ParEnumerable, pen);
3621         if (pen->parType == kElementParType)
3622                 n = gCountElementParameters;
3623         else {
3624                 switch (pen->parType) {
3625                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3626                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3627                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3628                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3629                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3630                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3631                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3632                         default:
3633                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3634                 }
3635                 if (pen->mol == NULL)
3636                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3637                 else if (pen->mol->par != NULL)
3638                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3639                 else return self;
3640         }               
3641         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3642         Data_Get_Struct(aval, ParameterRef, pref);
3643         for (i = n - 1; i >= 0; i--) {
3644                 pref->idx = i;
3645                 rb_yield(aval);
3646         }
3647     return self;
3648 }
3649
3650 /*
3651  *  call-seq:
3652  *     insert(idx = nil, pref = nil)       -> ParameterRef
3653  *  
3654  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3655  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3656  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3657  *  parameter is left undefined.
3658  *  Throws an exception if ParEnumerable points to the global parameter.
3659  */
3660 static VALUE
3661 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3662 {
3663         VALUE ival, pval;
3664         ParEnumerable *pen;
3665         int i, n;
3666         IntGroup *ig;
3667         UnionPar u;
3668         MolAction *act;
3669     Data_Get_Struct(self, ParEnumerable, pen);
3670         if (pen->mol == NULL)
3671                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3672         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3673         rb_scan_args(argc, argv, "02", &ival, &pval);
3674         if (ival != Qnil) {
3675                 i = NUM2INT(rb_Integer(ival));
3676                 if (i < 0 || i > n)
3677                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3678                 n = i;
3679         }
3680         if (pval != Qnil) {
3681                 Int type;
3682                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3683                 if (up == NULL || type != pen->parType)
3684                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3685                 ParameterCopyOneWithType(&u, up, pen->parType);
3686                 u.bond.src = 0;
3687         } else {
3688                 memset(&u, 0, sizeof(u));
3689                 u.bond.src = 0;
3690         }
3691         ig = IntGroupNewWithPoints(n, 1, -1);
3692         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3693
3694         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3695         MolActionCallback_registerUndo(pen->mol, act);
3696         MolActionRelease(act);
3697         
3698         IntGroupRelease(ig);
3699         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3700 }
3701
3702 /*
3703  *  call-seq:
3704  *     delete(Integer)
3705  *     delete(IntGroup)
3706  *  
3707  *  Delete the parameter(s) specified by the argument.
3708  */
3709 static VALUE
3710 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3711 {
3712         ParEnumerable *pen;
3713         int i, n;
3714         IntGroup *ig;
3715     Data_Get_Struct(self, ParEnumerable, pen);
3716         if (pen->mol == NULL)
3717                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3718         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3719         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3720                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3721                 i = 1;
3722         } else {
3723                 ig = IntGroupFromValue(ival);
3724                 if ((i = IntGroupGetCount(ig)) == 0) {
3725                         IntGroupRelease(ig);
3726                         return Qnil;
3727                 }
3728         }
3729         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3730                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3731         n = i;
3732
3733         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3734         IntGroupRelease(ig);
3735         return ival;
3736 }
3737
3738 /*
3739  *  call-seq:
3740  *     lookup(atom_types, options, ...) -> ParameterRef
3741  *     lookup(atom_type_string, options, ...) -> ParameterRef
3742  *
3743  *  Find the parameter record that matches the given atom types. The arguments are
3744  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3745  *  specified.
3746  */
3747 static VALUE
3748 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3749 {
3750         ParEnumerable *pen;
3751     Data_Get_Struct(self, ParEnumerable, pen);
3752         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3753 }
3754
3755 /*
3756  *  call-seq:
3757  *     self == parEnumerable -> boolean
3758  *  
3759  *  True if the arguments point to the same parameter table and type.
3760  */
3761 static VALUE
3762 s_ParEnumerable_Equal(VALUE self, VALUE val)
3763 {
3764         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3765                 ParEnumerable *pen1, *pen2;
3766                 Data_Get_Struct(self, ParEnumerable, pen1);
3767                 Data_Get_Struct(val, ParEnumerable, pen2);
3768                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3769         } else return Qfalse;
3770 }
3771
3772 #pragma mark ====== AtomRef Class ======
3773
3774 /*  Forward declaration for register undo  */
3775 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3776
3777 /*  Ruby string "set_atom_attr"  */
3778 static VALUE s_SetAtomAttrString;
3779
3780 static int
3781 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3782 {
3783         AtomRef *aref;
3784         int idx;
3785         Data_Get_Struct(self, AtomRef, aref);
3786         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3787         if (idx < 0 || idx >= aref->mol->natoms)
3788                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3789         if (app != NULL)
3790                 *app = aref->mol->atoms + idx;
3791         if (mpp != NULL)
3792                 *mpp = aref->mol;
3793         return idx;
3794 }
3795
3796 static Atom *
3797 s_AtomFromValue(VALUE self)
3798 {
3799         Atom *ap;
3800         s_AtomIndexFromValue(self, &ap, NULL);
3801         return ap;
3802 }
3803
3804 static Atom *
3805 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3806 {
3807         Atom *ap;
3808         s_AtomIndexFromValue(self, &ap, mpp);
3809         return ap;
3810 }
3811
3812 static void
3813 s_NotifyModificationForAtomRef(VALUE self)
3814 {
3815         AtomRef *aref;
3816         Data_Get_Struct(self, AtomRef, aref);
3817         MoleculeIncrementModifyCount(aref->mol);
3818 }
3819
3820 static void
3821 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3822 {
3823         AtomRef *aref;
3824         Data_Get_Struct(self, AtomRef, aref);
3825         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3826                 /*  Register undo  */
3827                 MolAction *act;
3828                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3829                 MolActionCallback_registerUndo(aref->mol, act);
3830                 MoleculeCallback_notifyModification(aref->mol, 0);
3831                 /*  Request MD rebuilt if necessary  */
3832                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3833                         aref->mol->needsMDRebuild = 1;
3834         }
3835 }
3836
3837 VALUE
3838 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3839 {
3840         AtomRef *aref;
3841         aref = AtomRefNew(mol, idx);
3842         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3843 }
3844
3845 static VALUE
3846 s_AtomRef_GetMolecule(VALUE self)
3847 {
3848         Molecule *mpp;
3849         s_AtomIndexFromValue(self, NULL, &mpp);
3850         return ValueFromMolecule(mpp);
3851 }
3852
3853 static VALUE s_AtomRef_GetIndex(VALUE self) {
3854         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3855 }
3856
3857 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3858         return INT2NUM(s_AtomFromValue(self)->segSeq);
3859 }
3860
3861 static VALUE s_AtomRef_GetSegName(VALUE self) {
3862         char *p = s_AtomFromValue(self)->segName;
3863         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3864 }
3865
3866 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3867         return INT2NUM(s_AtomFromValue(self)->resSeq);
3868 }
3869
3870 static VALUE s_AtomRef_GetResName(VALUE self) {
3871         char *p = s_AtomFromValue(self)->resName;
3872         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3873 }
3874
3875 static VALUE s_AtomRef_GetName(VALUE self) {
3876         char *p = s_AtomFromValue(self)->aname;
3877         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3878 }
3879
3880 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3881         int type = s_AtomFromValue(self)->type;
3882         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3883         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3884 }
3885
3886 static VALUE s_AtomRef_GetCharge(VALUE self) {
3887         return rb_float_new(s_AtomFromValue(self)->charge);
3888 }
3889
3890 static VALUE s_AtomRef_GetWeight(VALUE self) {
3891         return rb_float_new(s_AtomFromValue(self)->weight);
3892 }
3893
3894 static VALUE s_AtomRef_GetElement(VALUE self) {
3895         char *p = s_AtomFromValue(self)->element;
3896         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3897 }
3898
3899 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3900         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3901 }
3902
3903 static VALUE s_AtomRef_GetConnects(VALUE self) {
3904         VALUE retval;
3905         Int i, *cp;
3906         Atom *ap = s_AtomFromValue(self);
3907         retval = rb_ary_new();
3908         cp = AtomConnectData(&ap->connect);
3909         for (i = 0; i < ap->connect.count; i++)
3910                 rb_ary_push(retval, INT2NUM(cp[i]));
3911         return retval;
3912 }
3913
3914 static VALUE s_AtomRef_GetR(VALUE self) {
3915         return ValueFromVector(&(s_AtomFromValue(self)->r));
3916 }
3917
3918 static VALUE s_AtomRef_GetX(VALUE self) {
3919         return rb_float_new(s_AtomFromValue(self)->r.x);
3920 }
3921
3922 static VALUE s_AtomRef_GetY(VALUE self) {
3923         return rb_float_new(s_AtomFromValue(self)->r.y);
3924 }
3925
3926 static VALUE s_AtomRef_GetZ(VALUE self) {
3927         return rb_float_new(s_AtomFromValue(self)->r.z);
3928 }
3929
3930 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3931         Atom *ap;
3932         Molecule *mp;
3933         Vector r1;
3934         s_AtomIndexFromValue(self, &ap, &mp);
3935         r1 = ap->r;
3936         if (mp->cell != NULL)
3937                 TransformVec(&r1, mp->cell->rtr, &r1);
3938         return r1;
3939 }
3940
3941 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3942         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3943         return ValueFromVector(&r1);
3944 }
3945
3946 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3947         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3948 }
3949
3950 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3951         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3952 }
3953
3954 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3955         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3956 }
3957
3958 static VALUE s_AtomRef_GetSigma(VALUE self) {
3959         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3960 }
3961
3962 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3963         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3964 }
3965
3966 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3967         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3968 }
3969
3970 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3971         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3972 }
3973
3974 static VALUE s_AtomRef_GetV(VALUE self) {
3975         return ValueFromVector(&(s_AtomFromValue(self)->v));
3976 }
3977
3978 static VALUE s_AtomRef_GetF(VALUE self) {
3979         Vector v = s_AtomFromValue(self)->f;
3980         VecScaleSelf(v, INTERNAL2KCAL);
3981         return ValueFromVector(&v);
3982 }
3983
3984 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3985         return rb_float_new(s_AtomFromValue(self)->occupancy);
3986 }
3987
3988 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3989         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3990 }
3991
3992 static VALUE s_AtomRef_GetAniso(VALUE self) {
3993         VALUE retval;
3994         int i;
3995         Atom *ap = s_AtomFromValue(self);
3996         if (ap->aniso == NULL)
3997                 return Qnil;
3998         retval = rb_ary_new();
3999         for (i = 0; i < 6; i++)
4000                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
4001         if (ap->aniso->has_bsig) {
4002                 rb_ary_push(retval, INT2NUM(0));
4003                 for (i = 0; i < 6; i++)
4004                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
4005         }
4006         return retval;
4007 }
4008
4009 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
4010     VALUE retval;
4011     int i;
4012     Atom *ap = s_AtomFromValue(self);
4013     if (ap->aniso == NULL)
4014         return Qnil;
4015     retval = rb_ary_new();
4016     for (i = 0; i < 3; i++)
4017         rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
4018     return retval;
4019 }
4020
4021 static VALUE s_AtomRef_GetSymop(VALUE self) {
4022         VALUE retval;
4023         Atom *ap = s_AtomFromValue(self);
4024         if (!ap->symop.alive)
4025                 return Qnil;
4026         retval = rb_ary_new();
4027         rb_ary_push(retval, INT2NUM(ap->symop.sym));
4028         rb_ary_push(retval, INT2NUM(ap->symop.dx));
4029         rb_ary_push(retval, INT2NUM(ap->symop.dy));
4030         rb_ary_push(retval, INT2NUM(ap->symop.dz));
4031         rb_ary_push(retval, INT2NUM(ap->symbase));
4032         return retval;
4033 }
4034
4035 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
4036         return INT2NUM(s_AtomFromValue(self)->intCharge);
4037 }
4038
4039 static VALUE s_AtomRef_GetFixForce(VALUE self) {
4040         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
4041 }
4042
4043 static VALUE s_AtomRef_GetFixPos(VALUE self) {
4044         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
4045 }
4046
4047 static VALUE s_AtomRef_GetExclusion(VALUE self) {
4048         Molecule *mol;
4049         Atom *ap;
4050         int idx, i;
4051         MDExclusion *exinfo;
4052         Int *exlist;
4053         VALUE retval, aval;
4054         idx = s_AtomIndexFromValue(self, &ap, &mol);
4055         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
4056                 VALUE mval = ValueFromMolecule(mol);
4057                 s_RebuildMDParameterIfNecessary(mval, Qnil);
4058         }
4059         if (mol->arena->exinfo == NULL)
4060                 return Qnil;
4061         exinfo = mol->arena->exinfo + idx;
4062         exlist = mol->arena->exlist;
4063         retval = rb_ary_new();
4064         aval = rb_ary_new();
4065         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
4066                 rb_ary_push(aval, INT2NUM(exlist[i]));
4067         rb_ary_push(retval, aval);
4068         aval = rb_ary_new();
4069         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
4070                 rb_ary_push(aval, INT2NUM(exlist[i]));
4071         rb_ary_push(retval, aval);
4072         aval = rb_ary_new();
4073         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
4074                 rb_ary_push(aval, INT2NUM(exlist[i]));
4075         rb_ary_push(retval, aval);
4076         return retval;
4077 }
4078
4079 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
4080         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4081 }
4082
4083 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4084         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4085 }
4086
4087 static VALUE s_AtomRef_GetHidden(VALUE self) {
4088         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4089 }
4090
4091 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4092         VALUE retval;
4093         Int i, count, *cp;
4094         Atom *ap = s_AtomFromValue(self);
4095         if (ap->anchor == NULL)
4096                 return Qnil;
4097         count = ap->anchor->connect.count;
4098         retval = rb_ary_new2(count * 2);
4099         cp = AtomConnectData(&ap->anchor->connect);
4100         for (i = 0; i < count; i++) {
4101                 rb_ary_store(retval, i, INT2NUM(cp[i]));
4102                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4103         }
4104         return retval;
4105 }
4106
4107 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4108         char *p = s_AtomFromValue(self)->uff_type;
4109         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4110 }
4111
4112 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4113         rb_raise(rb_eMolbyError, "index cannot be directly set");
4114         return Qnil;
4115 }
4116
4117 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4118         VALUE oval = s_AtomRef_GetSegSeq(self);
4119         val = rb_Integer(val);
4120         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4121         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4122         return val;
4123 }
4124
4125 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4126         char *p = StringValuePtr(val);
4127         VALUE oval = s_AtomRef_GetSegName(self);
4128         strncpy(s_AtomFromValue(self)->segName, p, 4);
4129         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4130         return val;
4131 }
4132
4133 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4134         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4135         return val; /* Not reached */
4136 }
4137
4138 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4139         Atom *ap = s_AtomFromValue(self);
4140         char *p = StringValuePtr(val);
4141         VALUE oval = s_AtomRef_GetName(self);
4142         if (ap->anchor != NULL && p[0] == '_')
4143                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4144         strncpy(ap->aname, p, 4);
4145         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4146         return val;
4147 }
4148
4149 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4150         Molecule *mp;
4151         char *p = StringValuePtr(val);
4152         VALUE oval = s_AtomRef_GetAtomType(self);
4153         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4154         if (type != 0 && type < kAtomTypeMinimum)
4155                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4156         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4157         mp->needsMDRebuild = 1;
4158         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4159         return val;
4160 }
4161
4162 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4163         Molecule *mp;
4164         VALUE oval = s_AtomRef_GetCharge(self);
4165         val = rb_Float(val);
4166         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4167         mp->needsMDRebuild = 1;
4168         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4169         return val;
4170 }
4171
4172 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4173         Molecule *mp;
4174         VALUE oval = s_AtomRef_GetWeight(self);
4175         val = rb_Float(val);
4176         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4177         mp->needsMDRebuild = 1;
4178         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4179         return val;
4180 }
4181
4182 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4183         Double w;
4184         Molecule *mp;
4185         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4186         char *p = StringValuePtr(val);
4187         VALUE oval = s_AtomRef_GetElement(self);
4188         ap->atomicNumber = ElementToInt(p);
4189         ElementToString(ap->atomicNumber, ap->element);
4190         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4191                 ap->weight = w;
4192         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4193         mp->needsMDRebuild = 1;
4194         return val;
4195 }
4196
4197 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4198         Double w;
4199         Molecule *mp;
4200         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4201         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4202         val = rb_Integer(val);
4203         ap->atomicNumber = NUM2INT(val);
4204         ElementToString(ap->atomicNumber, ap->element);
4205         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4206                 ap->weight = w;
4207         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4208         mp->needsMDRebuild = 1;
4209         return val;
4210 }
4211
4212 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4213         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4214         return val; /* Not reached */
4215 }
4216
4217 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4218         Vector v;
4219         Molecule *mp;
4220         VALUE oval = s_AtomRef_GetR(self);
4221         VectorFromValue(val, &v);
4222         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4223         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4224         mp->needsMDCopyCoordinates = 1;
4225         return val;
4226 }
4227
4228 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4229         Double f;
4230         Molecule *mp;
4231         VALUE oval = s_AtomRef_GetX(self);
4232         val = rb_Float(val);
4233         f = NUM2DBL(val);
4234         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4235         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4236         mp->needsMDCopyCoordinates = 1;
4237         return val;
4238 }
4239
4240 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4241         Double f;
4242         Molecule *mp;
4243         VALUE oval = s_AtomRef_GetY(self);
4244         val = rb_Float(val);
4245         f = NUM2DBL(val);
4246         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4247         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4248         mp->needsMDCopyCoordinates = 1;
4249         return val;
4250 }
4251
4252 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4253         Double f;
4254         Molecule *mp;
4255         VALUE oval = s_AtomRef_GetZ(self);
4256         val = rb_Float(val);
4257         f = NUM2DBL(val);
4258         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4259         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4260         mp->needsMDCopyCoordinates = 1;
4261         return val;
4262 }
4263
4264 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4265         Vector v, ov;
4266         Atom *ap;
4267         Molecule *mp;
4268         s_AtomIndexFromValue(self, &ap, &mp);
4269         ov = ap->r;
4270         VectorFromValue(val, &v);
4271         if (mp->cell != NULL)
4272                 TransformVec(&v, mp->cell->tr, &v);
4273         ap->r = v;
4274         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4275         mp->needsMDCopyCoordinates = 1;
4276         return val;
4277 }
4278
4279 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4280         double f;
4281         Vector v, ov;
4282         Atom *ap;
4283         Molecule *mp;
4284         s_AtomIndexFromValue(self, &ap, &mp);
4285         ov = v = ap->r;
4286         val = rb_Float(val);
4287         f = NUM2DBL(val);
4288         if (mp->cell != NULL) {
4289                 TransformVec(&v, mp->cell->rtr, &v);
4290                 v.x = f;
4291                 TransformVec(&v, mp->cell->tr, &v);
4292         } else v.x = f;
4293         ap->r = v;
4294         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4295         mp->needsMDCopyCoordinates = 1;
4296         return val;
4297 }
4298
4299 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4300         double f;
4301         Vector v, ov;
4302         Atom *ap;
4303         Molecule *mp;
4304         s_AtomIndexFromValue(self, &ap, &mp);
4305         ov = v = ap->r;
4306         val = rb_Float(val);
4307         f = NUM2DBL(val);
4308         if (mp->cell != NULL) {
4309                 TransformVec(&v, mp->cell->rtr, &v);
4310                 v.y = f;
4311                 TransformVec(&v, mp->cell->tr, &v);
4312         } else v.y = f;
4313         ap->r = v;
4314         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4315         mp->needsMDCopyCoordinates = 1;
4316         return val;
4317 }
4318
4319 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4320         double f;
4321         Vector v, ov;
4322         Atom *ap;
4323         Molecule *mp;
4324         s_AtomIndexFromValue(self, &ap, &mp);
4325         ov = v = ap->r;
4326         val = rb_Float(val);
4327         f = NUM2DBL(val);
4328         if (mp->cell != NULL) {
4329                 TransformVec(&v, mp->cell->rtr, &v);
4330                 v.z = f;
4331                 TransformVec(&v, mp->cell->tr, &v);
4332         } else v.z = f;
4333         ap->r = v;
4334         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4335         mp->needsMDCopyCoordinates = 1;
4336         return val;
4337 }
4338
4339 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4340         Vector v;
4341         Molecule *mp;
4342         VALUE oval = s_AtomRef_GetSigma(self);
4343         VectorFromValue(val, &v);
4344         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4345         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4346         mp->needsMDCopyCoordinates = 1;
4347         return val;
4348 }
4349
4350 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4351         Double f;
4352         Molecule *mp;
4353         VALUE oval = s_AtomRef_GetSigmaX(self);
4354         val = rb_Float(val);
4355         f = NUM2DBL(val);
4356         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4357         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4358         mp->needsMDCopyCoordinates = 1;
4359         return val;
4360 }
4361
4362 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4363         Double f;
4364         Molecule *mp;
4365         VALUE oval = s_AtomRef_GetSigmaY(self);
4366         val = rb_Float(val);
4367         f = NUM2DBL(val);
4368         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4369         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4370         mp->needsMDCopyCoordinates = 1;
4371         return val;
4372 }
4373
4374 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4375         Double f;
4376         Molecule *mp;
4377         VALUE oval = s_AtomRef_GetSigmaZ(self);
4378         val = rb_Float(val);
4379         f = NUM2DBL(val);
4380         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4381         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4382         mp->needsMDCopyCoordinates = 1;
4383         return val;
4384 }
4385
4386 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4387         Vector v;
4388         Atom *ap;
4389         Molecule *mp;
4390         VALUE oval = s_AtomRef_GetV(self);
4391         VectorFromValue(val, &v);
4392         s_AtomIndexFromValue(self, &ap, &mp);
4393         ap->v = v;
4394         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4395         mp->needsMDCopyCoordinates = 1;
4396         return val;
4397 }
4398
4399 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4400         Vector v;
4401         Molecule *mp;
4402         VALUE oval = s_AtomRef_GetF(self);
4403         VectorFromValue(val, &v);
4404         VecScaleSelf(v, KCAL2INTERNAL);
4405         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4406         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4407         mp->needsMDCopyCoordinates = 1;
4408         return val;
4409 }
4410
4411 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4412         VALUE oval = s_AtomRef_GetOccupancy(self);
4413         Molecule *mp;
4414         val = rb_Float(val);
4415         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4416         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4417         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4418         return val;
4419 }
4420
4421 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4422         VALUE oval = s_AtomRef_GetTempFactor(self);
4423         val = rb_Float(val);
4424         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4425         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4426         return val;
4427 }
4428
4429 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4430         AtomRef *aref;
4431         int i, n, type;
4432         VALUE *valp;
4433         Double f[12];
4434         VALUE oval = s_AtomRef_GetAniso(self);
4435         Data_Get_Struct(self, AtomRef, aref);
4436         val = rb_funcall(val, rb_intern("to_a"), 0);
4437         n = RARRAY_LEN(val);
4438         valp = RARRAY_PTR(val);
4439         for (i = 0; i < 6; i++) {
4440                 if (i < n)
4441                         f[i] = NUM2DBL(rb_Float(valp[i]));
4442                 else f[i] = 0.0;
4443         }
4444         if (n >= 7)
4445                 type = NUM2INT(rb_Integer(valp[6]));
4446         else type = 0;
4447         if (n >= 13) {
4448                 for (i = 0; i < 6; i++)
4449                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4450         } else {
4451                 for (i = 0; i < 6; i++)
4452                         f[i + 6] = 0.0;
4453         }
4454         i = s_AtomIndexFromValue(self, NULL, NULL);
4455         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4456         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4457         return val;
4458 }
4459
4460 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4461     rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4462     return val; /* Not reached */
4463 }
4464
4465 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4466         Molecule *mol;
4467         Atom *ap;
4468         int i, n;
4469         VALUE *valp;
4470         Int ival[5];
4471         VALUE oval = s_AtomRef_GetSymop(self);
4472         i = s_AtomIndexFromValue(self, &ap, &mol);
4473         if (val == Qnil) {
4474                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4475         } else {
4476                 val = rb_funcall(val, rb_intern("to_a"), 0);
4477                 n = RARRAY_LEN(val);
4478                 valp = RARRAY_PTR(val);
4479                 for (i = 0; i < 5; i++) {
4480                         if (i < n) {
4481                                 if (valp[i] == Qnil)
4482                                         ival[i] = -100000;
4483                                 else 
4484                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4485                         } else ival[i] = -100000;
4486                 }
4487         }
4488         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4489                 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));
4490         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4491                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4492         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4493                 return val;  /*  No need to change  */
4494         if (ival[0] != -100000)
4495                 ap->symop.sym = ival[0];
4496         if (ival[1] != -100000)
4497                 ap->symop.dx = ival[1];
4498         if (ival[2] != -100000)
4499                 ap->symop.dy = ival[2];
4500         if (ival[3] != -100000)
4501                 ap->symop.dz = ival[3];
4502         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4503         if (ival[4] != -100000)
4504                 ap->symbase = ival[4];
4505         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4506                 /*  The anisotropic parameters should be recalculated  */
4507                 VALUE oaval = s_AtomRef_GetAniso(self);
4508                 MoleculeSetAnisoBySymop(mol, i);
4509                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4510         }
4511         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4512         return val;
4513 }
4514
4515 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4516         VALUE oval = s_AtomRef_GetIntCharge(self);
4517         val = rb_Integer(val);
4518         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4519         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4520         return val;
4521 }
4522
4523 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4524         Molecule *mp;
4525         VALUE oval = s_AtomRef_GetFixForce(self);
4526         val = rb_Float(val);
4527         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4528         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4529         mp->needsMDRebuild = 1;
4530         return val;
4531 }
4532
4533 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4534         Vector v;
4535         Molecule *mp;
4536         VALUE oval = s_AtomRef_GetFixPos(self);
4537         VectorFromValue(val, &v);
4538         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4539         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4540         mp->needsMDRebuild = 1;
4541         return val;
4542 }
4543
4544 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4545         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4546         return val; /* Not reached */
4547 }
4548
4549 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4550         VALUE oval = s_AtomRef_GetIntCharge(self);
4551         val = rb_Integer(val);
4552         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4553         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4554         return val;
4555 }
4556
4557 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4558         VALUE oval = s_AtomRef_GetIntCharge(self);
4559         val = rb_Integer(val);
4560         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4561         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4562         return val;
4563 }
4564
4565 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4566         Atom *ap = s_AtomFromValue(self);
4567         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4568         if (RTEST(val)) {
4569                 ap->exflags |= kAtomHiddenFlag;
4570         } else {
4571                 ap->exflags &= ~kAtomHiddenFlag;
4572         }
4573         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4574         return val;
4575 }
4576
4577 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4578         Int idx, i, j, k, n, *ip;
4579         Double *dp;
4580         Atom *ap;
4581         Molecule *mol;
4582         VALUE oval, v;
4583         AtomConnect ac;
4584         Int nUndoActions;
4585         MolAction **undoActions;
4586         memset(&ac, 0, sizeof(ac));
4587         idx = s_AtomIndexFromValue(self, &ap, &mol);
4588         oval = s_AtomRef_GetAnchorList(self);
4589         if (val != Qnil) {
4590                 val = rb_ary_to_ary(val);
4591                 n = RARRAY_LEN(val);
4592         } else n = 0;
4593         if (n == 0) {
4594                 if (ap->anchor != NULL) {
4595                         AtomConnectResize(&ap->anchor->connect, 0);
4596                         free(ap->anchor->coeffs);
4597                         free(ap->anchor);
4598                         ap->anchor = NULL;
4599                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4600                 }
4601                 return val;
4602         }
4603         if (n < 2)
4604                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4605         if (ap->aname[0] == '_')
4606                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4607         ip = (Int *)malloc(sizeof(Int) * n);
4608         dp = NULL;
4609         for (i = 0; i < n; i++) {
4610                 v = RARRAY_PTR(val)[i];
4611                 if (rb_obj_is_kind_of(v, rb_cFloat))
4612                         break;
4613                 j = NUM2INT(rb_Integer(v));
4614                 if (j < 0 || j >= mol->natoms)
4615                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4616                 for (k = 0; k < i; k++) {
4617                         if (ip[k] == j)
4618                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4619                 }
4620                 ip[i] = j;
4621         }
4622         if (i < n) {
4623                 if (i < 2)
4624                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4625                 else if (i * 2 != n)
4626                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4627                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4628                 for (i = 0; i < n / 2; i++) {
4629                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4630                         if (dp[i] <= 0.0)
4631                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4632                 }
4633                 n /= 2;
4634         }
4635         nUndoActions = 0;
4636         undoActions = NULL;
4637         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4638         free(dp);
4639         free(ip);
4640         if (i != 0)
4641                 rb_raise(rb_eMolbyError, "invalid argument");
4642         if (nUndoActions > 0) {
4643                 for (i = 0; i < nUndoActions; i++) {
4644                         MolActionCallback_registerUndo(mol, undoActions[i]);
4645                         MolActionRelease(undoActions[i]);
4646                 }
4647                 free(undoActions);
4648         }
4649         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4650         return val;
4651 }
4652
4653 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4654         Atom *ap = s_AtomFromValue(self);
4655         char *p = StringValuePtr(val);
4656         VALUE oval = s_AtomRef_GetUFFType(self);
4657         strncpy(ap->uff_type, p, 5);
4658         ap->uff_type[5] = 0;
4659         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4660         return val;
4661 }
4662
4663 static struct s_AtomAttrDef {
4664         char *name;
4665         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4666         ID id;                  /*  Will be set within InitMolby()  */
4667         VALUE (*getter)(VALUE);
4668         VALUE (*setter)(VALUE, VALUE);
4669 } s_AtomAttrDefTable[] = {
4670         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4671         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4672         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4673         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4674         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4675         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4676         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4677         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4678         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4679         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4680         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4681         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4682         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4683         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4684         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4685     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4686         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4687         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4688         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4689         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4690         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4691         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4692         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4693         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4694         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4695         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4696         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4697         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4698         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4699     {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues,        s_AtomRef_SetAnisoEigenValues},
4700         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4701         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4702         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4703         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4704         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4705         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4706         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4707         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4708         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4709         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4710         {NULL} /* Sentinel */
4711 };
4712
4713 static VALUE
4714 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4715 {
4716         int i;
4717         ID kid;
4718         if (TYPE(key) != T_SYMBOL) {
4719                 kid = rb_intern(StringValuePtr(key));
4720                 key = ID2SYM(kid);
4721         } else kid = SYM2ID(key);
4722         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4723                 if (s_AtomAttrDefTable[i].id == kid) {
4724                         if (value == Qundef)
4725                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4726                         else
4727                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4728                 }
4729         }
4730         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4731         return Qnil; /* not reached */
4732 }
4733
4734 static VALUE
4735 s_AtomRef_GetAttr(VALUE self, VALUE key)
4736 {
4737         return s_AtomRef_SetAttr(self, key, Qundef);
4738 }
4739
4740 /*
4741  *  call-seq:
4742  *     self == atomRef -> boolean
4743  *
4744  *  True if the two references point to the same atom.
4745  */
4746 static VALUE
4747 s_AtomRef_Equal(VALUE self, VALUE val)
4748 {
4749         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4750                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4751         } else return Qfalse;
4752 }
4753
4754 #pragma mark ====== MolEnumerable Class ======
4755
4756 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4757
4758 /*
4759  *  call-seq:
4760  *     self[idx] -> AtomRef or Array of Integers
4761  *  
4762  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4763  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4764  *  value is a String. Otherwise, the return value is an Array of Integers.
4765  */
4766 static VALUE
4767 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4768 {
4769         MolEnumerable *mseq;
4770         Molecule *mol;
4771         int idx1, idx2;
4772     Data_Get_Struct(self, MolEnumerable, mseq);
4773         mol = mseq->mol;
4774         if (mseq->kind == kAtomKind) {
4775                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4776         }
4777         idx1 = NUM2INT(arg1);
4778         switch (mseq->kind) {
4779                 case kBondKind: {
4780                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4781                         if (idx2 < 0 || idx2 >= mol->nbonds)
4782                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4783                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4784                 }
4785                 case kAngleKind: {
4786                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4787                         if (idx2 < 0 || idx2 >= mol->nangles)
4788                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4789                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4790                 }
4791                 case kDihedralKind: {
4792                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4793                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4794                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4795                         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]));
4796                 }
4797                 case kImproperKind: {
4798                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4799                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4800                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4801                         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]));
4802                 }
4803                 case kResidueKind: {
4804                         char *p;
4805                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4806                         if (idx2 < 0 || idx2 >= mol->nresidues)
4807                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4808                         p = mol->residues[idx2];
4809                         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4810                 }
4811         }
4812         return Qnil;
4813 }
4814
4815 /*
4816  *  call-seq:
4817  *     length          -> Integer
4818  *  
4819  *  Returns the number of objects included in this enumerable.
4820  */
4821 static VALUE
4822 s_MolEnumerable_Length(VALUE self)
4823 {
4824         MolEnumerable *mseq;
4825     Data_Get_Struct(self, MolEnumerable, mseq);
4826         switch (mseq->kind) {
4827                 case kAtomKind:
4828                         return INT2NUM(mseq->mol->natoms);
4829                 case kBondKind:
4830                         return INT2NUM(mseq->mol->nbonds);
4831                 case kAngleKind:
4832                         return INT2NUM(mseq->mol->nangles);
4833                 case kDihedralKind:
4834                         return INT2NUM(mseq->mol->ndihedrals);
4835                 case kImproperKind:
4836                         return INT2NUM(mseq->mol->nimpropers);
4837                 case kResidueKind:
4838                         return INT2NUM(mseq->mol->nresidues);
4839         }
4840         return INT2NUM(-1);
4841 }
4842
4843 /*
4844  *  call-seq:
4845  *     each {|obj| ...}
4846  *  
4847  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4848  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4849  *  For the atoms, a same AtomRef object is passed (with different internal information)
4850  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4851  *  for each iteration.
4852  */
4853 VALUE
4854 s_MolEnumerable_Each(VALUE self)
4855 {
4856         MolEnumerable *mseq;
4857         int i;
4858         int len = NUM2INT(s_MolEnumerable_Length(self));
4859     Data_Get_Struct(self, MolEnumerable, mseq);
4860         if (mseq->kind == kAtomKind) {
4861                 /*  The same AtomRef object will be used during the loop  */
4862                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4863                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4864                 for (i = 0; i < len; i++) {
4865                         aref->idx = i;
4866                         rb_yield(arval);
4867                 }
4868     } else {
4869                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4870                 for (i = 0; i < len; i++) {
4871                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4872                 }
4873         }
4874     return self;
4875 }
4876
4877 /*
4878  *  call-seq:
4879  *     self == molEnumerable -> boolean
4880  *
4881  *  True if the two arguments point to the same molecule and enumerable type.
4882  */
4883 static VALUE
4884 s_MolEnumerable_Equal(VALUE self, VALUE val)
4885 {
4886         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4887                 MolEnumerable *mseq1, *mseq2;
4888                 Data_Get_Struct(self, MolEnumerable, mseq1);
4889                 Data_Get_Struct(val, MolEnumerable, mseq2);
4890                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4891         } else return Qfalse;
4892 }
4893
4894
4895 #pragma mark ====== Molecule Class ======
4896
4897 #pragma mark ------ Allocate/Release/Accessor ------
4898
4899 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method.  */
4900 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4901 char *gLoadSaveErrorMessage = NULL;
4902
4903 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4904
4905 Molecule *
4906 MoleculeFromValue(VALUE val)
4907 {
4908         Molecule *mol;
4909         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4910                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4911     Data_Get_Struct(val, Molecule, mol);
4912         return mol;
4913 }
4914
4915 static VALUE sMoleculeRetainArray = Qnil;
4916
4917 /*  The function is called from MoleculeRelease()  */
4918 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4919 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4920 /*  object is always returned for the same Molecule.  */
4921 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4922 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4923 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4924 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4925 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4926
4927 /*  Register/unregister the exmolobj Ruby object  */
4928 void
4929 MoleculeReleaseExternalObj(Molecule *mol)
4930 {
4931         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4932                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4933                 mol->exmolobjProtected = 0;
4934         }
4935 }
4936
4937 void
4938 MoleculeRetainExternalObj(Molecule *mol)
4939 {
4940         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4941                 if (sMoleculeRetainArray == Qnil) {
4942                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4943                         sMoleculeRetainArray = rb_ary_new();
4944                 }
4945                 
4946                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4947                 mol->exmolobjProtected = 1;
4948         }
4949 }
4950
4951 /*  Release hook function for Ruby  */
4952 void
4953 MoleculeReleaseHook(Molecule *mol)
4954 {
4955         if (mol->exmolobj != NULL) {
4956                 /*  No need to remove from sMoleculeRetainArray  */
4957                 mol->exmolobj = NULL;
4958                 mol->exmolobjProtected = 0;
4959         }
4960         MoleculeRelease(mol);
4961 }
4962
4963 VALUE
4964 ValueFromMolecule(Molecule *mol)
4965 {
4966         if (mol == NULL)
4967                 return Qnil;
4968         if (mol->exmolobj != NULL)
4969                 return (VALUE)mol->exmolobj;
4970         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4971         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4972         return (VALUE)mol->exmolobj;
4973 }
4974
4975 /*  Allocator  */
4976 static VALUE
4977 s_Molecule_Alloc(VALUE klass)
4978 {
4979         VALUE val;
4980         Molecule *mol = MoleculeNew();
4981         val = ValueFromMolecule(mol);
4982         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4983         return val;
4984 }
4985
4986 static int
4987 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4988 {
4989         int n;
4990         char *p = "";
4991         if (FIXNUM_P(val)) {
4992                 n = FIX2INT(val);
4993                 if (n >= 0 && n < mol->natoms)
4994                         return n;
4995                 n = -1; /*  No such atom  */
4996                 val = rb_inspect(val);
4997         } else {
4998                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4999         }
5000         if (n >= 0 && n < mol->natoms)
5001                 return n;
5002         p = StringValuePtr(val);
5003         if (n == -1)
5004                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
5005         else if (n == -2)
5006                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
5007         else
5008                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
5009         return 0; /* Not reached */
5010 }
5011
5012 static IntGroup *
5013 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
5014 {
5015         IntGroup *ig;
5016     Molecule *mp1;
5017     Data_Get_Struct(self, Molecule, mp1);
5018         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
5019         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
5020                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5021         Data_Get_Struct(val, IntGroup, ig);
5022     IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
5023         IntGroupRetain(ig);
5024         return ig;
5025 }
5026
5027 /*
5028  *  call-seq:
5029  *     dup          -> Molecule
5030  *
5031  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
5032  *  created object does not affect the old object in any sense.
5033  */
5034 static VALUE
5035 s_Molecule_InitCopy(VALUE self, VALUE arg)
5036 {
5037         Molecule *mp1, *mp2;
5038         Data_Get_Struct(self, Molecule, mp1);
5039         mp2 = MoleculeFromValue(arg);
5040         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
5041                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
5042         return self;
5043 }
5044
5045 /*
5046  *  call-seq:
5047  *     atom_index(val)       -> Integer
5048  *
5049  *  Returns the atom index represented by val. val can be either a non-negative integer
5050  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5051  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
5052  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
5053  *  If val is a string and multiple atoms match the description, the atom with the lowest index
5054  *  is returned.
5055  */
5056 static VALUE
5057 s_Molecule_AtomIndex(VALUE self, VALUE val)
5058 {
5059     Molecule *mol;
5060     Data_Get_Struct(self, Molecule, mol);
5061         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5062 }
5063
5064 /*
5065  *  call-seq:
5066  *     self == Molecule -> boolean
5067  *
5068  *  True if the two arguments point to the same molecule.
5069  */
5070 static VALUE
5071 s_Molecule_Equal(VALUE self, VALUE val)
5072 {
5073         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
5074                 Molecule *mol1, *mol2;
5075                 Data_Get_Struct(self, Molecule, mol1);
5076                 Data_Get_Struct(val, Molecule, mol2);
5077                 return (mol1 == mol2 ? Qtrue : Qfalse);
5078         } else return Qfalse;
5079 }
5080
5081 #pragma mark ------ Load/Save ------
5082
5083 static void
5084 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5085 {
5086         if (gLoadSaveErrorMessage != NULL) {
5087                 MyAppCallback_setConsoleColor(1);
5088                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5089                 MyAppCallback_setConsoleColor(0);
5090         }
5091         if (status != 0)
5092                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5093 }
5094
5095 /*
5096  *  call-seq:
5097  *     loadmbsf(file)       -> bool
5098  *
5099  *  Read a structure from a mbsf file.
5100  *  Return true if successful.
5101  */
5102 static VALUE
5103 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5104 {
5105         VALUE fname;
5106         char *fstr;
5107         Molecule *mol;
5108         int retval;
5109         MoleculeClearLoadSaveErrorMessage();
5110         Data_Get_Struct(self, Molecule, mol);
5111         rb_scan_args(argc, argv, "1", &fname);
5112         fstr = FileStringValuePtr(fname);
5113         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5114         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5115         return Qtrue;   
5116 }
5117
5118 /*
5119  *  call-seq:
5120  *     loadpsf(file, pdbfile = nil)       -> bool
5121  *
5122  *  Read a structure from a psf file. molecule must be empty. The psf may be
5123  *  an "extended" version, which also contains coordinates. If pdbfile 
5124  *  is given, then atomic coordinates are read from that file.
5125  *  Return true if successful.
5126  */
5127 static VALUE
5128 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5129 {
5130         VALUE fname, pdbname;
5131         char *fstr, *pdbstr;
5132         Molecule *mol;
5133         int retval;
5134         Data_Get_Struct(self, Molecule, mol);
5135         if (mol->natoms > 0)
5136                 return Qnil;  /*  Must be a new molecule  */
5137         MoleculeClearLoadSaveErrorMessage();
5138         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5139         fstr = FileStringValuePtr(fname);
5140         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5141         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5142         pdbstr = NULL;
5143         if (!NIL_P(pdbname)) {
5144                 pdbstr = strdup(FileStringValuePtr(pdbname));
5145                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5146                 free(pdbstr);
5147                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5148         }
5149         return Qtrue;
5150 }
5151
5152 /*
5153  *  call-seq:
5154  *     loadpdb(file)       -> bool
5155  *
5156  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5157  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5158  *  Return true if successful.
5159  */
5160 static VALUE
5161 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5162 {
5163         VALUE fname;
5164         char *fstr;
5165         Molecule *mol;
5166         int retval;
5167         Data_Get_Struct(self, Molecule, mol);
5168         rb_scan_args(argc, argv, "1", &fname);
5169         MoleculeClearLoadSaveErrorMessage();
5170         fstr = FileStringValuePtr(fname);
5171         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5172         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5173         return Qtrue;   
5174 }
5175
5176 /*
5177  *  call-seq:
5178  *     loaddcd(file)       -> bool
5179  *
5180  *  Read coordinates from a dcd file. The molecule should not empty.
5181  *  Return true if successful.
5182  */
5183 static VALUE
5184 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5185 {
5186         VALUE fname;
5187         char *fstr;
5188         Molecule *mol;
5189         int retval;
5190         Data_Get_Struct(self, Molecule, mol);
5191         rb_scan_args(argc, argv, "1", &fname);
5192         MoleculeClearLoadSaveErrorMessage();
5193         fstr = FileStringValuePtr(fname);
5194         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5195         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5196         return Qtrue;   
5197 }
5198
5199 /*
5200  *  call-seq:
5201  *     loadtep(file)       -> bool
5202  *
5203  *  Read coordinates from an ortep .tep file.
5204  *  Return true if successful.
5205  */
5206 static VALUE
5207 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5208 {
5209         VALUE fname;
5210         char *fstr;
5211         Molecule *mol;
5212         int retval;
5213         Data_Get_Struct(self, Molecule, mol);
5214         rb_scan_args(argc, argv, "1", &fname);
5215         MoleculeClearLoadSaveErrorMessage();
5216         fstr = FileStringValuePtr(fname);
5217         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5218         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5219         return Qtrue;   
5220 }
5221
5222 /*
5223  *  call-seq:
5224  *     loadres(file)       -> bool
5225  *
5226  *  Read coordinates from a shelx .res file.
5227  *  Return true if successful.
5228  */
5229 static VALUE
5230 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5231 {
5232         VALUE fname;
5233         char *fstr;
5234         Molecule *mol;
5235         int retval;
5236         Data_Get_Struct(self, Molecule, mol);
5237         rb_scan_args(argc, argv, "1", &fname);
5238         MoleculeClearLoadSaveErrorMessage();
5239         fstr = FileStringValuePtr(fname);
5240         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5241         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5242         return Qtrue;   
5243 }
5244
5245 /*
5246  *  call-seq:
5247  *     loadfchk(file)       -> bool
5248  *
5249  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5250  *  Return true if successful.
5251  */
5252 static VALUE
5253 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5254 {
5255         VALUE fname;
5256         char *fstr;
5257         Molecule *mol;
5258         int retval;
5259         Data_Get_Struct(self, Molecule, mol);
5260         rb_scan_args(argc, argv, "1", &fname);
5261         MoleculeClearLoadSaveErrorMessage();
5262         fstr = FileStringValuePtr(fname);
5263         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5264         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5265         return Qtrue;   
5266 }
5267
5268 /*
5269  *  call-seq:
5270  *     loaddat(file)       -> bool
5271  *
5272  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5273  *  Return true if successful.
5274  */
5275 static VALUE
5276 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5277 {
5278         VALUE fname;
5279         char *fstr;
5280         Molecule *mol;
5281         int retval;
5282         Data_Get_Struct(self, Molecule, mol);
5283         rb_scan_args(argc, argv, "1", &fname);
5284         MoleculeClearLoadSaveErrorMessage();
5285         fstr = FileStringValuePtr(fname);
5286         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5287         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5288         MyAppCallback_hideProgressPanel();
5289         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5290         return Qtrue;   
5291 }
5292
5293 /*
5294  *  call-seq:
5295  *     savembsf(file)       -> bool
5296  *
5297  *  Write structure as a mbsf file. Returns true if successful.
5298  */
5299 static VALUE
5300 s_Molecule_Savembsf(VALUE self, VALUE fname)
5301 {
5302         char *fstr;
5303     Molecule *mol;
5304         int retval;
5305     Data_Get_Struct(self, Molecule, mol);
5306         MoleculeClearLoadSaveErrorMessage();
5307         fstr = FileStringValuePtr(fname);
5308         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5309         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5310         return Qtrue;
5311 }
5312
5313 /*
5314  *  call-seq:
5315  *     savepsf(file)       -> bool
5316  *
5317  *  Write structure as a psf file. Returns true if successful.
5318  */
5319 static VALUE
5320 s_Molecule_Savepsf(VALUE self, VALUE fname)
5321 {
5322         char *fstr;
5323     Molecule *mol;
5324         int retval;
5325     Data_Get_Struct(self, Molecule, mol);
5326         MoleculeClearLoadSaveErrorMessage();
5327         fstr = FileStringValuePtr(fname);
5328         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5329         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5330         return Qtrue;
5331 }
5332
5333 /*
5334  *  call-seq:
5335  *     savepdb(file)       -> bool
5336  *
5337  *  Write coordinates as a pdb file. Returns true if successful.
5338  */
5339 static VALUE
5340 s_Molecule_Savepdb(VALUE self, VALUE fname)
5341 {
5342         char *fstr;
5343     Molecule *mol;
5344         int retval;
5345     Data_Get_Struct(self, Molecule, mol);
5346         MoleculeClearLoadSaveErrorMessage();
5347         fstr = FileStringValuePtr(fname);
5348         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5349         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5350         return Qtrue;
5351 }
5352
5353 /*
5354  *  call-seq:
5355  *     savedcd(file)       -> bool
5356  *
5357  *  Write coordinates as a dcd file. Returns true if successful.
5358  */
5359 static VALUE
5360 s_Molecule_Savedcd(VALUE self, VALUE fname)
5361 {
5362         char *fstr;
5363     Molecule *mol;
5364         int retval;
5365     Data_Get_Struct(self, Molecule, mol);
5366         MoleculeClearLoadSaveErrorMessage();
5367         fstr = FileStringValuePtr(fname);
5368         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5369         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5370         return Qtrue;
5371 }
5372
5373 /*  load([ftype, ] fname, ...)  */
5374 static VALUE
5375 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5376 {
5377         VALUE rval;
5378         char *argstr, *methname, *p, *type = "";
5379         ID mid = 0;
5380         int i;
5381         const char *ls = (loadFlag ? "load" : "save");
5382         int lslen = strlen(ls);
5383
5384         if (argc == 0)
5385                 return Qnil;
5386
5387         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5388                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5389         if (argstr[0] == ':') {
5390                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5391                 methname = ALLOC_N(char, lslen + strlen(argstr));
5392                 strcpy(methname, ls);
5393                 strcat(methname, argstr + 1);
5394                 type = argstr + 1;
5395                 for (i = lslen; methname[i] != 0; i++)
5396                         methname[i] = tolower(methname[i]);
5397                 mid = rb_intern(methname);
5398                 xfree(methname);
5399                 argc--;
5400                 argv++;
5401                 rval = rb_funcall2(self, mid, argc, argv);
5402                 if (rval == Qnil)
5403                         goto failure;
5404                 else
5405                         goto success;
5406         }
5407         /*  Guess file type from extension  */
5408         p = strrchr(argstr, '.');
5409         if (p != NULL) {
5410                 p++;
5411                 type = p;
5412                 for (methname = p; *methname != 0; methname++) {
5413                         if (!isalpha(*methname))
5414                                 break;
5415                 }
5416                 if (*methname == 0) {
5417                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5418                         if (methname == NULL)
5419                                 rb_raise(rb_eMolbyError, "Low memory");
5420                         strcpy(methname, ls);
5421                         strcat(methname, p);
5422                         for (i = lslen; methname[i] != 0; i++)
5423                                 methname[i] = tolower(methname[i]);
5424                         mid = rb_intern(methname);
5425                         xfree(methname);
5426                         if (loadFlag) {
5427                                 if (rb_respond_to(self, mid)) {
5428                                         /*  Load: try to call the load procedure only if it is available  */
5429                                         rval = rb_funcall2(self, mid, argc, argv);
5430                                         if (rval != Qnil)
5431                                                 goto success;
5432                                 }
5433                         } else {
5434                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5435                                 rval = rb_funcall2(self, mid, argc, argv);
5436                                 if (rval != Qnil)
5437                                         goto success;
5438                         }
5439                 }
5440         }
5441 failure:
5442         rval = rb_str_to_str(argv[0]);
5443         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5444         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5445         return Qnil;  /*  Does not reach here  */
5446
5447 success:
5448         {
5449                 /*  Register the path  */
5450                 Molecule *mol;
5451         /*      Atom *ap; */
5452                 Data_Get_Struct(self, Molecule, mol);
5453                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5454                 
5455                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5456         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5457                         if (ap->occupancy != 0.0)
5458                                 break;
5459                 }
5460                 if (i == mol->natoms) {
5461                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5462                                 ap->occupancy = 1.0;
5463                         }
5464                 } */
5465         }
5466         return rval;
5467 }
5468
5469 /*
5470  *  call-seq:
5471  *     molload(file, *args)       -> bool
5472  *
5473  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5474  *  file type given by the extension). If this method fails, then all defined (public)
5475  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5476  */
5477 static VALUE
5478 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5479 {
5480         return s_Molecule_LoadSave(argc, argv, self, 1);
5481 }
5482
5483 /*
5484  *  call-seq:
5485  *     molsave(file, *args)       -> bool
5486  *
5487  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5488  *  (XXX is the file type given by the extension).
5489  */
5490 static VALUE
5491 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5492 {
5493         return s_Molecule_LoadSave(argc, argv, self, 0);
5494 }
5495
5496 /*
5497  *  call-seq:
5498  *     open        -> Molecule
5499  *     open(file)  -> Molecule
5500  *
5501  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5502  */
5503 static VALUE
5504 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5505 {
5506         VALUE fname;
5507         const char *p;
5508         Molecule *mp;
5509         VALUE iflag;
5510     
5511     if (!gUseGUI) {
5512         rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5513     }
5514     
5515         rb_scan_args(argc, argv, "01", &fname);
5516         if (NIL_P(fname))
5517                 p = NULL;
5518         else
5519                 p = FileStringValuePtr(fname);
5520         iflag = Ruby_SetInterruptFlag(Qfalse);
5521         mp = MoleculeCallback_openNewMolecule(p);
5522         Ruby_SetInterruptFlag(iflag);
5523         if (mp == NULL) {
5524                 if (p == NULL)
5525                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5526                 else
5527                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5528         }
5529         return ValueFromMolecule(mp);
5530 }
5531
5532 /*
5533  *  call-seq:
5534  *     new  -> Molecule
5535  *     new(file, *args)  -> Molecule
5536  *
5537  *  Create a new molecule and call "load" method with the same arguments.
5538  */
5539 static VALUE
5540 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5541 {
5542         if (argc > 0)
5543                 return s_Molecule_Load(argc, argv, self);
5544         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5545 }
5546
5547 /*
5548  *  call-seq:
5549  *     error_message       -> String
5550  *
5551  *  Get the error_message from the last load/save method. If no error, returns nil.
5552  */
5553 static VALUE
5554 s_Molecule_ErrorMessage(VALUE klass)
5555 {
5556         if (gLoadSaveErrorMessage == NULL)
5557                 return Qnil;
5558         else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5559 }
5560
5561 /*
5562  *  call-seq:
5563  *     set_error_message(String)
5564  *     Molecule.error_message = String
5565  *
5566  *  Set the error_message for the present load/save method.
5567  */
5568 static VALUE
5569 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5570 {
5571         if (gLoadSaveErrorMessage != NULL) {
5572                 free(gLoadSaveErrorMessage);
5573                 gLoadSaveErrorMessage = NULL;
5574         }
5575         if (sval != Qnil) {
5576                 sval = rb_str_to_str(sval);
5577                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5578         }
5579         return sval;
5580 }
5581
5582 /*
5583  *  call-seq:
5584  *     set_molecule(Molecule)
5585  *
5586  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5587  *  This method is exclusively used for associating a new document with an existing molecule.
5588  */
5589 static VALUE
5590 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5591 {
5592         Molecule *mp1, *mp2;
5593         Data_Get_Struct(self, Molecule, mp1);
5594         mp2 = MoleculeFromValue(mval);
5595         MoleculeInitWithMolecule(mp1, mp2);
5596         MoleculeCallback_notifyModification(mp1, 1);
5597         return self;
5598 }
5599
5600 #pragma mark ------ Name attributes ------
5601
5602 /*
5603  *  call-seq:
5604  *     name       -> String
5605  *
5606  *  Returns the display name of the molecule. If the molecule has no associated
5607  *  document, then returns nil.
5608  */
5609 static VALUE
5610 s_Molecule_Name(VALUE self)
5611 {
5612     Molecule *mol;
5613         char buf[1024];
5614     Data_Get_Struct(self, Molecule, mol);
5615         MoleculeCallback_displayName(mol, buf, sizeof buf);
5616         if (buf[0] == 0)
5617                 return Qnil;
5618         else
5619                 return Ruby_NewEncodedStringValue2(buf);
5620 }
5621
5622 /*
5623  *  call-seq:
5624  *     set_name(string) -> self
5625  *
5626  *  Set the name of an untitled molecule. If the molecule is not associated with window
5627  *  or it already has an associated file, then exception is thrown.
5628  */
5629 static VALUE
5630 s_Molecule_SetName(VALUE self, VALUE nval)
5631 {
5632     Molecule *mol;
5633     Data_Get_Struct(self, Molecule, mol);
5634         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5635                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5636         return self;
5637 }
5638
5639
5640 /*
5641  *  call-seq:
5642  *     path       -> String
5643  *
5644  *  Returns the full path name of the molecule, if it is associated with a file.
5645  *  If the molecule has no associated file, then returns nil.
5646  */
5647 static VALUE
5648 s_Molecule_Path(VALUE self)
5649 {
5650     Molecule *mol;
5651         char buf[1024];
5652     Data_Get_Struct(self, Molecule, mol);
5653         MoleculeCallback_pathName(mol, buf, sizeof buf);
5654         if (buf[0] == 0)
5655                 return Qnil;
5656         else
5657                 return Ruby_NewFileStringValue(buf);
5658 }
5659
5660 /*
5661  *  call-seq:
5662  *     dir       -> String
5663  *
5664  *  Returns the full path name of the directory in which the file associated with the
5665  *  molecule is located. If the molecule has no associated file, then returns nil.
5666  */
5667 static VALUE
5668 s_Molecule_Dir(VALUE self)
5669 {
5670     Molecule *mol;
5671         char buf[1024], *p;
5672     Data_Get_Struct(self, Molecule, mol);
5673         MoleculeCallback_pathName(mol, buf, sizeof buf);
5674 #if __WXMSW__
5675         translate_char(buf, '\\', '/');
5676 #endif
5677         if (buf[0] == 0)
5678                 return Qnil;
5679         else {
5680                 p = strrchr(buf, '/');
5681                 if (p != NULL)
5682                         *p = 0;
5683                 return Ruby_NewEncodedStringValue2(buf);
5684         }
5685 }
5686
5687 /*
5688  *  call-seq:
5689  *     inspect       -> String
5690  *
5691  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5692  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5693  *  the Molecule structure) is returned.
5694  */
5695 static VALUE
5696 s_Molecule_Inspect(VALUE self)
5697 {
5698     Molecule *mol;
5699         char buf[256];
5700     Data_Get_Struct(self, Molecule, mol);
5701         MoleculeCallback_displayName(mol, buf, sizeof buf);
5702         if (buf[0] == 0) {
5703                 /*  No associated document  */
5704                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5705                 return Ruby_NewEncodedStringValue2(buf);
5706         } else {
5707                 /*  Check whether the document name is duplicate  */
5708                 char buf2[256];
5709                 int idx, k, k2;
5710                 Molecule *mol2;
5711                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5712                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5713                         if (strcmp(buf, buf2) == 0) {
5714                                 k++;
5715                                 if (mol == mol2)
5716                                         k2 = k;
5717                         }
5718                 }
5719                 if (k > 1) {
5720                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5721                 } else {
5722                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5723                 }
5724                 return Ruby_NewEncodedStringValue2(buf2);
5725         }
5726 }
5727
5728 #pragma mark ------ MolEnumerables ------
5729
5730 static VALUE
5731 s_Molecule_MolEnumerable(VALUE self, int kind)
5732 {
5733     Molecule *mol;
5734         MolEnumerable *mseq;
5735     Data_Get_Struct(self, Molecule, mol);
5736         mseq = MolEnumerableNew(mol, kind);
5737         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5738 }
5739
5740 /*
5741  *  call-seq:
5742  *     atoms       -> MolEnumerable
5743  *
5744  *  Returns a MolEnumerable object representing the array of atoms.
5745  */
5746 static VALUE
5747 s_Molecule_Atoms(VALUE self)
5748 {
5749         return s_Molecule_MolEnumerable(self, kAtomKind);
5750 }
5751
5752 /*
5753  *  call-seq:
5754  *     bonds       -> MolEnumerable
5755  *
5756  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5757  *  by an array of two atom indices.
5758  */
5759 static VALUE
5760 s_Molecule_Bonds(VALUE self)
5761 {
5762         return s_Molecule_MolEnumerable(self, kBondKind);
5763 }
5764
5765 /*
5766  *  call-seq:
5767  *     angles       -> MolEnumerable
5768  *
5769  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5770  *  by an array of three atom indices.
5771  */
5772 static VALUE
5773 s_Molecule_Angles(VALUE self)
5774 {
5775         return s_Molecule_MolEnumerable(self, kAngleKind);
5776 }
5777
5778 /*
5779  *  call-seq:
5780  *     dihedrals       -> MolEnumerable
5781  *
5782  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5783  *  by an array of four atom indices.
5784  */
5785 static VALUE
5786 s_Molecule_Dihedrals(VALUE self)
5787 {
5788         return s_Molecule_MolEnumerable(self, kDihedralKind);
5789 }
5790
5791 /*
5792  *  call-seq:
5793  *     impropers       -> MolEnumerable
5794  *
5795  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5796  *  by an array of four atom indices.
5797  */
5798 static VALUE
5799 s_Molecule_Impropers(VALUE self)
5800 {
5801         return s_Molecule_MolEnumerable(self, kImproperKind);
5802 }
5803
5804 /*
5805  *  call-seq:
5806  *     residues       -> MolEnumerable
5807  *
5808  *  Returns a MolEnumerable object representing the array of residue names.
5809  */
5810 static VALUE
5811 s_Molecule_Residues(VALUE self)
5812 {
5813         return s_Molecule_MolEnumerable(self, kResidueKind);
5814 }
5815
5816 /*
5817  *  call-seq:
5818  *     natoms       -> Integer
5819  *
5820  *  Returns the number of atoms.
5821  */
5822 static VALUE
5823 s_Molecule_Natoms(VALUE self)
5824 {
5825     Molecule *mol;
5826     Data_Get_Struct(self, Molecule, mol);
5827         return INT2NUM(mol->natoms);
5828 }
5829
5830 /*
5831  *  call-seq:
5832  *     nbonds       -> Integer
5833  *
5834  *  Returns the number of bonds.
5835  */
5836 static VALUE
5837 s_Molecule_Nbonds(VALUE self)
5838 {
5839     Molecule *mol;
5840     Data_Get_Struct(self, Molecule, mol);
5841         return INT2NUM(mol->nbonds);
5842 }
5843
5844 /*
5845  *  call-seq:
5846  *     nangles       -> Integer
5847  *
5848  *  Returns the number of angles.
5849  */
5850 static VALUE
5851 s_Molecule_Nangles(VALUE self)
5852 {
5853     Molecule *mol;
5854     Data_Get_Struct(self, Molecule, mol);
5855         return INT2NUM(mol->nangles);
5856 }
5857
5858 /*
5859  *  call-seq:
5860  *     ndihedrals       -> Integer
5861  *
5862  *  Returns the number of dihedrals.
5863  */
5864 static VALUE
5865 s_Molecule_Ndihedrals(VALUE self)
5866 {
5867     Molecule *mol;
5868     Data_Get_Struct(self, Molecule, mol);
5869         return INT2NUM(mol->ndihedrals);
5870 }
5871
5872 /*
5873  *  call-seq:
5874  *     nimpropers       -> Integer
5875  *
5876  *  Returns the number of impropers.
5877  */
5878 static VALUE
5879 s_Molecule_Nimpropers(VALUE self)
5880 {
5881     Molecule *mol;
5882     Data_Get_Struct(self, Molecule, mol);
5883         return INT2NUM(mol->nimpropers);
5884 }
5885
5886 /*
5887  *  call-seq:
5888  *     nresidues       -> Integer
5889  *
5890  *  Returns the number of residues.
5891  */
5892 static VALUE
5893 s_Molecule_Nresidues(VALUE self)
5894 {
5895     Molecule *mol;
5896     Data_Get_Struct(self, Molecule, mol);
5897         return INT2NUM(mol->nresidues);
5898 }
5899
5900 /*
5901  *  call-seq:
5902  *     nresidues = Integer
5903  *
5904  *  Change the number of residues.
5905  */
5906 static VALUE
5907 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5908 {
5909     Molecule *mol;
5910         int ival = NUM2INT(val);
5911     Data_Get_Struct(self, Molecule, mol);
5912         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5913         if (ival != mol->nresidues)
5914                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5915         return val;
5916 }
5917
5918 /*
5919  *  call-seq:
5920  *     max_residue_number(atom_group = nil)     -> Integer
5921  *
5922  *  Returns the maximum residue number actually used. If an atom group is given, only
5923  *  these atoms are examined. If no atom is present, nil is returned.
5924  */
5925 static VALUE
5926 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5927 {
5928     Molecule *mol;
5929         VALUE gval;
5930         int maxSeq;
5931         IntGroup *ig;
5932     Data_Get_Struct(self, Molecule, mol);
5933         rb_scan_args(argc, argv, "01", &gval);
5934         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5935         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5936         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5937 }
5938
5939 /*
5940  *  call-seq:
5941  *     min_residue_number(atom_group = nil)     -> Integer
5942  *
5943  *  Returns the minimum residue number actually used. If an atom group is given, only
5944  *  these atoms are examined. If no atom is present, nil is returned.
5945  */
5946 static VALUE
5947 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5948 {
5949     Molecule *mol;
5950         VALUE gval;
5951         int minSeq;
5952         IntGroup *ig;
5953     Data_Get_Struct(self, Molecule, mol);
5954         rb_scan_args(argc, argv, "01", &gval);
5955         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5956         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5957         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5958 }
5959
5960 /*
5961  *  call-seq:
5962  *     each_atom(atom_group = nil) {|aref| ...}
5963  *
5964  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5965  *  group is given, only these atoms are processed.
5966  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5967  *  is self (a Molecule object).
5968  */
5969 static VALUE
5970 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5971 {
5972         int i;
5973     Molecule *mol;
5974         AtomRef *aref;
5975         VALUE arval;
5976         VALUE gval;
5977         IntGroup *ig;
5978     Data_Get_Struct(self, Molecule, mol);
5979         rb_scan_args(argc, argv, "01", &gval);
5980         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5981         arval = ValueFromMoleculeAndIndex(mol, 0);
5982         Data_Get_Struct(arval, AtomRef, aref);
5983         for (i = 0; i < mol->natoms; i++) {
5984                 aref->idx = i;
5985                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5986                         rb_yield(arval);
5987         }
5988         if (ig != NULL)
5989                 IntGroupRelease(ig);
5990     return self;
5991 }
5992
5993 #pragma mark ------ Atom Group ------
5994
5995 static VALUE
5996 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5997 {
5998         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5999         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6000         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6001         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6002         return Qnil;
6003 }
6004
6005 /*
6006  *  call-seq:
6007  *     atom_group
6008  *     atom_group {|aref| ...}
6009  *     atom_group(arg1, arg2, ...)
6010  *     atom_group(arg1, arg2, ...) {|aref| ...}
6011  *
6012  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6013  *  If arguments are given, then the atoms reprensented by the arguments are added to the
6014  *  group. For a conversion of a string to an atom index, see the description
6015  *  of Molecule#atom_index.
6016  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
6017  *  representing each atom, and the atoms are removed from the result if the block returns false.
6018  *
6019  */
6020 static VALUE
6021 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6022 {
6023         IntGroup *ig1, *ig2;
6024     Molecule *mol;
6025         Int i, startPt, interval;
6026         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6027         Data_Get_Struct(retval, IntGroup, ig1);
6028     Data_Get_Struct(self, Molecule, mol);
6029         if (argc == 0) {
6030                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6031         } else {
6032                 while (argc > 0) {
6033                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6034                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6035                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6036                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6037                                 ig2 = IntGroupFromValue(*argv);
6038                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6039                                         interval = IntGroupGetInterval(ig2, i);
6040                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6041                                 }
6042                                 IntGroupRelease(ig2);
6043                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
6044                                 VALUE values[2];
6045                                 values[0] = (VALUE)mol;
6046                                 values[1] = (VALUE)ig1;
6047                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6048                         } else
6049                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6050                         argc--;
6051                         argv++;
6052                 }
6053         }
6054         if (rb_block_given_p()) {
6055                 /*  Evaluate the given block with an AtomRef as the argument, and delete
6056                         the index if the block returns false  */
6057                 AtomRef *aref = AtomRefNew(mol, 0);
6058                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6059                 ig2 = IntGroupNew();
6060                 IntGroupCopy(ig2, ig1);
6061                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6062                         VALUE resval;
6063                         if (startPt >= mol->natoms)
6064                                 break;
6065                         aref->idx = startPt;
6066                         resval = rb_yield(arval);
6067                         if (!RTEST(resval))
6068                                 IntGroupRemove(ig1, startPt, 1);
6069                 }
6070                 IntGroupRelease(ig2);
6071         }
6072         
6073         /*  Remove points that are out of bounds */
6074         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6075
6076         return retval;                  
6077 }
6078
6079 /*
6080  *  call-seq:
6081  *     selection       -> IntGroup
6082  *
6083  *  Returns the current selection.
6084  */
6085 static VALUE
6086 s_Molecule_Selection(VALUE self)
6087 {
6088     Molecule *mol;
6089         IntGroup *ig;
6090         VALUE val;
6091     Data_Get_Struct(self, Molecule, mol);
6092         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6093                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
6094                 val = ValueFromIntGroup(ig);
6095                 IntGroupRelease(ig);
6096         } else {
6097                 val = IntGroup_Alloc(rb_cIntGroup);
6098         }
6099         return val;
6100 }
6101
6102 static VALUE
6103 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6104 {
6105     Molecule *mol;
6106         IntGroup *ig;
6107     Data_Get_Struct(self, Molecule, mol);
6108         if (val == Qnil)
6109                 ig = NULL;
6110         else
6111                 ig = s_Molecule_AtomGroupFromValue(self, val);
6112         if (undoable)
6113                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6114         else
6115                 MoleculeSetSelection(mol, ig);
6116         if (ig != NULL)
6117                 IntGroupRelease(ig);
6118         return val;
6119 }
6120
6121 /*
6122  *  call-seq:
6123  *     selection = IntGroup
6124  *
6125  *  Set the current selection. The right-hand operand may be nil.
6126  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6127  */
6128 static VALUE
6129 s_Molecule_SetSelection(VALUE self, VALUE val)
6130 {
6131         return s_Molecule_SetSelectionSub(self, val, 0);
6132 }
6133
6134 /*
6135  *  call-seq:
6136  *     set_undoable_selection(IntGroup)
6137  *
6138  *  Set the current selection with undo registration. The right-hand operand may be nil.
6139  *  This operation is undoable.
6140  */
6141 static VALUE
6142 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6143 {
6144         return s_Molecule_SetSelectionSub(self, val, 1);
6145 }
6146
6147 #pragma mark ------ Editing ------
6148
6149 /*
6150  *  call-seq:
6151  *     extract(group, dummy_flag = nil)       -> Molecule
6152  *
6153  *  Extract the atoms given by group and return as a new molecule object.
6154  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6155  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6156  *  names beginning with an underscore) and included in the new molecule object.
6157  */
6158 static VALUE
6159 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6160 {
6161     Molecule *mol1, *mol2;
6162         IntGroup *ig;
6163         VALUE group, dummy_flag, retval;
6164     Data_Get_Struct(self, Molecule, mol1);
6165         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6166         ig = s_Molecule_AtomGroupFromValue(self, group);
6167         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6168                 retval = Qnil;
6169         } else {
6170                 retval = ValueFromMolecule(mol2);
6171         }
6172         IntGroupRelease(ig);
6173         return retval;
6174 }
6175
6176 /*
6177  *  call-seq:
6178  *     add(molecule2)       -> self
6179  *
6180  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6181     conflicts.
6182     This operation is undoable.
6183  */
6184 static VALUE
6185 s_Molecule_Add(VALUE self, VALUE val)
6186 {
6187     Molecule *mol1, *mol2;
6188     Data_Get_Struct(self, Molecule, mol1);
6189         mol2 = MoleculeFromValue(val);
6190         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6191         return self; 
6192 }
6193
6194 /*
6195  *  call-seq:
6196  *     remove(group)       -> Molecule
6197  *
6198  *  The atoms designated by the given group are removed from the molecule.
6199  *  This operation is undoable.
6200  */
6201 static VALUE
6202 s_Molecule_Remove(VALUE self, VALUE group)
6203 {
6204     Molecule *mol1;
6205         IntGroup *ig, *bg;
6206         Int i;
6207         IntGroupIterator iter;
6208
6209     ig = s_Molecule_AtomGroupFromValue(self, group);
6210 /*    Data_Get_Struct(self, Molecule, mol1);
6211         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6212         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6213                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6214         Data_Get_Struct(group, IntGroup, ig); */
6215     Data_Get_Struct(self, Molecule, mol1);
6216     
6217         /*  Remove the bonds between the two fragments  */
6218         /*  (This is necessary for undo to work correctly)  */
6219         IntGroupIteratorInit(ig, &iter);
6220         bg = NULL;
6221         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6222                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6223                 Int j, *cp;
6224                 cp = AtomConnectData(&ap->connect);
6225                 for (j = 0; j < ap->connect.count; j++) {
6226                         int n = cp[j];
6227                         if (!IntGroupLookup(ig, n, NULL)) {
6228                                 /*  bond i-n, i is in ig and n is not  */
6229                                 int k = MoleculeLookupBond(mol1, i, n);
6230                                 if (k >= 0) {
6231                                         if (bg == NULL)
6232                                                 bg = IntGroupNew();
6233                                         IntGroupAdd(bg, k, 1);
6234                                 }
6235                         }
6236                 }
6237         }
6238         IntGroupIteratorRelease(&iter);
6239         if (bg != NULL) {
6240                 /*  Remove bonds  */
6241                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6242                 IntGroupRelease(bg);
6243         }
6244         /*  Remove atoms  */
6245         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6246                 return Qnil;
6247         return self;
6248 }
6249
6250 /*
6251  *  call-seq:
6252  *     create_atom(name, pos = -1)  -> AtomRef
6253  *
6254  *  Create a new atom with the specified name (may contain residue 
6255  *  information) and position (if position is out of range, the atom is appended at
6256  *  the end). Returns the reference to the new atom.
6257  *  This operation is undoable.
6258  */
6259 static VALUE
6260 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6261 {
6262     Molecule *mol;
6263     Int i, pos;
6264         VALUE name, ival;
6265     Atom arec;
6266     AtomRef *aref;
6267         char *p, resName[6], atomName[6];
6268         int resSeq;
6269     Data_Get_Struct(self, Molecule, mol);
6270         rb_scan_args(argc, argv, "02", &name, &ival);
6271         if (ival != Qnil)
6272                 pos = NUM2INT(rb_Integer(ival));
6273         else pos = -1;
6274         if (name != Qnil) {
6275                 p = StringValuePtr(name);
6276                 if (p[0] != 0) {
6277                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6278                         if (atomName[0] == 0)
6279                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6280                 }
6281         } else p = NULL;
6282         if (p == NULL || p[0] == 0) {
6283                 memset(atomName, 0, 4);
6284                 resSeq = -1;
6285         }
6286     memset(&arec, 0, sizeof(arec));
6287     strncpy(arec.aname, atomName, 4);
6288     if (resSeq >= 0) {
6289       strncpy(arec.resName, resName, 4);
6290       arec.resSeq = resSeq;
6291     }
6292         arec.occupancy = 1.0;
6293 //    i = MoleculeCreateAnAtom(mol, &arec);
6294         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6295                 return Qnil;
6296     aref = AtomRefNew(mol, pos);
6297     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6298 }
6299
6300 /*
6301  *  call-seq:
6302  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6303  *
6304  *  Create a new atom with the same attributes (but no bonding information)
6305  *  with the specified atom. Returns the reference to the new atom.
6306  *  This operation is undoable.
6307  */
6308 static VALUE
6309 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6310 {
6311     Molecule *mol;
6312         const Atom *apsrc;
6313     Atom arec;
6314         AtomRef *aref;
6315         VALUE retval, aval, ival;
6316         Int pos;
6317     Data_Get_Struct(self, Molecule, mol);
6318         rb_scan_args(argc, argv, "11", &aval, &ival);
6319         if (FIXNUM_P(aval)) {
6320                 int idx = NUM2INT(aval);
6321                 if (idx < 0 || idx >= mol->natoms)
6322                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6323                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6324         } else {
6325                 apsrc = s_AtomFromValue(aval);
6326         }
6327         if (apsrc == NULL)
6328                 rb_raise(rb_eMolbyError, "bad atom specification");
6329         if (ival != Qnil)
6330                 pos = NUM2INT(rb_Integer(ival));
6331         else pos = -1;
6332         AtomDuplicate(&arec, apsrc);
6333         arec.connect.count = 0;
6334         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6335                 retval = Qnil;
6336         else {
6337                 aref = AtomRefNew(mol, pos);
6338                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6339         }
6340         AtomClean(&arec);
6341         return retval;
6342 }
6343
6344 /*
6345  *  call-seq:
6346  *     create_bond(n1, n2, ...)       -> Integer
6347  *
6348  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6349  *  do nothing for that pair. Returns the number of bonds actually created.
6350  *  This operation is undoable.
6351  */
6352 static VALUE
6353 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6354 {
6355     Molecule *mol;
6356         Int i, j, k, *ip, old_nbonds;
6357         if (argc == 0)
6358                 rb_raise(rb_eMolbyError, "missing arguments");
6359         if (argc % 2 != 0)
6360                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6361     Data_Get_Struct(self, Molecule, mol);
6362         ip = ALLOC_N(Int, argc + 1);
6363         for (i = j = 0; i < argc; i++, j++) {
6364                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6365                 if (i % 2 == 1) {
6366                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6367                                 j -= 2;  /*  This bond is already present: skip it  */
6368                         else {
6369                                 for (k = 0; k < j - 1; k += 2) {
6370                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6371                                                 j -= 2;   /*  The same entry is already in the argument  */
6372                                                 break;
6373                                         }
6374                                 }
6375                         }
6376                 }
6377         }
6378         old_nbonds = mol->nbonds;
6379         if (j > 0) {
6380                 ip[j] = kInvalidIndex;
6381                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6382         } else i = 0;
6383         xfree(ip);
6384         if (i == -1)
6385                 rb_raise(rb_eMolbyError, "atom index out of range");
6386         else if (i == -2)
6387                 rb_raise(rb_eMolbyError, "too many bonds");
6388         else if (i == -3)
6389                 rb_raise(rb_eMolbyError, "duplicate bonds");
6390         else if (i == -5)
6391                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6392         else if (i != 0)
6393                 rb_raise(rb_eMolbyError, "error in creating bonds");
6394         return INT2NUM(mol->nbonds - old_nbonds);
6395 }
6396
6397 /*
6398  *  call-seq:
6399  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6400  *
6401  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6402  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6403  *  This operation is undoable.
6404  */
6405 static VALUE
6406 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6407 {
6408     Molecule *mol;
6409         Int i, j, n[2];
6410         IntGroup *bg;
6411         if (argc == 0)
6412                 rb_raise(rb_eMolbyError, "missing arguments");
6413         if (argc % 2 != 0)
6414                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6415     Data_Get_Struct(self, Molecule, mol);
6416         bg = NULL;
6417         for (i = j = 0; i < argc; i++, j = 1 - j) {
6418                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6419                 if (j == 1) {
6420                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6421                         if (k >= 0) {
6422                                 if (bg == NULL)
6423                                         bg = IntGroupNew();
6424                                 IntGroupAdd(bg, k, 1);
6425                         }
6426                 }
6427         }
6428         if (bg != NULL) {
6429                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6430                 i = IntGroupGetCount(bg);
6431                 IntGroupRelease(bg);
6432         } else i = 0;
6433         return INT2NUM(i);
6434 }
6435
6436 /*
6437  *  call-seq:
6438  *     assign_bond_order(idx, d1)
6439  *     assign_bond_orders(group, [d1, d2, ...])
6440  *
6441  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6442  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6443  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6444  *  (This may change in the future)
6445  *  This operation is undoable.
6446  */
6447 static VALUE
6448 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6449 {
6450     Molecule *mol;
6451         IntGroup *ig;
6452     Data_Get_Struct(self, Molecule, mol);
6453         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6454                 /*  The first form  */
6455                 Int idx = NUM2INT(rb_Integer(idxval));
6456                 Double d1 = NUM2DBL(rb_Float(dval));
6457                 if (idx < 0 || idx >= mol->nbonds)
6458                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6459                 ig = IntGroupNewWithPoints(idx, 1, -1);
6460                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6461                 IntGroupRelease(ig);
6462         } else {
6463                 Int i, n;
6464                 Double *dp;
6465                 ig = IntGroupFromValue(idxval);
6466                 n = IntGroupGetCount(ig);
6467                 if (n == 0)
6468                         rb_raise(rb_eMolbyError, "the bond index is empty");
6469                 dval = rb_ary_to_ary(dval);
6470                 dp = (Double *)calloc(sizeof(Double), n);
6471                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6472                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6473                 }
6474                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6475                 free(dp);
6476                 IntGroupRelease(ig);
6477         }
6478         return self;
6479 }
6480
6481 /*
6482  *  call-seq:
6483  *     get_bond_order(idx) -> Float
6484  *     get_bond_orders(group) -> Array
6485  *
6486  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6487  *  In the second form, the bond orders at the indices in the group are returned as an array.
6488  *  If no bond order information have been assigned, returns nil (the first form)
6489  *  or an empty array (the second form).
6490  */
6491 static VALUE
6492 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6493 {
6494     Molecule *mol;
6495         IntGroup *ig;
6496         Double *dp;
6497         VALUE retval;
6498         Int i, n, numericArg;
6499     Data_Get_Struct(self, Molecule, mol);
6500         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6501                 /*  The first form  */
6502                 Int idx = NUM2INT(rb_Integer(idxval));
6503                 if (idx < 0 || idx >= mol->nbonds)
6504                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6505                 if (mol->bondOrders == NULL)
6506                         return Qnil;
6507                 ig = IntGroupNewWithPoints(idx, 1, -1);
6508                 n = 1;
6509                 numericArg = 1;
6510         } else {
6511                 if (mol->bondOrders == NULL)
6512                         return rb_ary_new();
6513                 ig = IntGroupFromValue(idxval);
6514                 n = IntGroupGetCount(ig);
6515                 if (n == 0)
6516                         rb_raise(rb_eMolbyError, "the bond index is empty");
6517                 numericArg = 0;
6518         }
6519         dp = (Double *)calloc(sizeof(Double), n);
6520         MoleculeGetBondOrders(mol, dp, ig);
6521         if (numericArg)
6522                 retval = rb_float_new(dp[0]);
6523         else {
6524                 retval = rb_ary_new();
6525                 for (i = 0; i < n; i++)
6526                         rb_ary_push(retval, rb_float_new(dp[i]));
6527         }
6528         free(dp);
6529         IntGroupRelease(ig);
6530         return retval;
6531 }
6532
6533 /*
6534  *  call-seq:
6535  *     bond_exist?(idx1, idx2) -> bool
6536  *
6537  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6538  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6539  */
6540 static VALUE
6541 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6542 {
6543         Molecule *mol;
6544         Int idx1, idx2, i;
6545         Atom *ap;
6546         Int *cp;
6547     Data_Get_Struct(self, Molecule, mol);
6548         idx1 = NUM2INT(rb_Integer(ival1));
6549         idx2 = NUM2INT(rb_Integer(ival2));
6550         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6551                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6552         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6553         cp = AtomConnectData(&ap->connect);
6554         for (i = 0; i < ap->connect.count; i++) {
6555                 if (cp[i] == idx2)
6556                         return Qtrue;
6557         }
6558         return Qfalse;
6559 }
6560
6561 /*
6562  *  call-seq:
6563  *     add_angle(n1, n2, n3)       -> Molecule
6564  *
6565  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6566  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6567  *  This operation is undoable.
6568  */
6569 static VALUE
6570 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6571 {
6572         Int n[4];
6573     Molecule *mol;
6574     Data_Get_Struct(self, Molecule, mol);
6575         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6576         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6577         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6578         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6579                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6580         n[3] = kInvalidIndex;
6581         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6582         return self;
6583 }
6584
6585 /*
6586  *  call-seq:
6587  *     remove_angle(n1, n2, n3)       -> Molecule
6588  *
6589  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6590  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6591  *  This operation is undoable.
6592  */
6593 static VALUE
6594 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6595 {
6596         Int n[4];
6597     Molecule *mol;
6598         IntGroup *ig;
6599     Data_Get_Struct(self, Molecule, mol);
6600         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6601         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6602         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6603         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6604                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6605         ig = IntGroupNewWithPoints(n[3], 1, -1);
6606         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6607         IntGroupRelease(ig);
6608         return self;
6609 }
6610
6611 /*
6612  *  call-seq:
6613  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6614  *
6615  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6616  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6617  *  This operation is undoable.
6618  */
6619 static VALUE
6620 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6621 {
6622         Int n[5];
6623     Molecule *mol;
6624     Data_Get_Struct(self, Molecule, mol);
6625         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6626         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6627         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6628         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6629         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6630                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6631         n[4] = kInvalidIndex;
6632         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6633         return self;
6634 }
6635
6636 /*
6637  *  call-seq:
6638  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6639  *
6640  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6641  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6642  *  This operation is undoable.
6643  */
6644 static VALUE
6645 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6646 {
6647         Int n[5];
6648     Molecule *mol;
6649         IntGroup *ig;
6650     Data_Get_Struct(self, Molecule, mol);
6651         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6652         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6653         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6654         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6655         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6656                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6657         ig = IntGroupNewWithPoints(n[4], 1, -1);
6658         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6659         IntGroupRelease(ig);
6660         return self;
6661 }
6662
6663 /*
6664  *  call-seq:
6665  *     add_improper(n1, n2, n3, n4)       -> Molecule
6666  *
6667  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6668  *  not automatically added when a new bond is created, so this method is more useful than
6669  *  the angle/dihedral counterpart.
6670  *  This operation is undoable.
6671  */
6672 static VALUE
6673 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6674 {
6675         Int n[5];
6676     Molecule *mol;
6677     Data_Get_Struct(self, Molecule, mol);
6678         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6679         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6680         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6681         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6682         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6683                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6684         n[4] = kInvalidIndex;
6685         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6686         return self;
6687 }
6688
6689 /*
6690  *  call-seq:
6691  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6692  *     remove_improper(intgroup)             -> Molecule
6693  *
6694  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6695  *  Returns self. Unlike angles and dihedrals, impropers are
6696  *  not automatically added when a new bond is created, so this method is more useful than
6697  *  the angle/dihedral counterpart.
6698  *  This operation is undoable.
6699  */
6700 static VALUE
6701 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6702 {
6703         Int n[5];
6704         VALUE v1, v2, v3, v4;
6705     Molecule *mol;
6706         IntGroup *ig;
6707     Data_Get_Struct(self, Molecule, mol);
6708         if (argc == 1) {
6709                 ig = IntGroupFromValue(argv[0]);
6710         } else {
6711                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6712                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6713                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6714                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6715                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6716                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6717                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6718                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6719         }
6720         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6721         IntGroupRelease(ig);
6722         return self;
6723 }
6724
6725 /*
6726  *  call-seq:
6727  *     assign_residue(group, res)       -> Molecule
6728  *
6729  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6730  *  or "resname.resno". When the residue number is not specified, the residue number of
6731  *  the first atom in the group is used.
6732  *  This operation is undoable.
6733  */
6734 static VALUE
6735 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6736 {
6737     Molecule *mol;
6738         IntGroup *ig;
6739         char *p, *pp, buf[16];
6740         Int resid, n;
6741         Atom *ap;
6742     Data_Get_Struct(self, Molecule, mol);
6743         
6744         /*  Parse the argument res  */
6745         if (FIXNUM_P(res)) {
6746                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6747                 resid = NUM2INT(res);
6748                 buf[0] = 0;
6749         } else {
6750                 p = StringValuePtr(res);
6751                 pp = strchr(p, '.');
6752                 if (pp != NULL) {
6753                         resid = atoi(pp + 1);
6754                         n = pp - p;
6755                 } else {
6756                         resid = -1;
6757                         n = strlen(p);
6758                 }
6759                 if (n > sizeof buf - 1)
6760                         n = sizeof buf - 1;
6761                 strncpy(buf, p, n);
6762                 buf[n] = 0;
6763         }
6764         ig = s_Molecule_AtomGroupFromValue(self, range);
6765         if (ig == NULL || IntGroupGetCount(ig) == 0)
6766                 return Qnil;
6767
6768         if (resid < 0) {
6769                 /*  Use the residue number of the first specified atom  */
6770                 n = IntGroupGetNthPoint(ig, 0);
6771                 if (n >= mol->natoms)
6772                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6773                 ap = ATOM_AT_INDEX(mol->atoms, n);
6774                 resid = ap->resSeq;
6775         }
6776         /*  Change the residue number  */
6777         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6778         /*  Change the residue name if necessary  */
6779         if (buf[0] != 0) {
6780         /*      Int seqs[2];
6781                 seqs[0] = resid;
6782                 seqs[1] = kInvalidIndex; */
6783                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6784         }
6785         IntGroupRelease(ig);
6786         return self;
6787 }
6788
6789 /*
6790  *  call-seq:
6791  *     offset_residue(group, offset)       -> Molecule
6792  *
6793  *  Offset the residue number of the specified atoms. If any of the residue number gets
6794  *  negative, then exception is thrown.
6795  *  This operation is undoable.
6796  */
6797 static VALUE
6798 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6799 {
6800     Molecule *mol;
6801         IntGroup *ig;
6802         int ofs, result;
6803     Data_Get_Struct(self, Molecule, mol);
6804         ig = s_Molecule_AtomGroupFromValue(self, range);
6805         ofs = NUM2INT(offset);
6806         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6807         if (result > 0)
6808                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6809         IntGroupRelease(ig);
6810         return self;
6811 }
6812
6813 /*
6814  *  call-seq:
6815  *     renumber_atoms(array)       -> IntGroup
6816  *
6817  *  Change the order of atoms so that the atoms specified in the array argument appear
6818  *  in this order from the top of the molecule. The atoms that are not included in array
6819  *  are placed after these atoms, and these atoms are returned as an intGroup.
6820  *  This operation is undoable.
6821  */
6822 static VALUE
6823 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6824 {
6825     Molecule *mol;
6826         Int *new2old;
6827         IntGroup *ig;
6828         int i, n;
6829         VALUE *valp, retval;
6830     Data_Get_Struct(self, Molecule, mol);
6831         if (TYPE(array) != T_ARRAY)
6832                 array = rb_funcall(array, rb_intern("to_a"), 0);
6833         n = RARRAY_LEN(array);
6834         valp = RARRAY_PTR(array);
6835         new2old = ALLOC_N(Int, n + 1);
6836         for (i = 0; i < n; i++)
6837                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6838         new2old[i] = kInvalidIndex;
6839         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6840         if (i == 1)
6841                 rb_raise(rb_eMolbyError, "Atom index out of range");
6842         else if (i == 2)
6843                 rb_raise(rb_eMolbyError, "Duplicate entry");
6844         else if (i == 3)
6845                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6846         retval = IntGroup_Alloc(rb_cIntGroup);
6847         Data_Get_Struct(retval, IntGroup, ig);
6848         if (mol->natoms > n)
6849                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6850         xfree(new2old);
6851         return retval;
6852 }
6853
6854 /*
6855  *  call-seq:
6856  *     set_atom_attr(index, key, value)
6857  *
6858  *  Set the atom attribute for the specified atom.
6859  *  This operation is undoable.
6860  */
6861 static VALUE
6862 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6863 {
6864         Molecule *mol;
6865         VALUE aref, oldval;
6866     Data_Get_Struct(self, Molecule, mol);
6867         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6868         oldval = s_AtomRef_GetAttr(aref, key);
6869         if (val == Qundef)
6870                 return oldval;
6871         s_AtomRef_SetAttr(aref, key, val);
6872         return val;
6873 }
6874
6875 /*
6876  *  call-seq:
6877  *     get_atom_attr(index, key)
6878  *
6879  *  Get the atom attribute for the specified atom.
6880  */
6881 static VALUE
6882 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6883 {
6884         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6885 }
6886
6887 #pragma mark ------ Undo Support ------
6888
6889 /*
6890  *  call-seq:
6891  *     register_undo(script, *args)
6892  *
6893  *  Register an undo operation with the current molecule.
6894  */
6895 static VALUE
6896 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6897 {
6898         Molecule *mol;
6899         VALUE script, args;
6900         MolAction *act;
6901     Data_Get_Struct(self, Molecule, mol);
6902         rb_scan_args(argc, argv, "1*", &script, &args);
6903         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6904         MolActionCallback_registerUndo(mol, act);
6905         return script;
6906 }
6907
6908 /*
6909  *  call-seq:
6910  *     undo_enabled? -> bool
6911  *
6912  *  Returns true if undo is enabled for this molecule; otherwise no.
6913  */
6914 static VALUE
6915 s_Molecule_UndoEnabled(VALUE self)
6916 {
6917     Molecule *mol;
6918     Data_Get_Struct(self, Molecule, mol);
6919         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6920                 return Qtrue;
6921         else return Qfalse;
6922 }
6923
6924 /*
6925  *  call-seq:
6926  *     undo_enabled = bool
6927  *
6928  *  Enable or disable undo.
6929  */
6930 static VALUE
6931 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6932 {
6933     Molecule *mol;
6934     Data_Get_Struct(self, Molecule, mol);
6935         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6936         return val;
6937 }
6938
6939 #pragma mark ------ Measure ------
6940
6941 static void
6942 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6943 {
6944         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6945                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6946                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6947                 case 0: break;
6948                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6949         }
6950 }
6951
6952 /*
6953  *  call-seq:
6954  *     center_of_mass(group = nil)       -> Vector3D
6955  *
6956  *  Calculate the center of mass for the given set of atoms. The argument
6957  *  group is null, then all atoms are considered.
6958  */
6959 static VALUE
6960 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6961 {
6962     Molecule *mol;
6963         VALUE group;
6964         IntGroup *ig;
6965         Vector v;
6966     Data_Get_Struct(self, Molecule, mol);
6967         rb_scan_args(argc, argv, "01", &group);
6968         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6969         s_Molecule_DoCenterOfMass(mol, &v, ig);
6970         if (ig != NULL)
6971                 IntGroupRelease(ig);
6972         return ValueFromVector(&v);
6973 }
6974
6975 /*
6976  *  call-seq:
6977  *     centralize(group = nil)       -> self
6978  *
6979  *  Translate the molecule so that the center of mass of the given group is located
6980  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6981  */
6982 static VALUE
6983 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6984 {
6985     Molecule *mol;
6986         VALUE group;
6987         IntGroup *ig;
6988         Vector v;
6989     Data_Get_Struct(self, Molecule, mol);
6990         rb_scan_args(argc, argv, "01", &group);
6991         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6992         s_Molecule_DoCenterOfMass(mol, &v, ig);
6993         if (ig != NULL)
6994                 IntGroupRelease(ig);
6995         v.x = -v.x;
6996         v.y = -v.y;
6997         v.z = -v.z;
6998         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6999         return self;
7000 }
7001
7002 /*
7003  *  call-seq:
7004  *     bounds(group = nil)       -> [min, max]
7005  *
7006  *  Calculate the boundary. The return value is an array of two Vector3D objects.
7007  */
7008 static VALUE
7009 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7010 {
7011     Molecule *mol;
7012         VALUE group;
7013         IntGroup *ig;
7014         Vector vmin, vmax;
7015         int n;
7016         Atom *ap;
7017     Data_Get_Struct(self, Molecule, mol);
7018         rb_scan_args(argc, argv, "01", &group);
7019         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7020         if (ig != NULL && IntGroupGetCount(ig) == 0)
7021                 rb_raise(rb_eMolbyError, "atom group is empty");
7022         vmin.x = vmin.y = vmin.z = 1e30;
7023         vmax.x = vmax.y = vmax.z = -1e30;
7024         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7025                 Vector r;
7026                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7027                         continue;
7028                 r = ap->r;
7029                 if (r.x < vmin.x)
7030                         vmin.x = r.x;
7031                 if (r.y < vmin.y)
7032                         vmin.y = r.y;
7033                 if (r.z < vmin.z)
7034                         vmin.z = r.z;
7035                 if (r.x > vmax.x)
7036                         vmax.x = r.x;
7037                 if (r.y > vmax.y)
7038                         vmax.y = r.y;
7039                 if (r.z > vmax.z)
7040                         vmax.z = r.z;
7041         }
7042         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7043 }
7044
7045 /*  Get atom position or a vector  */
7046 static void
7047 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7048 {
7049         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7050                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7051                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7052         } else {
7053                 VectorFromValue(val, vp);
7054         }
7055 }
7056
7057 /*
7058  *  call-seq:
7059  *     measure_bond(n1, n2)       -> Float
7060  *
7061  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
7062  *  or Vector3D values.
7063  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7064  */
7065 static VALUE
7066 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7067 {
7068     Molecule *mol;
7069         Vector v1, v2;
7070     Data_Get_Struct(self, Molecule, mol);
7071         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7072         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7073         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7074 }
7075
7076 /*
7077  *  call-seq:
7078  *     measure_angle(n1, n2, n3)       -> Float
7079  *
7080  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
7081  *  or Vector3D values. The return value is in degree.
7082  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7083  */
7084 static VALUE
7085 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7086 {
7087     Molecule *mol;
7088         Vector v1, v2, v3;
7089         Double d;
7090     Data_Get_Struct(self, Molecule, mol);
7091         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7092         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7093         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7094         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7095         if (isnan(d))
7096                 return Qnil;  /*  Cannot define  */
7097         else return rb_float_new(d);
7098 }
7099
7100 /*
7101  *  call-seq:
7102  *     measure_dihedral(n1, n2, n3, n4)       -> Float
7103  *
7104  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
7105  *  or Vector3D values. The return value is in degree.
7106  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7107  */
7108 static VALUE
7109 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7110 {
7111     Molecule *mol;
7112         Vector v1, v2, v3, v4;
7113         Double d;
7114     Data_Get_Struct(self, Molecule, mol);
7115         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7116         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7117         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7118         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
7119         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7120         if (isnan(d))
7121                 return Qnil;  /*  Cannot define  */
7122         else return rb_float_new(d);
7123 }
7124
7125 /*
7126  *  call-seq:
7127  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7128  *
7129  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7130  *  first and second atom in the pair should belong to group1 and group2, respectively.
7131  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7132  */
7133 static VALUE
7134 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7135 {
7136     Molecule *mol;
7137         VALUE limval, gval1, gval2, rval, igval;
7138         IntGroup *ig1, *ig2;
7139         IntGroupIterator iter1, iter2;
7140         Int npairs, *pairs;
7141         Int n[2], i;
7142         Double lim;
7143         Vector r1;
7144         Atom *ap1, *ap2;
7145         MDExclusion *exinfo;
7146         Int *exlist;
7147         
7148     Data_Get_Struct(self, Molecule, mol);
7149         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7150         lim = NUM2DBL(rb_Float(limval));
7151         if (lim <= 0.0)
7152                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7153         if (gval1 != Qnil)
7154                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7155         else
7156                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7157         if (gval2 != Qnil)
7158                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7159         else
7160                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7161         
7162         if (!RTEST(igval)) {
7163                 /*  Use the exclusion table in MDArena  */
7164                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7165                         VALUE mval = ValueFromMolecule(mol);
7166                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7167                 }
7168                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7169                 exlist = mol->arena->exlist;    
7170         } else {
7171                 exinfo = NULL;
7172                 exlist = NULL;
7173         }
7174         IntGroupIteratorInit(ig1, &iter1);
7175         IntGroupIteratorInit(ig2, &iter2);
7176         npairs = 0;
7177         pairs = NULL;
7178         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7179                 Int exn1, exn2;
7180                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7181                 r1 = ap1->r;
7182                 if (exinfo != NULL) {
7183                         exn1 = exinfo[n[0]].index1;
7184                         exn2 = exinfo[n[0] + 1].index1;
7185                 } else exn1 = exn2 = -1;
7186                 IntGroupIteratorReset(&iter2);
7187                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7188                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7189                         if (n[0] == n[1])
7190                                 continue;  /*  Same atom  */
7191                         if (exinfo != NULL) {
7192                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7193                                 for (i = exn1; i < exn2; i++) {
7194                                         if (exlist[i] == n[1])
7195                                                 break;
7196                                 }
7197                                 if (i < exn2)
7198                                         continue;  /*  Should be excluded  */
7199                         }
7200                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7201                                 /*  Is this pair already registered?  */
7202                                 Int *ip;
7203                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7204                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7205                                                 break;
7206                                 }
7207                                 if (i >= npairs) {
7208                                         /*  Not registered yet  */
7209                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7210                                 }
7211                         }
7212                 }
7213         }
7214         IntGroupIteratorRelease(&iter2);
7215         IntGroupIteratorRelease(&iter1);
7216         IntGroupRelease(ig2);
7217         IntGroupRelease(ig1);
7218         rval = rb_ary_new2(npairs);
7219         if (pairs != NULL) {
7220                 for (i = 0; i < npairs; i++) {
7221                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7222                 }
7223                 free(pairs);
7224         }
7225         return rval;
7226 }
7227
7228 /*
7229  *  call-seq:
7230  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7231  *
7232  *  Find atoms that are within the threshold distance from the given atom.
7233  *  (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.)
7234  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7235  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7236  *  If limit is not given, a default value of 1.2 is used.
7237  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7238  */
7239 static VALUE
7240 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7241 {
7242     Molecule *mol;
7243         VALUE aval, limval, radval;
7244         double limit, radius;
7245         Int n1, nbonds, *bonds, an;
7246         Vector v;
7247     Data_Get_Struct(self, Molecule, mol);
7248         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7249         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)) {
7250                 VectorFromValue(aval, &v);
7251                 if (radval == Qnil)
7252                         radius = gElementParameters[6].radius;
7253                 else
7254                         radius = NUM2DBL(rb_Float(radval));
7255                 n1 = mol->natoms;
7256         } else {
7257                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7258                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7259                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7260                 if (an >= 0 && an < gCountElementParameters)
7261                         radius = gElementParameters[an].radius;
7262                 else radius = gElementParameters[6].radius;
7263         }
7264         if (limval == Qnil)
7265                 limit = 1.2;
7266         else
7267                 limit = NUM2DBL(rb_Float(limval));
7268         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7269         bonds = NULL;
7270         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7271         aval = rb_ary_new();
7272         if (nbonds > 0) {
7273                 for (n1 = 0; n1 < nbonds; n1++)
7274                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7275                 free(bonds);
7276         }
7277         return aval;
7278 }
7279
7280 /*
7281  *  call-seq:
7282  *     guess_bonds(limit = 1.2)       -> Integer
7283  *
7284  *  Create bonds between atoms that are within the threshold distance.
7285  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7286  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7287  *  If limit is not given, a default value of 1.2 is used.
7288  *  The number of the newly created bonds is returned.
7289  *  This operation is undoable.
7290  */
7291 static VALUE
7292 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7293 {
7294     Molecule *mol;
7295         VALUE limval;
7296         double limit;
7297         Int nbonds, *bonds;
7298     Data_Get_Struct(self, Molecule, mol);
7299         rb_scan_args(argc, argv, "01", &limval);
7300         if (limval == Qnil)
7301                 limit = 1.2;
7302         else
7303                 limit = NUM2DBL(rb_Float(limval));
7304         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7305         if (nbonds > 0) {
7306                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7307                 free(bonds);
7308         }
7309         return INT2NUM(nbonds);
7310 }
7311
7312 #pragma mark ------ Cell and Symmetry ------
7313
7314 /*
7315  *  call-seq:
7316  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7317  *
7318  *  Returns the unit cell parameters. If cell is not set, returns nil.
7319  */
7320 static VALUE
7321 s_Molecule_Cell(VALUE self)
7322 {
7323     Molecule *mol;
7324         int i;
7325         VALUE val;
7326     Data_Get_Struct(self, Molecule, mol);
7327         if (mol->cell == NULL)
7328                 return Qnil;
7329         val = rb_ary_new2(6);
7330         for (i = 0; i < 6; i++)
7331                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7332         if (mol->cell->has_sigma) {
7333                 for (i = 0; i < 6; i++) {
7334                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7335                 }
7336         }
7337         return val;
7338 }
7339
7340 /*
7341  *  call-seq:
7342  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7343  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7344  *
7345  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7346  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7347  This operation is undoable.
7348  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7349  */
7350 static VALUE
7351 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7352 {
7353     Molecule *mol;
7354         VALUE val, cval;
7355         int i, convert_coord, n;
7356         double d[12];
7357     Data_Get_Struct(self, Molecule, mol);
7358         rb_scan_args(argc, argv, "11", &val, &cval);
7359         if (val == Qnil) {
7360                 n = 0;
7361         } else {
7362                 int len;
7363                 val = rb_ary_to_ary(val);
7364                 len = RARRAY_LEN(val);
7365                 if (len >= 12) {
7366                         n = 12;
7367                 } else if (len >= 6) {
7368                         n = 6;
7369                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7370                 for (i = 0; i < n; i++)
7371                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7372         }
7373         convert_coord = (RTEST(cval) ? 1 : 0);
7374         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7375         return val;
7376 }
7377
7378 /*
7379  *  call-seq:
7380  *     box -> [avec, bvec, cvec, origin, flags]
7381  *
7382  *  Get the unit cell information in the form of a periodic bounding box.
7383  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7384  *  Integers which define whether the system is periodic along the axis.
7385  *  If no unit cell is defined, nil is returned.
7386  */
7387 static VALUE
7388 s_Molecule_Box(VALUE self)
7389 {
7390     Molecule *mol;
7391         VALUE v[5], val;
7392     Data_Get_Struct(self, Molecule, mol);
7393         if (mol == NULL || mol->cell == NULL)
7394                 return Qnil;
7395         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7396         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7397         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7398         v[3] = ValueFromVector(&(mol->cell->origin));
7399         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7400         val = rb_ary_new4(5, v);
7401         return val;
7402 }
7403
7404 /*
7405  *  call-seq:
7406  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7407  *     set_box(d, origin = [0, 0, 0])
7408  *     set_box
7409  *
7410  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7411  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7412  as the box vector.
7413  Flags, if present, is a 3-member array of Integers defining whether the system is
7414  periodic along the axis.
7415  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7416  In the second form, an isotropic box with cell-length d is set.
7417  In the third form, the existing box is cleared.
7418  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7419  */
7420 static VALUE
7421 s_Molecule_SetBox(VALUE self, VALUE aval)
7422 {
7423     Molecule *mol;
7424         VALUE v[6];
7425         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7426         Vector vv[3];
7427         Vector origin = {0, 0, 0};
7428         char flags[3];
7429         Double d;
7430         int i, convertCoordinates = 0;
7431     Data_Get_Struct(self, Molecule, mol);
7432         if (aval == Qnil) {
7433                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7434                 return self;
7435         }
7436         aval = rb_ary_to_ary(aval);
7437         for (i = 0; i < 6; i++) {
7438                 if (i < RARRAY_LEN(aval))
7439                         v[i] = (RARRAY_PTR(aval))[i];
7440                 else v[i] = Qnil;
7441         }
7442         if (v[0] == Qnil) {
7443                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7444                 return self;
7445         }
7446         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7447                 d = NUM2DBL(rb_Float(v[0]));
7448                 for (i = 0; i < 3; i++)
7449                         VecScale(vv[i], ax[i], d);
7450                 if (v[1] != Qnil)
7451                         VectorFromValue(v[1], &origin);
7452                 flags[0] = flags[1] = flags[2] = 1;
7453         } else {
7454                 for (i = 0; i < 3; i++) {
7455                         if (v[i] == Qnil) {
7456                                 VecZero(vv[i]);
7457                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7458                                 d = NUM2DBL(rb_Float(v[i]));
7459                                 VecScale(vv[i], ax[i], d);
7460                         } else {
7461                                 VectorFromValue(v[i], &vv[i]);
7462                         }
7463                         flags[i] = (VecLength2(vv[i]) > 0.0);
7464                 }
7465                 if (v[3] != Qnil)
7466                         VectorFromValue(v[3], &origin);
7467                 if (v[4] != Qnil) {
7468                         for (i = 0; i < 3; i++) {
7469                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7470                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7471                         }
7472                 }
7473                 if (RTEST(v[5]))
7474                         convertCoordinates = 1;
7475         }
7476         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7477         return self;
7478 }
7479
7480 /*
7481  *  call-seq:
7482  *     cell_periodicity -> [n1, n2, n3]
7483  *
7484  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7485  *  nil is returned.
7486  */
7487 static VALUE
7488 s_Molecule_CellPeriodicity(VALUE self)
7489 {
7490     Molecule *mol;
7491     Data_Get_Struct(self, Molecule, mol);
7492         if (mol->cell == NULL)
7493                 return Qnil;
7494         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7495 }
7496
7497 /*
7498  *  call-seq:
7499  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7500  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7501  *
7502  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7503  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7504  *  If cell is not defined, exception is raised.
7505  *  This operation is undoable.
7506  */
7507 static VALUE
7508 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7509 {
7510     Molecule *mol;
7511         Int flag;
7512     Data_Get_Struct(self, Molecule, mol);
7513         if (mol->cell == NULL)
7514                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7515         if (arg == Qnil)
7516                 flag = 0;
7517         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7518                 flag = NUM2INT(rb_Integer(arg));
7519         else {
7520                 Int i;
7521                 VALUE arg0;
7522                 arg = rb_ary_to_ary(arg);
7523                 flag = 0;
7524                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7525                         arg0 = RARRAY_PTR(arg)[i];
7526                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7527                                 flag |= (1 << (2 - i));
7528                 }
7529         }
7530         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7531         return arg;
7532 }
7533
7534 /*
7535  *  call-seq:
7536  *     cell_flexibility -> bool
7537  *
7538  *  Returns the unit cell is flexible or not
7539  */
7540 static VALUE
7541 s_Molecule_CellFlexibility(VALUE self)
7542 {
7543         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7544         return Qtrue;
7545         /*    Molecule *mol;
7546          Data_Get_Struct(self, Molecule, mol);
7547          if (mol->cell == NULL)
7548          return Qfalse;
7549          if (mol->useFlexibleCell)
7550          return Qtrue;
7551          else return Qfalse; */
7552 }
7553
7554 /*
7555  *  call-seq:
7556  *     self.cell_flexibility = bool
7557  *     set_cell_flexibility(bool)
7558  *
7559  *  Change the unit cell is flexible or not
7560  */
7561 static VALUE
7562 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7563 {
7564         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7565         return self;
7566         /*    Molecule *mol;
7567          Data_Get_Struct(self, Molecule, mol);
7568          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7569          return self; */
7570 }
7571
7572 /*
7573  *  call-seq:
7574  *     cell_transform -> Transform
7575  *
7576  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7577  *  If cell is not defined, nil is returned.
7578  */
7579 static VALUE
7580 s_Molecule_CellTransform(VALUE self)
7581 {
7582     Molecule *mol;
7583     Data_Get_Struct(self, Molecule, mol);
7584         if (mol == NULL || mol->cell == NULL)
7585                 return Qnil;
7586         return ValueFromTransform(&(mol->cell->tr));
7587 }
7588
7589 /*
7590  *  call-seq:
7591  *     symmetry -> Array of Transforms
7592  *     symmetries -> Array of Transforms
7593  *
7594  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7595  *  returns an empty array.
7596  */
7597 static VALUE
7598 s_Molecule_Symmetry(VALUE self)
7599 {
7600     Molecule *mol;
7601         VALUE val;
7602         int i;
7603     Data_Get_Struct(self, Molecule, mol);
7604         if (mol->nsyms <= 0)
7605                 return rb_ary_new();
7606         val = rb_ary_new2(mol->nsyms);
7607         for (i = 0; i < mol->nsyms; i++) {
7608                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7609         }
7610         return val;
7611 }
7612
7613 /*
7614  *  call-seq:
7615  *     nsymmetries -> Integer
7616  *
7617  *  Get the number of currently defined symmetry operations.
7618  */
7619 static VALUE
7620 s_Molecule_Nsymmetries(VALUE self)
7621 {
7622     Molecule *mol;
7623     Data_Get_Struct(self, Molecule, mol);
7624         return INT2NUM(mol->nsyms);
7625 }
7626
7627 /*
7628  *  call-seq:
7629  *     add_symmetry(Transform) -> Integer
7630  *
7631  *  Add a new symmetry operation. If no symmetry operation is defined and the
7632  *  given argument is not an identity transform, then also add an identity
7633  *  transform at the index 0.
7634  *  Returns the total number of symmetries after operation.
7635  */
7636 static VALUE
7637 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7638 {
7639     Molecule *mol;
7640         Transform tr;
7641     Data_Get_Struct(self, Molecule, mol);
7642         TransformFromValue(trans, &tr);
7643         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7644         return INT2NUM(mol->nsyms);
7645 }
7646
7647 /*
7648  *  call-seq:
7649  *     remove_symmetry(count = nil) -> Integer
7650  *     remove_symmetries(count = nil) -> Integer
7651  *
7652  *  Remove the specified number of symmetry operations. The last added ones are removed
7653  *  first. If count is nil, then all symmetry operations are removed. Returns the
7654  *  number of leftover symmetries.
7655  */
7656 static VALUE
7657 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7658 {
7659     Molecule *mol;
7660         VALUE cval;
7661         int i, n;
7662     Data_Get_Struct(self, Molecule, mol);
7663         rb_scan_args(argc, argv, "01", &cval);
7664         if (cval == Qnil)
7665                 n = mol->nsyms - 1;
7666         else {
7667                 n = NUM2INT(rb_Integer(cval));
7668                 if (n < 0 || n > mol->nsyms)
7669                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7670                 if (n == mol->nsyms)
7671                         n = mol->nsyms - 1;
7672         }
7673         for (i = 0; i < n; i++)
7674                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7675         return INT2NUM(mol->nsyms);
7676 }
7677
7678 /*
7679  *  call-seq:
7680  *     wrap_unit_cell(group) -> Vector3D
7681  *
7682  *  Move the specified group so that the center of mass of the group is within the
7683  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7684  *  exception is raised.
7685  */
7686 static VALUE
7687 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7688 {
7689     Molecule *mol;
7690         IntGroup *ig;
7691         Vector v, cv, dv;
7692     Data_Get_Struct(self, Molecule, mol);
7693         if (mol->cell == NULL)
7694                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7695         ig = s_Molecule_AtomGroupFromValue(self, gval);
7696         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7697         TransformVec(&v, mol->cell->rtr, &cv);
7698         if (mol->cell->flags[0])
7699                 v.x -= floor(v.x);
7700         if (mol->cell->flags[1])
7701                 v.y -= floor(v.y);
7702         if (mol->cell->flags[2])
7703                 v.z -= floor(v.z);
7704         TransformVec(&dv, mol->cell->tr, &v);
7705         VecDec(dv, cv);
7706         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7707         IntGroupRelease(ig);
7708         return ValueFromVector(&dv);
7709 }
7710
7711 /*
7712  *  call-seq:
7713  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7714  *
7715  *  Expand the specified part of the molecule by the given symmetry operation.
7716  *  Returns the array of atom indices corresponding to the expanded atoms.
7717  *  If allow_overlap is true, then new atoms are created even when the
7718  *  coordinates coincide with the some other atom (special position) of the
7719  *  same element; otherwise, such atom will not be created and the index of the
7720  *  existing atom is given in the returned array.
7721  */
7722 static VALUE
7723 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7724 {
7725     Molecule *mol;
7726         VALUE gval, sval, xval, yval, zval, rval, oval;
7727         IntGroup *ig;
7728         Int n[4], allow_overlap;
7729         Int natoms;
7730         Int nidx, *idx;
7731         
7732     Data_Get_Struct(self, Molecule, mol);
7733         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7734         n[0] = NUM2INT(rb_Integer(sval));
7735         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7736         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7737         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7738         allow_overlap = (RTEST(oval) ? 1 : 0);
7739         ig = s_Molecule_AtomGroupFromValue(self, gval);
7740         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7741                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7742         natoms = mol->natoms;
7743         
7744         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7745         
7746         rval = rb_ary_new2(nidx);
7747         while (--nidx >= 0) {
7748                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7749         }
7750         /*      if (natoms == mol->natoms)
7751          rval = Qnil;
7752          else {
7753          rval = IntGroup_Alloc(rb_cIntGroup);
7754          Data_Get_Struct(rval, IntGroup, ig);
7755          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7756          } */
7757         return rval;
7758 }
7759
7760 /*
7761  *  call-seq:
7762  *     amend_by_symmetry(group = nil) -> IntGroup
7763  *
7764  *  Expand the specified part of the molecule by the given symmetry operation.
7765  *  Returns an IntGroup containing the added atoms.
7766  */
7767 static VALUE
7768 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7769 {
7770     Molecule *mol;
7771         IntGroup *ig, *ig2;
7772         VALUE rval, gval;
7773     Data_Get_Struct(self, Molecule, mol);
7774         rb_scan_args(argc, argv, "01", &gval);
7775         if (gval != Qnil)
7776                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7777         else ig = NULL;
7778         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7779         rval = ValueFromIntGroup(ig2);
7780         IntGroupRelease(ig2);
7781         return rval;
7782 }
7783
7784 #pragma mark ------ Transforms ------
7785
7786 /*
7787  *  call-seq:
7788  *     translate(vec, group = nil)       -> Molecule
7789  *
7790  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7791  *  This operation is undoable.
7792  */
7793 static VALUE
7794 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7795 {
7796     Molecule *mol;
7797         VALUE vec, group;
7798         Vector v;
7799         IntGroup *ig;
7800     Data_Get_Struct(self, Molecule, mol);
7801         rb_scan_args(argc, argv, "11", &vec, &group);
7802         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7803         VectorFromValue(vec, &v);
7804         //      MoleculeTranslate(mol, &v, ig);
7805         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7806         if (ig != NULL)
7807                 IntGroupRelease(ig);
7808         return self;
7809 }
7810
7811 /*
7812  *  call-seq:
7813  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7814  *
7815  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7816  *  If group is given, only atoms in the group are moved.
7817  *  This operation is undoable.
7818  */
7819 static VALUE
7820 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7821 {
7822     Molecule *mol;
7823         volatile VALUE aval, anval, cval, gval;
7824         Double angle;
7825         Vector av, cv;
7826         Transform tr;
7827         IntGroup *ig;
7828     Data_Get_Struct(self, Molecule, mol);
7829         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7830         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7831         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7832         VectorFromValue(aval, &av);
7833         if (NIL_P(cval))
7834                 cv.x = cv.y = cv.z = 0.0;
7835         else
7836                 VectorFromValue(cval, &cv);
7837         if (TransformForRotation(tr, &av, angle, &cv))
7838                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7839         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7840         if (ig != NULL)
7841                 IntGroupRelease(ig);
7842         return self;
7843 }
7844
7845 /*
7846  *  call-seq:
7847  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7848  *
7849  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7850  *  axis must not be a zero vector.
7851  *  If group is given, only atoms in the group are moved.
7852  *  This operation is undoable.
7853  */
7854 static VALUE
7855 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7856 {
7857     Molecule *mol;
7858         volatile VALUE aval, cval, gval;
7859         Vector av, cv;
7860         Transform tr;
7861         IntGroup *ig;
7862     Data_Get_Struct(self, Molecule, mol);
7863         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7864         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7865         VectorFromValue(aval, &av);
7866         if (NIL_P(cval))
7867                 cv.x = cv.y = cv.z = 0.0;
7868         else
7869                 VectorFromValue(cval, &cv);
7870         if (TransformForReflection(tr, &av, &cv))
7871                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7872         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7873         if (ig != NULL)
7874                 IntGroupRelease(ig);
7875         return self;
7876 }
7877
7878 /*
7879  *  call-seq:
7880  *     invert(center = [0,0,0], group = nil)       -> Molecule
7881  *
7882  *  Invert the molecule with the given center.
7883  *  If group is given, only atoms in the group are moved.
7884  *  This operation is undoable.
7885  */
7886 static VALUE
7887 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7888 {
7889         Molecule *mol;
7890         volatile VALUE cval, gval;
7891         Vector cv;
7892         Transform tr;
7893         IntGroup *ig;
7894     Data_Get_Struct(self, Molecule, mol);
7895         rb_scan_args(argc, argv, "02", &cval, &gval);
7896         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7897         if (NIL_P(cval))
7898                 cv.x = cv.y = cv.z = 0.0;
7899         else
7900                 VectorFromValue(cval, &cv);
7901         TransformForInversion(tr, &cv);
7902         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7903         if (ig != NULL)
7904                 IntGroupRelease(ig);
7905         return self;
7906 }
7907
7908 /*
7909  *  call-seq:
7910  *     transform(transform, group = nil)       -> Molecule
7911  *
7912  *  Transform the molecule by the given Transform object.
7913  *  If group is given, only atoms in the group are moved.
7914  *  This operation is undoable.
7915  */
7916 static VALUE
7917 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7918 {
7919     Molecule *mol;
7920         VALUE trans, group;
7921         Transform tr;
7922         IntGroup *ig;
7923     Data_Get_Struct(self, Molecule, mol);
7924         rb_scan_args(argc, argv, "11", &trans, &group);
7925         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7926         TransformFromValue(trans, &tr);
7927         /*      MoleculeTransform(mol, tr, ig); */
7928         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7929         if (ig != NULL)
7930                 IntGroupRelease(ig);
7931         return self;
7932 }
7933
7934 /*
7935  *  call-seq:
7936  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7937  *
7938  *  Get the transform corresponding to the symmetry operation. The symop can either be
7939  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7940  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7941  *  Otherwise, the returned transform is for fractional coordinates.
7942  *  Raises exception when no cell or no transform are defined.
7943  */
7944 static VALUE
7945 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7946 {
7947     Molecule *mol;
7948         VALUE sval, fval;
7949         Symop symop;
7950         Transform tr;
7951     Data_Get_Struct(self, Molecule, mol);
7952         if (mol->cell == NULL)
7953                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7954         if (mol->nsyms == 0)
7955                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7956         rb_scan_args(argc, argv, "11", &sval, &fval);
7957         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7958                 symop.sym = NUM2INT(rb_Integer(sval));
7959                 symop.dx = symop.dy = symop.dz = 0;
7960         } else {
7961                 sval = rb_ary_to_ary(sval);
7962                 if (RARRAY_LEN(sval) < 4)
7963                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7964                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7965                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7966                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7967                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7968         }
7969         if (symop.sym >= mol->nsyms)
7970                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7971         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7972         return ValueFromTransform(&tr);
7973 }
7974
7975 /*
7976  *  call-seq:
7977  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7978  *
7979  *  Get the symmetry operation corresponding to the given transform.
7980  *  If is_cartesian is true, the given transform is for cartesian coordinates.
7981  *  Otherwise, the given transform is for fractional coordinates.
7982  *  Raises exception when no cell or no transform are defined.
7983  */
7984 static VALUE
7985 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7986 {
7987     Molecule *mol;
7988         VALUE tval, fval;
7989         Symop symop;
7990         Transform tr;
7991         int n;
7992     Data_Get_Struct(self, Molecule, mol);
7993         if (mol->cell == NULL)
7994                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7995         if (mol->nsyms == 0)
7996                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7997         rb_scan_args(argc, argv, "11", &tval, &fval);
7998         TransformFromValue(tval, &tr);
7999         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8000         if (n == 0) {
8001                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8002         } else {
8003                 return Qnil;  /*  Not found  */
8004         }
8005 }
8006
8007 #pragma mark ------ Frames ------
8008
8009 /*
8010  *  call-seq:
8011  *     select_frame(index)
8012  *     frame = index
8013  *
8014  *  Select the specified frame. If successful, returns true, otherwise returns false.
8015  */
8016 static VALUE
8017 s_Molecule_SelectFrame(VALUE self, VALUE val)
8018 {
8019     Molecule *mol;
8020         int ival = NUM2INT(val);
8021     Data_Get_Struct(self, Molecule, mol);
8022         ival = MoleculeSelectFrame(mol, ival, 1);
8023         if (ival >= 0)
8024                 return Qtrue;
8025         else return Qfalse;
8026 }
8027
8028 /*
8029  *  call-seq:
8030  *     frame -> Integer
8031  *
8032  *  Get the current frame.
8033  */
8034 static VALUE
8035 s_Molecule_Frame(VALUE self)
8036 {
8037     Molecule *mol;
8038     Data_Get_Struct(self, Molecule, mol);
8039         return INT2NUM(mol->cframe);
8040 }
8041
8042 /*
8043  *  call-seq:
8044  *     nframes -> Integer
8045  *
8046  *  Get the number of frames.
8047  */
8048 static VALUE
8049 s_Molecule_Nframes(VALUE self)
8050 {
8051     Molecule *mol;
8052     Data_Get_Struct(self, Molecule, mol);
8053         return INT2NUM(MoleculeGetNumberOfFrames(mol));
8054 }
8055
8056 /*
8057  *  call-seq:
8058  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8059  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8060  *
8061  *  Insert new frames at the indices specified by the intGroup. If the first argument is
8062  *  an integer, a single new frame is inserted at that index. If the first argument is 
8063  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8064  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
8065  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
8066  *  to the new frame.
8067  *  Returns an intGroup representing the inserted frames if successful, nil if not.
8068  */
8069 static VALUE
8070 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8071 {
8072         VALUE val, coords, cells;
8073     Molecule *mol;
8074         IntGroup *ig;
8075         int count, ival, i, j, len, len_c, len2, nframes;
8076         VALUE *ptr, *ptr2;
8077         Vector *vp, *vp2;
8078     Data_Get_Struct(self, Molecule, mol);
8079         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8080         if (coords != Qnil) {
8081                 if (TYPE(coords) != T_ARRAY)
8082                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8083                 len = RARRAY_LEN(coords);
8084         } else len = 0;
8085         if (cells != Qnil) {
8086                 if (mol->cell == NULL)
8087                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8088                 if (TYPE(cells) != T_ARRAY)
8089                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8090                 len_c = RARRAY_LEN(cells);
8091         } else len_c = 0;
8092         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
8093         nframes = MoleculeGetNumberOfFrames(mol);
8094         if (val == Qnil) {
8095                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8096                 val = ValueFromIntGroup(ig);
8097         } else {
8098                 ig = IntGroupFromValue(val);
8099         }
8100         count = IntGroupGetCount(ig);  /*  Count is updated here  */
8101         vp = ALLOC_N(Vector, mol->natoms * count);
8102         if (cells != Qnil)
8103                 vp2 = ALLOC_N(Vector, 4 * count);
8104         else vp2 = NULL;
8105         if (len > 0) {
8106                 if (len < count)
8107                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8108                 ptr = RARRAY_PTR(coords);
8109                 for (i = 0; i < count; i++) {
8110                         if (TYPE(ptr[i]) != T_ARRAY)
8111                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8112                         len2 = RARRAY_LEN(ptr[i]);
8113                         if (len2 < mol->natoms)
8114                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8115                         ptr2 = RARRAY_PTR(ptr[i]);
8116                         for (j = 0; j < mol->natoms; j++)
8117                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8118                 }
8119         } else {
8120                 Atom *ap;
8121                 for (i = 0; i < count; i++) {
8122                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8123                                 vp[i * mol->natoms + j] = ap->r;
8124                         }
8125                 }
8126         }
8127         if (len_c > 0) {
8128                 if (len_c < count)
8129                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8130                 ptr = RARRAY_PTR(cells);
8131                 for (i = 0; i < count; i++) {
8132                         if (TYPE(ptr[i]) != T_ARRAY)
8133                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8134                         len2 = RARRAY_LEN(ptr[i]);
8135                         if (len2 < 4)
8136                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8137                         ptr2 = RARRAY_PTR(ptr[i]);
8138                         for (j = 0; j < 4; j++)
8139                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8140                 }
8141         }
8142         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8143         IntGroupRelease(ig);
8144         xfree(vp);
8145         if (vp2 != NULL)
8146                 xfree(vp2);
8147         return (ival >= 0 ? val : Qnil);
8148 }
8149
8150 /*
8151  *  call-seq:
8152  *     create_frame(coordinates = nil) -> Integer
8153  *     create_frames(coordinates = nil) -> Integer
8154  *
8155  *  Same as molecule.insert_frames(nil, coordinates).
8156  */
8157 static VALUE
8158 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8159 {
8160         VALUE vals[3];
8161         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8162         vals[0] = Qnil;
8163         return s_Molecule_InsertFrames(3, vals, self);
8164 }
8165
8166 /*
8167  *  call-seq:
8168  *     remove_frames(IntGroup, wantCoordinates = false)
8169  *
8170  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8171  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8172  *  removed frames is returned if operation is successful.
8173  */
8174 static VALUE
8175 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8176 {
8177         VALUE val, flag;
8178         VALUE retval;
8179     Molecule *mol;
8180         IntGroup *ig;
8181         int count;
8182     Data_Get_Struct(self, Molecule, mol);
8183         rb_scan_args(argc, argv, "11", &val, &flag);
8184         ig = IntGroupFromValue(val);
8185         count = IntGroupGetCount(ig);
8186         if (RTEST(flag)) {
8187                 /*  Create return value before removing frames  */
8188                 VALUE coords;
8189                 int i, j, n;
8190                 Atom *ap;
8191                 Vector v;
8192                 retval = rb_ary_new2(count);
8193                 for (i = 0; i < count; i++) {
8194                         n = IntGroupGetNthPoint(ig, i);
8195                         coords = rb_ary_new2(mol->natoms);
8196                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8197                                 if (n < ap->nframes && n != mol->cframe)
8198                                         v = ap->frames[n];
8199                                 else v = ap->r;
8200                                 rb_ary_push(coords, ValueFromVector(&v));
8201                         }
8202                         rb_ary_push(retval, coords);
8203                 }
8204         } else retval = Qtrue;
8205         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8206                 return retval;
8207         else return Qnil;
8208 }
8209
8210 /*
8211  *  call-seq:
8212  *     each_frame {|n| ...}
8213  *
8214  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8215  *  the frame number. After completion, the original frame number is restored.
8216  */
8217 static VALUE
8218 s_Molecule_EachFrame(VALUE self)
8219 {
8220         int i, cframe, nframes;
8221     Molecule *mol;
8222     Data_Get_Struct(self, Molecule, mol);
8223         cframe = mol->cframe;
8224         nframes = MoleculeGetNumberOfFrames(mol);
8225         if (nframes > 0) {
8226                 for (i = 0; i < nframes; i++) {
8227                         MoleculeSelectFrame(mol, i, 1);
8228                         rb_yield(INT2NUM(i));
8229                 }
8230                 MoleculeSelectFrame(mol, cframe, 1);
8231         }
8232     return self;
8233 }
8234
8235 /*
8236  *  call-seq:
8237  *     get_coord_from_frame(index, group = nil)
8238  *
8239  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8240  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8241  *  copied; now they are always copied)
8242  */
8243 static VALUE
8244 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8245 {
8246         Molecule *mol;
8247         VALUE ival, gval, cval;
8248         Int index, i, j, n, nn;
8249         IntGroup *ig;
8250         IntGroupIterator iter;
8251         Atom *ap;
8252         Vector *vp;
8253     Data_Get_Struct(self, Molecule, mol);
8254         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8255         if (argc == 3)
8256                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8257         index = NUM2INT(rb_Integer(ival));
8258         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8259                 if (n == 0)
8260                         rb_raise(rb_eMolbyError, "No frame is present");
8261                 else
8262                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8263         }
8264         if (gval == Qnil) {
8265                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8266         } else {
8267                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8268         }
8269         n = IntGroupGetCount(ig);
8270         if (n > 0) {
8271                 vp = (Vector *)calloc(sizeof(Vector), n);
8272                 IntGroupIteratorInit(ig, &iter);
8273                 j = 0;
8274                 nn = 0;
8275                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8276                         ap = ATOM_AT_INDEX(mol->atoms, i);
8277                         if (index < ap->nframes) {
8278                                 vp[j] = ap->frames[index];
8279                                 nn++;
8280                         } else {
8281                                 vp[j] = ap->r;
8282                         }
8283                         j++;
8284                 }
8285                 if (nn > 0)
8286                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8287                 free(vp);
8288                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8289                         vp = mol->frame_cells + index * 4;
8290                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8291                 }
8292                 IntGroupIteratorRelease(&iter);
8293         }
8294         /*  Copy the extra properties  */
8295         IntGroupRelease(ig);
8296         for (i = 0; i < mol->nmolprops; i++) {
8297                 Double *dp = (Double *)malloc(sizeof(Double));
8298                 ig = IntGroupNew();
8299                 IntGroupAdd(ig, mol->cframe, 1);
8300                 *dp = mol->molprops[i].propvals[index];
8301                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8302                 free(dp);
8303                 IntGroupRelease(ig);
8304         }
8305         
8306         return self;
8307 }
8308
8309 /*
8310  *  call-seq:
8311  *     reorder_frames(old_indices)
8312  *
8313  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8314  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8315  *  same as the old frames 2/0/1, respectively.
8316  *  The argument must have the same number of integers as the number of frames.
8317  */
8318 static VALUE
8319 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8320 {
8321         Molecule *mol;
8322         Int *ip, *ip2, i, n, nframes;
8323     Data_Get_Struct(self, Molecule, mol);
8324         aval = rb_ary_to_ary(aval);
8325         nframes = MoleculeGetNumberOfFrames(mol);
8326         if (RARRAY_LEN(aval) != nframes)
8327                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8328         ip2 = (Int *)calloc(sizeof(Int), nframes);
8329         ip = (Int *)calloc(sizeof(Int), nframes);
8330         for (i = 0; i < nframes; i++) {
8331                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8332                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8333                         free(ip2);
8334                         free(ip);
8335                         if (n < 0 || n >= nframes)
8336                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8337                         else
8338                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8339                 }
8340                 ip2[n] = 1;
8341                 ip[i] = n;
8342         }
8343         free(ip2);
8344         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8345         free(ip);
8346         return self;
8347 }
8348
8349 #pragma mark ------ Fragments ------
8350
8351 /*
8352  *  call-seq:
8353  *     fragment(n1, *exatoms)  -> IntGroup
8354  *     fragment(group, *exatoms)  -> IntGroup
8355  *
8356  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8357  *  those atoms will not be counted during the search.
8358  */
8359 static VALUE
8360 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8361 {
8362     Molecule *mol;
8363         IntGroup *baseg, *ig, *exatoms;
8364         int n;
8365         volatile VALUE nval, exval;
8366     Data_Get_Struct(self, Molecule, mol);
8367         rb_scan_args(argc, argv, "1*", &nval, &exval);
8368         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8369                 baseg = NULL;
8370                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8371         } else {
8372                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8373         }
8374         if (RARRAY_LEN(exval) == 0) {
8375                 exatoms = NULL;
8376         } else {
8377                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8378                 Data_Get_Struct(exval, IntGroup, exatoms);
8379         }
8380         if (baseg == NULL) {
8381                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8382         } else {
8383                 IntGroupIterator iter;
8384                 IntGroupIteratorInit(baseg, &iter);
8385                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8386                         ig = IntGroupNew();
8387                 } else {
8388                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8389                         if (ig != NULL) {
8390                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8391                                         IntGroup *subg;
8392                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8393                                         if (subg != NULL) {
8394                                                 IntGroupAddIntGroup(ig, subg);
8395                                                 IntGroupRelease(subg);
8396                                         }
8397                                 }
8398                         }
8399                 }
8400                 IntGroupIteratorRelease(&iter);
8401                 IntGroupRelease(baseg);
8402         }
8403         if (ig == NULL)
8404                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8405         nval = ValueFromIntGroup(ig);
8406         IntGroupRelease(ig);
8407         return nval;
8408 }
8409
8410 /*
8411  *  call-seq:
8412  *     fragments(exclude = nil)
8413  *
8414  *  Returns the fragments as an array of IntGroups. 
8415  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8416  *  in defining the fragment.
8417  */
8418 static VALUE
8419 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8420 {
8421     Molecule *mol;
8422         IntGroup *ag, *fg, *eg;
8423         VALUE gval, exval, retval;
8424     Data_Get_Struct(self, Molecule, mol);
8425         if (mol == NULL)
8426                 return Qnil;
8427         if (mol->natoms == 0)
8428                 return rb_ary_new();
8429         rb_scan_args(argc, argv, "01", &exval);
8430         if (exval == Qnil)
8431                 eg = NULL;
8432         else
8433                 eg = IntGroupFromValue(exval);
8434         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8435         if (eg != NULL)
8436                 IntGroupRemoveIntGroup(ag, eg);
8437         retval = rb_ary_new();
8438         while (IntGroupGetCount(ag) > 0) {
8439                 int n = IntGroupGetNthPoint(ag, 0);
8440                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8441                 if (fg == NULL)
8442                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8443                 gval = ValueFromIntGroup(fg);
8444                 rb_ary_push(retval, gval);
8445                 IntGroupRemoveIntGroup(ag, fg);
8446                 IntGroupRelease(fg);
8447         }
8448         IntGroupRelease(ag);
8449         if (eg != NULL)
8450                 IntGroupRelease(eg);
8451         return retval;
8452 }
8453
8454 /*
8455  *  call-seq:
8456  *     each_fragment(exclude = nil) {|group| ...}
8457  *
8458  *  Execute the block, with the IntGroup object for each fragment as the argument.
8459  *  Atoms or bonds should not be added or removed during the execution of the block.
8460  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8461  *  in defining the fragment.
8462  */
8463 static VALUE
8464 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8465 {
8466     Molecule *mol;
8467         IntGroup *ag, *fg, *eg;
8468         VALUE gval, exval;
8469     Data_Get_Struct(self, Molecule, mol);
8470         if (mol == NULL || mol->natoms == 0)
8471                 return self;
8472         rb_scan_args(argc, argv, "01", &exval);
8473         if (exval == Qnil)
8474                 eg = NULL;
8475         else
8476                 eg = IntGroupFromValue(exval);
8477         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8478         if (eg != NULL)
8479                 IntGroupRemoveIntGroup(ag, eg);
8480         while (IntGroupGetCount(ag) > 0) {
8481                 int n = IntGroupGetNthPoint(ag, 0);
8482                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8483                 if (fg == NULL)
8484                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8485                 gval = ValueFromIntGroup(fg);
8486                 rb_yield(gval);
8487                 IntGroupRemoveIntGroup(ag, fg);
8488                 IntGroupRelease(fg);
8489         }
8490         IntGroupRelease(ag);
8491         if (eg != NULL)
8492                 IntGroupRelease(eg);
8493         return self;
8494 }
8495
8496 /*
8497  *  call-seq:
8498  *     detachable?(group)  -> [n1, n2]
8499  *
8500  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8501  *  of the molecule via only one bond. If it is, then the indices of the atoms
8502  *  belonging to the bond is returned, the first element being the atom included
8503  *  in the fragment. Otherwise, Qnil is returned.
8504  */
8505 static VALUE
8506 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8507 {
8508         Molecule *mol;
8509         IntGroup *ig;
8510         int n1, n2;
8511         VALUE retval;
8512     Data_Get_Struct(self, Molecule, mol);
8513         ig = s_Molecule_AtomGroupFromValue(self, gval);
8514         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8515                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8516         } else retval = Qnil;
8517         IntGroupRelease(ig);
8518         return retval;
8519 }
8520
8521 /*
8522  *  call-seq:
8523  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8524  *
8525  *  Returns an array of bonds that connect an atom in the group and an atom out
8526  *  of the group. The first atom in the bond always belongs to the group. If no
8527  *  such bonds are present, an empty array is returned.
8528  */
8529 static VALUE
8530 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8531 {
8532         Molecule *mol;
8533         IntGroup *ig, *bg;
8534         VALUE gval, retval;
8535     Data_Get_Struct(self, Molecule, mol);
8536         rb_scan_args(argc, argv, "01", &gval);
8537         if (gval == Qnil) {
8538                 ig = MoleculeGetSelection(mol);
8539                 if (ig != NULL)
8540                         IntGroupRetain(ig);
8541         } else {
8542                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8543         }
8544         retval = rb_ary_new();
8545         if (ig == NULL)
8546                 return retval;
8547         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8548         if (bg != NULL) {
8549                 IntGroupIterator iter;
8550                 Int i;
8551                 IntGroupIteratorInit(bg, &iter);
8552                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8553                         /*  The atoms at the border  */
8554                         Int n1, n2;
8555                         n1 = mol->bonds[i * 2];
8556                         n2 = mol->bonds[i * 2 + 1];
8557                         if (IntGroupLookupPoint(ig, n1) < 0) {
8558                                 int w = n1;
8559                                 n1 = n2;
8560                                 n2 = w;
8561                                 if (IntGroupLookupPoint(ig, n1) < 0)
8562                                         continue;  /*  Actually this is an internal error  */
8563                         }
8564                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8565                 }
8566                 IntGroupIteratorRelease(&iter);
8567         }
8568         IntGroupRelease(bg);
8569         IntGroupRelease(ig);
8570         return retval;
8571 }
8572
8573 /*  Calculate the transform that moves the current coordinates to the reference
8574  coordinates with least displacements.   */
8575 static Double
8576 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8577 {
8578         Atom *ap, *ap1;
8579         Int natoms, nn;
8580         Vector org1, org2;
8581         Int i, in, j, k;
8582         Double w, w1;
8583         Mat33 r, q, u;
8584         Double eigen_val[3];
8585         Vector eigen_vec[3];
8586         Vector s[3];
8587         IntGroupIterator iter;
8588
8589         natoms = mol->natoms;
8590         ap = mol->atoms;
8591         IntGroupIteratorInit(ig, &iter);
8592         
8593         /*  Calculate the weighted center  */
8594         VecZero(org1);
8595         VecZero(org2);
8596         w = 0.0;
8597         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8598                 ap1 = ATOM_AT_INDEX(ap, in);
8599                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8600                 VecScaleInc(org1, ap1->r, w1);
8601                 VecScaleInc(org2, ref[i], w1);
8602                 w += w1;
8603         }
8604         w = 1.0 / w;
8605         VecScaleSelf(org1, w);
8606         VecScaleSelf(org2, w);
8607
8608     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8609     /*  Matrix to diagonalize = R * tR    */
8610         memset(r, 0, sizeof(Mat33));
8611         memset(q, 0, sizeof(Mat33));
8612         memset(u, 0, sizeof(Mat33));
8613         nn = 0;
8614         IntGroupIteratorReset(&iter);
8615         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8616                 Vector v1, v2;
8617                 ap1 = ATOM_AT_INDEX(ap, in);
8618                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8619                 w1 *= w1;
8620                 VecSub(v1, ap1->r, org1);
8621                 VecSub(v2, ref[i], org2);
8622                 r[0] += w1 * v1.x * v2.x;
8623                 r[1] += w1 * v1.y * v2.x;
8624                 r[2] += w1 * v1.z * v2.x;
8625                 r[3] += w1 * v1.x * v2.y;
8626                 r[4] += w1 * v1.y * v2.y;
8627                 r[5] += w1 * v1.z * v2.y;
8628                 r[6] += w1 * v1.x * v2.z;
8629                 r[7] += w1 * v1.y * v2.z;
8630                 r[8] += w1 * v1.z * v2.z;
8631                 nn++;
8632         }
8633         for (i = 0; i < 9; i++)
8634                 r[i] /= (nn * nn);
8635         for (i = 0; i < 3; i++) {
8636                 for (j = 0; j < 3; j++) {
8637                         for (k = 0; k < 3; k++) {
8638                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8639                         }
8640                 }
8641         }
8642         
8643         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8644                 IntGroupIteratorRelease(&iter);
8645                 return -1.0;  /*  Cannot determine the eigenvector  */
8646         }
8647
8648     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8649     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8650         MatrixTranspose(r, r);
8651         for (i = 0; i < 3; i++) {
8652                 MatrixVec(&s[i], r, &eigen_vec[i]);
8653                 w1 = 1.0 / sqrt(eigen_val[i]);
8654                 VecScaleSelf(s[i], w1);
8655         }
8656         for (k = 0; k < 3; k++) {
8657                 u[0] += s[k].x * eigen_vec[k].x;
8658                 u[1] += s[k].y * eigen_vec[k].x;
8659                 u[2] += s[k].z * eigen_vec[k].x;
8660                 u[3] += s[k].x * eigen_vec[k].y;
8661                 u[4] += s[k].y * eigen_vec[k].y;
8662                 u[5] += s[k].z * eigen_vec[k].y;
8663                 u[6] += s[k].x * eigen_vec[k].z;
8664                 u[7] += s[k].y * eigen_vec[k].z;
8665                 u[8] += s[k].z * eigen_vec[k].z;
8666         }
8667         
8668         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8669         MatrixVec(&org1, u, &org1);
8670         VecDec(org2, org1);
8671         for (i = 0; i < 9; i++)
8672                 trans[i] = u[i];
8673         trans[9] = org2.x;
8674         trans[10] = org2.y;
8675         trans[11] = org2.z;
8676         
8677         /*  Calculate rmsd  */
8678         IntGroupIteratorReset(&iter);
8679         w = 0.0;
8680         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8681                 Vector tv;
8682                 ap1 = ATOM_AT_INDEX(ap, in);
8683                 TransformVec(&tv, trans, &ap1->r);
8684                 VecDec(tv, ref[i]);
8685                 w += VecLength2(tv);
8686         }
8687         w = sqrt(w / nn);
8688         IntGroupIteratorRelease(&iter);
8689         return w;
8690 }
8691
8692 /*
8693  *  call-seq:
8694  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8695  *
8696  *  Calculate the transform to fit the given group to the set of reference coordinates.
8697  *  The reference coordinates ref is given as either a frame number, an array of
8698  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8699  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8700  *  Return values are the transform (that converts the present coordinates to the
8701  *  target coordinates) and root mean square deviation (without weight).
8702  */
8703 static VALUE
8704 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8705 {
8706         Molecule *mol;
8707         Atom *ap;
8708         VALUE gval, rval, wval;
8709         IntGroup *ig;
8710         IntGroupIterator iter;
8711         int nn, errnum, i, j, in, status;
8712         Vector *ref;
8713         Double *weights, dval[3];
8714         Transform tr;
8715
8716         Data_Get_Struct(self, Molecule, mol);
8717         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8718         if (gval == Qnil)
8719                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8720         else
8721                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8722         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8723                 IntGroupRelease(ig);
8724                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8725         }
8726         ref = (Vector *)calloc(sizeof(Vector), nn);
8727         weights = (Double *)calloc(sizeof(Double), nn);
8728         IntGroupIteratorInit(ig, &iter);
8729         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8730                 int fn = NUM2INT(rb_Integer(rval));
8731                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8732                         errnum = 1;
8733                         status = fn;
8734                         goto err;
8735                 }
8736                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8737                         ap = ATOM_AT_INDEX(mol->atoms, in);
8738                         if (fn < ap->nframes)
8739                                 ref[i] = ap->frames[fn];
8740                         else ref[i] = ap->r;
8741                 }
8742         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8743                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8744                 if (m->row * m->column < nn * 3) {
8745                         errnum = 2;
8746                         goto err;
8747                 }
8748                 for (i = 0; i < nn; i++) {
8749                         ref[i].x = m->data[i * 3];
8750                         ref[i].y = m->data[i * 3 + 1];
8751                         ref[i].z = m->data[i * 3 + 2];
8752                 }
8753         } else {
8754                 VALUE aval;
8755                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8756                 if (status != 0) {
8757                         errnum = 3;
8758                         goto err;
8759                 }
8760                 if (RARRAY_LEN(rval) < nn) {
8761                         errnum = 2;
8762                         goto err;
8763                 }
8764                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8765                         /*  Array of 3*nn numbers  */
8766                         if (RARRAY_LEN(rval) < nn * 3) {
8767                                 errnum = 2;
8768                                 goto err;
8769                         }
8770                         for (i = 0; i < nn; i++) {
8771                                 for (j = 0; j < 3; j++) {
8772                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8773                                         if (status != 0) {
8774                                                 errnum = 3;
8775                                                 goto err;
8776                                         }
8777                                         dval[j] = NUM2DBL(aval);
8778                                 }
8779                                 ref[i].x = dval[0];
8780                                 ref[i].y = dval[1];
8781                                 ref[i].z = dval[2];
8782                         }
8783                 } else {
8784                         /*  Array of nn Vector3Ds or Arrays  */
8785                         for (i = 0; i < nn; i++) {
8786                                 aval = (RARRAY_PTR(rval))[i];
8787                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8788                                         VectorFromValue(aval, &ref[i]);
8789                                 } else {
8790                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8791                                         if (status != 0) {
8792                                                 errnum = 3;
8793                                                 goto err;
8794                                         }
8795                                         if (RARRAY_LEN(aval) < 3) {
8796                                                 errnum = 4;
8797                                                 status = i;
8798                                                 goto err;
8799                                         }
8800                                         for (j = 0; j < 3; j++) {
8801                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8802                                                 if (status != 0) {
8803                                                         errnum = 3;
8804                                                         goto err;
8805                                                 }
8806                                                 dval[j] = NUM2DBL(aaval);
8807                                         }
8808                                         ref[i].x = dval[0];
8809                                         ref[i].y = dval[1];
8810                                         ref[i].z = dval[2];
8811                                 }
8812                         }
8813                 }
8814         }
8815         if (wval == Qnil) {
8816                 /*  Use atomic weights  */
8817                 IntGroupIteratorReset(&iter);
8818                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8819                         ap = ATOM_AT_INDEX(mol->atoms, in);
8820                         weights[i] = ap->weight;
8821                 }
8822         } else {
8823                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8824                 if (status != 0) {
8825                         errnum = 3;
8826                         goto err;
8827                 }
8828                 if (RARRAY_LEN(wval) < nn) {
8829                         errnum = 5;
8830                         goto err;
8831                 }
8832                 for (i = 0; i < nn; i++) {
8833                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8834                         if (status != 0) {
8835                                 errnum = 3;
8836                                 goto err;
8837                         }
8838                         weights[i] = NUM2DBL(wwval);
8839                 }
8840         }
8841         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8842         if (dval[0] < 0) {
8843                 errnum = 6;
8844                 goto err;
8845         }
8846         errnum = 0;
8847 err:
8848         IntGroupIteratorRelease(&iter);
8849         free(ref);
8850         free(weights);
8851         if (errnum == 0) {
8852                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8853         } else if (errnum == 1) {
8854                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8855         } else if (errnum == 2) {
8856                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8857         } else if (errnum == 3) {
8858                 rb_jump_tag(status);
8859         } else if (errnum == 4) {
8860                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8861         } else if (errnum == 5) {
8862                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8863         } else if (errnum == 6) {
8864                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8865         }
8866         return Qnil;  /*  Not reached  */
8867 }
8868
8869 #pragma mark ------ Screen Display ------
8870
8871 /*
8872  *  call-seq:
8873  *     display
8874  *
8875  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8876  */
8877 static VALUE
8878 s_Molecule_Display(VALUE self)
8879 {
8880     Molecule *mol;
8881     Data_Get_Struct(self, Molecule, mol);
8882         if (mol->mview != NULL)
8883                 MainViewCallback_display(mol->mview);
8884         return Qnil;
8885 }
8886
8887 /*
8888  *  call-seq:
8889  *     make_front
8890  *
8891  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8892  */
8893 static VALUE
8894 s_Molecule_MakeFront(VALUE self)
8895 {
8896     Molecule *mol;
8897     Data_Get_Struct(self, Molecule, mol);
8898         if (mol->mview != NULL)
8899                 MainViewCallback_makeFront(mol->mview);
8900         return Qnil;
8901 }
8902
8903 /*
8904  *  call-seq:
8905  *     update_enabled? -> bool
8906  *
8907  *  Returns true if screen update is enabled; otherwise no.
8908  */
8909 static VALUE
8910 s_Molecule_UpdateEnabled(VALUE self)
8911 {
8912     Molecule *mol;
8913     Data_Get_Struct(self, Molecule, mol);
8914         if (mol->mview != NULL && !mol->mview->freezeScreen)
8915                 return Qtrue;
8916         else return Qfalse;
8917 }
8918
8919 /*
8920  *  call-seq:
8921  *     update_enabled = bool
8922  *
8923  *  Enable or disable screen update. This is effective for automatic update on modification.
8924  *  Explicit call to molecule.display() always updates the screen.
8925  */
8926 static VALUE
8927 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8928 {
8929     Molecule *mol;
8930     Data_Get_Struct(self, Molecule, mol);
8931         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8932         if (mol->mview != NULL)
8933                 mol->mview->freezeScreen = (val == Qfalse);
8934         else val = Qfalse;
8935         return val;
8936 }
8937
8938 /*
8939  *  call-seq:
8940  *     show_unitcell
8941  *     show_unitcell(bool)
8942  *     show_unitcell = bool
8943  *
8944  *  Set the flag whether to show the unit cell. If no argument is given, the
8945  *  current flag is returned.
8946  */
8947 static VALUE
8948 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8949 {
8950     Molecule *mol;
8951     Data_Get_Struct(self, Molecule, mol);
8952         if (mol->mview == NULL)
8953                 return Qnil;
8954         if (argc > 0) {
8955                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8956                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8957         }
8958         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8959 }
8960
8961 /*
8962  *  call-seq:
8963  *     show_hydrogens
8964  *     show_hydrogens(bool)
8965  *     show_hydrogens = bool
8966  *
8967  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8968  *  current flag is returned.
8969  */
8970 static VALUE
8971 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8972 {
8973     Molecule *mol;
8974     Data_Get_Struct(self, Molecule, mol);
8975         if (mol->mview == NULL)
8976                 return Qnil;
8977         if (argc > 0) {
8978                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8979                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8980         }
8981         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8982 }
8983
8984 /*
8985  *  call-seq:
8986  *     show_dummy_atoms
8987  *     show_dummy_atoms(bool)
8988  *     show_dummy_atoms = bool
8989  *
8990  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8991  *  current flag is returned.
8992  */
8993 static VALUE
8994 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8995 {
8996     Molecule *mol;
8997     Data_Get_Struct(self, Molecule, mol);
8998         if (mol->mview == NULL)
8999                 return Qnil;
9000         if (argc > 0) {
9001                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9002                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9003         }
9004         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9005 }
9006
9007 /*
9008  *  call-seq:
9009  *     show_expanded
9010  *     show_expanded(bool)
9011  *     show_expanded = bool
9012  *
9013  *  Set the flag whether to show the expanded atoms. If no argument is given, the
9014  *  current flag is returned.
9015  */
9016 static VALUE
9017 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9018 {
9019     Molecule *mol;
9020     Data_Get_Struct(self, Molecule, mol);
9021         if (mol->mview == NULL)
9022                 return Qnil;
9023         if (argc > 0) {
9024                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9025                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9026         }
9027         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9028 }
9029
9030 /*
9031  *  call-seq:
9032  *     show_ellipsoids
9033  *     show_ellipsoids(bool)
9034  *     show_ellipsoids = bool
9035  *
9036  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9037  *  current flag is returned.
9038  */
9039 static VALUE
9040 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9041 {
9042     Molecule *mol;
9043     Data_Get_Struct(self, Molecule, mol);
9044         if (mol->mview == NULL)
9045                 return Qnil;
9046         if (argc > 0) {
9047                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9048                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9049         }
9050         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9051 }
9052
9053 /*
9054  *  call-seq:
9055  *     is_atom_visible(index)  -> Boolean
9056  *
9057  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9058  *  as well as the molecule attributes (showHydrogens, etc.)
9059  */
9060 static VALUE
9061 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9062 {
9063         Molecule *mol;
9064         Int idx;
9065         Atom *ap;
9066     Data_Get_Struct(self, Molecule, mol);
9067         idx = s_Molecule_AtomIndexFromValue(mol, ival);
9068         if (idx < 0 || idx >= mol->natoms)
9069                 return Qnil;
9070         ap = ATOM_AT_INDEX(mol->atoms, idx);
9071         if (mol->mview != NULL) {
9072                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9073                         return Qfalse;
9074                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9075                         return Qfalse;
9076                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9077                         return Qfalse;
9078         }
9079         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9080 }
9081
9082 /*
9083  *  call-seq:
9084  *     hidden_atoms       -> IntGroup
9085  *
9086  *  Returns the currently hidden atoms.
9087  */
9088 static VALUE
9089 s_Molecule_HiddenAtoms(VALUE self)
9090 {
9091         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9092         return Qnil;  /*  Not reached  */
9093 }
9094
9095 /*
9096  *  call-seq:
9097  *     set_hidden_atoms(IntGroup)
9098  *     self.hidden_atoms = IntGroup
9099  *
9100  *  Hide the specified atoms. This operation is _not_ undoable.
9101  */
9102 static VALUE
9103 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9104 {
9105         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9106         return Qnil;  /*  Not reached  */
9107 }
9108
9109 /*
9110  *  call-seq:
9111  *     show_graphite -> Integer
9112  *     show_graphite = Integer
9113  *     show_graphite = boolean
9114  *
9115  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9116  *  number of rings to display for each direction.
9117  *  If the argument is boolean, only the show/hide flag is set.
9118  */
9119 static VALUE
9120 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9121 {
9122     Molecule *mol;
9123     Data_Get_Struct(self, Molecule, mol);
9124         if (mol->mview == NULL)
9125                 return Qnil;
9126         if (argc > 0) {
9127                 if (argv[0] == Qnil || argv[0] == Qfalse)
9128                         mol->mview->showGraphiteFlag = 0;
9129                 else if (argv[0] == Qtrue)
9130                         mol->mview->showGraphiteFlag = 1;
9131                 else {
9132                         int n = NUM2INT(rb_Integer(argv[0]));
9133                         if (n < 0)
9134                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9135                         mol->mview->showGraphite = n;
9136                 }
9137                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9138         }
9139         return INT2NUM(mol->mview->showGraphite);
9140 }
9141
9142 /*
9143  *  call-seq:
9144  *     show_graphite? -> boolean
9145  *
9146  *  Return whether the graphite is set visible or not.
9147 */
9148 static VALUE
9149 s_Molecule_ShowGraphiteFlag(VALUE self)
9150 {
9151     Molecule *mol;
9152     Data_Get_Struct(self, Molecule, mol);
9153         if (mol->mview == NULL)
9154                 return Qnil;
9155         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9156 }
9157         
9158 /*
9159  *  call-seq:
9160  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9161  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9162  *     show_periodic_image = boolean
9163  *
9164  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9165  *  set but no visual effects are observed.
9166  *  If the argument is boolean, only the show/hide flag is modified.
9167  */
9168 static VALUE
9169 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9170 {
9171     Molecule *mol;
9172         VALUE val;
9173         int ival[6];
9174         int i;
9175     Data_Get_Struct(self, Molecule, mol);
9176         if (mol->mview == NULL)
9177                 return Qnil;
9178         rb_scan_args(argc, argv, "01", &val);
9179         if (argc > 0) {
9180                 /*  Change current settings  */
9181                 if (val == Qnil || val == Qfalse)
9182                         mol->mview->showPeriodicImageFlag = 0;
9183                 else if (val == Qtrue)
9184                         mol->mview->showPeriodicImageFlag = 1;
9185                 else {
9186                         val = rb_ary_to_ary(val);
9187                         for (i = 0; i < 6; i++) {
9188                                 if (i < RARRAY_LEN(val))
9189                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9190                         }
9191                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9192                                 rb_raise(rb_eMolbyError, "bad arguments");
9193                         for (i = 0; i < 6; i++)
9194                                 mol->mview->showPeriodicImage[i] = ival[i];
9195                 }
9196                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9197         }
9198         val = rb_ary_new();
9199         for (i = 0; i < 6; i++)
9200                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9201         return val;
9202 }
9203
9204 /*
9205  *  call-seq:
9206  *     show_periodic_image? -> boolean
9207  *
9208  *  Return whether the periodic images are set to visible or not. This flag is
9209  *  independent from the show_periodic_image settings.
9210  */
9211 static VALUE
9212 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9213 {
9214     Molecule *mol;
9215     Data_Get_Struct(self, Molecule, mol);
9216         if (mol->mview == NULL)
9217                 return Qnil;
9218         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9219 }
9220
9221 /*
9222  *  call-seq:
9223  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9224  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9225  *     show_rotation_center = boolean
9226  *
9227  *  Set to show the rotation center of the screen.
9228  *  If the argument is boolean, only the show/hide flag is modified.
9229  */
9230 static VALUE
9231 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9232 {
9233     Molecule *mol;
9234     Data_Get_Struct(self, Molecule, mol);
9235         if (mol->mview == NULL)
9236                 return Qnil;
9237         if (argc > 0) {
9238                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9239                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9240         }
9241         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9242 }
9243
9244 /*
9245  *  call-seq:
9246  *     line_mode
9247  *     line_mode(bool)
9248  *     line_mode = bool
9249  *
9250  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9251  *  current flag is returned.
9252  */
9253 static VALUE
9254 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9255 {
9256     Molecule *mol;
9257     Data_Get_Struct(self, Molecule, mol);
9258         if (mol->mview == NULL)
9259                 return Qnil;
9260         if (argc > 0) {
9261                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9262                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9263         }
9264         return (mol->mview->lineMode ? Qtrue : Qfalse);
9265 }
9266
9267 /*
9268  *  call-seq:
9269  *     atom_radius = float
9270  *     atom_radius
9271  *
9272  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9273  *  (Default = 0.4)
9274  *  If no argument is given, the current value is returned.
9275  */
9276 static VALUE
9277 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9278 {
9279     Molecule *mol;
9280     Data_Get_Struct(self, Molecule, mol);
9281         if (mol->mview == NULL)
9282                 return Qnil;
9283         if (argc > 0) {
9284                 double rad = NUM2DBL(rb_Float(argv[0]));
9285                 if (rad > 0.0) {
9286                         mol->mview->atomRadius = rad;
9287                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9288                 }
9289                 return argv[0];
9290         }
9291         return rb_float_new(mol->mview->atomRadius);
9292 }
9293
9294 /*
9295  *  call-seq:
9296  *     bond_radius = float
9297  *     bond_radius
9298  *
9299  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9300  *  If no argument is given, the current value is returned.
9301  */
9302 static VALUE
9303 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9304 {
9305     Molecule *mol;
9306     Data_Get_Struct(self, Molecule, mol);
9307         if (mol->mview == NULL)
9308                 return Qnil;
9309         if (argc > 0) {
9310                 double rad = NUM2DBL(rb_Float(argv[0]));
9311                 if (rad > 0.0) {
9312                         mol->mview->bondRadius = rad;
9313                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9314                 }
9315                 return argv[0];
9316         }
9317         return rb_float_new(mol->mview->bondRadius);
9318 }
9319
9320 /*
9321  *  call-seq:
9322  *     atom_resolution = integer
9323  *     atom_resolution
9324  *
9325  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9326  *  (Default = 12; minimum = 6)
9327  *  If no argument is given, the current value is returned.
9328  */
9329 static VALUE
9330 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9331 {
9332     Molecule *mol;
9333     Data_Get_Struct(self, Molecule, mol);
9334         if (mol->mview == NULL)
9335                 return Qnil;
9336         if (argc > 0) {
9337                 int res = NUM2INT(rb_Integer(argv[0]));
9338                 if (res < 6)
9339                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9340                 mol->mview->atomResolution = res;
9341                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9342                 return INT2NUM(res);
9343         }
9344         return INT2NUM(mol->mview->atomResolution);
9345 }
9346
9347 /*
9348  *  call-seq:
9349  *     bond_resolution = integer
9350  *     bond_resolution
9351  *
9352  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9353  *  (Default = 8; minimum = 4)
9354  *  If no argument is given, the current value is returned.
9355  */
9356 static VALUE
9357 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9358 {
9359     Molecule *mol;
9360     Data_Get_Struct(self, Molecule, mol);
9361         if (mol->mview == NULL)
9362                 return Qnil;
9363         if (argc > 0) {
9364                 int res = NUM2INT(rb_Integer(argv[0]));
9365                 if (res < 4)
9366                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9367                 mol->mview->bondResolution = res;
9368                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9369                 return INT2NUM(res);
9370         }
9371         return INT2NUM(mol->mview->bondResolution);
9372 }
9373
9374 /*
9375  *  call-seq:
9376  *     resize_to_fit
9377  *
9378  *  Resize the model drawing to fit in the window.
9379  */
9380 static VALUE
9381 s_Molecule_ResizeToFit(VALUE self)
9382 {
9383     Molecule *mol;
9384     Data_Get_Struct(self, Molecule, mol);
9385         if (mol->mview != NULL)
9386                 MainView_resizeToFit(mol->mview);
9387         return self;    
9388 }
9389
9390 /*
9391  *  call-seq:
9392  *     get_view_rotation -> [[ax, ay, az], angle]
9393  *
9394  *  Get the current rotation for the view. Angle is in degree, not radian.
9395  */
9396 static VALUE
9397 s_Molecule_GetViewRotation(VALUE self)
9398 {
9399     Molecule *mol;
9400         double f[4];
9401         Vector v;
9402     Data_Get_Struct(self, Molecule, mol);
9403         if (mol->mview == NULL)
9404                 return Qnil;
9405         TrackballGetRotate(mol->mview->track, f);
9406         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9407         v.x = f[1];
9408         v.y = f[2];
9409         v.z = f[3];
9410         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9411 }
9412
9413 /*
9414  *  call-seq:
9415  *     get_view_scale -> float
9416  *
9417  *  Get the current scale for the view.
9418  */
9419 static VALUE
9420 s_Molecule_GetViewScale(VALUE self)
9421 {
9422     Molecule *mol;
9423     Data_Get_Struct(self, Molecule, mol);
9424         if (mol->mview == NULL)
9425                 return Qnil;
9426         return rb_float_new(TrackballGetScale(mol->mview->track));
9427 }
9428
9429 /*
9430  *  call-seq:
9431  *     get_view_center -> Vector
9432  *
9433  *  Get the current center point of the view.
9434  */
9435 static VALUE
9436 s_Molecule_GetViewCenter(VALUE self)
9437 {
9438     Molecule *mol;
9439         double f[4];
9440         Vector v;
9441     Data_Get_Struct(self, Molecule, mol);
9442         if (mol->mview == NULL)
9443                 return Qnil;
9444         TrackballGetTranslate(mol->mview->track, f);
9445         v.x = -f[0] * mol->mview->dimension;
9446         v.y = -f[1] * mol->mview->dimension;
9447         v.z = -f[2] * mol->mview->dimension;
9448         return ValueFromVector(&v);
9449 }
9450
9451 /*
9452  *  call-seq:
9453  *     set_view_rotation([ax, ay, az], angle) -> self
9454  *
9455  *  Set the current rotation for the view. Angle is in degree, not radian.
9456  */
9457 static VALUE
9458 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9459 {
9460     Molecule *mol;
9461         double f[4];
9462         Vector v;
9463     Data_Get_Struct(self, Molecule, mol);
9464         if (mol->mview == NULL)
9465                 return Qnil;
9466         VectorFromValue(aval, &v);
9467         if (NormalizeVec(&v, &v))
9468                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9469         f[1] = v.x;
9470         f[2] = v.y;
9471         f[3] = v.z;
9472         f[0] = -NUM2DBL(rb_Float(angval));
9473         TrackballSetRotate(mol->mview->track, f);
9474         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9475         return self;
9476 }
9477
9478 /*
9479  *  call-seq:
9480  *     set_view_scale(scale) -> self
9481  *
9482  *  Set the current scale for the view.
9483  */
9484 static VALUE
9485 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9486 {
9487     Molecule *mol;
9488     Data_Get_Struct(self, Molecule, mol);
9489         if (mol->mview == NULL)
9490                 return Qnil;
9491         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9492         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9493         return self;
9494 }
9495
9496 /*
9497  *  call-seq:
9498  *     set_view_center(vec) -> self
9499  *
9500  *  Set the current center point of the view.
9501  */
9502 static VALUE
9503 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9504 {
9505     Molecule *mol;
9506         Vector v;
9507         double f[4];
9508     Data_Get_Struct(self, Molecule, mol);
9509         if (mol->mview == NULL)
9510                 return Qnil;
9511         VectorFromValue(aval, &v);
9512         f[0] = -v.x / mol->mview->dimension;
9513         f[1] = -v.y / mol->mview->dimension;
9514         f[2] = -v.z / mol->mview->dimension;
9515         TrackballSetTranslate(mol->mview->track, f);
9516         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9517         return self;
9518 }
9519
9520 /*
9521  *  call-seq:
9522  *     set_background_color(red, green, blue)
9523  *
9524  *  Set the background color of the model window.
9525  */
9526 static VALUE
9527 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9528 {
9529     Molecule *mol;
9530     Data_Get_Struct(self, Molecule, mol);
9531         if (mol->mview != NULL) {
9532                 VALUE rval, gval, bval;
9533                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9534                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9535         }
9536         return self;    
9537 }
9538
9539 /*
9540  *  call-seq:
9541  *     export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9542  *
9543  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9544  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9545  *  If either width or height is not specified, then the screen width/height is used instead.
9546  */
9547 static VALUE
9548 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9549 {
9550         Molecule *mol;
9551         VALUE fval, sval, bval, wval, hval;
9552         char *fname;
9553         float scale;
9554         int bg_color, width, height;
9555     Data_Get_Struct(self, Molecule, mol);
9556         if (mol->mview == NULL)
9557                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9558         rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9559         fname = FileStringValuePtr(fval);
9560         if (sval == Qnil)
9561                 scale = 1.0;
9562         else scale = NUM2DBL(rb_Float(sval));
9563         if (bval == Qnil)
9564                 bg_color = -1;
9565         else bg_color = NUM2INT(rb_Integer(bval));
9566         if (wval == Qnil)
9567                 width = 0;
9568         else width = NUM2INT(rb_Integer(wval));
9569         if (hval == Qnil)
9570                 height = 0;
9571         else height = NUM2INT(rb_Integer(hval));
9572         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9573                 return fval;
9574         else return Qnil;
9575 }
9576
9577 #pragma mark ------ Graphics ------
9578
9579 static void
9580 s_CalculateGraphicNormals(MainViewGraphic *gp)
9581 {
9582         int i;
9583         Vector v1, v2, v3;
9584         if (gp == NULL || gp->npoints < 3)
9585                 return;
9586         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9587         v1.x = gp->points[3] - gp->points[0];
9588         v1.y = gp->points[4] - gp->points[1];
9589         v1.z = gp->points[5] - gp->points[2];
9590         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9591         for (i = 2; i < gp->npoints; i++) {
9592                 v2.x = gp->points[i * 3] - gp->points[0];
9593                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9594                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9595                 VecCross(v3, v1, v2);
9596                 NormalizeVec(&v3, &v3);
9597                 gp->normals[i * 3] = v3.x;
9598                 gp->normals[i * 3 + 1] = v3.y;
9599                 gp->normals[i * 3 + 2] = v3.z;
9600                 v1 = v2;
9601         }
9602         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9603         VecZero(v1);
9604         for (i = 2; i < gp->npoints; i++) {
9605                 v1.x += gp->normals[i * 3];
9606                 v1.y += gp->normals[i * 3 + 1];
9607                 v1.z += gp->normals[i * 3 + 2];
9608         }
9609         NormalizeVec(&v1, &v1);
9610         gp->normals[0] = v1.x;
9611         gp->normals[1] = v1.y;
9612         gp->normals[2] = v1.z;
9613         /*  normals[1] = nv[2].normalize  */
9614         v2.x = gp->normals[6];
9615         v2.y = gp->normals[7];
9616         v2.z = gp->normals[8];
9617         NormalizeVec(&v1, &v2);
9618         gp->normals[3] = v1.x;
9619         gp->normals[4] = v1.y;
9620         gp->normals[5] = v1.z;
9621         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9622         for (i = 2; i < gp->npoints; i++) {
9623                 if (i == gp->npoints - 1)
9624                         VecZero(v3);
9625                 else {
9626                         v3.x = gp->normals[i * 3 + 3];
9627                         v3.y = gp->normals[i * 3 + 4];
9628                         v3.z = gp->normals[i * 3 + 5];
9629                 }
9630                 VecInc(v2, v3);
9631                 NormalizeVec(&v1, &v2);
9632                 gp->normals[i * 3] = v1.x;
9633                 gp->normals[i * 3 + 1] = v1.y;
9634                 gp->normals[i * 3 + 2] = v1.z;
9635                 v2 = v3;
9636         }
9637 }
9638
9639 /*
9640  *  call-seq:
9641  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9642  *
9643  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9644  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9645  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9646  *   points: an array of Vectors
9647  *   
9648  */
9649 static VALUE
9650 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9651 {
9652     Molecule *mol;
9653         MainViewGraphic g;
9654         int i, n, ni, idx;
9655         const char *p;
9656         VALUE kval, cval, pval, fval, ival;
9657     Data_Get_Struct(self, Molecule, mol);
9658         if (mol->mview == NULL)
9659                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9660         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9661         idx = NUM2INT(rb_Integer(ival));
9662         if (idx == -1)
9663                 idx = mol->mview->ngraphics;
9664         else if (idx < 0 || idx > mol->mview->ngraphics)
9665                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9666         memset(&g, 0, sizeof(g));
9667         g.visible = 1;
9668         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9669                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9670         } else {
9671                 kval = rb_obj_as_string(kval);
9672                 p = StringValuePtr(kval);
9673                 if (strcmp(p, "line") == 0)
9674                         g.kind = kMainViewGraphicLine;
9675                 else if (strcmp(p, "poly") == 0)
9676                         g.kind = kMainViewGraphicPoly;
9677                 else if (strcmp(p, "cylinder") == 0)
9678                         g.kind = kMainViewGraphicCylinder;
9679                 else if (strcmp(p, "cone") == 0)
9680                         g.kind = kMainViewGraphicCone;
9681                 else if (strcmp(p, "ellipsoid") == 0)
9682                         g.kind = kMainViewGraphicEllipsoid;
9683                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9684         }
9685         g.closed = (RTEST(fval) ? 1 : 0);
9686         cval = rb_ary_to_ary(cval);
9687         n = RARRAY_LEN(cval);
9688         if (n < 3 || n >= 5)
9689                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9690         if (n == 3)
9691                 g.rgba[3] = 1.0;
9692         for (i = 0; i < n; i++)
9693                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9694         pval = rb_ary_to_ary(pval);
9695         n = RARRAY_LEN(pval);
9696         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9697         if (n <= 0)
9698                 rb_raise(rb_eArgError, "no control points are given");
9699         switch (g.kind) {
9700                 case kMainViewGraphicLine:
9701                         if (n < 2)
9702                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9703                         break;
9704                 case kMainViewGraphicPoly:
9705                         if (n < 3)
9706                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9707                         break;
9708                 case kMainViewGraphicCylinder:
9709                 case kMainViewGraphicCone:
9710                         if (n != 3)
9711                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9712                         ni = 2;
9713                         break;
9714                 case kMainViewGraphicEllipsoid:
9715                         if (n == 2) {
9716                                 ni = 1;
9717                         } else if (n != 4)
9718                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9719                         break;
9720         }
9721         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9722         for (i = 0; i < n; i++) {
9723                 Vector v;
9724                 VALUE rval = RARRAY_PTR(pval)[i];
9725                 if (i == ni) {
9726                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9727                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9728                                 VectorFromValue(rval, &v);
9729                         } else {
9730                                 v.x = NUM2DBL(rb_Float(rval));
9731                         }
9732                         v.y = v.z = 0;
9733                 } else {
9734                         VectorFromValue(rval, &v);
9735                 }
9736                 g.points[i * 3] = v.x;
9737                 g.points[i * 3 + 1] = v.y;
9738                 g.points[i * 3 + 2] = v.z;
9739         }
9740         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9741                 /*  Sphere  */
9742                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9743                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9744                 g.points[7] = g.points[11] = g.points[3];
9745         }
9746         if (g.kind == kMainViewGraphicPoly) {
9747                 /*  Calculate normals  */
9748                 s_CalculateGraphicNormals(&g);
9749         }
9750         MainView_insertGraphic(mol->mview, idx, &g);
9751         
9752         {
9753                 /*  Register undo  */
9754                 MolAction *act;
9755                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9756                 MolActionCallback_registerUndo(mol, act);
9757                 MolActionRelease(act);
9758         }
9759
9760         return INT2NUM(idx);    
9761 }
9762
9763 /*
9764  *  call-seq:
9765  *     create_graphic(kind, color, points, fill = nil) -> integer
9766  *
9767  *  Create a new graphic object. The arguments are similar as insert_graphic.
9768  */
9769 static VALUE
9770 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9771 {
9772         VALUE args[5];
9773         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9774         args[0] = INT2NUM(-1);
9775         return s_Molecule_InsertGraphic(argc + 1, args, self);
9776 }
9777
9778 /*
9779  *  call-seq:
9780  *     remove_graphic(index) -> integer
9781  *
9782  *  Remove a graphic object.
9783  */
9784 static VALUE
9785 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9786 {
9787     Molecule *mol;
9788         int i;
9789     Data_Get_Struct(self, Molecule, mol);
9790         if (mol->mview == NULL)
9791                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9792         i = NUM2INT(rb_Integer(ival));
9793         if (i < 0 || i >= mol->mview->ngraphics)
9794                 rb_raise(rb_eArgError, "graphic index is out of range");
9795         {
9796                 /*  Prepare data for undo  */
9797                 MainViewGraphic *gp;
9798                 Vector *vp;
9799                 MolAction *act;
9800                 double col[4];
9801                 int n;
9802                 gp = mol->mview->graphics + i;
9803                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9804                 for (n = 0; n < gp->npoints; n++) {
9805                         vp[n].x = gp->points[n * 3];
9806                         vp[n].y = gp->points[n * 3 + 1];
9807                         vp[n].z = gp->points[n * 3 + 2];
9808                 }
9809                 col[0] = gp->rgba[0];
9810                 col[1] = gp->rgba[1];
9811                 col[2] = gp->rgba[2];
9812                 col[3] = gp->rgba[3];
9813                 if (gp->visible == 0) {
9814                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9815                         MolActionCallback_registerUndo(mol, act);
9816                         MolActionRelease(act);
9817                 }
9818                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9819                 MolActionCallback_registerUndo(mol, act);
9820                 free(vp);
9821                 MolActionRelease(act);
9822         }
9823         MainView_removeGraphic(mol->mview, i);
9824         return ival;
9825 }
9826
9827 /*
9828  *  call-seq:
9829  *     ngraphics -> integer
9830  *
9831  *  Get the number of graphic objects.
9832  */
9833 static VALUE
9834 s_Molecule_NGraphics(VALUE self)
9835 {
9836     Molecule *mol;
9837     Data_Get_Struct(self, Molecule, mol);
9838         if (mol->mview == NULL)
9839                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9840         return INT2NUM(mol->mview->ngraphics);
9841 }
9842
9843 /*
9844  *  call-seq:
9845  *     get_graphic_point(graphic_index, point_index) -> value
9846  *     get_graphic_points(graphic_index) -> values
9847  *
9848  *  Get the point_index-th control point of graphic_index-th graphic object.
9849  *  Get an array of all control points with the given values.
9850  *   
9851  */
9852 static VALUE
9853 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9854 {
9855         MainViewGraphic *gp;
9856     Molecule *mol;
9857         int index, pindex;
9858         Vector v;
9859         VALUE gval, pval;
9860     Data_Get_Struct(self, Molecule, mol);
9861         if (mol->mview == NULL)
9862                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9863         rb_scan_args(argc, argv, "11", &gval, &pval);
9864         index = NUM2INT(rb_Integer(gval));
9865         if (index < 0 || index >= mol->mview->ngraphics)
9866                 rb_raise(rb_eArgError, "the graphic index is out of range");
9867         gp = mol->mview->graphics + index;
9868         if (pval != Qnil) {
9869                 pindex = NUM2INT(rb_Integer(pval));
9870                 if (pindex < 0 || pindex >= gp->npoints)
9871                         rb_raise(rb_eArgError, "the point index is out of range");
9872                 v.x = gp->points[pindex * 3];
9873                 v.y = gp->points[pindex * 3 + 1];
9874                 v.z = gp->points[pindex * 3 + 2];
9875                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9876                         return rb_float_new(v.x);
9877                 } else {
9878                         return ValueFromVector(&v);
9879                 }
9880         } else {
9881                 pval = rb_ary_new();
9882                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9883                         v.x = gp->points[pindex * 3];
9884                         v.y = gp->points[pindex * 3 + 1];
9885                         v.z = gp->points[pindex * 3 + 2];
9886                         rb_ary_push(pval, ValueFromVector(&v));
9887                 }
9888                 return pval;
9889         }
9890 }
9891
9892 /*
9893  *  call-seq:
9894  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9895  *     set_graphic_points(graphic_index, new_values) -> new_values
9896  *
9897  *  Change the point_index-th control point of graphic_index-th graphic object.
9898  *  Replace the control points with the given values.
9899  *   
9900  */
9901 static VALUE
9902 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9903 {
9904         MainViewGraphic *gp;
9905     Molecule *mol;
9906         int index, pindex;
9907         Vector v, v0;
9908         VALUE gval, pval, nval;
9909         MolAction *act;
9910     Data_Get_Struct(self, Molecule, mol);
9911         if (mol->mview == NULL)
9912                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9913         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9914         index = NUM2INT(rb_Integer(gval));
9915         if (index < 0 || index >= mol->mview->ngraphics)
9916                 rb_raise(rb_eArgError, "the graphic index is out of range");
9917         gp = mol->mview->graphics + index;
9918         if (nval != Qnil) {
9919                 pindex = NUM2INT(rb_Integer(pval));
9920                 if (pindex < 0 || pindex >= gp->npoints)
9921                         rb_raise(rb_eArgError, "the point index is out of range");
9922                 v0.x = gp->points[pindex * 3];
9923                 v0.y = gp->points[pindex * 3 + 1];
9924                 v0.z = gp->points[pindex * 3 + 2];
9925                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9926                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9927                                 v.x = NUM2DBL(rb_Float(nval));
9928                                 v.y = v.z = 0;
9929                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9930                                 v.x = NUM2DBL(rb_Float(nval));
9931                                 v.y = v.z = 0;
9932                                 gp->points[7] = gp->points[11] = v.x;
9933                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9934                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9935                 } else {
9936                         if (nval == Qnil) {
9937                                 v.x = kInvalidFloat;
9938                                 v.y = v.z = 0.0;
9939                         } else VectorFromValue(nval, &v);
9940                 }
9941                 gp->points[pindex * 3] = v.x;
9942                 gp->points[pindex * 3 + 1] = v.y;
9943                 gp->points[pindex * 3 + 2] = v.z;
9944                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9945         } else {
9946                 VALUE aval;
9947                 int len;
9948                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9949                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9950                         vp[pindex].x = gp->points[pindex * 3];
9951                         vp[pindex].y = gp->points[pindex * 3 + 1];
9952                         vp[pindex].z = gp->points[pindex * 3 + 2];
9953                 }
9954                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9955                 free(vp);
9956                 pval = rb_ary_to_ary(pval);
9957                 len = RARRAY_LEN(pval);
9958                 if (gp->npoints < len) {
9959                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9960                         gp->npoints = len;
9961                 } else if (gp->npoints > len) {
9962                         int len2 = 3;
9963                         switch (gp->kind) {
9964                                 case kMainViewGraphicLine: len2 = 2; break;
9965                                 case kMainViewGraphicPoly: len2 = 3; break;
9966                                 case kMainViewGraphicCylinder: len2 = 3; break;
9967                                 case kMainViewGraphicCone: len2 = 3; break;
9968                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9969                         }
9970                         if (len2 < len)
9971                                 len2 = len;
9972                         gp->npoints = len2;
9973                 }
9974                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9975                         aval = RARRAY_PTR(pval)[pindex];
9976                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9977                                 v.x = NUM2DBL(rb_Float(aval));
9978                                 v.y = v.z = 0;
9979                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9980                                 v.x = NUM2DBL(rb_Float(aval));
9981                                 v.y = v.z = 0;
9982                                 gp->points[7] = gp->points[11] = v.x;
9983                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9984                                 break;
9985                         } else VectorFromValue(aval, &v);
9986                         gp->points[pindex * 3] = v.x;
9987                         gp->points[pindex * 3 + 1] = v.y;
9988                         gp->points[pindex * 3 + 2] = v.z;
9989                 }
9990         }
9991         if (gp->kind == kMainViewGraphicPoly) {
9992                 /*  Calculate normals  */
9993                 s_CalculateGraphicNormals(gp);
9994         }
9995         MolActionCallback_registerUndo(mol, act);
9996         MolActionRelease(act);          
9997         MoleculeCallback_notifyModification(mol, 0);
9998         return nval;
9999 }
10000
10001 /*
10002  *  call-seq:
10003  *     get_graphic_color(graphic_index) -> value
10004  *
10005  *  Get the color of graphic_index-th graphic object
10006  */
10007 static VALUE
10008 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10009 {
10010         MainViewGraphic *gp;
10011     Molecule *mol;
10012         int index;
10013     Data_Get_Struct(self, Molecule, mol);
10014         if (mol->mview == NULL)
10015                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10016         index = NUM2INT(rb_Integer(gval));
10017         if (index < 0 || index >= mol->mview->ngraphics)
10018                 rb_raise(rb_eArgError, "the graphic index is out of range");
10019         gp = mol->mview->graphics + index;
10020         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]));
10021 }
10022
10023 /*
10024  *  call-seq:
10025  *     set_graphic_color(graphic_index, new_value) -> new_value
10026  *
10027  *  Change the color of graphic_index-th graphic object
10028  *   
10029  */
10030 static VALUE
10031 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10032 {
10033         MainViewGraphic *gp;
10034     Molecule *mol;
10035         MolAction *act;
10036         double c[4];
10037         int index, i, n;
10038     Data_Get_Struct(self, Molecule, mol);
10039         if (mol->mview == NULL)
10040                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10041         index = NUM2INT(rb_Integer(gval));
10042         if (index < 0 || index >= mol->mview->ngraphics)
10043                 rb_raise(rb_eArgError, "the graphic index is out of range");
10044         gp = mol->mview->graphics + index;
10045         for (i = 0; i < 4; i++)
10046                 c[i] = gp->rgba[i];
10047         cval = rb_ary_to_ary(cval);
10048         n = RARRAY_LEN(cval);
10049         if (n != 3 && n != 4)
10050                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10051
10052         for (i = 0; i < n; i++) {
10053                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10054         }
10055         if (n == 3)
10056                 gp->rgba[3] = 1.0;
10057         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10058         MolActionCallback_registerUndo(mol, act);
10059         MolActionRelease(act);          
10060         MoleculeCallback_notifyModification(mol, 0);
10061         return cval;
10062 }
10063
10064 /*
10065  *  call-seq:
10066  *     show_graphic(graphic_index) -> self
10067  *
10068  *  Enable the visible flag of the graphic_index-th graphic object
10069  *   
10070  */
10071 static VALUE
10072 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10073 {
10074         MainViewGraphic *gp;
10075     Molecule *mol;
10076         int index;
10077     Data_Get_Struct(self, Molecule, mol);
10078         if (mol->mview == NULL)
10079                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10080         index = NUM2INT(rb_Integer(gval));
10081         if (index < 0 || index >= mol->mview->ngraphics)
10082                 rb_raise(rb_eArgError, "the graphic index is out of range");
10083         gp = mol->mview->graphics + index;
10084         gp->visible = 1;
10085         MoleculeCallback_notifyModification(mol, 0);
10086         return self;
10087 }
10088
10089 /*
10090  *  call-seq:
10091  *     hide_graphic(graphic_index) -> self
10092  *
10093  *  Disable the visible flag of the graphic_index-th graphic object
10094  *   
10095  */
10096 static VALUE
10097 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10098 {
10099         MainViewGraphic *gp;
10100     Molecule *mol;
10101         int index;
10102     Data_Get_Struct(self, Molecule, mol);
10103         if (mol->mview == NULL)
10104                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10105         index = NUM2INT(rb_Integer(gval));
10106         if (index < 0 || index >= mol->mview->ngraphics)
10107                 rb_raise(rb_eArgError, "the graphic index is out of range");
10108         gp = mol->mview->graphics + index;
10109         gp->visible = 0;
10110         MoleculeCallback_notifyModification(mol, 0);
10111         return self;
10112 }
10113
10114 /*
10115  *  call-seq:
10116  *     show_text(string)
10117  *
10118  *  Show the string in the info text box.
10119  */
10120 static VALUE
10121 s_Molecule_ShowText(VALUE self, VALUE arg)
10122 {
10123     Molecule *mol;
10124     Data_Get_Struct(self, Molecule, mol);
10125         if (mol->mview != NULL)
10126                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10127         return Qnil;
10128 }
10129
10130 #pragma mark ------ MD Support ------
10131
10132 /*
10133  *  call-seq:
10134  *     md_arena -> MDArena
10135  *
10136  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10137  *  this molecule, a new arena is created.
10138  */
10139 static VALUE
10140 s_Molecule_MDArena(VALUE self)
10141 {
10142     Molecule *mol;
10143         VALUE retval;
10144     Data_Get_Struct(self, Molecule, mol);
10145         if (mol->arena == NULL)
10146                 md_arena_new(mol);
10147         retval = ValueFromMDArena(mol->arena);
10148         return retval;
10149 }
10150
10151 /*
10152  *  call-seq:
10153  *     set_parameter_attr(type, index, key, value, src) -> value
10154  *
10155  *  This method is used only internally.
10156  */
10157 static VALUE
10158 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10159 {
10160         /*  This method is called from MolAction to change a MM parameter attribute.  */
10161     Molecule *mol;
10162         VALUE pval;
10163         ParameterRef *pref;
10164         UnionPar *up;
10165     Data_Get_Struct(self, Molecule, mol);
10166         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10167         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10168         
10169         /*  This is the special part of this method; it allows modification of the src field. */
10170         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10171         Data_Get_Struct(pval, ParameterRef, pref);
10172         up = ParameterRefGetPar(pref);
10173         up->bond.src = FIX2INT(sval);
10174         
10175         return vval;
10176 }
10177
10178 /*
10179  *  call-seq:
10180  *     parameter -> Parameter
10181  *
10182  *  Get the local parameter of this molecule. If not defined, returns nil.
10183  */
10184 static VALUE
10185 s_Molecule_Parameter(VALUE self)
10186 {
10187     Molecule *mol;
10188     Data_Get_Struct(self, Molecule, mol);
10189 /*      if (mol->par == NULL)
10190                 return Qnil; */
10191         return s_NewParameterValueFromValue(self);
10192 }
10193
10194 /*
10195  *  call-seq:
10196  *     start_step       -> Integer
10197  *
10198  *  Returns the start step (defined by dcd format).
10199  */
10200 static VALUE
10201 s_Molecule_StartStep(VALUE self)
10202 {
10203     Molecule *mol;
10204     Data_Get_Struct(self, Molecule, mol);
10205         return INT2NUM(mol->startStep);
10206 }
10207
10208 /*
10209  *  call-seq:
10210  *     start_step = Integer
10211  *
10212  *  Set the start step (defined by dcd format).
10213  */
10214 static VALUE
10215 s_Molecule_SetStartStep(VALUE self, VALUE val)
10216 {
10217     Molecule *mol;
10218     Data_Get_Struct(self, Molecule, mol);
10219         mol->startStep = NUM2INT(rb_Integer(val));
10220         return val;
10221 }
10222
10223 /*
10224  *  call-seq:
10225  *     steps_per_frame       -> Integer
10226  *
10227  *  Returns the number of steps between frames (defined by dcd format).
10228  */
10229 static VALUE
10230 s_Molecule_StepsPerFrame(VALUE self)
10231 {
10232     Molecule *mol;
10233     Data_Get_Struct(self, Molecule, mol);
10234         return INT2NUM(mol->stepsPerFrame);
10235 }
10236
10237 /*
10238  *  call-seq:
10239  *     steps_per_frame = Integer
10240  *
10241  *  Set the number of steps between frames (defined by dcd format).
10242  */
10243 static VALUE
10244 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10245 {
10246     Molecule *mol;
10247     Data_Get_Struct(self, Molecule, mol);
10248         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10249         return val;
10250 }
10251
10252 /*
10253  *  call-seq:
10254  *     ps_per_step       -> Float
10255  *
10256  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10257  */
10258 static VALUE
10259 s_Molecule_PsPerStep(VALUE self)
10260 {
10261     Molecule *mol;
10262     Data_Get_Struct(self, Molecule, mol);
10263         return rb_float_new(mol->psPerStep);
10264 }
10265
10266 /*
10267  *  call-seq:
10268  *     ps_per_step = Float
10269  *
10270  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10271  */
10272 static VALUE
10273 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10274 {
10275     Molecule *mol;
10276     Data_Get_Struct(self, Molecule, mol);
10277         mol->psPerStep = NUM2DBL(rb_Float(val));
10278         return val;
10279 }
10280
10281 static VALUE
10282 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10283 {
10284         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.");
10285 }
10286
10287 #pragma mark ------ MO Handling ------
10288
10289 /*
10290  *  call-seq:
10291  *     selectedMO -> IntGroup
10292  *
10293  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10294  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10295  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10296  */
10297 static VALUE
10298 s_Molecule_SelectedMO(VALUE self)
10299 {
10300     Molecule *mol;
10301         IntGroup *ig;
10302         VALUE val;
10303     Data_Get_Struct(self, Molecule, mol);
10304         if (mol->mview == NULL)
10305                 return Qnil;
10306         ig = MainView_selectedMO(mol->mview);
10307         if (ig == NULL)
10308                 return Qnil;
10309         IntGroupOffset(ig, 1);
10310         val = ValueFromIntGroup(ig);
10311         IntGroupRelease(ig);
10312         return val;
10313 }
10314
10315 /*
10316  *  call-seq:
10317  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10318  *
10319  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10320  *  If the molecule does not contain a basis set information, then returns nil.
10321  */
10322 static VALUE
10323 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10324 {
10325     Molecule *mol;
10326         Vector o, dx, dy, dz;
10327         Int nx, ny, nz;
10328         VALUE nval;
10329         Int npoints = 80 * 80 * 80;
10330     Data_Get_Struct(self, Molecule, mol);
10331         if (mol->bset == NULL)
10332                 return Qnil;
10333         rb_scan_args(argc, argv, "01", &nval);
10334         if (nval != Qnil)
10335                 npoints = NUM2INT(rb_Integer(nval));
10336         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10337                 return Qnil;
10338         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));
10339 }
10340
10341 static int
10342 s_Cubegen_callback(double progress, void *ref)
10343 {
10344         MyAppCallback_setProgressValue(progress);
10345         if (MyAppCallback_checkInterrupt())
10346                 return 1;
10347         else return 0;
10348 }
10349
10350 /*
10351  *  call-seq:
10352  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10353  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10354  *
10355  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10356  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10357  *  second form, the cube dimension is explicitly given.
10358  *  Returns fname when successful, nil otherwise.
10359  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10360  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10361  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10362  */
10363 static VALUE
10364 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10365 {
10366         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10367     Molecule *mol;
10368         Int mono, nx, ny, nz, npoints;
10369         Vector o, dx, dy, dz;
10370         int index, n;
10371         char buf[1024];
10372     Data_Get_Struct(self, Molecule, mol);
10373         if (mol->bset == NULL)
10374                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10375         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10376         
10377         /*  Set up parameters  */
10378         mono = NUM2INT(rb_Integer(mval));
10379         if (mono < 0 || mono > mol->bset->ncomps)
10380                 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);
10381         if (RTEST(bval)) {
10382                 if (mol->bset->rflag != 0)
10383                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10384                 mono += mol->bset->ncomps;
10385         }
10386                 
10387         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10388                 /*  Automatic grid formation  */
10389                 if (oval != Qnil)
10390                         npoints = NUM2INT(rb_Integer(oval));
10391                 else npoints = 0;
10392                 if (npoints == 0)
10393                         npoints = 1000000;
10394                 else if (npoints < 8)
10395                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10396                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10397                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10398                 ival = dxval;
10399                 bval = dyval;
10400         } else {
10401                 VectorFromValue(oval, &o);
10402                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10403                         VectorFromValue(dxval, &dx);
10404                 else {
10405                         dx.x = NUM2DBL(rb_Float(dxval));
10406                         dx.y = dx.z = 0.0;
10407                 }
10408                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10409                         VectorFromValue(dyval, &dy);
10410                 else {
10411                         dy.y = NUM2DBL(rb_Float(dyval));
10412                         dy.x = dy.z = 0.0;
10413                 }
10414                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10415                         VectorFromValue(dzval, &dz);
10416                 else {
10417                         dz.z = NUM2DBL(rb_Float(dzval));
10418                         dz.x = dz.y = 0.0;
10419                 }
10420                 nx = NUM2INT(rb_Integer(nxval));
10421                 ny = NUM2INT(rb_Integer(nyval));
10422                 nz = NUM2INT(rb_Integer(nzval));
10423                 if (nx <= 0 || ny <= 0 || nz <= 0)
10424                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10425                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10426                         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);
10427         }
10428         
10429         /*  Calc MO  */
10430         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10431         if (index == -2)
10432                 rb_interrupt();
10433         else if (index < 0)
10434                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10435         
10436         /*  Output to file  */
10437         MoleculeCallback_displayName(mol, buf, sizeof buf);
10438         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10439         if (n != 0)
10440                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10441         
10442         /*  Discard the cube  */
10443         MoleculeClearCubeAtIndex(mol, index);
10444         return fval;
10445 }
10446
10447 /*
10448  *  call-seq:
10449  *     clear_surface
10450  *
10451  *  Clear the MO surface if present.
10452  */
10453 static VALUE
10454 s_Molecule_ClearSurface(VALUE self)
10455 {
10456     Molecule *mol;
10457     Data_Get_Struct(self, Molecule, mol);
10458         if (mol->mcube != NULL)
10459                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10460         return self;
10461 }
10462
10463 /*
10464  *  call-seq:
10465  *     hide_surface
10466  *
10467  *  Hide the MO surface if present.
10468  */
10469 static VALUE
10470 s_Molecule_HideSurface(VALUE self)
10471 {
10472     Molecule *mol;
10473     Data_Get_Struct(self, Molecule, mol);
10474         if (mol->mcube != NULL) {
10475                 mol->mcube->hidden = 1;
10476                 MoleculeCallback_notifyModification(mol, 0);
10477         }
10478         return self;
10479 }
10480
10481 /*
10482  *  call-seq:
10483  *     show_surface
10484  *
10485  *  Show the MO surface if present.
10486  */
10487 static VALUE
10488 s_Molecule_ShowSurface(VALUE self)
10489 {
10490     Molecule *mol;
10491     Data_Get_Struct(self, Molecule, mol);
10492         if (mol->mcube != NULL) {
10493                 mol->mcube->hidden = 0;
10494                 MoleculeCallback_notifyModification(mol, 0);
10495         }
10496         return self;
10497 }
10498
10499 /*
10500  *  call-seq:
10501  *     create_surface(mo, attr = nil)
10502  *
10503  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10504  *  then it denotes the beta orbital.
10505  *  If mo is nil, then the attributes of the current surface are modified.
10506  *  Attributes:
10507  *    :npoints : the approximate number of grid points
10508  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10509  *    :thres   : the threshold for the isovalue surface
10510  *  If the molecule does not contain MO information, raises exception.
10511  */
10512 static VALUE
10513 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10514 {
10515     Molecule *mol;
10516         Vector o, dx, dy, dz;
10517         Int nmo, nx, ny, nz, i;
10518         Int need_recalc = 0;
10519         VALUE nval, hval, aval;
10520         Int npoints;
10521         Double expand;
10522         Double thres;
10523         Double d[4];
10524     Data_Get_Struct(self, Molecule, mol);
10525         rb_scan_args(argc, argv, "11", &nval, &hval);
10526         if (mol->bset == NULL)
10527                 rb_raise(rb_eMolbyError, "No MO information is given");
10528         if (nval == Qnil) {
10529                 nmo = -1;
10530         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10531                 nmo = mol->bset->nmos + 1;
10532         } else {
10533                 nmo = NUM2INT(rb_Integer(nval));
10534                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10535                         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);
10536                 if (nmo < 0)
10537                         nmo = -nmo + mol->bset->ncomps;
10538         }
10539         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10540                 npoints = NUM2INT(rb_Integer(aval));
10541                 need_recalc = 1;
10542         } else if (mol->mcube != NULL) {
10543                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10544         } else npoints = 80 * 80 * 80;
10545         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10546                 expand = NUM2DBL(rb_Float(aval));
10547         } else if (mol->mcube != NULL) {
10548                 expand = mol->mcube->expand;
10549         } else expand = 1.0;
10550         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10551                 thres = NUM2DBL(rb_Float(aval));
10552         } else if (mol->mcube != NULL) {
10553                 thres = mol->mcube->thres;
10554         } else thres = 0.05;
10555         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10556                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10557                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10558                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10559                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10560         }
10561         for (nx = 0; nx < 2; nx++) {
10562                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10563                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10564                         aval = rb_ary_to_ary(aval);
10565                         if (RARRAY_LEN(aval) < 3) {
10566                         raise:
10567                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10568                         }
10569                         for (i = 0; i < 4; i++)
10570                                 d[i] = mol->mcube->c[nx].rgba[i];
10571                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10572                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10573                                 if (d[i] < 0.0 && d[i] > 1.0)
10574                                         goto raise;
10575                         }
10576                         for (i = 0; i < 4; i++)
10577                                 mol->mcube->c[nx].rgba[i] = d[i];
10578                 }
10579         }
10580         if (mol->mcube->expand != expand)
10581                 need_recalc = 1;
10582         mol->mcube->thres = thres;
10583         mol->mcube->expand = expand;
10584         if (nmo < 0) {
10585                 if (mol->mcube->idn < 0)
10586                         return self;  /*  Only set attributes for now  */
10587                 if (need_recalc)
10588                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10589         }
10590         if (MoleculeUpdateMCube(mol, nmo) != 0)
10591                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10592         return self;
10593 }
10594
10595 /*
10596  *  call-seq:
10597  *     set_surface_attr(attr = nil)
10598  *
10599  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10600  */
10601 static VALUE
10602 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10603 {
10604         VALUE args[2];
10605         args[0] = Qnil;
10606         args[1] = hval;
10607         return s_Molecule_CreateSurface(2, args, self);
10608 }
10609
10610 /*
10611  *  call-seq:
10612  *     nelpots
10613  *
10614  *  Get the number of electrostatic potential info.
10615  */
10616 static VALUE
10617 s_Molecule_NElpots(VALUE self)
10618 {
10619         Molecule *mol;
10620     Data_Get_Struct(self, Molecule, mol);
10621         return INT2NUM(mol->nelpots);
10622 }
10623
10624 /*
10625  *  call-seq:
10626  *     elpot(idx)
10627  *
10628  *  Get the electrostatic potential info at the given index. If present, then the
10629  *  return value is [Vector, Float] (position and potential). If not present, then
10630  *  returns nil.
10631  */
10632 static VALUE
10633 s_Molecule_Elpot(VALUE self, VALUE ival)
10634 {
10635         Molecule *mol;
10636         int idx;
10637     Data_Get_Struct(self, Molecule, mol);
10638         idx = NUM2INT(rb_Integer(ival));
10639         if (idx < 0 || idx >= mol->nelpots)
10640                 return Qnil;
10641         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10642 }
10643
10644 /*
10645  *  call-seq:
10646  *     clear_basis_set
10647  *
10648  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10649  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10650  */
10651 static VALUE
10652 s_Molecule_ClearBasisSet(VALUE self)
10653 {
10654         Molecule *mol;
10655     Data_Get_Struct(self, Molecule, mol);
10656         if (mol != NULL) {
10657                 if (mol->bset != NULL) {
10658                         BasisSetRelease(mol->bset);
10659                         mol->bset = NULL;
10660                 }
10661                 if (mol->mcube != NULL) {
10662                         MoleculeDeallocateMCube(mol->mcube);
10663                         mol->mcube = NULL;
10664                 }
10665         }
10666         return self;
10667 }
10668
10669 /*
10670  *  call-seq:
10671  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10672  *
10673  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10674  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10675  *  -2, D5-type.
10676  */
10677 static VALUE
10678 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10679 {
10680         Molecule *mol;
10681         int sym, nprims, a_idx, n;
10682     Data_Get_Struct(self, Molecule, mol);
10683         a_idx = NUM2INT(rb_Integer(aval));
10684         sym = NUM2INT(rb_Integer(symval));
10685         nprims = NUM2INT(rb_Integer(npval));
10686         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10687         if (n == -1)
10688                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10689         else if (n == -2)
10690                 rb_raise(rb_eMolbyError, "Low memory");
10691         else if (n == -3)
10692                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10693         else if (n != 0)
10694                 rb_raise(rb_eMolbyError, "Unknown error");
10695         return self;
10696 }
10697
10698 /*
10699  *  call-seq:
10700  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10701  *
10702  *  To be used internally. Add a gaussian primitive coefficients.
10703  */
10704 static VALUE
10705 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10706 {
10707         Molecule *mol;
10708         Int n;
10709         Double exponent, contraction, contraction_sp;
10710     Data_Get_Struct(self, Molecule, mol);
10711         exponent = NUM2DBL(rb_Float(expval));
10712         contraction = NUM2DBL(rb_Float(cval));
10713         contraction_sp = NUM2DBL(rb_Float(cspval));
10714         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10715         if (n == -1)
10716                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10717         else if (n == -2)
10718                 rb_raise(rb_eMolbyError, "Low memory");
10719         else if (n != 0)
10720                 rb_raise(rb_eMolbyError, "Unknown error");
10721         return self;
10722 }
10723
10724 /*
10725  *  call-seq:
10726  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10727  *
10728  *  Get the Gaussian shell information for the given MO coefficient index.
10729  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10730  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10731  *  is the number of MO component belonging to this shell.
10732  */
10733 static VALUE
10734 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10735 {
10736         Molecule *mol;
10737         ShellInfo *sp;
10738         int s_idx, sym;
10739     Data_Get_Struct(self, Molecule, mol);
10740         if (mol->bset == NULL)
10741                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10742         s_idx = NUM2INT(rb_Integer(sval));
10743         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10744                 return Qnil;
10745         sp = mol->bset->shells + s_idx;
10746         sym = sp->sym;
10747         switch (sym) {
10748                 case kGTOType_S:  sym = 0;  break;
10749                 case kGTOType_SP: sym = -1; break;
10750                 case kGTOType_P:  sym = 1;  break;
10751                 case kGTOType_D:  sym = 2;  break;
10752                 case kGTOType_D5: sym = -2; break;
10753                 case kGTOType_F:  sym = 3;  break;
10754                 case kGTOType_F7: sym = -3; break;
10755                 case kGTOType_G:  sym = 4;  break;
10756                 case kGTOType_G9: sym = -4; break;
10757                 default:
10758                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10759         }
10760         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10761 }
10762
10763 /*
10764  *  call-seq:
10765  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10766  *
10767  *  Get the Gaussian primitive coefficients for the given MO component.
10768  */
10769 static VALUE
10770 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10771 {
10772         Molecule *mol;
10773         ShellInfo *sp;
10774         PrimInfo *pp;
10775         int s_idx, i;
10776         VALUE retval, aval;
10777     Data_Get_Struct(self, Molecule, mol);
10778         if (mol->bset == NULL)
10779                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10780         s_idx = NUM2INT(rb_Integer(sval));
10781         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10782                 return Qnil;
10783         sp = mol->bset->shells + s_idx;
10784         pp = mol->bset->priminfos + sp->p_idx;
10785         retval = rb_ary_new2(sp->nprim);
10786         for (i = 0; i < sp->nprim; i++) {
10787                 if (sp->sym == kGTOType_SP) {
10788                         /*  With P contraction coefficient  */
10789                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10790                 } else {
10791                         /*  Without P contraction coefficient  */
10792                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10793                 }
10794                 rb_ary_store(retval, i, aval);
10795         }
10796         return retval;
10797 }
10798
10799 /*
10800  *  call-seq:
10801  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10802  *
10803  *  Get the Gaussian shell information for the given MO coefficient index.
10804  */
10805 static VALUE
10806 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10807 {
10808         Molecule *mol;
10809         Int n, c, atom_idx, shell_idx;
10810         char label[32];
10811     Data_Get_Struct(self, Molecule, mol);
10812         if (mol->bset == NULL)
10813                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10814         c = NUM2INT(rb_Integer(cval));
10815         if (c < 0 || c >= mol->bset->ncomps)
10816                 return Qnil;
10817         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10818         if (n != 0)
10819                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10820         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10821 }
10822
10823 /*
10824  *  call-seq:
10825  *     clear_mo_coefficients
10826  *
10827  *  Clear the existing MO coefficients.
10828  */
10829 static VALUE
10830 s_Molecule_ClearMOCoefficients(VALUE self)
10831 {
10832         Molecule *mol;
10833         Data_Get_Struct(self, Molecule, mol);
10834         if (mol->bset != NULL) {
10835                 if (mol->bset->moenergies != NULL) {
10836                         free(mol->bset->moenergies);
10837                         mol->bset->moenergies = NULL;
10838                 }
10839                 if (mol->bset->mo != NULL) {
10840                         free(mol->bset->mo);
10841                         mol->bset->mo = NULL;
10842                 }
10843                 mol->bset->nmos = 0;
10844         }
10845         return self;
10846 }
10847
10848 /*
10849  *  call-seq:
10850  *     set_mo_coefficients(idx, energy, coefficients)
10851  *
10852  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10853  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10854  *  Energy is the MO energy, and coefficients is an array
10855  *  of MO coefficients.
10856  */
10857 static VALUE
10858 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10859 {
10860         Molecule *mol;
10861         Int idx, ncomps, i;
10862         Double energy;
10863         Double *coeffs;
10864     Data_Get_Struct(self, Molecule, mol);
10865         idx = NUM2INT(rb_Integer(ival));
10866         energy = NUM2DBL(rb_Float(eval));
10867         aval = rb_ary_to_ary(aval);
10868         ncomps = RARRAY_LEN(aval);
10869         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10870         if (coeffs == NULL) {
10871                 i = -2;
10872                 goto end;
10873         }
10874         for (i = 0; i < ncomps; i++)
10875                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10876         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10877 end:
10878         if (i == -1)
10879                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10880         else if (i == -2)
10881                 rb_raise(rb_eMolbyError, "Low memory");
10882         else if (i == -3)
10883                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10884         else if (i == -4)
10885                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10886         else if (i == -5)
10887                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10888         else if (i != 0)
10889                 rb_raise(rb_eMolbyError, "Unknown error");
10890         return self;
10891 }
10892
10893 /*
10894  *  call-seq:
10895  *     get_mo_coefficients(idx)
10896  *
10897  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10898  */
10899 static VALUE
10900 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10901 {
10902         Molecule *mol;
10903         Int idx, ncomps, n;
10904         Double energy;
10905         Double *coeffs;
10906         VALUE retval;
10907     Data_Get_Struct(self, Molecule, mol);
10908         idx = NUM2INT(rb_Integer(ival));
10909         ncomps = 0;
10910         coeffs = NULL;
10911         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10912         if (n == -1)
10913                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10914         else if (n == -2)
10915                 rb_raise(rb_eMolbyError, "No basis set information is present");
10916         else if (n == -3)
10917                 return Qnil;  /*  Silently returns nil  */
10918         retval = rb_ary_new2(ncomps);
10919         for (n = 0; n < ncomps; n++)
10920                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10921         free(coeffs);
10922         return retval;
10923 }
10924
10925 /*
10926  *  call-seq:
10927  *     get_mo_energy(idx)
10928  *
10929  *  To be used internally. Get the MO energy for the given MO index (1-based).
10930  */
10931 static VALUE
10932 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10933 {
10934         Molecule *mol;
10935         Int idx, n;
10936         Double energy;
10937     Data_Get_Struct(self, Molecule, mol);
10938         idx = NUM2INT(rb_Integer(ival));
10939         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10940         if (n == -1)
10941                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10942         else if (n == -2)
10943                 rb_raise(rb_eMolbyError, "No basis set information is present");
10944         else if (n == -3)
10945                 return Qnil;
10946         return rb_float_new(energy);
10947 }
10948
10949 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10950
10951 static inline void
10952 s_InitMOInfoKeys(void)
10953 {
10954         if (sTypeSym == 0) {
10955                 sTypeSym = ID2SYM(rb_intern("type"));
10956                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10957                 sBetaSym = ID2SYM(rb_intern("beta"));
10958                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10959                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10960         }
10961 }
10962
10963 /*
10964  *  call-seq:
10965  *     set_mo_info(hash)
10966  *
10967  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10968  *  :alpha=>integer, :beta=>integer
10969  */
10970 static VALUE
10971 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10972 {
10973         Molecule *mol;
10974         VALUE aval;
10975         Int rflag, na, nb, n;
10976         char *s;
10977     Data_Get_Struct(self, Molecule, mol);
10978         if (mol->bset != NULL) {
10979                 rflag = mol->bset->rflag;
10980                 na = mol->bset->ne_alpha;
10981                 nb = mol->bset->ne_beta;
10982         } else {
10983                 rflag = 1;
10984                 na = 0;
10985                 nb = 0;
10986         }
10987         if (hval != Qnil) {
10988                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10989                         s = StringValuePtr(aval);
10990                         if (strcasecmp(s, "RHF") == 0)
10991                                 rflag = 1;
10992                         else if (strcasecmp(s, "UHF") == 0)
10993                                 rflag = 0;
10994                         else if (strcasecmp(s, "ROHF") == 0)
10995                                 rflag = 2;
10996                 }
10997                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10998                         n = NUM2INT(rb_Integer(aval));
10999                         if (n >= 0)
11000                                 na = n;
11001                 }
11002                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
11003                         n = NUM2INT(rb_Integer(aval));
11004                         if (n >= 0)
11005                                 nb = n;
11006                 }
11007                 MoleculeSetMOInfo(mol, rflag, na, nb);
11008         }
11009         return self;
11010 }
11011
11012 /*
11013  *  call-seq:
11014  *     get_mo_info(key)
11015  *
11016  *  Get the MO info. The key is as described in set_mo_info.
11017  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11018  */
11019 static VALUE
11020 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11021 {
11022         Molecule *mol;
11023     Data_Get_Struct(self, Molecule, mol);
11024         if (mol->bset == NULL)
11025                 return Qnil;
11026         if (kval == sTypeSym) {
11027                 switch (mol->bset->rflag) {
11028                         case 0: return Ruby_NewEncodedStringValue2("UHF");
11029                         case 1: return Ruby_NewEncodedStringValue2("RHF");
11030                         case 2: return Ruby_NewEncodedStringValue2("ROHF");
11031                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11032                 }
11033         } else if (kval == sAlphaSym) {
11034                 return INT2NUM(mol->bset->ne_alpha);
11035         } else if (kval == sBetaSym) {
11036                 return INT2NUM(mol->bset->ne_beta);
11037         } else if (kval == sNcompsSym) {
11038                 return INT2NUM(mol->bset->ncomps);
11039         } else if (kval == sNshellsSym) {
11040                 return INT2NUM(mol->bset->nshells);
11041         } else {
11042                 kval = rb_inspect(kval);
11043                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11044                 return Qnil;  /*  Does not reach here  */
11045         }
11046 }
11047
11048 /*
11049  *  call-seq:
11050  *     mo_type
11051  *
11052  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11053  */
11054 static VALUE
11055 s_Molecule_MOType(VALUE self)
11056 {
11057         return s_Molecule_GetMOInfo(self, sTypeSym);
11058 }
11059
11060 #pragma mark ------ Molecular Topology ------
11061
11062 /*
11063  *  call-seq:
11064  *     search_equivalent_atoms(ig = nil)
11065  *
11066  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
11067  */
11068 static VALUE
11069 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11070 {
11071         Molecule *mol;
11072         Int *result, i;
11073         VALUE val;
11074         IntGroup *ig;
11075     Data_Get_Struct(self, Molecule, mol);
11076         if (mol->natoms == 0)
11077                 return Qnil;
11078         rb_scan_args(argc, argv, "01", &val);
11079         if (val != Qnil)
11080                 ig = s_Molecule_AtomGroupFromValue(self, val);
11081         else ig = NULL;
11082         result = MoleculeSearchEquivalentAtoms(mol, ig);
11083         if (result == NULL)
11084                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11085         if (ig != NULL)
11086                 IntGroupRelease(ig);
11087         val = rb_ary_new2(mol->natoms);
11088         for (i = 0; i < mol->natoms; i++)
11089                 rb_ary_push(val, INT2NUM(result[i]));
11090         free(result);
11091         return val;
11092 }
11093
11094 /*
11095  *  call-seq:
11096  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11097  *
11098  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11099  *  Name is the name of the new pi anchor, and group is the atoms that define
11100  *  the pi system. Type (a String) is an atom type for MM implementation.
11101  *  Weights represent the relative significance of the component atoms; if omitted, then
11102  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
11103  *  The weight values will be normalized so that the sum of the weights is 1.0.
11104  *  The weight values must be positive.
11105  *  Index is the atom index where the created pi-anchor is inserted in the 
11106  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
11107  *  having the largest index.
11108  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
11109  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11110  */
11111 static VALUE
11112 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11113 {
11114         Molecule *mol;
11115         VALUE nval, gval;
11116         IntGroup *ig;
11117         Int i, n, idx, last_component;
11118         Atom a, *ap;
11119         PiAnchor an;
11120         AtomRef *aref;
11121         if (argc < 2 || argc >= 6)
11122                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11123         nval = *argv++;
11124         gval = *argv++;
11125         argc -= 2;
11126     Data_Get_Struct(self, Molecule, mol);
11127     if (gval == Qnil)
11128         ig = NULL;
11129     else
11130         ig = s_Molecule_AtomGroupFromValue(self, gval);
11131     if (ig == NULL || IntGroupGetCount(ig) == 0)
11132     rb_raise(rb_eMolbyError, "atom group is not given correctly");
11133         memset(&a, 0, sizeof(a));
11134         memset(&an, 0, sizeof(an));
11135         strncpy(a.aname, StringValuePtr(nval), 4);
11136         if (a.aname[0] == '_')
11137                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11138         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11139         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11140                 if (n >= mol->natoms) {
11141                         AtomConnectResize(&an.connect, 0);
11142                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11143                 }
11144                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11145                 last_component = n;
11146         }
11147         if (an.connect.count == 0)
11148                 rb_raise(rb_eMolbyError, "no atoms are specified");
11149         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11150         for (i = 0; i < an.connect.count; i++) {
11151                 an.coeffs[i] = 1.0 / an.connect.count;
11152         }
11153         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11154                 /*  Atom type  */
11155                 if (argv[0] != Qnil)
11156                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11157                 argc--;
11158                 argv++;
11159         }
11160         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11161                 if (argv[0] != Qnil) {
11162                         VALUE aval = rb_ary_to_ary(argv[0]);
11163                         Double d, sum;
11164                         if (RARRAY_LEN(aval) != an.connect.count)
11165                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11166                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11167                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11168                                 if (d <= 0.0)
11169                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11170                                 sum += d;
11171                                 an.coeffs[i] = d;
11172                         }
11173                         for (i = 0; i < an.connect.count; i++)
11174                                 an.coeffs[i] /= sum;
11175                 }
11176                 argc--;
11177                 argv++;
11178         }
11179         if (argc > 0 && argv[0] != Qnil) {
11180                 /*  Index  */
11181                 idx = NUM2INT(rb_Integer(argv[0]));
11182         } else idx = -1;
11183         if (idx < 0 || idx > mol->natoms) {
11184                 /*  Immediately after the last specified atom  */
11185                 idx = last_component + 1;
11186         }
11187         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11188         memmove(a.anchor, &an, sizeof(PiAnchor));
11189         /*  Use residue information of the last specified atom  */
11190         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11191         a.resSeq = ap->resSeq;
11192         strncpy(a.resName, ap->resName, 4);
11193         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11194                 return Qnil;
11195         MoleculeCalculatePiAnchorPosition(mol, idx);
11196     aref = AtomRefNew(mol, idx);
11197     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11198 }
11199
11200 #pragma mark ------ Molecular Properties ------
11201
11202 /*
11203  *  call-seq:
11204  *     set_property(name, value[, index]) -> value
11205  *     set_property(name, values, group) -> values
11206  *
11207  *  Set molecular property. A property is a floating-point number with a specified name,
11208  *  and can be set for each frame separately. The name of the property is given as a String.
11209  *  The value can be a single floating point number, which is set to the current frame.
11210  *  
11211  */
11212 static VALUE
11213 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11214 {
11215         Molecule *mol;
11216         VALUE nval, vval, ival;
11217         char *name;
11218         IntGroup *ig;
11219         Int i, n, idx, fidx;
11220         Double *dp;
11221         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11222     Data_Get_Struct(self, Molecule, mol);
11223         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11224                 idx = NUM2INT(rb_Integer(nval));
11225                 if (idx < 0 || idx >= mol->nmolprops)
11226                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11227         } else {
11228                 name = StringValuePtr(nval);
11229                 idx = MoleculeLookUpProperty(mol, name);
11230                 if (idx < 0) {
11231                         idx = MoleculeCreateProperty(mol, name);
11232                         if (idx < 0)
11233                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11234                 }
11235         }
11236         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11237                 if (ival == Qnil)
11238                         fidx = mol->cframe;
11239                 else {
11240                         fidx = NUM2INT(rb_Integer(ival));
11241                         n = MoleculeGetNumberOfFrames(mol);
11242                         if (fidx < 0 || fidx >= n)
11243                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11244                 }
11245                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11246                 dp = (Double *)malloc(sizeof(Double));
11247                 *dp = NUM2DBL(rb_Float(vval));
11248                 n = 1;
11249         } else {
11250                 vval = rb_ary_to_ary(vval);
11251                 ig = IntGroupFromValue(ival);
11252                 n = IntGroupGetCount(ig);
11253                 if (n == 0)
11254                         rb_raise(rb_eMolbyError, "No frames are specified");
11255                 if (RARRAY_LEN(vval) < n)
11256                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11257                 dp = (Double *)calloc(sizeof(Double), n);
11258                 for (i = 0; i < n; i++)
11259                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11260         }
11261         
11262         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11263         free(dp);
11264         IntGroupRelease(ig);
11265         return self;
11266 }
11267
11268 /*
11269  *  call-seq:
11270  *     get_property(name[, index]) -> value
11271  *     get_property(name, group) -> values
11272  *
11273  *  Get molecular property. In the first form, a property value for a single frame is returned.
11274  *  (If index is omitted, then the value for the current frame is given)
11275  *  In the second form, an array of property values for the given frames is returned.
11276  *  If name is not one of known properties or a valid index integer, exception is raised.
11277  */
11278 static VALUE
11279 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11280 {
11281         Molecule *mol;
11282         VALUE nval, ival;
11283         char *name;
11284         IntGroup *ig;
11285         Int i, n, idx, fidx;
11286         Double *dp;
11287         rb_scan_args(argc, argv, "11", &nval, &ival);
11288     Data_Get_Struct(self, Molecule, mol);
11289         if (mol->nmolprops == 0)
11290                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11291         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11292                 idx = NUM2INT(rb_Integer(nval));
11293                 if (idx < 0 || idx >= mol->nmolprops)
11294                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11295         } else {
11296                 name = StringValuePtr(nval);
11297                 idx = MoleculeLookUpProperty(mol, name);
11298                 if (idx < 0)
11299                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11300         }
11301         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11302                 if (ival == Qnil)
11303                         fidx = mol->cframe;
11304                 else {
11305                         fidx = NUM2INT(rb_Integer(ival));
11306                         n = MoleculeGetNumberOfFrames(mol);
11307                         if (fidx < 0 || fidx >= n)
11308                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11309                 }
11310                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11311                 ival = INT2FIX(fidx);
11312                 n = 1;
11313         } else {
11314                 ig = IntGroupFromValue(ival);
11315                 n = IntGroupGetCount(ig);
11316                 if (n == 0)
11317                         return rb_ary_new();
11318         }
11319         dp = (Double *)calloc(sizeof(Double), n);
11320         MoleculeGetProperty(mol, idx, ig, dp);  
11321         if (FIXNUM_P(ival))
11322                 ival = rb_float_new(dp[0]);
11323         else {
11324                 ival = rb_ary_new();
11325                 for (i = n - 1; i >= 0; i--) {
11326                         nval = rb_float_new(dp[i]);
11327                         rb_ary_store(ival, i, nval);
11328                 }
11329         }
11330         free(dp);
11331         IntGroupRelease(ig);
11332         return ival;
11333 }
11334
11335 /*
11336  *  call-seq:
11337  *     property_names -> Array
11338  *
11339  *  Get an array of property names.
11340  */
11341 static VALUE
11342 s_Molecule_PropertyNames(VALUE self)
11343 {
11344         Molecule *mol;
11345         VALUE rval, nval;
11346         int i;
11347     Data_Get_Struct(self, Molecule, mol);
11348         rval = rb_ary_new();
11349         for (i = mol->nmolprops - 1; i >= 0; i--) {
11350                 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11351                 rb_ary_store(rval, i, nval);
11352         }
11353         return rval;
11354 }
11355
11356 #pragma mark ------ Class methods ------
11357
11358 /*
11359  *  call-seq:
11360  *     current       -> Molecule
11361  *
11362  *  Get the currently "active" molecule.
11363  */
11364 static VALUE
11365 s_Molecule_Current(VALUE klass)
11366 {
11367         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11368 }
11369
11370 /*
11371  *  call-seq:
11372  *     Molecule[]          -> Molecule
11373  *     Molecule[n]         -> Molecule
11374  *     Molecule[name]      -> Molecule
11375  *     Molecule[name, k]   -> Molecule
11376  *     Molecule[regex]     -> Molecule
11377  *     Molecule[regex, k]  -> Molecule
11378  *
11379  *  Molecule[] is equivalent to Molecule.current.
11380  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11381  *  Molecule[name] gives the first document (in the order of creation time) that has
11382  *  the given name. If a second argument (k) is given, the k-th document that has the
11383  *  given name is returned.
11384  *  Molecule[regex] gives the first document (in the order of creation time) that
11385  *  has a name matching the regular expression. If a second argument (k) is given, 
11386  *  the k-th document that has a name matching the re is returned.
11387  */
11388 static VALUE
11389 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11390 {
11391         VALUE val, kval;
11392         int idx, k;
11393         Molecule *mol;
11394         char buf[1024];
11395         rb_scan_args(argc, argv, "02", &val, &kval);
11396         if (val == Qnil)
11397                 return s_Molecule_Current(klass);
11398         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11399                 idx = NUM2INT(val);
11400                 mol = MoleculeCallback_moleculeAtIndex(idx);
11401         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11402                 char *p = StringValuePtr(val);
11403                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11404                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11405                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11406                         if (strcmp(buf, p) == 0 && --k == 0)
11407                                 break;
11408                 }
11409         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11410                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11411                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11412                         VALUE name;
11413                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11414                         name = Ruby_NewEncodedStringValue2(buf);
11415                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11416                                 break;
11417                 }       
11418         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11419         
11420         if (mol == NULL)
11421                 return Qnil;
11422         else return ValueFromMolecule(mol);
11423 }
11424
11425 /*
11426  *  call-seq:
11427  *     list         -> array of Molecules
11428  *
11429  *  Get the list of molecules associated to the documents, in the order of creation
11430  *  time of the document. If no document is open, returns an empry array.
11431  */
11432 static VALUE
11433 s_Molecule_List(VALUE klass)
11434 {
11435         Molecule *mol;
11436         int i;
11437         VALUE ary;
11438         i = 0;
11439         ary = rb_ary_new();
11440         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11441                 rb_ary_push(ary, ValueFromMolecule(mol));
11442                 i++;
11443         }
11444         return ary;
11445 }
11446
11447 /*
11448  *  call-seq:
11449  *     ordered_list         -> array of Molecules
11450  *
11451  *  Get the list of molecules associated to the documents, in the order of front-to-back
11452  *  ordering of the associated window. If no document is open, returns an empry array.
11453  */
11454 static VALUE
11455 s_Molecule_OrderedList(VALUE klass)
11456 {
11457         Molecule *mol;
11458         int i;
11459         VALUE ary;
11460         i = 0;
11461         ary = rb_ary_new();
11462         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11463                 rb_ary_push(ary, ValueFromMolecule(mol));
11464                 i++;
11465         }
11466         return ary;
11467 }
11468
11469 #pragma mark ------ Call Subprocess ------
11470
11471 /*  The callback functions for call_subprocess_async  */
11472 static int
11473 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11474 {
11475         int ruby_status;
11476         VALUE procval, retval, args[2];
11477         args[0] = ValueFromMolecule(mol);
11478         args[1] = INT2NUM(status);
11479         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11480         if (procval != Qnil) {
11481                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11482                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11483                         return 1;
11484         }
11485         return 0;
11486 }
11487
11488 static int
11489 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11490 {
11491         int ruby_status;
11492         VALUE procval, retval, args[2];
11493         args[0] = ValueFromMolecule(mol);
11494         args[1] = INT2NUM(tcount);
11495         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11496         if (procval != Qnil) {
11497                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11498                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11499                         return 1;
11500         }
11501         return 0;
11502 }
11503
11504 /*
11505  *  call-seq:
11506  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11507  *
11508  *  Call subprocess asynchronically.
11509  *  If end_callback is given, it will be called (with two arguments self and termination status)
11510  *  when the subprocess terminated.
11511  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11512  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11513  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11514  *  filename begins with ">>", then the message will be appended to the file.
11515  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11516  *  If the argument is nil, then the message will be sent to the Ruby console.
11517  *  Returns the process ID as an integer.
11518  */
11519 static VALUE
11520 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11521 {
11522         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11523         Molecule *mol;
11524         char *sout, *serr;
11525         int n;
11526         FILE *fpout, *fperr;
11527         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11528         Data_Get_Struct(self, Molecule, mol);
11529
11530         if (stdout_val == Qnil) {
11531                 fpout = (FILE *)1;
11532         } else {
11533                 sout = StringValuePtr(stdout_val);
11534                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11535                         fpout = NULL;
11536                 else {
11537                         if (strncmp(sout, ">>", 2) == 0) {
11538                                 sout += 2;
11539                                 fpout = fopen(sout, "a");
11540                         } else {
11541                                 if (*sout == '>')
11542                                         sout++;
11543                                 fpout = fopen(sout, "w");
11544                         }
11545                         if (fpout == NULL)
11546                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11547                 }
11548         }
11549         if (stderr_val == Qnil) {
11550                 fperr = (FILE *)1;
11551         } else {
11552                 serr = StringValuePtr(stderr_val);
11553                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11554                         fperr = NULL;
11555                 else {
11556                         if (strncmp(serr, ">>", 2) == 0) {
11557                                 serr += 2;
11558                                 fpout = fopen(serr, "a");
11559                         } else {
11560                                 if (*serr == '>')
11561                                         serr++;
11562                                 fperr = fopen(serr, "w");
11563                         }
11564                         if (fperr == NULL)
11565                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11566                 }
11567         }
11568         
11569         /*  Register procs as instance variables  */
11570         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11571         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11572         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11573         if (fpout != NULL && fpout != (FILE *)1)
11574                 fclose(fpout);
11575         if (fperr != NULL && fperr != (FILE *)1)
11576                 fclose(fperr);
11577         return INT2NUM(n);
11578 }
11579
11580 #pragma mark ====== Define Molby Classes ======
11581
11582 void
11583 Init_Molby(void)
11584 {
11585         int i;
11586         
11587         /*  Define module Molby  */
11588         rb_mMolby = rb_define_module("Molby");
11589         
11590         /*  Define Vector3D, Transform, IntGroup  */
11591         Init_MolbyTypes();
11592         
11593         /*  Define MDArena  */
11594         Init_MolbyMDTypes();
11595
11596         /*  class Molecule  */
11597         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11598
11599         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11600     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11601     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11602         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11603         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11604
11605     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11606     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11607     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11608     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11609     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11610     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11611     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11612     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11613     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11614     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11615         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11616     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11617     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11618     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11619     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11620     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11621     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11622     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11623         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11624         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11625         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11626         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11627         
11628     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11629         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11630     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11631     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11632     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11633
11634     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11635     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11636     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11637     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11638     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11639     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11640         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11641         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11642         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11643         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11644         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11645         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11646         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11647         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11648         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11649         
11650         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11651         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11652         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11653         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11654         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11655         
11656         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11657     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11658         rb_define_alias(rb_cMolecule, "+", "add");
11659     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11660         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11661         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11662         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11663         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11664         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11665         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11666         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11667         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11668         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11669         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11670         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11671         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11672         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11673         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11674         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11675         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11676         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11677         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11678         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11679         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11680
11681         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11682         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11683         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11684         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11685         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11686
11687         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11688         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11689         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11690         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11691         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11692         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11693         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11694         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11695         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11696
11697         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11698         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11699         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11700         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11701         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11702         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11703         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11704         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11705         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11706         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11707         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11708         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11709         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11710         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11711         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11712         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11713         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11714         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11715         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11716         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11717         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11718         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11719
11720         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11721         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11722         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11723         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11724         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11725         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11726         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11727
11728         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11729         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11730         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11731         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11732         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11733         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11734         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11735         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11736         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11737         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11738         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11739         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11740         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11741
11742         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11743         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11744         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11745         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11746         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11747         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11748
11749         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11750         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11751         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11752         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11753         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11754         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11755         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11756         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11757         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11758         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11759         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11760         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11761         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11762         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11763         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11764         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11765         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11766         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11767         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11768         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11769         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11770         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11771         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11772         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11773         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11774         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11775         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11776         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11777         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11778         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11779         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11780         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11781         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11782         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11783         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11784         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11785         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11786         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11787         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11788         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11789         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11790         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11791         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11792         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11793         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11794         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11795         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11796         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11797         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11798         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11799         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11800         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11801         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11802         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11803         
11804         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11805         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11806         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11807         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11808         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11809         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11810         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11811         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11812         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11813         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11814         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11815         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11816         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11817
11818         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11819         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11820         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11821         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11822         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11823         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11824         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11825         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11826         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11827         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11828         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11829         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11830         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11831         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11832                 
11833         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11834         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11835         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11836         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11837         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11838         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11839         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11840         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11841         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11842         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11843         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11844         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11845         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11846         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11847         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11848         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11849         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11850         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11851         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11852         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11853         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11854         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11855         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11856
11857         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11858         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11859         
11860         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11861         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11862         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11863                 
11864         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11865         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11866         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11867         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11868         
11869         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11870         
11871         /*  class MolEnumerable  */
11872         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11873     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11874         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11875         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11876     rb_define_alias(rb_cMolEnumerable, "size", "length");
11877         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11878         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11879
11880         /*  class AtomRef  */
11881         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11882         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11883                 char buf[64];
11884                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11885                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11886                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11887                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11888                 strcat(buf, "=");
11889                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11890         }
11891         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11892         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11893         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11894         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11895         s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11896         rb_global_variable(&s_SetAtomAttrString);
11897         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11898         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11899
11900         /*  class Parameter  */
11901         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11902         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11903         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11904         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11905         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11906         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11907         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11908         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11909         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11910         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11911         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11912         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11913         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11914         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11915         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11916         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11917         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11918         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11919         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11920         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11921         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11922         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11923         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11924         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11925         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11926         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11927         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11928         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11929         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11930         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11931         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11932         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11933         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11934         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11935         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11936         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11937         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11938         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11939         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11940         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11941         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11942         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11943         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11944         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11945         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11946         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11947         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11948         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11949         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11950         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11951         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11952         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11953         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11954         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11955
11956         /*  class ParEnumerable  */
11957         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11958     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11959         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11960         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11961         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11962         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11963         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11964         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11965         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11966         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11967         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11968         
11969         /*  class ParameterRef  */
11970         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11971         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11972                 char buf[64];
11973                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11974                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11975                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11976                 if (s_ParameterAttrDefTable[i].symref != NULL)
11977                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11978                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11979                         strcat(buf, "=");
11980                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11981                 }
11982         }
11983         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11984         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11985         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11986         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11987         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11988         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11989         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11990         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11991
11992         /*  class MolbyError  */
11993         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11994
11995         /*  module Kernel  */
11996         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11997         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11998         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11999         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
12000         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
12001         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
12002         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
12003         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12004         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12005         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12006         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12007         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12008         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12009         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12010         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12011         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12012         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12013         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12014         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12015         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12016         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12017         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12018         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12019         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12020     rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12021     rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12022     rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12023     rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12024     rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12025     rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12026
12027         /*  class IO  */
12028         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12029         
12030         s_ID_equal = rb_intern("==");
12031         g_RubyID_call = rb_intern("call");
12032         
12033         s_InitMOInfoKeys();
12034         
12035         /*  Symbols for graphics  */
12036         s_LineSym = ID2SYM(rb_intern("line"));
12037         s_PolySym = ID2SYM(rb_intern("poly"));
12038         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12039         s_ConeSym = ID2SYM(rb_intern("cone"));
12040         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12041 }
12042
12043 #pragma mark ====== Interface with RubyDialog class ======
12044
12045 RubyValue
12046 RubyDialogCallback_parentModule(void)
12047 {
12048         return (RubyValue)rb_mMolby;
12049 }
12050
12051 #pragma mark ====== External functions ======
12052
12053 static VALUE s_ruby_top_self = Qfalse;
12054 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12055 static VALUE s_ruby_export_local_variables = Qfalse;
12056
12057 static VALUE
12058 s_evalRubyScriptOnMoleculeSub(VALUE val)
12059 {
12060         void **ptr = (void **)val;
12061         Molecule *mol = (Molecule *)ptr[1];
12062         VALUE sval, fnval, lnval, retval;
12063         VALUE binding;
12064
12065         /*  Clear the error information (store in the history array if necessary)  */
12066         sval = rb_errinfo();
12067         if (sval != Qnil) {
12068                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12069                 rb_set_errinfo(Qnil);
12070         }
12071
12072         if (s_ruby_top_self == Qfalse) {
12073                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12074         }
12075         if (s_ruby_get_binding_for_molecule == Qfalse) {
12076                 const char *s1 =
12077                  "lambda { |_mol_, _bind_| \n"
12078                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12079                  "  _proc_.call(_mol_) } ";
12080                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12081                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12082         }
12083         if (s_ruby_export_local_variables == Qfalse) {
12084                 const char *s2 =
12085                 "lambda { |_bind_| \n"
12086                 "   # find local variables newly defined in _bind_ \n"
12087                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12088                 " _a_.each { |_vsym_| \n"
12089                 "   _vname_ = _vsym_.to_s \n"
12090                 "   _vval_ = _bind_.eval(_vname_) \n"
12091                 "   #  Define local variable \n"
12092                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12093                 "   #  Then set value  \n"
12094                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12095                 " } \n"
12096                 "}";
12097                 s_ruby_export_local_variables = rb_eval_string(s2);
12098                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12099         }
12100         if (ptr[2] == NULL) {
12101                 char *scr;
12102                 /*  String literal: we need to specify string encoding  */
12103 #if defined(__WXMSW__)
12104                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12105 #else
12106                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12107 #endif
12108                 sval = Ruby_NewEncodedStringValue2(scr);
12109                 free(scr);
12110                 fnval = Ruby_NewEncodedStringValue2("(eval)");
12111                 lnval = INT2FIX(0);
12112         } else {
12113                 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12114                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12115                 lnval = INT2FIX(1);
12116         }
12117         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12118         if (mol != NULL) {
12119                 VALUE mval = ValueFromMolecule(mol);
12120                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12121         }
12122         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12123         if (mol != NULL) {
12124                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12125         }
12126         return retval;
12127 }
12128
12129 RubyValue
12130 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12131 {
12132         RubyValue retval;
12133         void *args[3];
12134         VALUE save_interrupt_flag;
12135 /*      char *save_ruby_sourcefile;
12136         int save_ruby_sourceline; */
12137         if (gMolbyIsCheckingInterrupt) {
12138                 MolActionAlertRubyIsRunning();
12139                 *status = -1;
12140                 return (RubyValue)Qnil;
12141         }
12142         gMolbyRunLevel++;
12143         args[0] = (void *)script;
12144         args[1] = (void *)mol;
12145         args[2] = (void *)fname;
12146         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12147 /*      save_ruby_sourcefile = ruby_sourcefile;
12148         save_ruby_sourceline = ruby_sourceline; */
12149         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12150         if (*status != 0) {
12151                 /*  Is this 'exit' exception?  */
12152                 VALUE last_exception = rb_gv_get("$!");
12153                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12154                         /*  Capture exit and return the status value  */
12155                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12156                         *status = 0;
12157                         rb_set_errinfo(Qnil);
12158                 }
12159         }
12160         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12161 /*      ruby_sourcefile = save_ruby_sourcefile;
12162         ruby_sourceline = save_ruby_sourceline; */
12163         gMolbyRunLevel--;
12164         return retval;
12165 }
12166
12167 /*  For debug  */
12168 char *
12169 Ruby_inspectValue(RubyValue value)
12170 {
12171     int status;
12172     static char buf[256];
12173     VALUE val = (VALUE)value;
12174     gMolbyRunLevel++;
12175     val = rb_protect(rb_inspect, val, &status);
12176     gMolbyRunLevel--;
12177     if (status == 0) {
12178         char *str = StringValuePtr(val);
12179         strncpy(buf, str, sizeof(buf) - 1);
12180         buf[sizeof(buf) - 1] = 0;
12181     } else {
12182         snprintf(buf, sizeof(buf), "Error status = %d", status);
12183     }
12184     return buf;
12185 }
12186
12187 int
12188 Ruby_showValue(RubyValue value, char **outValueString)
12189 {
12190         VALUE val = (VALUE)value;
12191         if (gMolbyIsCheckingInterrupt) {
12192                 MolActionAlertRubyIsRunning();
12193                 return 0;
12194         }
12195         if (val != Qnil) {
12196                 int status;
12197                 char *str;
12198                 gMolbyRunLevel++;
12199                 val = rb_protect(rb_inspect, val, &status);
12200                 gMolbyRunLevel--;
12201                 if (status != 0)
12202                         return status;
12203                 str = StringValuePtr(val);
12204                 if (outValueString != NULL)
12205                         *outValueString = strdup(str);
12206                 MyAppCallback_showScriptMessage("%s", str);
12207         } else {
12208                 if (outValueString != NULL)
12209                         *outValueString = NULL;
12210         }
12211         return 0;
12212 }
12213
12214 void
12215 Ruby_showError(int status)
12216 {
12217         static const int tag_raise = 6;
12218     char *main_message = "Molby script error";
12219         char *msg = NULL, *msg2;
12220         VALUE val, backtrace;
12221         int interrupted = 0;
12222     int exit_status = -1;
12223         if (status == tag_raise) {
12224                 VALUE errinfo = rb_errinfo();
12225                 VALUE eclass = CLASS_OF(errinfo);
12226                 if (eclass == rb_eInterrupt) {
12227             main_message = "Molby script interrupted";
12228             msg = "Interrupt";
12229                         interrupted = 1;
12230         } else if (eclass == rb_eSystemExit) {
12231             main_message = "Molby script exit";
12232             interrupted = 2;
12233             val = rb_eval_string_protect("$!.status", &status);
12234             if (status == 0) {
12235                 exit_status = NUM2INT(rb_Integer(val));
12236                 asprintf(&msg, "Molby script exit with status %d", exit_status);
12237             } else {
12238                 asprintf(&msg, "Molby script exit with unknown status");
12239             }
12240         }
12241         }
12242         gMolbyRunLevel++;
12243     if (exit_status != 0) {
12244         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12245         if (msg == NULL) {
12246             val = rb_eval_string_protect("$!.to_s", &status);
12247             if (status == 0)
12248                 msg = RSTRING_PTR(val);
12249             else
12250                 msg = "(message not available)";
12251         }
12252         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12253     } else {
12254         msg2 = strdup(msg);
12255     }
12256         MyAppCallback_messageBox(msg2, main_message, 0, 3);
12257         free(msg2);
12258     if (interrupted == 2) {
12259         free(msg);
12260         if (!gUseGUI && exit_status == 0)
12261             exit(0);  // Capture exit(0) here and force exit
12262     }
12263         gMolbyRunLevel--;
12264 }
12265
12266 /*  Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode.  */
12267 int
12268 Molby_loadScript(const char *script, int from_file)
12269 {
12270     int status;
12271     gMolbyRunLevel++;
12272     if (from_file)
12273         rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12274     else
12275         rb_eval_string_protect(script, &status);
12276     gMolbyRunLevel--;
12277     return status;
12278 }
12279
12280 void
12281 Molby_getDescription(char **versionString, char **auxString)
12282 {
12283         extern const char *gVersionString, *gCopyrightString;
12284         extern int gRevisionNumber;
12285         extern char *gLastBuildString;
12286     char *s1, *s2;
12287         char *revisionString;
12288         if (gRevisionNumber > 0) {
12289                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12290         } else revisionString = "";
12291
12292     asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12293 #if defined(__WXMSW__)
12294     #if TARGET_ARCH == 64
12295              "Molby (64 bit)",
12296     #else
12297              "Molby (32 bit)",
12298     #endif
12299 #else
12300              "Molby",
12301 #endif
12302              gVersionString, revisionString, gCopyrightString, gLastBuildString);
12303     if (gUseGUI) {
12304         asprintf(&s2,
12305                  "\nIncluding:\n"
12306                  "%s"
12307                  "ruby %s, http://www.ruby-lang.org/\n"
12308                  "%s\n"
12309                  "FFTW 3.3.2, http://www.fftw.org/\n"
12310                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12311                  "  and Massachusetts Institute of Technology",
12312                  MyAppCallback_getGUIDescriptionString(),
12313                  gRubyVersion, gRubyCopyright);
12314     } else {
12315         asprintf(&s2,
12316                  "Including "
12317                  "ruby %s, http://www.ruby-lang.org/\n"
12318                  "%s\n"
12319                  "FFTW 3.3.2, http://www.fftw.org/\n"
12320                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12321                  "  and Massachusetts Institute of Technology",
12322                  gRubyVersion, gRubyCopyright);
12323
12324     }
12325         if (revisionString[0] != 0)
12326                 free(revisionString);
12327         if (versionString != NULL)
12328         *versionString = s1;
12329     if (auxString != NULL)
12330         *auxString = s2;
12331 }
12332
12333 void
12334 Molby_startup(const char *script, const char *dir)
12335 {
12336         VALUE val;
12337         int status;
12338         char *libpath;
12339         char *respath, *p, *wbuf;
12340
12341         /*  Get version/copyright string from Ruby interpreter  */
12342         {
12343                 gRubyVersion = strdup(ruby_version);
12344                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12345                                  "  ",  /*  Indent for displaying in About dialog  */
12346                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12347         }
12348         
12349         /*  Read build and revision information for Molby  */
12350 /*      {
12351                 char buf[200];
12352                 extern int gRevisionNumber;
12353                 extern char *gLastBuildString;
12354                 FILE *fp = fopen("../buildInfo.txt", "r");
12355                 gLastBuildString = "";
12356                 if (fp != NULL) {
12357                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12358                                 char *p1 = strchr(buf, '\"');
12359                                 char *p2 = strrchr(buf, '\"');
12360                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12361                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12362                                         buf[p2 - p1 - 1] = 0;
12363                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12364                                 }
12365                         }
12366                         fclose(fp);
12367                 }
12368                 fp = fopen("../revisionInfo.txt", "r");
12369                 gRevisionNumber = 0;
12370                 if (fp != NULL) {
12371                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12372                                 gRevisionNumber = strtol(buf, NULL, 0);
12373                         }
12374                         fclose(fp);
12375                 }
12376     } */
12377
12378     if (!gUseGUI) {
12379         char *wbuf2;
12380         Molby_getDescription(&wbuf, &wbuf2);
12381         MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12382         free(wbuf);
12383         free(wbuf2);
12384     }
12385         
12386         /*  Read atom display parameters  */
12387         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12388         MyAppCallback_setConsoleColor(1);
12389         MyAppCallback_showScriptMessage("%s", wbuf);
12390         MyAppCallback_setConsoleColor(0);
12391                 free(wbuf);
12392         }
12393         
12394         /*  Read default parameters  */
12395         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12396         if (wbuf != NULL) {
12397         MyAppCallback_setConsoleColor(1);
12398         MyAppCallback_showScriptMessage("%s", wbuf);
12399         MyAppCallback_setConsoleColor(0);
12400                 free(wbuf);
12401         }
12402                 
12403         /*  Initialize Ruby interpreter  */
12404 #if __WXMSW__
12405         if (gUseGUI) {
12406                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12407                     it causes rb_bug() (= fatal error) during ruby_init().
12408                     As a workaround, these standard streams are reopend as
12409                     NUL stream.  */
12410                 freopen("NUL", "r", stdin);
12411                 freopen("NUL", "w", stdout);
12412                 freopen("NUL", "w", stderr);
12413         }
12414 #endif
12415         ruby_init();
12416
12417         {
12418         /*  Initialize CP932/Windows-31J encodings  */
12419                 extern void Init_shift_jis(void), Init_windows_31j(void),  Init_trans_japanese_sjis(void);
12420         extern int rb_enc_alias(const char *, const char *);
12421         Init_shift_jis();
12422         Init_windows_31j();
12423         Init_trans_japanese_sjis();
12424         rb_enc_alias("CP932", "Windows-31J");
12425     }
12426     
12427 #if defined(__WXMSW__)
12428     {
12429         /*  Set default external encoding  */
12430         /*  The following snippet is taken from encoding.c  */
12431         extern void rb_enc_set_default_external(VALUE encoding);
12432         char cp[sizeof(int) * 8 / 3 + 22];
12433         int status;
12434         VALUE enc;
12435         snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12436         enc = rb_eval_string_protect(cp, &status);
12437         if (status == 0 && !NIL_P(enc)) {
12438             rb_enc_set_default_external(enc);
12439         }
12440         }
12441 #endif
12442
12443         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12444         ruby_incpush(".");
12445         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12446         ruby_incpush(libpath);
12447         free(libpath);
12448         ruby_incpush(dir);
12449
12450         ruby_script("Molby");
12451         
12452         /*  Find the resource path (the parent directory of the given directory)  */
12453         respath = strdup(dir);
12454         p = strrchr(respath, '/');
12455         if (p == NULL && PATH_SEPARATOR != '/')
12456                 p = strrchr(respath, PATH_SEPARATOR);
12457         if (p != NULL)
12458                 *p = 0;
12459         val = Ruby_NewFileStringValue(respath);
12460         rb_define_global_const("MolbyResourcePath", val);
12461         free(respath);
12462
12463         /*  Define Molby classes  */
12464         Init_Molby();
12465     if (gUseGUI)
12466         RubyDialogInitClass();
12467
12468         rb_define_const(rb_mMolby, "ResourcePath", val);
12469         val = Ruby_NewFileStringValue(dir);
12470         rb_define_const(rb_mMolby, "ScriptPath", val);
12471         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12472         val = Ruby_NewFileStringValue(p);
12473         rb_define_const(rb_mMolby, "MbsfPath", val);    
12474         free(p);
12475         
12476         p = MyAppCallback_getHomeDir();
12477         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12478         rb_define_const(rb_mMolby, "HomeDirectory", val);
12479         free(p);
12480         p = MyAppCallback_getDocumentHomeDir();
12481         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12482         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12483         free(p);
12484         
12485     if (gUseGUI)
12486         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12487     else
12488         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12489
12490     {
12491         /*  Create objects for stdout and stderr  */
12492         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12493         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12494         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12495         rb_gv_set("$stdout", val);
12496         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12497         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12498         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12499         rb_gv_set("$stderr", val);
12500
12501         /*  Create objects for stdin  */
12502         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12503         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12504         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12505         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12506         rb_gv_set("$stdin", val);
12507     }
12508         
12509         /*  Global variable to hold error information  */
12510         rb_define_variable("$backtrace", &gMolbyBacktrace);
12511         rb_define_variable("$error_history", &gMolbyErrorHistory);
12512         gMolbyErrorHistory = rb_ary_new();
12513         
12514         /*  Global variables for script menus  */
12515         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12516         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12517         gScriptMenuCommands = rb_ary_new();
12518         gScriptMenuEnablers = rb_ary_new();
12519         
12520     if (gUseGUI) {
12521         /*  Register interrupt check code  */
12522         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12523         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12524         s_SetIntervalTimer(0, 50);
12525     }
12526         
12527         /*  Read the startup script  */
12528         if (script != NULL && script[0] != 0) {
12529                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12530                 gMolbyRunLevel++;
12531                 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12532                 gMolbyRunLevel--;
12533                 if (status != 0)
12534                         Ruby_showError(status);
12535                 else
12536                         MyAppCallback_showScriptMessage("Done.\n");
12537         }
12538 }
12539
12540 void
12541 Molby_buildARGV(int argc, const char **argv)
12542 {
12543         int i;
12544     rb_ary_clear(rb_argv);
12545     for (i = 0; i < argc; i++) {
12546                 VALUE arg = rb_tainted_str_new2(argv[i]);
12547                 OBJ_FREEZE(arg);
12548                 rb_ary_push(rb_argv, arg);
12549     }
12550 }