OSDN Git Service

520889de310e0a02f1a212f71fcd9f1b3e6a2498
[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         FILE *fpout, *fperr;
1016
1017         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
1018
1019         if (stdout_val == Qnil) {
1020                 fpout = (FILE *)1;
1021         } else {
1022                 sout = StringValuePtr(stdout_val);
1023                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
1024                         fpout = NULL;
1025                 else {
1026                         if (strncmp(sout, ">>", 2) == 0) {
1027                                 sout += 2;
1028                                 fpout = fopen(sout, "a");
1029                         } else {
1030                                 if (*sout == '>')
1031                                         sout++;
1032                                 fpout = fopen(sout, "w");
1033                         }
1034                         if (fpout == NULL)
1035                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
1036                 }
1037         }
1038         if (stderr_val == Qnil) {
1039                 fperr = (FILE *)1;
1040         } else {
1041                 serr = StringValuePtr(stderr_val);
1042                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
1043                         fperr = NULL;
1044                 else {
1045                         if (strncmp(serr, ">>", 2) == 0) {
1046                                 serr += 2;
1047                                 fpout = fopen(serr, "a");
1048                         } else {
1049                                 if (*serr == '>')
1050                                         serr++;
1051                                 fperr = fopen(serr, "w");
1052                         }
1053                         if (fperr == NULL)
1054                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
1055                 }
1056         }
1057     
1058     save_interruptFlag = s_SetInterruptFlag(self, Qnil);
1059         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
1060     s_SetInterruptFlag(self, save_interruptFlag);
1061     
1062         if (fpout != NULL && fpout != (FILE *)1)
1063                 fclose(fpout);
1064         if (fperr != NULL && fperr != (FILE *)1)
1065                 fclose(fperr);
1066
1067         return INT2NUM(n);
1068
1069         
1070 }
1071
1072 /*
1073  *  call-seq:
1074  *     backquote(cmd)
1075  *
1076  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
1077  */
1078 static VALUE
1079 s_Kernel_Backquote(VALUE self, VALUE cmd)
1080 {
1081         char *buf;
1082         int n, exitstatus, pid;
1083         VALUE val;
1084         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1085 /*      fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1086         if (n >= 0 && buf != NULL) {
1087                 val = Ruby_NewEncodedStringValue(buf, 0);
1088                 free(buf);
1089         } else {
1090                 val = Ruby_NewEncodedStringValue("", 0);
1091         }
1092         rb_last_status_set(exitstatus, pid);
1093         return val;
1094 }
1095
1096 #pragma mark ====== User defaults ======
1097
1098 /*
1099  *  call-seq:
1100  *     get_global_settings(key)
1101  *
1102  *  Get a setting data for key from the application preferences.
1103  */
1104 static VALUE
1105 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1106 {
1107         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1108         if (p != NULL) {
1109                 VALUE retval = rb_eval_string(p);
1110                 free(p);
1111                 return retval;
1112         } else return Qnil;
1113 }
1114
1115 /*
1116  *  call-seq:
1117  *     set_global_settings(key, value)
1118  *
1119  *  Set a setting data for key to the application preferences.
1120  */
1121 static VALUE
1122 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1123 {
1124         VALUE sval = rb_inspect(value);
1125         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1126         return value;
1127 }
1128
1129 #pragma mark ====== IO extension ======
1130
1131 static VALUE
1132 s_Ruby_str_encode_protected(VALUE val)
1133 {
1134         return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1135                                   ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1136 }
1137
1138 /*
1139  *  call-seq:
1140  *     gets_any_eol
1141  *
1142  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1143  */
1144 static VALUE
1145 s_IO_gets_any_eol(VALUE self)
1146 {
1147         VALUE val, val2, cval;
1148         char buf[1024];
1149         int i, c, status;
1150         static ID id_getbyte = 0, id_ungetbyte;
1151         if (id_getbyte == 0) {
1152                 id_getbyte = rb_intern("getbyte");
1153                 id_ungetbyte = rb_intern("ungetbyte");
1154         }
1155         i = 0;
1156         val = Qnil;
1157         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1158                 c = NUM2INT(rb_Integer(cval));
1159                 if (c == 0x0d) {
1160                         cval = rb_funcall(self, id_getbyte, 0);
1161                         if (cval != Qnil) {
1162                                 c = NUM2INT(rb_Integer(cval));
1163                                 if (c != 0x0a)
1164                                         rb_funcall(self, id_ungetbyte, 1, cval);
1165                         }
1166                         break;
1167                 } else if (c != 0x0a) {
1168                         buf[i++] = c;
1169                         if (i >= 1020) {
1170                                 buf[i] = 0;
1171                                 if (val == Qnil)
1172                                         val = rb_str_new(buf, i);
1173                                 else
1174                                         rb_str_append(val, rb_str_new(buf, i));
1175                                 i = 0;
1176                         }
1177                 } else break;
1178         }
1179         if (cval == Qnil && i == 0 && val == Qnil)
1180                 return Qnil;  /*  End of file  */
1181         buf[i] = 0;
1182         if (val == Qnil)
1183                 val = rb_str_new(buf, i);
1184         else if (i > 0)
1185                 rb_str_append(val, rb_str_new(buf, i));
1186         val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /*  Ignore exception  */
1187         if (status == 0)
1188                 val = val2;
1189         if (cval != Qnil) {
1190                 /*  Needs a end-of-line mark  */
1191                 cval = rb_gv_get("$/");
1192                 rb_str_append(val, cval);
1193         }
1194         rb_gv_set("$_", val);
1195         return val;
1196 }
1197
1198 #pragma mark ====== Utility functions (protected funcall) ======
1199
1200 struct Ruby_funcall2_record {
1201         VALUE recv;
1202         ID mid;
1203         int argc;
1204         VALUE *argv;
1205 };
1206
1207 static VALUE
1208 s_Ruby_funcall2_sub(VALUE data)
1209 {
1210         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1211         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1212 }
1213
1214 VALUE
1215 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1216 {
1217         struct Ruby_funcall2_record rec;
1218         rec.recv = recv;
1219         rec.mid = mid;
1220         rec.argc = argc;
1221         rec.argv = argv;
1222         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1223 }
1224
1225 RubyValue
1226 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1227 {
1228         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1229 }
1230
1231 #pragma mark ====== ParameterRef Class ======
1232
1233 static UnionPar *
1234 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1235 {
1236         UnionPar *up;
1237         ParameterRef *pref;
1238         Data_Get_Struct(self, ParameterRef, pref);
1239         if (typep != NULL)
1240                 *typep = pref->parType;
1241         if (pref->parType == kElementParType) {
1242                 up = (UnionPar *)&gElementParameters[pref->idx];
1243         } else {
1244                 up = ParameterRefGetPar(pref);
1245                 if (checkEditable) {
1246                         if (pref->idx < 0)
1247                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1248                         if (up->bond.src != 0 && up->bond.src != -1)
1249                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1250                 }
1251         }
1252         return up;
1253 }
1254
1255 static void
1256 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1257 {
1258         UnionPar *up;
1259         ParameterRef *pref;
1260         Data_Get_Struct(self, ParameterRef, pref);
1261         if (pref->mol == NULL)
1262                 return;
1263         up = ParameterRefGetPar(pref);
1264         if (key != s_SourceSym)
1265                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1266         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1267                 /*  Register undo  */
1268                 MolAction *act;
1269                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1270                 MolActionCallback_registerUndo(pref->mol, act);
1271                 MoleculeCallback_notifyModification(pref->mol, 0);
1272                 pref->mol->needsMDRebuild = 1;
1273         }
1274 }
1275
1276 VALUE
1277 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1278 {
1279         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1280         if (pref != NULL)
1281                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1282         else
1283                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1284 }
1285
1286 static int
1287 s_AtomTypeIndexFromValue(VALUE val)
1288 {
1289         if (rb_obj_is_kind_of(val, rb_cNumeric))
1290                 return NUM2INT(val);
1291         else
1292                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1293 }
1294
1295 static const char *s_ParameterTypeNames[] = {
1296         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1297 };
1298 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1299
1300 static int
1301 s_ParTypeFromValue(VALUE val)
1302 {
1303         int i, n;
1304         ID valid;
1305         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1306         if (s_ParameterTypeIDs[0] == 0) {
1307                 for (i = 0; i < n; i++)
1308                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1309         }
1310         valid = rb_to_id(val);
1311         for (i = 0; i < n; i++) {
1312                 if (valid == s_ParameterTypeIDs[i]) {
1313                         if (i == 7)
1314                                 return kElementParType;
1315                         else return kFirstParType + i;
1316                 }
1317         }
1318         return kInvalidParType;
1319 }
1320
1321 /*
1322  *  call-seq:
1323  *     index -> Integer
1324  *
1325  *  Get the index in the parameter list.
1326  */
1327 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1328         ParameterRef *pref;
1329         Data_Get_Struct(self, ParameterRef, pref);
1330         return INT2NUM(pref->idx);
1331 }
1332
1333 /*
1334  *  call-seq:
1335  *     par_type -> String
1336  *
1337  *  Get the parameter type, like "bond", "angle", etc.
1338  */
1339 static VALUE s_ParameterRef_GetParType(VALUE self) {
1340         Int tp;
1341         s_UnionParFromValue(self, &tp, 0);
1342         if (tp == kElementParType)
1343                 return Ruby_NewEncodedStringValue2("element");
1344         tp -= kFirstParType;
1345         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1346                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1347         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1348 }
1349
1350 /*
1351  *  call-seq:
1352  *     atom_type -> String or Array of String
1353  *     atom_types -> String or Array of String
1354  *
1355  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1356  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1357  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1358  *  The atom type may be "X", which is a wildcard that matches any atom type.
1359  */
1360 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1361         UnionPar *up;
1362         Int tp, i, n;
1363         UInt types[4];
1364         VALUE vals[4];
1365         up = s_UnionParFromValue(self, &tp, 0);
1366         n = ParameterGetAtomTypes(tp, up, types);
1367         if (n == 0)
1368                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1369         for (i = 0; i < n; i++) {
1370                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1371                         vals[i] = INT2NUM(types[i]);
1372                 else
1373                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1374         }
1375         if (n == 1)
1376                 return vals[0];
1377         else
1378                 return rb_ary_new4(n, vals);
1379 }
1380
1381 /*
1382  *  call-seq:
1383  *     k -> Float
1384  *
1385  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1386  */
1387 static VALUE s_ParameterRef_GetK(VALUE self) {
1388         UnionPar *up;
1389         Int tp, i, n;
1390         VALUE vals[3];
1391         up = s_UnionParFromValue(self, &tp, 0);
1392         switch (tp) {
1393                 case kBondParType:
1394                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1395                 case kAngleParType:
1396                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1397                 case kDihedralParType:
1398                 case kImproperParType:
1399                         if (up->torsion.mult == 1)
1400                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1401                         n = up->torsion.mult;
1402                         if (n > 3)
1403                                 n = 3;
1404                         for (i = 0; i < n; i++)
1405                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1406                         return rb_ary_new4(n, vals);
1407                 default:
1408                         rb_raise(rb_eMolbyError, "invalid member k");
1409         }
1410 }
1411
1412 /*
1413  *  call-seq:
1414  *     r0 -> Float
1415  *
1416  *  Get the equilibrium bond length. Only available for bond parameters.
1417  */
1418 static VALUE s_ParameterRef_GetR0(VALUE self) {
1419         UnionPar *up;
1420         Int tp;
1421         up = s_UnionParFromValue(self, &tp, 0);
1422         if (tp == kBondParType)
1423                 return rb_float_new(up->bond.r0);
1424         else rb_raise(rb_eMolbyError, "invalid member r0");
1425 }
1426
1427 /*
1428  *  call-seq:
1429  *     a0 -> Float
1430  *
1431  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1432  */
1433 static VALUE s_ParameterRef_GetA0(VALUE self) {
1434         UnionPar *up;
1435         Int tp;
1436         up = s_UnionParFromValue(self, &tp, 0);
1437         if (tp == kAngleParType)
1438                 return rb_float_new(up->angle.a0 * kRad2Deg);
1439         else rb_raise(rb_eMolbyError, "invalid member a0");
1440 }
1441
1442 /*
1443  *  call-seq:
1444  *     mult -> Float
1445  *
1446  *  Get the multiplicity. Only available for dihedral and improper parameters.
1447  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1448  */
1449 static VALUE s_ParameterRef_GetMult(VALUE self) {
1450         UnionPar *up;
1451         Int tp;
1452         up = s_UnionParFromValue(self, &tp, 0);
1453         if (tp == kDihedralParType || tp == kImproperParType)
1454                 return rb_float_new(up->torsion.mult);
1455         else rb_raise(rb_eMolbyError, "invalid member mult");
1456 }
1457
1458 /*
1459  *  call-seq:
1460  *     period -> Integer or Array of Integers
1461  *
1462  *  Get the periodicity. Only available for dihedral and improper parameters.
1463  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1464  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1465  */
1466 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1467         UnionPar *up;
1468         Int tp, i, n;
1469         VALUE vals[3];
1470         up = s_UnionParFromValue(self, &tp, 0);
1471         if (tp == kDihedralParType || tp == kImproperParType) {
1472                 if (up->torsion.mult == 1)
1473                         return INT2NUM(up->torsion.period[0]);
1474                 n = up->torsion.mult;
1475                 if (n > 3)
1476                         n = 3;
1477                 for (i = 0; i < n; i++)
1478                         vals[i] = INT2NUM(up->torsion.period[i]);
1479                 return rb_ary_new4(n, vals);
1480         } else rb_raise(rb_eMolbyError, "invalid member period");
1481 }
1482
1483 /*
1484  *  call-seq:
1485  *     phi0 -> Float or Array of Floats
1486  *
1487  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1488  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1489  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1490  */
1491 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1492         UnionPar *up;
1493         Int tp, i, n;
1494         VALUE vals[3];
1495         up = s_UnionParFromValue(self, &tp, 0);
1496         if (tp == kDihedralParType || tp == kImproperParType) {
1497                 if (up->torsion.mult == 1)
1498                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1499                 n = up->torsion.mult;
1500                 if (n > 3)
1501                         n = 3;
1502                 for (i = 0; i < n; i++)
1503                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1504                 return rb_ary_new4(n, vals);
1505         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1506 }
1507
1508 /*
1509  *  call-seq:
1510  *     A -> Float
1511  *
1512  *  Get the "A" value for the van der Waals parameter.
1513  */
1514 /*
1515  static VALUE s_ParameterRef_GetA(VALUE self) {
1516         UnionPar *up;
1517         Int tp;
1518         up = s_UnionParFromValue(self, &tp, 0);
1519         if (tp == kVdwParType)
1520                 return rb_float_new(up->vdw.A);
1521         else if (tp == kVdwPairParType)
1522                 return rb_float_new(up->vdwp.A);
1523         else rb_raise(rb_eMolbyError, "invalid member A");
1524 }
1525 */
1526
1527 /*
1528  *  call-seq:
1529  *     B -> Float
1530  *
1531  *  Get the "B" value for the van der Waals parameter.
1532  */
1533 /*
1534 static VALUE s_ParameterRef_GetB(VALUE self) {
1535         UnionPar *up;
1536         Int tp;
1537         up = s_UnionParFromValue(self, &tp, 0);
1538         if (tp == kVdwParType)
1539                 return rb_float_new(up->vdw.B);
1540         else if (tp == kVdwPairParType)
1541                 return rb_float_new(up->vdwp.B);
1542         else rb_raise(rb_eMolbyError, "invalid member B");
1543 }
1544 */
1545
1546 /*
1547  *  call-seq:
1548  *     r_eq -> Float
1549  *
1550  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1551  */
1552 static VALUE s_ParameterRef_GetReq(VALUE self) {
1553         UnionPar *up;
1554         Int tp;
1555 /*      Double a, b, r; */
1556         Double r;
1557         up = s_UnionParFromValue(self, &tp, 0);
1558         if (tp == kVdwParType) {
1559         /*      a = up->vdw.A;
1560                 b = up->vdw.B;  */
1561                 r = up->vdw.r_eq;
1562         } else if (tp == kVdwPairParType) {
1563         /*      a = up->vdwp.A;
1564                 b = up->vdwp.B;  */
1565                 r = up->vdwp.r_eq;
1566         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1567 /*      if (a == 0.0 || b == 0.0) */
1568         return rb_float_new(r);
1569 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1570 }
1571
1572 /*
1573  *  call-seq:
1574  *     eps -> Float
1575  *
1576  *  Get the minimum energy for the van der Waals parameter.
1577  */
1578 static VALUE s_ParameterRef_GetEps(VALUE self) {
1579         UnionPar *up;
1580         Int tp;
1581 /*      Double a, b; */
1582         Double eps;
1583         up = s_UnionParFromValue(self, &tp, 0);
1584         if (tp == kVdwParType) {
1585         /*      a = up->vdw.A;
1586                 b = up->vdw.B;  */
1587                 eps = up->vdw.eps;
1588         } else if (tp == kVdwPairParType) {
1589         /*      a = up->vdwp.A;
1590                 b = up->vdwp.B; */
1591                 eps = up->vdwp.eps;
1592         } else rb_raise(rb_eMolbyError, "invalid member eps");
1593 /*      if (a == 0.0 || b == 0.0)  */
1594                 return rb_float_new(eps * INTERNAL2KCAL);
1595 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1596 }
1597
1598 /*
1599  *  call-seq:
1600  *     A14 -> Float
1601  *
1602  *  Get the "A" value for the 1-4 van der Waals parameter.
1603  */
1604 /*
1605 static VALUE s_ParameterRef_GetA14(VALUE self) {
1606         UnionPar *up;
1607         Int tp;
1608         up = s_UnionParFromValue(self, &tp, 0);
1609         if (tp == kVdwParType)
1610                 return rb_float_new(up->vdw.A14);
1611         else if (tp == kVdwPairParType)
1612                 return rb_float_new(up->vdwp.A14);
1613         else rb_raise(rb_eMolbyError, "invalid member A14");
1614 }
1615 */
1616
1617 /*
1618  *  call-seq:
1619  *     B14 -> Float
1620  *
1621  *  Get the "B" value for the 1-4 van der Waals parameter.
1622  */
1623 /*
1624 static VALUE s_ParameterRef_GetB14(VALUE self) {
1625         UnionPar *up;
1626         Int tp;
1627         up = s_UnionParFromValue(self, &tp, 0);
1628         if (tp == kVdwParType)
1629                 return rb_float_new(up->vdw.B14);
1630         else if (tp == kVdwPairParType)
1631                 return rb_float_new(up->vdwp.B14);
1632         else rb_raise(rb_eMolbyError, "invalid member B14");
1633 }
1634 */
1635
1636 /*
1637  *  call-seq:
1638  *     r_eq14 -> Float
1639  *
1640  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1641  */
1642 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1643         UnionPar *up;
1644         Int tp;
1645 /*      Double a, b, r; */
1646         Double r;
1647         up = s_UnionParFromValue(self, &tp, 0);
1648         if (tp == kVdwParType) {
1649         /*      a = up->vdw.A14;
1650                 b = up->vdw.B14; */
1651                 r = up->vdw.r_eq14;
1652         } else if (tp == kVdwPairParType) {
1653         /*      a = up->vdwp.A14;
1654                 b = up->vdwp.B14;  */
1655                 r = up->vdwp.r_eq14;
1656         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1657 /*      if (a == 0.0 || b == 0.0)  */
1658         return rb_float_new(r);
1659 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1660 }
1661
1662 /*
1663  *  call-seq:
1664  *     eps14 -> Float
1665  *
1666  *  Get the minimum energy for the 1-4 van der Waals parameter.
1667  */
1668 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1669         UnionPar *up;
1670         Int tp;
1671 /*      Double a, b;  */
1672         Double eps;
1673         up = s_UnionParFromValue(self, &tp, 0);
1674         if (tp == kVdwParType) {
1675         /*      a = up->vdw.A14;
1676                 b = up->vdw.B14;  */
1677                 eps = up->vdw.eps14;
1678         } else if (tp == kVdwPairParType) {
1679         /*      a = up->vdwp.A14;
1680                 b = up->vdwp.B14; */
1681                 eps = up->vdwp.eps14;
1682         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1683 /*      if (a == 0.0 || b == 0.0) */
1684         return rb_float_new(eps * INTERNAL2KCAL);
1685 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1686 }
1687
1688 /*
1689  *  call-seq:
1690  *     cutoff -> Float
1691  *
1692  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1693  */
1694 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1695         UnionPar *up;
1696         Int tp;
1697         up = s_UnionParFromValue(self, &tp, 0);
1698         if (tp == kVdwCutoffParType)
1699                 return rb_float_new(up->vdwcutoff.cutoff);
1700         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1701 }
1702
1703 /*
1704  *  call-seq:
1705  *     radius -> Float
1706  *
1707  *  Get the atomic (covalent) radius for the element parameter.
1708  */
1709 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1710         UnionPar *up;
1711         Int tp;
1712         up = s_UnionParFromValue(self, &tp, 0);
1713         if (tp == kElementParType)
1714                 return rb_float_new(up->atom.radius);
1715         else rb_raise(rb_eMolbyError, "invalid member radius");
1716 }
1717
1718 /*
1719  *  call-seq:
1720  *     vdw_radius -> Float
1721  *
1722  *  Get the van der Waals radius for the element parameter. (0 if not given)
1723  */
1724 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1725         UnionPar *up;
1726         Int tp;
1727         up = s_UnionParFromValue(self, &tp, 0);
1728         if (tp == kElementParType)
1729                 return rb_float_new(up->atom.vdw_radius);
1730         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1731 }
1732
1733 /*
1734  *  call-seq:
1735  *     color -> [Float, Float, Float]
1736  *
1737  *  Get the rgb color for the element parameter.
1738  */
1739 static VALUE s_ParameterRef_GetColor(VALUE self) {
1740         UnionPar *up;
1741         Int tp;
1742         up = s_UnionParFromValue(self, &tp, 0);
1743         if (tp == kElementParType)
1744                 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));
1745         else rb_raise(rb_eMolbyError, "invalid member color");
1746 }
1747
1748 /*
1749  *  call-seq:
1750  *     atomic_number -> Integer
1751  *
1752  *  Get the atomic number for the vdw or element parameter.
1753  */
1754 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1755         UnionPar *up;
1756         Int tp;
1757         up = s_UnionParFromValue(self, &tp, 0);
1758         if (tp == kElementParType)
1759                 return INT2NUM(up->atom.number);
1760         else if (tp == kVdwParType)
1761                 return INT2NUM(up->vdw.atomicNumber);
1762         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1763 }
1764
1765 /*
1766  *  call-seq:
1767  *     name -> String
1768  *
1769  *  Get the name for the element parameter.
1770  */
1771 static VALUE s_ParameterRef_GetName(VALUE self) {
1772         UnionPar *up;
1773         Int tp;
1774         up = s_UnionParFromValue(self, &tp, 0);
1775         if (tp == kElementParType) {
1776                 char name[5];
1777                 strncpy(name, up->atom.name, 4);
1778                 name[4] = 0;
1779                 return Ruby_NewEncodedStringValue2(name);
1780         } else rb_raise(rb_eMolbyError, "invalid member name");
1781 }
1782
1783 /*
1784  *  call-seq:
1785  *     weight -> Float
1786  *
1787  *  Get the atomic weight for the element parameter.
1788  */
1789 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1790         UnionPar *up;
1791         Int tp;
1792         up = s_UnionParFromValue(self, &tp, 0);
1793         if (tp == kElementParType)
1794                 return rb_float_new(up->atom.weight);
1795         else if (tp == kVdwParType)
1796                 return rb_float_new(up->vdw.weight);
1797         else rb_raise(rb_eMolbyError, "invalid member weight");
1798 }
1799
1800 /*
1801  *  call-seq:
1802  *     fullname -> String
1803  *
1804  *  Get the full name for the element parameter.
1805  */
1806 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1807         UnionPar *up;
1808         Int tp;
1809         up = s_UnionParFromValue(self, &tp, 0);
1810         if (tp == kElementParType) {
1811                 char fullname[16];
1812                 strncpy(fullname, up->atom.fullname, 15);
1813                 fullname[15] = 0;
1814                 return Ruby_NewEncodedStringValue2(fullname);
1815         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1816 }
1817
1818 /*
1819  *  call-seq:
1820  *     comment -> String
1821  *
1822  *  Get the comment for the parameter.
1823  */
1824 static VALUE s_ParameterRef_GetComment(VALUE self) {
1825         UnionPar *up;
1826         Int tp, com;
1827         up = s_UnionParFromValue(self, &tp, 0);
1828         com = up->bond.com;
1829         if (com == 0)
1830                 return Qnil;
1831         else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1832 }
1833
1834 /*
1835  *  call-seq:
1836  *     source -> String
1837  *
1838  *  Get the source string for the parameter. Returns false for undefined parameter,
1839  *  and nil for "local" parameter that is specific for the molecule.
1840  */
1841 static VALUE s_ParameterRef_GetSource(VALUE self) {
1842         UnionPar *up;
1843         Int tp, src;
1844         up = s_UnionParFromValue(self, &tp, 0);
1845         src = up->bond.src;
1846         if (src < 0)
1847                 return Qfalse;  /* undefined */
1848         else if (src == 0)
1849                 return Qnil;  /*  local  */
1850         else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1851 }
1852
1853 static void
1854 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1855 {
1856         VALUE *valp;
1857         int i;
1858         if (n == 1)
1859                 valp = &val;
1860         else {
1861                 if (rb_obj_is_kind_of(val, rb_cString)) {
1862                         char *s = StringValuePtr(val);
1863                         char *p;
1864                         for (i = 0; i < n; i++) {
1865                                 char buf[40];
1866                                 int len;
1867                                 /*  Skip leading separaters  */
1868                                 while (*s == '-' || *s == ' ' || *s == '\t')
1869                                         s++;
1870                                 for (p = s; *p != 0; p++) {
1871                                         if (*p == '-' || *p == ' ' || *p == '\t')
1872                                                 break;
1873                                 }
1874                                 len = p - s;
1875                                 if (len >= sizeof(buf))
1876                                         len = sizeof(buf) - 1;
1877                                 strncpy(buf, s, len);
1878                                 buf[len] = 0;
1879                                 /*  Skip trailing blanks  */
1880                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1881                                         buf[len] = 0;
1882                                 if (buf[0] == 0)
1883                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1884                                 if (buf[0] >= '0' && buf[0] <= '9')
1885                                         types[i] = atoi(buf);
1886                                 else
1887                                         types[i] = AtomTypeEncodeToUInt(buf);
1888                                 if (p == NULL || *p == 0) {
1889                                         i++;
1890                                         break;
1891                                 } else s = p + 1;
1892                         }
1893                         if (i < n)
1894                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1895                         return;
1896                 }
1897                 val = rb_ary_to_ary(val);
1898                 if (RARRAY_LEN(val) != n)
1899                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1900                 valp = RARRAY_PTR(val);
1901         }
1902         for (i = 0; i < n; i++) {
1903                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1904                         types[i] = NUM2INT(rb_Integer(valp[i]));
1905                 else {
1906                         VALUE sval = valp[i];
1907                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1908                 }
1909         }
1910 }
1911
1912 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1913         UnionPar *up;
1914         VALUE oldval;
1915         Int oldsrc, tp;
1916         UInt types[4];
1917         up = s_UnionParFromValue(self, &tp, 1);
1918         oldval = s_ParameterRef_GetAtomTypes(self);
1919         oldsrc = up->bond.src;
1920         switch (tp) {
1921                 case kBondParType:
1922                         s_ScanAtomTypes(val, 2, types);
1923                         up->bond.type1 = types[0];
1924                         up->bond.type2 = types[1];
1925                         break;
1926                 case kAngleParType:
1927                         s_ScanAtomTypes(val, 3, types);
1928                         up->angle.type1 = types[0];
1929                         up->angle.type2 = types[1];
1930                         up->angle.type3 = types[2];
1931                         break;
1932                 case kDihedralParType:
1933                 case kImproperParType:
1934                         s_ScanAtomTypes(val, 4, types);
1935                         up->torsion.type1 = types[0];
1936                         up->torsion.type2 = types[1];
1937                         up->torsion.type3 = types[2];
1938                         up->torsion.type4 = types[3];
1939                         break;
1940                 case kVdwParType:
1941                         s_ScanAtomTypes(val, 1, types);
1942                         up->vdw.type1 = types[0];
1943                         break;
1944                 case kVdwPairParType:
1945                         s_ScanAtomTypes(val, 2, types);
1946                         up->vdwp.type1 = types[0];
1947                         up->vdwp.type2 = types[1];
1948                         break;
1949                 case kVdwCutoffParType:
1950                         s_ScanAtomTypes(val, 2, types);
1951                         up->vdwcutoff.type1 = types[0];
1952                         up->vdwcutoff.type2 = types[1];
1953                         break;
1954                 default:
1955                         return Qnil;
1956         }
1957         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1958         return val;
1959 }
1960
1961 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1962         UnionPar *up;
1963         Int tp, i, n, oldsrc;
1964         VALUE *valp, oldval;
1965         up = s_UnionParFromValue(self, &tp, 1);
1966         oldval = s_ParameterRef_GetK(self);
1967         oldsrc = up->bond.src;
1968         switch (tp) {
1969                 case kBondParType:
1970                         val = rb_Float(val);
1971                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1972                         break;
1973                 case kAngleParType:
1974                         val = rb_Float(val);
1975                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1976                         break;
1977                 case kDihedralParType:
1978                 case kImproperParType:
1979                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1980                                 up->torsion.mult = 1;
1981                                 val = rb_Float(val);
1982                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1983                                 break;
1984                         }
1985                         n = up->torsion.mult;
1986                         if (n > 3)
1987                                 n = 3;
1988                         val = rb_ary_to_ary(val);
1989                         if (RARRAY_LEN(val) != n)
1990                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1991                         valp = RARRAY_PTR(val);
1992                         for (i = 0; i < n; i++) {
1993                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1994                         }
1995                         break;
1996                 default:
1997                         rb_raise(rb_eMolbyError, "invalid member k");
1998         }
1999         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
2000         return val;
2001 }
2002
2003 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
2004         UnionPar *up;
2005         Int tp, oldsrc;
2006         VALUE oldval;
2007         up = s_UnionParFromValue(self, &tp, 1);
2008         oldval = s_ParameterRef_GetR0(self);
2009         oldsrc = up->bond.src;
2010         if (tp == kBondParType) {
2011                 val = rb_Float(val);
2012                 up->bond.r0 = NUM2DBL(val);
2013         } else rb_raise(rb_eMolbyError, "invalid member r0");
2014         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
2015         return val;
2016 }
2017
2018 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
2019         UnionPar *up;
2020         Int tp, oldsrc;
2021         VALUE oldval;
2022         up = s_UnionParFromValue(self, &tp, 1);
2023         oldval = s_ParameterRef_GetA0(self);
2024         oldsrc = up->bond.src;
2025         if (tp == kAngleParType) {
2026                 val = rb_Float(val);
2027                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
2028         } else rb_raise(rb_eMolbyError, "invalid member a0");
2029         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
2030         return val;
2031 }
2032
2033 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
2034         UnionPar *up;
2035         Int tp, oldsrc;
2036         VALUE oldval;
2037         up = s_UnionParFromValue(self, &tp, 1);
2038         oldval = s_ParameterRef_GetMult(self);
2039         oldsrc = up->bond.src;
2040         if (tp == kDihedralParType || tp == kImproperParType) {
2041                 int i;
2042                 val = rb_Integer(val);
2043                 i = NUM2INT(val);
2044                 if (i < 0 || i > 3)
2045                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
2046                 up->torsion.mult = i;
2047         } else rb_raise(rb_eMolbyError, "invalid member mult");
2048         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
2049         return val;
2050 }
2051
2052 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
2053         UnionPar *up;
2054         Int tp, i, n, oldsrc;
2055         VALUE *valp, oldval;
2056         up = s_UnionParFromValue(self, &tp, 1);
2057         oldval = s_ParameterRef_GetPeriod(self);
2058         oldsrc = up->bond.src;
2059         if (tp == kDihedralParType || tp == kImproperParType) {
2060                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2061                         up->torsion.mult = 1;
2062                         val = rb_Integer(val);
2063                         up->torsion.period[0] = NUM2INT(val);
2064                 } else {
2065                         n = up->torsion.mult;
2066                         if (n > 3)
2067                                 n = 3;
2068                         val = rb_ary_to_ary(val);
2069                         if (RARRAY_LEN(val) != n)
2070                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
2071                         valp = RARRAY_PTR(val);
2072                         for (i = 0; i < n; i++) {
2073                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
2074                         }
2075                 }
2076         } else rb_raise(rb_eMolbyError, "invalid member period");
2077         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2078         return val;
2079 }
2080
2081 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2082         UnionPar *up;
2083         Int tp, i, n, oldsrc;
2084         VALUE *valp, oldval;
2085         up = s_UnionParFromValue(self, &tp, 1);
2086         oldval = s_ParameterRef_GetPhi0(self);
2087         oldsrc = up->bond.src;
2088         if (tp == kDihedralParType || tp == kImproperParType) {
2089                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2090                         up->torsion.mult = 1;
2091                         val = rb_Float(val);
2092                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2093                 } else {
2094                         n = up->torsion.mult;
2095                         if (n > 3)
2096                                 n = 3;
2097                         val = rb_ary_to_ary(val);
2098                         if (RARRAY_LEN(val) != n)
2099                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2100                         valp = RARRAY_PTR(val);
2101                         for (i = 0; i < n; i++)
2102                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2103                 }
2104         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2105         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2106         return val;
2107 }
2108
2109 /*
2110 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2111         UnionPar *up;
2112         Int tp, oldsrc;
2113         double d;
2114         VALUE oldval;
2115         up = s_UnionParFromValue(self, &tp, 1);
2116         oldval = s_ParameterRef_GetA(self);
2117         oldsrc = up->bond.src;
2118         val = rb_Float(val);
2119         d = NUM2DBL(val);
2120         if (tp == kVdwParType)
2121                 up->vdw.A = d;
2122         else if (tp == kVdwPairParType)
2123                 up->vdwp.A = d;
2124         else rb_raise(rb_eMolbyError, "invalid member A");
2125         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2126         return val;
2127 }
2128
2129 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2130         UnionPar *up;
2131         Int tp, oldsrc;
2132         double d;
2133         VALUE oldval;
2134         up = s_UnionParFromValue(self, &tp, 1);
2135         oldval = s_ParameterRef_GetB(self);
2136         oldsrc = up->bond.src;
2137         val = rb_Float(val);
2138         d = NUM2DBL(val);
2139         if (tp == kVdwParType)
2140                 up->vdw.B = d;
2141         else if (tp == kVdwPairParType)
2142                 up->vdwp.B = d;
2143         else rb_raise(rb_eMolbyError, "invalid member B");
2144         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2145         return val;
2146 }
2147 */
2148
2149 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2150         UnionPar *up;
2151         Int tp, oldsrc;
2152         Double r;
2153         VALUE oldval;
2154         up = s_UnionParFromValue(self, &tp, 1);
2155         oldval = s_ParameterRef_GetReq(self);
2156         oldsrc = up->bond.src;
2157         val = rb_Float(val);
2158         r = NUM2DBL(val);
2159         if (tp == kVdwParType) {
2160                 up->vdw.r_eq = r;
2161                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2162                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2163         } else if (tp == kVdwPairParType) {
2164                 up->vdwp.r_eq = r;
2165                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2166                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2167         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2168         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2169         return val;
2170 }
2171
2172 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2173         UnionPar *up;
2174         Int tp, oldsrc;
2175         Double e;
2176         VALUE oldval;
2177         up = s_UnionParFromValue(self, &tp, 1);
2178         oldval = s_ParameterRef_GetEps(self);
2179         oldsrc = up->bond.src;
2180         val = rb_Float(val);
2181         e = NUM2DBL(val) * KCAL2INTERNAL;
2182         if (tp == kVdwParType) {
2183                 up->vdw.eps = e;
2184                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2185                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2186         } else if (tp == kVdwPairParType) {
2187                 up->vdwp.eps = e;
2188                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2189                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2190         } else rb_raise(rb_eMolbyError, "invalid member eps");
2191         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2192         return val;
2193 }
2194
2195 /*
2196 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2197         UnionPar *up;
2198         Int tp, oldsrc;
2199         double d;
2200         VALUE oldval;
2201         up = s_UnionParFromValue(self, &tp, 1);
2202         oldval = s_ParameterRef_GetA14(self);
2203         oldsrc = up->bond.src;
2204         val = rb_Float(val);
2205         d = NUM2DBL(val);
2206         if (tp == kVdwParType)
2207                 up->vdw.A14 = d;
2208         else if (tp == kVdwPairParType)
2209                 up->vdwp.A14 = d;
2210         else rb_raise(rb_eMolbyError, "invalid member A14");
2211         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2212         return val;
2213 }
2214
2215 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2216         UnionPar *up;
2217         Int tp, oldsrc;
2218         double d;
2219         VALUE oldval;
2220         up = s_UnionParFromValue(self, &tp, 1);
2221         oldval = s_ParameterRef_GetB14(self);
2222         oldsrc = up->bond.src;
2223         val = rb_Float(val);
2224         d = NUM2DBL(val);
2225         if (tp == kVdwParType)
2226                 up->vdw.B14 = d;
2227         else if (tp == kVdwPairParType)
2228                 up->vdwp.B14 = d;
2229         else rb_raise(rb_eMolbyError, "invalid member B14");
2230         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2231         return val;
2232 }
2233 */
2234
2235 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2236         UnionPar *up;
2237         Int tp, oldsrc;
2238         Double r;
2239         VALUE oldval;
2240         up = s_UnionParFromValue(self, &tp, 1);
2241         oldval = s_ParameterRef_GetReq14(self);
2242         oldsrc = up->bond.src;
2243         val = rb_Float(val);
2244         r = NUM2DBL(val);
2245         if (tp == kVdwParType) {
2246                 up->vdw.r_eq14 = r;
2247                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2248                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2249         } else if (tp == kVdwPairParType) {
2250                 up->vdwp.r_eq14 = r;
2251                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2252                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2253         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2254         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2255         return val;
2256 }
2257
2258 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2259         UnionPar *up;
2260         Int tp, oldsrc;
2261         Double e;
2262         VALUE oldval;
2263         up = s_UnionParFromValue(self, &tp, 1);
2264         oldval = s_ParameterRef_GetEps14(self);
2265         oldsrc = up->bond.src;
2266         val = rb_Float(val);
2267         e = NUM2DBL(val) * KCAL2INTERNAL;
2268         if (tp == kVdwParType) {
2269                 up->vdw.eps14 = e;
2270                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2271                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2272         } else if (tp == kVdwPairParType) {
2273                 up->vdwp.eps14 = e;
2274                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2275                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2276         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2277         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2278         return val;
2279 }
2280
2281 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2282         UnionPar *up;
2283         Int tp, oldsrc;
2284         VALUE oldval;
2285         oldval = s_ParameterRef_GetCutoff(self);
2286         oldsrc = up->bond.src;
2287         up = s_UnionParFromValue(self, &tp, 1);
2288         val = rb_Float(val);
2289         if (tp == kVdwCutoffParType) {
2290                 up->vdwcutoff.cutoff = NUM2DBL(val);
2291         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2292         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2293         return val;
2294 }
2295
2296 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2297         UnionPar *up;
2298         Int tp, oldsrc;
2299         VALUE oldval;
2300         up = s_UnionParFromValue(self, &tp, 1);
2301         oldval = s_ParameterRef_GetRadius(self);
2302         oldsrc = up->bond.src;
2303         val = rb_Float(val);
2304         if (tp == kElementParType) {
2305                 up->atom.radius = NUM2DBL(val);
2306         } else rb_raise(rb_eMolbyError, "invalid member radius");
2307         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2308         return val;
2309 }
2310
2311 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2312         UnionPar *up;
2313         Int tp, oldsrc;
2314         VALUE oldval;
2315         up = s_UnionParFromValue(self, &tp, 1);
2316         oldval = s_ParameterRef_GetVdwRadius(self);
2317         oldsrc = up->bond.src;
2318         val = rb_Float(val);
2319         if (tp == kElementParType) {
2320                 up->atom.vdw_radius = NUM2DBL(val);
2321         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2322         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2323         return val;
2324 }
2325
2326 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2327         UnionPar *up;
2328         Int tp, oldsrc;
2329         VALUE *valp, oldval;
2330         up = s_UnionParFromValue(self, &tp, 1);
2331         oldval = s_ParameterRef_GetColor(self);
2332         oldsrc = up->bond.src;
2333         val = rb_ary_to_ary(val);
2334         if (RARRAY_LEN(val) != 3)
2335                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2336         valp = RARRAY_PTR(val);
2337         if (tp == kElementParType) {
2338                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2339                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2340                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2341         } else rb_raise(rb_eMolbyError, "invalid member color");
2342         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2343         return val;
2344 }
2345
2346 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2347         UnionPar *up;
2348         Int tp, oldsrc;
2349         VALUE oldval;
2350         up = s_UnionParFromValue(self, &tp, 1);
2351         oldval = s_ParameterRef_GetAtomicNumber(self);
2352         oldsrc = up->bond.src;
2353         val = rb_Integer(val);
2354         if (tp == kElementParType)
2355                 up->atom.number = NUM2INT(val);
2356         else if (tp == kVdwParType) {
2357                 up->vdw.atomicNumber = NUM2INT(val);
2358                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2359         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2360         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2361         return val;
2362 }
2363
2364 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2365         UnionPar *up;
2366         Int tp, oldsrc;
2367         VALUE oldval;
2368         up = s_UnionParFromValue(self, &tp, 1);
2369         oldval = s_ParameterRef_GetName(self);
2370         oldsrc = up->bond.src;
2371         if (tp == kElementParType) {
2372                 strncpy(up->atom.name, StringValuePtr(val), 4);
2373         } else rb_raise(rb_eMolbyError, "invalid member name");
2374         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2375         return val;
2376 }
2377
2378 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2379         UnionPar *up;
2380         Int tp, oldsrc;
2381         VALUE oldval;
2382         val = rb_Float(val);
2383         oldval = s_ParameterRef_GetWeight(self);
2384         up = s_UnionParFromValue(self, &tp, 1);
2385         oldsrc = up->bond.src;
2386         if (tp == kElementParType)
2387                 up->atom.weight = NUM2DBL(val);
2388         else if (tp == kVdwParType)
2389                 up->vdw.weight = NUM2DBL(val);
2390         else rb_raise(rb_eMolbyError, "invalid member weight");
2391         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2392         return val;
2393 }
2394
2395 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2396         UnionPar *up;
2397         Int tp, oldsrc;
2398         VALUE oldval;
2399         up = s_UnionParFromValue(self, &tp, 1);
2400         oldval = s_ParameterRef_GetFullName(self);
2401         oldsrc = up->bond.src;
2402         if (tp == kElementParType) {
2403                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2404                 up->atom.fullname[15] = 0;
2405         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2406         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2407         return val;
2408 }
2409
2410 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2411         UnionPar *up;
2412         Int tp, com, oldsrc;
2413         VALUE oldval;
2414         up = s_UnionParFromValue(self, &tp, 1);
2415         oldval = s_ParameterRef_GetComment(self);
2416         oldsrc = up->bond.src;
2417         if (val == Qnil)
2418                 up->bond.com = 0;
2419         else {
2420                 com = ParameterCommentIndex(StringValuePtr(val));
2421                 up->bond.com = com;
2422         }
2423         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2424         return val;     
2425 }
2426
2427 /*  Only false (undefined) and nil (local) can be set  */
2428 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2429         UnionPar *up;
2430         Int tp, oldsrc;
2431         VALUE oldval;
2432         up = s_UnionParFromValue(self, &tp, 1);
2433         if (val != Qfalse && val != Qnil)
2434                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2435         oldval = s_ParameterRef_GetSource(self);
2436         oldsrc = up->bond.src;
2437         if (oldsrc != 0 && oldsrc != -1)
2438                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2439         up->bond.src = (val == Qfalse ? -1 : 0);
2440         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2441         return val;     
2442 }
2443
2444 static struct s_ParameterAttrDef {
2445         char *name;
2446         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2447         ID id;                  /*  Will be set within InitMolby()  */
2448         VALUE (*getter)(VALUE);
2449         VALUE (*setter)(VALUE, VALUE);
2450 } s_ParameterAttrDefTable[] = {
2451         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2452         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2453         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2454         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2455         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2456         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2457         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2458         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2459         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2460         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2461 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2462         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2463         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2464         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2465 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2466         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2467         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2468         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2469         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2470         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2471         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2472         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2473         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2474         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2475         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2476         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2477         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2478         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2479         {NULL} /* Sentinel */
2480 };
2481
2482 static VALUE
2483 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2484 {
2485         int i;
2486         ID kid;
2487         if (TYPE(key) != T_SYMBOL) {
2488                 kid = rb_intern(StringValuePtr(key));
2489                 key = ID2SYM(kid);
2490         } else kid = SYM2ID(key);
2491         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2492                 if (s_ParameterAttrDefTable[i].id == kid) {
2493                         if (value == Qundef)
2494                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2495                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2496                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2497                         else
2498                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2499                 }
2500         }
2501         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2502         return Qnil; /* not reached */
2503 }
2504
2505 static VALUE
2506 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2507 {
2508         return s_ParameterRef_SetAttr(self, key, Qundef);
2509 }
2510
2511 /*
2512  *  call-seq:
2513  *     keys(idx)          -> array of valid parameter attributes
2514  *  
2515  *  Returns an array of valid parameter attributes (as Symbols).
2516  */
2517 static VALUE
2518 s_ParameterRef_Keys(VALUE self)
2519 {
2520         ParameterRef *pref;
2521         Data_Get_Struct(self, ParameterRef, pref);
2522         switch (pref->parType) {
2523                 case kBondParType:
2524                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2525                 case kAngleParType:
2526                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2527                 case kDihedralParType:
2528                 case kImproperParType:
2529                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2530                 case kVdwParType:
2531                         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);
2532                 case kVdwPairParType:
2533                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2534                 case kVdwCutoffParType:
2535                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2536                 case kElementParType:
2537                         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);
2538                 default:
2539                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2540         }
2541         return Qnil;  /*  Not reached  */
2542 }
2543
2544 /*
2545  *  call-seq:
2546  *     to_hash(idx)          -> Hash
2547  *  
2548  *  Returns a hash containing valid parameter names and values
2549  */
2550 static VALUE
2551 s_ParameterRef_ToHash(VALUE self)
2552 {
2553         VALUE keys = s_ParameterRef_Keys(self);
2554         VALUE retval;
2555         int i;
2556         if (keys == Qnil)
2557                 return Qnil;
2558         retval = rb_hash_new();
2559         for (i = 0; i < RARRAY_LEN(keys); i++) {
2560                 VALUE key = RARRAY_PTR(keys)[i];
2561                 VALUE val = s_ParameterRef_GetAttr(self, key);
2562                 rb_hash_aset(retval, key, val);
2563         }
2564         return retval;
2565 }
2566
2567 /*
2568  *  call-seq:
2569  *     parameter.to_s(idx)          -> String
2570  *  
2571  *  Returns a string representation of the given parameter
2572  */
2573 static VALUE
2574 s_ParameterRef_ToString(VALUE self)
2575 {
2576         Int tp, i, n;
2577         char buf[1024], types[4][8];
2578         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2579         switch (tp) {
2580                 case kBondParType:
2581                         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);
2582                         break;
2583                 case kAngleParType:
2584                         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);
2585                         break;
2586                 case kDihedralParType:
2587                 case kImproperParType:
2588                         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]));
2589                         n = strlen(buf);
2590                         for (i = 0; i < up->torsion.mult; i++) {
2591                                 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);
2592                                 n = strlen(buf);
2593                         }
2594                         break;
2595                 case kVdwParType:
2596                         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);
2597                         break;
2598                 case kVdwPairParType:
2599                         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);
2600                         break;
2601                 case kVdwCutoffParType:
2602                         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);
2603                         break;
2604                 case kElementParType:
2605                         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);
2606                         break;
2607         }
2608         return Ruby_NewEncodedStringValue2(buf);
2609 }
2610
2611 /*
2612  *  call-seq:
2613  *     self == parameterRef -> boolean
2614  *  
2615  *  True if the parameters point to the same parameter record.
2616  */
2617 static VALUE
2618 s_ParameterRef_Equal(VALUE self, VALUE val)
2619 {
2620         Int tp1, tp2;
2621         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2622                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2623         } else return Qfalse;
2624 }
2625         
2626 #pragma mark ====== Parameter Class ======
2627
2628 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2629  *  is NULL, then the global parameters are looked for.  */
2630
2631 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2632 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2633 static void
2634 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2635 {
2636         Molecule *mol;
2637         Data_Get_Struct(val, Molecule, mol);
2638         if (mol == NULL)
2639                 rb_raise(rb_eMolbyError, "the molecule is empty");
2640         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2641                 /*  Do self.md_arena.prepare  */
2642                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2643                 if (val2 != Qnil)
2644                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2645         }
2646 }
2647
2648 static VALUE
2649 s_NewParameterValueFromValue(VALUE val)
2650 {
2651         Molecule *mol;
2652         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2653                 Data_Get_Struct(val, Molecule, mol);
2654                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2655                 MoleculeRetain(mol);
2656                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2657         } else {
2658                 mol = NULL;
2659                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2660         }
2661 }
2662
2663 static Molecule *
2664 s_MoleculeFromParameterValue(VALUE val)
2665 {
2666         Molecule *mol;
2667         Data_Get_Struct(val, Molecule, mol);
2668         return mol;
2669 }
2670
2671 static Parameter *
2672 s_ParameterFromParameterValue(VALUE val)
2673 {
2674         Molecule *mol;
2675         Data_Get_Struct(val, Molecule, mol);
2676         if (mol != NULL)
2677                 return mol->par;
2678         return gBuiltinParameters;
2679 }
2680
2681 /*  Forward declarations  */
2682 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2683 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2684
2685 static Molecule *
2686 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2687 {
2688         if (val == rb_cParameter) {
2689                 return NULL;  /*  Parameter class method: builtin parameters  */
2690         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2691                 return s_MoleculeFromParameterValue(val);
2692         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2693                 return s_MoleculeFromParEnumerableValue(val);
2694         } else return NULL;
2695 }
2696
2697 /*
2698  *  call-seq:
2699  *     builtin    -> Parameter
2700  *  
2701  *  Returns a parameter value that points to the global (builtin) parameters.
2702  *  Equivalent to Parameter::Builtin (constant).
2703  */
2704 static VALUE
2705 s_Parameter_Builtin(VALUE self)
2706 {
2707         static ID s_builtin_id = 0;
2708         if (s_builtin_id == 0)
2709                 s_builtin_id = rb_intern("Builtin");
2710         return rb_const_get(rb_cParameter, s_builtin_id);
2711 }
2712
2713 /*
2714  *  call-seq:
2715  *     bond(idx)          -> ParameterRef
2716  *  
2717  *  The index-th bond parameter record is returned.
2718  */
2719 static VALUE
2720 s_Parameter_Bond(VALUE self, VALUE ival)
2721 {
2722         Molecule *mol;
2723         int idx, n;
2724         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2725         idx = NUM2INT(rb_Integer(ival));
2726         if (mol == NULL)
2727                 n = gBuiltinParameters->nbondPars;
2728         else if (mol->par != NULL)
2729                 n = mol->par->nbondPars;
2730         else n = 0;
2731         if (idx < -n || idx >= n)
2732                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2733         if (idx < 0)
2734                 idx += n;
2735         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2736 }
2737
2738 /*
2739  *  call-seq:
2740  *     angle(idx)          -> ParameterRef
2741  *  
2742  *  The index-th angle parameter record is returned.
2743  */
2744 static VALUE
2745 s_Parameter_Angle(VALUE self, VALUE ival)
2746 {
2747         Molecule *mol;
2748         int idx, n;
2749         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2750         idx = NUM2INT(rb_Integer(ival));
2751         if (mol == NULL)
2752                 n = gBuiltinParameters->nanglePars;
2753         else if (mol->par != NULL)
2754                 n = mol->par->nanglePars;
2755         else n = 0;
2756         if (idx < -n || idx >= n)
2757                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2758         if (idx < 0)
2759                 idx += n;
2760         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2761 }
2762
2763 /*
2764  *  call-seq:
2765  *     dihedral(idx)          -> ParameterRef
2766  *  
2767  *  The index-th dihedral parameter record is returned.
2768  */
2769 static VALUE
2770 s_Parameter_Dihedral(VALUE self, VALUE ival)
2771 {
2772         Molecule *mol;
2773         int idx, n;
2774         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2775         idx = NUM2INT(rb_Integer(ival));
2776         if (mol == NULL)
2777                 n = gBuiltinParameters->ndihedralPars;
2778         else if (mol->par != NULL)
2779                 n = mol->par->ndihedralPars;
2780         else n = 0;
2781         if (idx < -n || idx >= n)
2782                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2783         if (idx < 0)
2784                 idx += n;
2785         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2786 }
2787
2788 /*
2789  *  call-seq:
2790  *     improper(idx)          -> ParameterRef
2791  *  
2792  *  The index-th improper parameter record is returned.
2793  */
2794 static VALUE
2795 s_Parameter_Improper(VALUE self, VALUE ival)
2796 {
2797         Molecule *mol;
2798         int idx, n;
2799         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2800         idx = NUM2INT(rb_Integer(ival));
2801         if (mol == NULL)
2802                 n = gBuiltinParameters->nimproperPars;
2803         else if (mol->par != NULL)
2804                 n = mol->par->nimproperPars;
2805         else n = 0;
2806         if (idx < -n || idx >= n)
2807                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2808         if (idx < 0)
2809                 idx += n;
2810         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2811 }
2812
2813 /*
2814  *  call-seq:
2815  *     vdw(idx)          -> ParameterRef
2816  *  
2817  *  The index-th vdw parameter record is returned.
2818  */
2819 static VALUE
2820 s_Parameter_Vdw(VALUE self, VALUE ival)
2821 {
2822         Molecule *mol;
2823         int idx, n;
2824         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2825         idx = NUM2INT(rb_Integer(ival));
2826         if (mol == NULL)
2827                 n = gBuiltinParameters->nvdwPars;
2828         else if (mol->par != NULL)
2829                 n = mol->par->nvdwPars;
2830         else n = 0;
2831         if (idx < -n || idx >= n)
2832                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2833         if (idx < 0)
2834                 idx += n;
2835         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2836 }
2837
2838 /*
2839  *  call-seq:
2840  *     vdw_pair(idx)          -> ParameterRef
2841  *  
2842  *  The index-th vdw pair parameter record is returned.
2843  */
2844 static VALUE
2845 s_Parameter_VdwPair(VALUE self, VALUE ival)
2846 {
2847         Molecule *mol;
2848         int idx, n;
2849         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2850         idx = NUM2INT(rb_Integer(ival));
2851         if (mol == NULL)
2852                 n = gBuiltinParameters->nvdwpPars;
2853         else if (mol->par != NULL)
2854                 n = mol->par->nvdwpPars;
2855         else n = 0;
2856         if (idx < -n || idx >= n)
2857                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2858         if (idx < 0)
2859                 idx += n;
2860         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2861 }
2862
2863 /*
2864  *  call-seq:
2865  *     vdw_cutoff(idx)          -> ParameterRef
2866  *  
2867  *  The index-th vdw cutoff parameter record is returned.
2868  */
2869 static VALUE
2870 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2871 {
2872         Molecule *mol;
2873         int idx, n;
2874         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2875         idx = NUM2INT(rb_Integer(ival));
2876         if (mol == NULL)
2877                 n = gBuiltinParameters->nvdwCutoffPars;
2878         else if (mol->par != NULL)
2879                 n = mol->par->nvdwCutoffPars;
2880         else n = 0;
2881         if (idx < -n || idx >= n)
2882                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2883         if (idx < 0)
2884                 idx += n;
2885         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2886 }
2887
2888 /*
2889  *  call-seq:
2890  *     element(idx)            -> ParameterRef
2891  *     element(t1)             -> ParameterRef
2892  *  
2893  *  In the first form, the index-th element parameter record is returned. In the second
2894  *  form, the element parameter for t1 is looked up (the last index first). t1
2895  *  is the element name string (up to 4 characters).
2896  *  Unlike other Parameter methods, this is used only for the global parameter.
2897  */
2898 static VALUE
2899 s_Parameter_Element(VALUE self, VALUE ival)
2900 {
2901         Int idx1;
2902         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2903                 int n = gCountElementParameters;
2904                 idx1 = NUM2INT(rb_Integer(ival));
2905                 if (idx1 < -n || idx1 >= n)
2906                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2907                 if (idx1 < 0)
2908                         idx1 += n;
2909                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2910         } else {
2911                 ElementPar *ep;
2912                 char name[6];
2913                 int i;
2914                 strncpy(name, StringValuePtr(ival), 4);
2915                 name[4] = 0;
2916                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2917                         if (strncmp(ep->name, name, 4) == 0)
2918                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2919                 }
2920                 return Qnil;
2921         }
2922 }
2923
2924 /*
2925  *  call-seq:
2926  *     nbonds          -> Integer
2927  *  
2928  *  Returns the number of bond parameters.
2929  */
2930 static VALUE
2931 s_Parameter_Nbonds(VALUE self)
2932 {
2933         Int n;
2934         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2935         if (mol == NULL)
2936                 n = gBuiltinParameters->nbondPars;
2937         else if (mol->par != NULL)
2938                 n = mol->par->nbondPars;
2939         else n = 0;
2940         return INT2NUM(n);
2941 }
2942
2943 /*
2944  *  call-seq:
2945  *     nangles          -> Integer
2946  *  
2947  *  Returns the number of angle parameters.
2948  */
2949 static VALUE
2950 s_Parameter_Nangles(VALUE self)
2951 {
2952         Int n;
2953         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2954         if (mol == NULL)
2955                 n = gBuiltinParameters->nanglePars;
2956         else if (mol->par != NULL)
2957                 n = mol->par->nanglePars;
2958         else n = 0;
2959         return INT2NUM(n);
2960 }
2961
2962 /*
2963  *  call-seq:
2964  *     ndihedrals          -> Integer
2965  *  
2966  *  Returns the number of dihedral parameters.
2967  */
2968 static VALUE
2969 s_Parameter_Ndihedrals(VALUE self)
2970 {
2971         Int n;
2972         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2973         if (mol == NULL)
2974                 n = gBuiltinParameters->ndihedralPars;
2975         else if (mol->par != NULL)
2976                 n = mol->par->ndihedralPars;
2977         else n = 0;
2978         return INT2NUM(n);
2979 }
2980
2981 /*
2982  *  call-seq:
2983  *     nimpropers          -> Integer
2984  *  
2985  *  Returns the number of improper parameters.
2986  */
2987 static VALUE
2988 s_Parameter_Nimpropers(VALUE self)
2989 {
2990         Int n;
2991         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2992         if (mol == NULL)
2993                 n = gBuiltinParameters->nimproperPars;
2994         else if (mol->par != NULL)
2995                 n = mol->par->nimproperPars;
2996         else n = 0;
2997         return INT2NUM(n);
2998 }
2999
3000 /*
3001  *  call-seq:
3002  *     nvdws          -> Integer
3003  *  
3004  *  Returns the number of vdw parameters.
3005  */
3006 static VALUE
3007 s_Parameter_Nvdws(VALUE self)
3008 {
3009         Int n;
3010         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3011         if (mol == NULL)
3012                 n = gBuiltinParameters->nvdwPars;
3013         else if (mol->par != NULL)
3014                 n = mol->par->nvdwPars;
3015         else n = 0;
3016         return INT2NUM(n);
3017 }
3018
3019 /*
3020  *  call-seq:
3021  *     nvdw_pairs          -> Integer
3022  *  
3023  *  Returns the number of vdw pair parameters.
3024  */
3025 static VALUE
3026 s_Parameter_NvdwPairs(VALUE self)
3027 {
3028         Int n;
3029         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3030         if (mol == NULL)
3031                 n = gBuiltinParameters->nvdwpPars;
3032         else if (mol->par != NULL)
3033                 n = mol->par->nvdwpPars;
3034         else n = 0;
3035         return INT2NUM(n);
3036 }
3037
3038 /*
3039  *  call-seq:
3040  *     nvdw_cutoffs          -> Integer
3041  *  
3042  *  Returns the number of vdw cutoff parameters.
3043  */
3044 static VALUE
3045 s_Parameter_NvdwCutoffs(VALUE self)
3046 {
3047         Int n;
3048         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3049         if (mol == NULL)
3050                 n = gBuiltinParameters->nvdwCutoffPars;
3051         else if (mol->par != NULL)
3052                 n = mol->par->nvdwCutoffPars;
3053         else n = 0;
3054         return INT2NUM(n);
3055 }
3056
3057 /*
3058  *  call-seq:
3059  *     nelements          -> Integer
3060  *  
3061  *  Returns the number of element parameters.
3062  */
3063 static VALUE
3064 s_Parameter_Nelements(VALUE self)
3065 {
3066         return INT2NUM(gCountElementParameters);
3067 }
3068
3069 /*
3070  *  call-seq:
3071  *     bonds          -> ParEnumerable
3072  *  
3073  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
3074  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
3075  *  useful when all accessible parameters should be examined by use of 'each' method.
3076  */
3077 static VALUE
3078 s_Parameter_Bonds(VALUE self)
3079 {
3080         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3081         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3082 }
3083
3084 /*
3085  *  call-seq:
3086  *     angles          -> ParEnumerable
3087  *  
3088  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3089  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3090  *  useful when all accessible parameters should be examined by use of 'each' method.
3091  */
3092 static VALUE
3093 s_Parameter_Angles(VALUE self)
3094 {
3095         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3096         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3097 }
3098
3099 /*
3100  *  call-seq:
3101  *     dihedrals          -> ParEnumerable
3102  *  
3103  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3104  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3105  *  useful when all accessible parameters should be examined by use of 'each' method.
3106  */
3107 static VALUE
3108 s_Parameter_Dihedrals(VALUE self)
3109 {
3110         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3111         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3112 }
3113
3114 /*
3115  *  call-seq:
3116  *     impropers          -> ParEnumerable
3117  *  
3118  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3119  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3120  *  useful when all accessible parameters should be examined by use of 'each' method.
3121  */
3122 static VALUE
3123 s_Parameter_Impropers(VALUE self)
3124 {
3125         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3126         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3127 }
3128
3129 /*
3130  *  call-seq:
3131  *     vdws          -> ParEnumerable
3132  *  
3133  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3134  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3135  *  useful when all accessible parameters should be examined by use of 'each' method.
3136  */
3137 static VALUE
3138 s_Parameter_Vdws(VALUE self)
3139 {
3140         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3141         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3142 }
3143
3144 /*
3145  *  call-seq:
3146  *     vdw_pairs          -> ParEnumerable
3147  *  
3148  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3149  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3150  *  useful when all accessible parameters should be examined by use of 'each' method.
3151  */
3152 static VALUE
3153 s_Parameter_VdwPairs(VALUE self)
3154 {
3155         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3156         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3157 }
3158
3159 /*
3160  *  call-seq:
3161  *     vdw_cutoffs          -> ParEnumerable
3162  *  
3163  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3164  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3165  *  useful when all accessible parameters should be examined by use of 'each' method.
3166  */
3167 static VALUE
3168 s_Parameter_VdwCutoffs(VALUE self)
3169 {
3170         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3171         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3172 }
3173
3174 /*
3175  *  call-seq:
3176  *     elements          -> ParEnumerable
3177  *  
3178  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3179  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3180  *  useful when all accessible parameters should be examined by use of 'each' method.
3181  */
3182 static VALUE
3183 s_Parameter_Elements(VALUE self)
3184 {
3185         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3186         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3187 }
3188
3189 static VALUE
3190 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3191 {
3192         VALUE atval, optval;
3193         UInt t[4];
3194         Int ii[4];
3195         int i, n, idx, flags, is_global;
3196
3197         rb_scan_args(argc, argv, "1*", &atval, &optval);
3198         
3199         /*  Get the atom types  */
3200         switch (parType) {
3201                 case kBondParType: n = 2; break;
3202                 case kAngleParType: n = 3; break;
3203                 case kDihedralParType: n = 4; break;
3204                 case kImproperParType: n = 4; break;
3205                 case kVdwParType: n = 1; break;
3206                 case kVdwPairParType: n = 2; break;
3207                 default: return Qnil;
3208         }
3209         s_ScanAtomTypes(atval, n, t);
3210         for (i = 0; i < n; i++) {
3211                 if (t[i] < kAtomTypeMinimum) {
3212                         /*  Explicit atom index  */
3213                         if (mol == NULL)
3214                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3215                         if (t[i] >= mol->natoms)
3216                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3217                         ii[i] = t[i];
3218                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3219                 } else ii[i] = -1;
3220         }
3221         
3222         /*  Analyze options  */
3223         flags = 0;
3224         n = RARRAY_LEN(optval);
3225         for (i = 0; i < n; i++) {
3226                 VALUE oval = RARRAY_PTR(optval)[i];
3227                 if (oval == ID2SYM(rb_intern("global")))
3228                         flags |= kParameterLookupGlobal;
3229                 else if (oval == ID2SYM(rb_intern("local")))
3230                         flags |= kParameterLookupLocal;
3231                 else if (oval == ID2SYM(rb_intern("missing")))
3232                         flags |= kParameterLookupMissing;
3233                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3234                         flags |= kParameterLookupNoWildcard;
3235                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3236                         flags |= kParameterLookupNoBaseAtomType;
3237                 else if (oval == ID2SYM(rb_intern("create")))
3238                         flags |= 256;
3239         }
3240         if (flags == 0)
3241                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3242         
3243         idx = -1;
3244         is_global = 0;
3245         switch (parType) {
3246                 case kBondParType: {
3247                         BondPar *bp;
3248                         if (mol != NULL) {
3249                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3250                                 if (bp != NULL) {
3251                                         idx = bp - mol->par->bondPars;
3252                                         break;
3253                                 }
3254                         }
3255                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3256                         if (bp != NULL) {
3257                                 idx = bp - gBuiltinParameters->bondPars;
3258                                 is_global = 1;
3259                         }
3260                         break;
3261                 }
3262                 case kAngleParType: {
3263                         AnglePar *ap;
3264                         if (mol != NULL) {
3265                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3266                                 if (ap != NULL) {
3267                                         idx = ap - mol->par->anglePars;
3268                                         break;
3269                                 }
3270                         }
3271                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3272                         if (ap != NULL) {
3273                                 idx = ap - gBuiltinParameters->anglePars;
3274                                 is_global = 1;
3275                         }
3276                         break;
3277                 }
3278                 case kDihedralParType: {
3279                         TorsionPar *tp;
3280                         if (mol != NULL) {
3281                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3282                                 if (tp != NULL) {
3283                                         idx = tp - mol->par->dihedralPars;
3284                                         break;
3285                                 }
3286                         }
3287                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3288                         if (tp != NULL) {
3289                                 idx = tp - gBuiltinParameters->dihedralPars;
3290                                 is_global = 1;
3291                         }
3292                         break;
3293                 }
3294                 case kImproperParType: {
3295                         TorsionPar *tp;
3296                         if (mol != NULL) {
3297                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3298                                 if (tp != NULL) {
3299                                         idx = tp - mol->par->improperPars;
3300                                         break;
3301                                 }
3302                         }
3303                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3304                         if (tp != NULL) {
3305                                 idx = tp - gBuiltinParameters->improperPars;
3306                                 is_global = 1;
3307                         }
3308                         break;
3309                 }       
3310                 case kVdwParType: {
3311                         VdwPar *vp;
3312                         if (mol != NULL) {
3313                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3314                                 if (vp != NULL) {
3315                                         idx = vp - mol->par->vdwPars;
3316                                         break;
3317                                 }
3318                         }
3319                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3320                         if (vp != NULL) {
3321                                 idx = vp - gBuiltinParameters->vdwPars;
3322                                 is_global = 1;
3323                         }
3324                         break;
3325                 }       
3326                 case kVdwPairParType: {
3327                         VdwPairPar *vp;
3328                         if (mol != NULL) {
3329                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3330                                 if (vp != NULL) {
3331                                         idx = vp - mol->par->vdwpPars;
3332                                         break;
3333                                 }
3334                         }
3335                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3336                         if (vp != NULL) {
3337                                 idx = vp - gBuiltinParameters->vdwpPars;
3338                                 is_global = 1;
3339                         }
3340                         break;
3341                 }
3342                 default:
3343                         return Qnil;
3344         }
3345         if (idx < 0) {
3346                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3347                         return Qnil;            
3348                 else {
3349                         /*  Insert a new parameter record  */
3350                         UnionPar *up;
3351                         Int count = ParameterGetCountForType(mol->par, parType);
3352                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3353                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3354                         IntGroupRelease(ig);
3355                         is_global = 0;
3356                         idx = count;
3357                         /*  Set atom types  */
3358                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3359                         if (up == NULL)
3360                                 return Qnil;
3361                         switch (parType) {
3362                                 case kBondParType:
3363                                         up->bond.type1 = t[0];
3364                                         up->bond.type2 = t[1];
3365                                         break;
3366                                 case kAngleParType:
3367                                         up->angle.type1 = t[0];
3368                                         up->angle.type2 = t[1];
3369                                         up->angle.type3 = t[2];
3370                                         break;
3371                                 case kDihedralParType:
3372                                 case kImproperParType:
3373                                         up->torsion.type1 = t[0];
3374                                         up->torsion.type2 = t[1];
3375                                         up->torsion.type3 = t[2];
3376                                         up->torsion.type4 = t[3];
3377                                         break;
3378                                 case kVdwParType:
3379                                         up->vdw.type1 = t[0];
3380                                         break;
3381                                 case kVdwPairParType:
3382                                         up->vdwp.type1 = t[0];
3383                                         up->vdwp.type2 = t[1];
3384                                         break;
3385                                 default:
3386                                         return Qnil;
3387                         }
3388                 }
3389         }
3390         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3391 }
3392
3393 /*
3394  *  call-seq:
3395  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3396  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3397  *
3398  *  Find the parameter record that matches the given atom types. The atom types are given
3399  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3400  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3401  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3402  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3403  */
3404 static VALUE
3405 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3406 {
3407         int parType;
3408         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3409         if (argc == 0)
3410                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3411         parType = s_ParTypeFromValue(argv[0]);
3412         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3413 }
3414
3415 /*
3416  *  call-seq:
3417  *     self == parameter -> boolean
3418  *  
3419  *  True if the parameters point to the same parameter table.
3420  */
3421 static VALUE
3422 s_Parameter_Equal(VALUE self, VALUE val)
3423 {
3424         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3425                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3426         } else return Qfalse;
3427 }
3428
3429 #pragma mark ====== ParEnumerable Class ======
3430
3431 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3432  and the parameter type. If the Molecule is NULL, then it refers to the
3433  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3434  the global parameters are always accessible. */
3435
3436 typedef struct ParEnumerable {
3437         Molecule *mol;
3438         Int parType;   /*  Same as parType in ParameterRef  */
3439 } ParEnumerable;
3440
3441 static ParEnumerable *
3442 s_ParEnumerableNew(Molecule *mol, Int parType)
3443 {
3444         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3445         if (pen != NULL) {
3446                 pen->mol = mol;
3447                 if (mol != NULL)
3448                         MoleculeRetain(mol);
3449                 pen->parType = parType;
3450         }
3451         return pen;
3452 }
3453
3454 static void
3455 s_ParEnumerableRelease(ParEnumerable *pen)
3456 {
3457         if (pen != NULL) {
3458                 if (pen->mol != NULL)
3459                         MoleculeRelease(pen->mol);
3460                 free(pen);
3461         }
3462 }
3463
3464 static Molecule *
3465 s_MoleculeFromParEnumerableValue(VALUE val)
3466 {
3467         ParEnumerable *pen;
3468     Data_Get_Struct(val, ParEnumerable, pen);
3469         return pen->mol;
3470 }
3471
3472 static VALUE
3473 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3474 {
3475         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3476         if (pen == NULL)
3477                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3478         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3479 }
3480
3481 /*
3482  *  call-seq:
3483  *     par_type -> String
3484  *
3485  *  Get the parameter type, like "bond", "angle", etc.
3486  */
3487 static VALUE
3488 s_ParEnumerable_ParType(VALUE self) {
3489         ParEnumerable *pen;
3490         Int tp;
3491     Data_Get_Struct(self, ParEnumerable, pen);
3492         tp = pen->parType;
3493         if (tp == kElementParType)
3494                 return Ruby_NewEncodedStringValue2("element");
3495         tp -= kFirstParType;
3496         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3497                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3498         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3499 }
3500
3501 /*
3502  *  call-seq:
3503  *     self[idx]          -> ParameterRef
3504  *  
3505  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3506  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3507  *  parent Parameter object of self.
3508  *
3509  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3510  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3511  */
3512 static VALUE
3513 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3514 {
3515         ParEnumerable *pen;
3516     Data_Get_Struct(self, ParEnumerable, pen);
3517         switch (pen->parType) {
3518                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3519                 case kBondParType:      return s_Parameter_Bond(self, ival);
3520                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3521                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3522                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3523                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3524                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3525                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3526                 case kElementParType:   return s_Parameter_Element(self, ival);
3527                 default:
3528                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3529         }
3530         return Qnil;  /*  Not reached  */
3531 }
3532
3533 /*
3534  *  call-seq:
3535  *     length          -> Integer
3536  *  
3537  *  Returns the number of parameters included in this enumerable.
3538  */
3539 static VALUE
3540 s_ParEnumerable_Length(VALUE self)
3541 {
3542         ParEnumerable *pen;
3543     Data_Get_Struct(self, ParEnumerable, pen);
3544         switch (pen->parType) {
3545                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3546                 case kBondParType:      return s_Parameter_Nbonds(self);
3547                 case kAngleParType:     return s_Parameter_Nangles(self); 
3548                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3549                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3550                 case kVdwParType:       return s_Parameter_Nvdws(self);
3551                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3552                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3553                 case kElementParType:   return s_Parameter_Nelements(self);
3554                 default:
3555                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3556         }
3557         return Qnil;  /*  Not reached  */
3558 }
3559
3560 /*
3561  *  call-seq:
3562  *     each {|pref| ...}
3563  *  
3564  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3565  */
3566 VALUE
3567 s_ParEnumerable_Each(VALUE self)
3568 {
3569         VALUE aval;
3570         ParEnumerable *pen;
3571         ParameterRef *pref;
3572         int i, ofs, n;
3573     Data_Get_Struct(self, ParEnumerable, pen);
3574         if (pen->parType == kElementParType)
3575                 n = gCountElementParameters;
3576         else {
3577                 switch (pen->parType) {
3578                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3579                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3580                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3581                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3582                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3583                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3584                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3585                         default:
3586                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3587                 }
3588                 if (pen->mol == NULL)
3589                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3590                 else if (pen->mol->par != NULL)
3591                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3592                 else return self;
3593         }               
3594         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3595         Data_Get_Struct(aval, ParameterRef, pref);
3596         for (i = 0; i < n; i++) {
3597                 pref->idx = i;
3598                 rb_yield(aval);
3599         }
3600     return self;
3601 }
3602
3603 /*
3604  *  call-seq:
3605  *     reverse_each {|pref| ...}
3606  *  
3607  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3608  */
3609 VALUE
3610 s_ParEnumerable_ReverseEach(VALUE self)
3611 {
3612         VALUE aval;
3613         ParEnumerable *pen;
3614         ParameterRef *pref;
3615         int i, ofs, n;
3616     Data_Get_Struct(self, ParEnumerable, pen);
3617         if (pen->parType == kElementParType)
3618                 n = gCountElementParameters;
3619         else {
3620                 switch (pen->parType) {
3621                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3622                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3623                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3624                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3625                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3626                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3627                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3628                         default:
3629                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3630                 }
3631                 if (pen->mol == NULL)
3632                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3633                 else if (pen->mol->par != NULL)
3634                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3635                 else return self;
3636         }               
3637         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3638         Data_Get_Struct(aval, ParameterRef, pref);
3639         for (i = n - 1; i >= 0; i--) {
3640                 pref->idx = i;
3641                 rb_yield(aval);
3642         }
3643     return self;
3644 }
3645
3646 /*
3647  *  call-seq:
3648  *     insert(idx = nil, pref = nil)       -> ParameterRef
3649  *  
3650  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3651  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3652  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3653  *  parameter is left undefined.
3654  *  Throws an exception if ParEnumerable points to the global parameter.
3655  */
3656 static VALUE
3657 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3658 {
3659         VALUE ival, pval;
3660         ParEnumerable *pen;
3661         int i, n;
3662         IntGroup *ig;
3663         UnionPar u;
3664         MolAction *act;
3665     Data_Get_Struct(self, ParEnumerable, pen);
3666         if (pen->mol == NULL)
3667                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3668         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3669         rb_scan_args(argc, argv, "02", &ival, &pval);
3670         if (ival != Qnil) {
3671                 i = NUM2INT(rb_Integer(ival));
3672                 if (i < 0 || i > n)
3673                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3674                 n = i;
3675         }
3676         if (pval != Qnil) {
3677                 Int type;
3678                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3679                 if (up == NULL || type != pen->parType)
3680                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3681                 ParameterCopyOneWithType(&u, up, pen->parType);
3682                 u.bond.src = 0;
3683         } else {
3684                 memset(&u, 0, sizeof(u));
3685                 u.bond.src = 0;
3686         }
3687         ig = IntGroupNewWithPoints(n, 1, -1);
3688         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3689
3690         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3691         MolActionCallback_registerUndo(pen->mol, act);
3692         MolActionRelease(act);
3693         
3694         IntGroupRelease(ig);
3695         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3696 }
3697
3698 /*
3699  *  call-seq:
3700  *     delete(Integer)
3701  *     delete(IntGroup)
3702  *  
3703  *  Delete the parameter(s) specified by the argument.
3704  */
3705 static VALUE
3706 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3707 {
3708         ParEnumerable *pen;
3709         int i, n;
3710         IntGroup *ig;
3711     Data_Get_Struct(self, ParEnumerable, pen);
3712         if (pen->mol == NULL)
3713                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3714         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3715         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3716                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3717                 i = 1;
3718         } else {
3719                 ig = IntGroupFromValue(ival);
3720                 if ((i = IntGroupGetCount(ig)) == 0) {
3721                         IntGroupRelease(ig);
3722                         return Qnil;
3723                 }
3724         }
3725         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3726                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3727         n = i;
3728
3729         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3730         IntGroupRelease(ig);
3731         return ival;
3732 }
3733
3734 /*
3735  *  call-seq:
3736  *     lookup(atom_types, options, ...) -> ParameterRef
3737  *     lookup(atom_type_string, options, ...) -> ParameterRef
3738  *
3739  *  Find the parameter record that matches the given atom types. The arguments are
3740  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3741  *  specified.
3742  */
3743 static VALUE
3744 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3745 {
3746         ParEnumerable *pen;
3747     Data_Get_Struct(self, ParEnumerable, pen);
3748         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3749 }
3750
3751 /*
3752  *  call-seq:
3753  *     self == parEnumerable -> boolean
3754  *  
3755  *  True if the arguments point to the same parameter table and type.
3756  */
3757 static VALUE
3758 s_ParEnumerable_Equal(VALUE self, VALUE val)
3759 {
3760         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3761                 ParEnumerable *pen1, *pen2;
3762                 Data_Get_Struct(self, ParEnumerable, pen1);
3763                 Data_Get_Struct(val, ParEnumerable, pen2);
3764                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3765         } else return Qfalse;
3766 }
3767
3768 #pragma mark ====== AtomRef Class ======
3769
3770 /*  Forward declaration for register undo  */
3771 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3772
3773 /*  Ruby string "set_atom_attr"  */
3774 static VALUE s_SetAtomAttrString;
3775
3776 static int
3777 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3778 {
3779         AtomRef *aref;
3780         int idx;
3781         Data_Get_Struct(self, AtomRef, aref);
3782         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3783         if (idx < 0 || idx >= aref->mol->natoms)
3784                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3785         if (app != NULL)
3786                 *app = aref->mol->atoms + idx;
3787         if (mpp != NULL)
3788                 *mpp = aref->mol;
3789         return idx;
3790 }
3791
3792 static Atom *
3793 s_AtomFromValue(VALUE self)
3794 {
3795         Atom *ap;
3796         s_AtomIndexFromValue(self, &ap, NULL);
3797         return ap;
3798 }
3799
3800 static Atom *
3801 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3802 {
3803         Atom *ap;
3804         s_AtomIndexFromValue(self, &ap, mpp);
3805         return ap;
3806 }
3807
3808 static void
3809 s_NotifyModificationForAtomRef(VALUE self)
3810 {
3811         AtomRef *aref;
3812         Data_Get_Struct(self, AtomRef, aref);
3813         MoleculeIncrementModifyCount(aref->mol);
3814 }
3815
3816 static void
3817 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3818 {
3819         AtomRef *aref;
3820         Data_Get_Struct(self, AtomRef, aref);
3821         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3822                 /*  Register undo  */
3823                 MolAction *act;
3824                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3825                 MolActionCallback_registerUndo(aref->mol, act);
3826                 MoleculeCallback_notifyModification(aref->mol, 0);
3827                 /*  Request MD rebuilt if necessary  */
3828                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3829                         aref->mol->needsMDRebuild = 1;
3830         }
3831 }
3832
3833 VALUE
3834 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3835 {
3836         AtomRef *aref;
3837         aref = AtomRefNew(mol, idx);
3838         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3839 }
3840
3841 static VALUE
3842 s_AtomRef_GetMolecule(VALUE self)
3843 {
3844         Molecule *mpp;
3845         s_AtomIndexFromValue(self, NULL, &mpp);
3846         return ValueFromMolecule(mpp);
3847 }
3848
3849 static VALUE s_AtomRef_GetIndex(VALUE self) {
3850         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3851 }
3852
3853 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3854         return INT2NUM(s_AtomFromValue(self)->segSeq);
3855 }
3856
3857 static VALUE s_AtomRef_GetSegName(VALUE self) {
3858         char *p = s_AtomFromValue(self)->segName;
3859         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3860 }
3861
3862 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3863         return INT2NUM(s_AtomFromValue(self)->resSeq);
3864 }
3865
3866 static VALUE s_AtomRef_GetResName(VALUE self) {
3867         char *p = s_AtomFromValue(self)->resName;
3868         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3869 }
3870
3871 static VALUE s_AtomRef_GetName(VALUE self) {
3872         char *p = s_AtomFromValue(self)->aname;
3873         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3874 }
3875
3876 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3877         int type = s_AtomFromValue(self)->type;
3878         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3879         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3880 }
3881
3882 static VALUE s_AtomRef_GetCharge(VALUE self) {
3883         return rb_float_new(s_AtomFromValue(self)->charge);
3884 }
3885
3886 static VALUE s_AtomRef_GetWeight(VALUE self) {
3887         return rb_float_new(s_AtomFromValue(self)->weight);
3888 }
3889
3890 static VALUE s_AtomRef_GetElement(VALUE self) {
3891         char *p = s_AtomFromValue(self)->element;
3892         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3893 }
3894
3895 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3896         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3897 }
3898
3899 static VALUE s_AtomRef_GetConnects(VALUE self) {
3900         VALUE retval;
3901         Int i, *cp;
3902         Atom *ap = s_AtomFromValue(self);
3903         retval = rb_ary_new();
3904         cp = AtomConnectData(&ap->connect);
3905         for (i = 0; i < ap->connect.count; i++)
3906                 rb_ary_push(retval, INT2NUM(cp[i]));
3907         return retval;
3908 }
3909
3910 static VALUE s_AtomRef_GetR(VALUE self) {
3911         return ValueFromVector(&(s_AtomFromValue(self)->r));
3912 }
3913
3914 static VALUE s_AtomRef_GetX(VALUE self) {
3915         return rb_float_new(s_AtomFromValue(self)->r.x);
3916 }
3917
3918 static VALUE s_AtomRef_GetY(VALUE self) {
3919         return rb_float_new(s_AtomFromValue(self)->r.y);
3920 }
3921
3922 static VALUE s_AtomRef_GetZ(VALUE self) {
3923         return rb_float_new(s_AtomFromValue(self)->r.z);
3924 }
3925
3926 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3927         Atom *ap;
3928         Molecule *mp;
3929         Vector r1;
3930         s_AtomIndexFromValue(self, &ap, &mp);
3931         r1 = ap->r;
3932         if (mp->cell != NULL)
3933                 TransformVec(&r1, mp->cell->rtr, &r1);
3934         return r1;
3935 }
3936
3937 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3938         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3939         return ValueFromVector(&r1);
3940 }
3941
3942 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3943         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3944 }
3945
3946 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3947         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3948 }
3949
3950 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3951         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3952 }
3953
3954 static VALUE s_AtomRef_GetSigma(VALUE self) {
3955         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3956 }
3957
3958 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3959         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3960 }
3961
3962 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3963         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3964 }
3965
3966 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3967         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3968 }
3969
3970 static VALUE s_AtomRef_GetV(VALUE self) {
3971         return ValueFromVector(&(s_AtomFromValue(self)->v));
3972 }
3973
3974 static VALUE s_AtomRef_GetF(VALUE self) {
3975         Vector v = s_AtomFromValue(self)->f;
3976         VecScaleSelf(v, INTERNAL2KCAL);
3977         return ValueFromVector(&v);
3978 }
3979
3980 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3981         return rb_float_new(s_AtomFromValue(self)->occupancy);
3982 }
3983
3984 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3985         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3986 }
3987
3988 static VALUE s_AtomRef_GetAniso(VALUE self) {
3989         VALUE retval;
3990         int i;
3991         Atom *ap = s_AtomFromValue(self);
3992         if (ap->aniso == NULL)
3993                 return Qnil;
3994         retval = rb_ary_new();
3995         for (i = 0; i < 6; i++)
3996                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3997         if (ap->aniso->has_bsig) {
3998                 rb_ary_push(retval, INT2NUM(0));
3999                 for (i = 0; i < 6; i++)
4000                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
4001         }
4002         return retval;
4003 }
4004
4005 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
4006     VALUE retval;
4007     int i;
4008     Atom *ap = s_AtomFromValue(self);
4009     if (ap->aniso == NULL)
4010         return Qnil;
4011     retval = rb_ary_new();
4012     for (i = 0; i < 3; i++)
4013         rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
4014     return retval;
4015 }
4016
4017 static VALUE s_AtomRef_GetSymop(VALUE self) {
4018         VALUE retval;
4019         Atom *ap = s_AtomFromValue(self);
4020         if (!ap->symop.alive)
4021                 return Qnil;
4022         retval = rb_ary_new();
4023         rb_ary_push(retval, INT2NUM(ap->symop.sym));
4024         rb_ary_push(retval, INT2NUM(ap->symop.dx));
4025         rb_ary_push(retval, INT2NUM(ap->symop.dy));
4026         rb_ary_push(retval, INT2NUM(ap->symop.dz));
4027         rb_ary_push(retval, INT2NUM(ap->symbase));
4028         return retval;
4029 }
4030
4031 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
4032         return INT2NUM(s_AtomFromValue(self)->intCharge);
4033 }
4034
4035 static VALUE s_AtomRef_GetFixForce(VALUE self) {
4036         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
4037 }
4038
4039 static VALUE s_AtomRef_GetFixPos(VALUE self) {
4040         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
4041 }
4042
4043 static VALUE s_AtomRef_GetExclusion(VALUE self) {
4044         Molecule *mol;
4045         Atom *ap;
4046         int idx, i;
4047         MDExclusion *exinfo;
4048         Int *exlist;
4049         VALUE retval, aval;
4050         idx = s_AtomIndexFromValue(self, &ap, &mol);
4051         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
4052                 VALUE mval = ValueFromMolecule(mol);
4053                 s_RebuildMDParameterIfNecessary(mval, Qnil);
4054         }
4055         if (mol->arena->exinfo == NULL)
4056                 return Qnil;
4057         exinfo = mol->arena->exinfo + idx;
4058         exlist = mol->arena->exlist;
4059         retval = rb_ary_new();
4060         aval = rb_ary_new();
4061         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
4062                 rb_ary_push(aval, INT2NUM(exlist[i]));
4063         rb_ary_push(retval, aval);
4064         aval = rb_ary_new();
4065         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
4066                 rb_ary_push(aval, INT2NUM(exlist[i]));
4067         rb_ary_push(retval, aval);
4068         aval = rb_ary_new();
4069         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
4070                 rb_ary_push(aval, INT2NUM(exlist[i]));
4071         rb_ary_push(retval, aval);
4072         return retval;
4073 }
4074
4075 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
4076         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4077 }
4078
4079 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4080         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4081 }
4082
4083 static VALUE s_AtomRef_GetHidden(VALUE self) {
4084         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4085 }
4086
4087 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4088         VALUE retval;
4089         Int i, count, *cp;
4090         Atom *ap = s_AtomFromValue(self);
4091         if (ap->anchor == NULL)
4092                 return Qnil;
4093         count = ap->anchor->connect.count;
4094         retval = rb_ary_new2(count * 2);
4095         cp = AtomConnectData(&ap->anchor->connect);
4096         for (i = 0; i < count; i++) {
4097                 rb_ary_store(retval, i, INT2NUM(cp[i]));
4098                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4099         }
4100         return retval;
4101 }
4102
4103 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4104         char *p = s_AtomFromValue(self)->uff_type;
4105         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4106 }
4107
4108 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4109         rb_raise(rb_eMolbyError, "index cannot be directly set");
4110         return Qnil;
4111 }
4112
4113 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4114         VALUE oval = s_AtomRef_GetSegSeq(self);
4115         val = rb_Integer(val);
4116         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4117         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4118         return val;
4119 }
4120
4121 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4122         char *p = StringValuePtr(val);
4123         VALUE oval = s_AtomRef_GetSegName(self);
4124         strncpy(s_AtomFromValue(self)->segName, p, 4);
4125         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4126         return val;
4127 }
4128
4129 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4130         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4131         return val; /* Not reached */
4132 }
4133
4134 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4135         Atom *ap = s_AtomFromValue(self);
4136         char *p = StringValuePtr(val);
4137         VALUE oval = s_AtomRef_GetName(self);
4138         if (ap->anchor != NULL && p[0] == '_')
4139                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4140         strncpy(ap->aname, p, 4);
4141         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4142         return val;
4143 }
4144
4145 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4146         Molecule *mp;
4147         char *p = StringValuePtr(val);
4148         VALUE oval = s_AtomRef_GetAtomType(self);
4149         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4150         if (type != 0 && type < kAtomTypeMinimum)
4151                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4152         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4153         mp->needsMDRebuild = 1;
4154         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4155         return val;
4156 }
4157
4158 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4159         Molecule *mp;
4160         VALUE oval = s_AtomRef_GetCharge(self);
4161         val = rb_Float(val);
4162         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4163         mp->needsMDRebuild = 1;
4164         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4165         return val;
4166 }
4167
4168 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4169         Molecule *mp;
4170         VALUE oval = s_AtomRef_GetWeight(self);
4171         val = rb_Float(val);
4172         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4173         mp->needsMDRebuild = 1;
4174         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4175         return val;
4176 }
4177
4178 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4179         Double w;
4180         Molecule *mp;
4181         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4182         char *p = StringValuePtr(val);
4183         VALUE oval = s_AtomRef_GetElement(self);
4184         ap->atomicNumber = ElementToInt(p);
4185         ElementToString(ap->atomicNumber, ap->element);
4186         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4187                 ap->weight = w;
4188         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4189         mp->needsMDRebuild = 1;
4190         return val;
4191 }
4192
4193 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4194         Double w;
4195         Molecule *mp;
4196         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4197         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4198         val = rb_Integer(val);
4199         ap->atomicNumber = NUM2INT(val);
4200         ElementToString(ap->atomicNumber, ap->element);
4201         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4202                 ap->weight = w;
4203         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4204         mp->needsMDRebuild = 1;
4205         return val;
4206 }
4207
4208 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4209         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4210         return val; /* Not reached */
4211 }
4212
4213 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4214         Vector v;
4215         Molecule *mp;
4216         VALUE oval = s_AtomRef_GetR(self);
4217         VectorFromValue(val, &v);
4218         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4219         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4220         mp->needsMDCopyCoordinates = 1;
4221         return val;
4222 }
4223
4224 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4225         Double f;
4226         Molecule *mp;
4227         VALUE oval = s_AtomRef_GetX(self);
4228         val = rb_Float(val);
4229         f = NUM2DBL(val);
4230         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4231         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4232         mp->needsMDCopyCoordinates = 1;
4233         return val;
4234 }
4235
4236 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4237         Double f;
4238         Molecule *mp;
4239         VALUE oval = s_AtomRef_GetY(self);
4240         val = rb_Float(val);
4241         f = NUM2DBL(val);
4242         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4243         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4244         mp->needsMDCopyCoordinates = 1;
4245         return val;
4246 }
4247
4248 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4249         Double f;
4250         Molecule *mp;
4251         VALUE oval = s_AtomRef_GetZ(self);
4252         val = rb_Float(val);
4253         f = NUM2DBL(val);
4254         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4255         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4256         mp->needsMDCopyCoordinates = 1;
4257         return val;
4258 }
4259
4260 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4261         Vector v, ov;
4262         Atom *ap;
4263         Molecule *mp;
4264         s_AtomIndexFromValue(self, &ap, &mp);
4265         ov = ap->r;
4266         VectorFromValue(val, &v);
4267         if (mp->cell != NULL)
4268                 TransformVec(&v, mp->cell->tr, &v);
4269         ap->r = v;
4270         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4271         mp->needsMDCopyCoordinates = 1;
4272         return val;
4273 }
4274
4275 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4276         double f;
4277         Vector v, ov;
4278         Atom *ap;
4279         Molecule *mp;
4280         s_AtomIndexFromValue(self, &ap, &mp);
4281         ov = v = ap->r;
4282         val = rb_Float(val);
4283         f = NUM2DBL(val);
4284         if (mp->cell != NULL) {
4285                 TransformVec(&v, mp->cell->rtr, &v);
4286                 v.x = f;
4287                 TransformVec(&v, mp->cell->tr, &v);
4288         } else v.x = f;
4289         ap->r = v;
4290         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4291         mp->needsMDCopyCoordinates = 1;
4292         return val;
4293 }
4294
4295 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4296         double f;
4297         Vector v, ov;
4298         Atom *ap;
4299         Molecule *mp;
4300         s_AtomIndexFromValue(self, &ap, &mp);
4301         ov = v = ap->r;
4302         val = rb_Float(val);
4303         f = NUM2DBL(val);
4304         if (mp->cell != NULL) {
4305                 TransformVec(&v, mp->cell->rtr, &v);
4306                 v.y = f;
4307                 TransformVec(&v, mp->cell->tr, &v);
4308         } else v.y = f;
4309         ap->r = v;
4310         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4311         mp->needsMDCopyCoordinates = 1;
4312         return val;
4313 }
4314
4315 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4316         double f;
4317         Vector v, ov;
4318         Atom *ap;
4319         Molecule *mp;
4320         s_AtomIndexFromValue(self, &ap, &mp);
4321         ov = v = ap->r;
4322         val = rb_Float(val);
4323         f = NUM2DBL(val);
4324         if (mp->cell != NULL) {
4325                 TransformVec(&v, mp->cell->rtr, &v);
4326                 v.z = f;
4327                 TransformVec(&v, mp->cell->tr, &v);
4328         } else v.z = f;
4329         ap->r = v;
4330         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4331         mp->needsMDCopyCoordinates = 1;
4332         return val;
4333 }
4334
4335 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4336         Vector v;
4337         Molecule *mp;
4338         VALUE oval = s_AtomRef_GetSigma(self);
4339         VectorFromValue(val, &v);
4340         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4341         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4342         mp->needsMDCopyCoordinates = 1;
4343         return val;
4344 }
4345
4346 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4347         Double f;
4348         Molecule *mp;
4349         VALUE oval = s_AtomRef_GetSigmaX(self);
4350         val = rb_Float(val);
4351         f = NUM2DBL(val);
4352         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4353         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4354         mp->needsMDCopyCoordinates = 1;
4355         return val;
4356 }
4357
4358 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4359         Double f;
4360         Molecule *mp;
4361         VALUE oval = s_AtomRef_GetSigmaY(self);
4362         val = rb_Float(val);
4363         f = NUM2DBL(val);
4364         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4365         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4366         mp->needsMDCopyCoordinates = 1;
4367         return val;
4368 }
4369
4370 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4371         Double f;
4372         Molecule *mp;
4373         VALUE oval = s_AtomRef_GetSigmaZ(self);
4374         val = rb_Float(val);
4375         f = NUM2DBL(val);
4376         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4377         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4378         mp->needsMDCopyCoordinates = 1;
4379         return val;
4380 }
4381
4382 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4383         Vector v;
4384         Atom *ap;
4385         Molecule *mp;
4386         VALUE oval = s_AtomRef_GetV(self);
4387         VectorFromValue(val, &v);
4388         s_AtomIndexFromValue(self, &ap, &mp);
4389         ap->v = v;
4390         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4391         mp->needsMDCopyCoordinates = 1;
4392         return val;
4393 }
4394
4395 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4396         Vector v;
4397         Molecule *mp;
4398         VALUE oval = s_AtomRef_GetF(self);
4399         VectorFromValue(val, &v);
4400         VecScaleSelf(v, KCAL2INTERNAL);
4401         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4402         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4403         mp->needsMDCopyCoordinates = 1;
4404         return val;
4405 }
4406
4407 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4408         VALUE oval = s_AtomRef_GetOccupancy(self);
4409         Molecule *mp;
4410         val = rb_Float(val);
4411         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4412         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4413         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4414         return val;
4415 }
4416
4417 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4418         VALUE oval = s_AtomRef_GetTempFactor(self);
4419         val = rb_Float(val);
4420         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4421         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4422         return val;
4423 }
4424
4425 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4426         AtomRef *aref;
4427         int i, n, type;
4428         VALUE *valp;
4429         Double f[12];
4430         VALUE oval = s_AtomRef_GetAniso(self);
4431         Data_Get_Struct(self, AtomRef, aref);
4432         val = rb_funcall(val, rb_intern("to_a"), 0);
4433         n = RARRAY_LEN(val);
4434         valp = RARRAY_PTR(val);
4435         for (i = 0; i < 6; i++) {
4436                 if (i < n)
4437                         f[i] = NUM2DBL(rb_Float(valp[i]));
4438                 else f[i] = 0.0;
4439         }
4440         if (n >= 7)
4441                 type = NUM2INT(rb_Integer(valp[6]));
4442         else type = 0;
4443         if (n >= 13) {
4444                 for (i = 0; i < 6; i++)
4445                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4446         } else {
4447                 for (i = 0; i < 6; i++)
4448                         f[i + 6] = 0.0;
4449         }
4450         i = s_AtomIndexFromValue(self, NULL, NULL);
4451         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4452         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4453         return val;
4454 }
4455
4456 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4457     rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4458     return val; /* Not reached */
4459 }
4460
4461 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4462         Molecule *mol;
4463         Atom *ap;
4464         int i, n;
4465         VALUE *valp;
4466         Int ival[5];
4467         VALUE oval = s_AtomRef_GetSymop(self);
4468         i = s_AtomIndexFromValue(self, &ap, &mol);
4469         if (val == Qnil) {
4470                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4471         } else {
4472                 val = rb_funcall(val, rb_intern("to_a"), 0);
4473                 n = RARRAY_LEN(val);
4474                 valp = RARRAY_PTR(val);
4475                 for (i = 0; i < 5; i++) {
4476                         if (i < n) {
4477                                 if (valp[i] == Qnil)
4478                                         ival[i] = -100000;
4479                                 else 
4480                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4481                         } else ival[i] = -100000;
4482                 }
4483         }
4484         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4485                 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));
4486         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4487                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4488         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4489                 return val;  /*  No need to change  */
4490         if (ival[0] != -100000)
4491                 ap->symop.sym = ival[0];
4492         if (ival[1] != -100000)
4493                 ap->symop.dx = ival[1];
4494         if (ival[2] != -100000)
4495                 ap->symop.dy = ival[2];
4496         if (ival[3] != -100000)
4497                 ap->symop.dz = ival[3];
4498         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4499         if (ival[4] != -100000)
4500                 ap->symbase = ival[4];
4501         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4502                 /*  The anisotropic parameters should be recalculated  */
4503                 VALUE oaval = s_AtomRef_GetAniso(self);
4504                 MoleculeSetAnisoBySymop(mol, i);
4505                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4506         }
4507         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4508         return val;
4509 }
4510
4511 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4512         VALUE oval = s_AtomRef_GetIntCharge(self);
4513         val = rb_Integer(val);
4514         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4515         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4516         return val;
4517 }
4518
4519 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4520         Molecule *mp;
4521         VALUE oval = s_AtomRef_GetFixForce(self);
4522         val = rb_Float(val);
4523         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4524         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4525         mp->needsMDRebuild = 1;
4526         return val;
4527 }
4528
4529 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4530         Vector v;
4531         Molecule *mp;
4532         VALUE oval = s_AtomRef_GetFixPos(self);
4533         VectorFromValue(val, &v);
4534         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4535         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4536         mp->needsMDRebuild = 1;
4537         return val;
4538 }
4539
4540 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4541         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4542         return val; /* Not reached */
4543 }
4544
4545 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4546         VALUE oval = s_AtomRef_GetIntCharge(self);
4547         val = rb_Integer(val);
4548         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4549         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4550         return val;
4551 }
4552
4553 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4554         VALUE oval = s_AtomRef_GetIntCharge(self);
4555         val = rb_Integer(val);
4556         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4557         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4558         return val;
4559 }
4560
4561 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4562         Atom *ap = s_AtomFromValue(self);
4563         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4564         if (RTEST(val)) {
4565                 ap->exflags |= kAtomHiddenFlag;
4566         } else {
4567                 ap->exflags &= ~kAtomHiddenFlag;
4568         }
4569         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4570         return val;
4571 }
4572
4573 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4574         Int idx, i, j, k, n, *ip;
4575         Double *dp;
4576         Atom *ap;
4577         Molecule *mol;
4578         VALUE oval, v;
4579         AtomConnect ac;
4580         Int nUndoActions;
4581         MolAction **undoActions;
4582         memset(&ac, 0, sizeof(ac));
4583         idx = s_AtomIndexFromValue(self, &ap, &mol);
4584         oval = s_AtomRef_GetAnchorList(self);
4585         if (val != Qnil) {
4586                 val = rb_ary_to_ary(val);
4587                 n = RARRAY_LEN(val);
4588         } else n = 0;
4589         if (n == 0) {
4590                 if (ap->anchor != NULL) {
4591                         AtomConnectResize(&ap->anchor->connect, 0);
4592                         free(ap->anchor->coeffs);
4593                         free(ap->anchor);
4594                         ap->anchor = NULL;
4595                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4596                 }
4597                 return val;
4598         }
4599         if (n < 2)
4600                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4601         if (ap->aname[0] == '_')
4602                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4603         ip = (Int *)malloc(sizeof(Int) * n);
4604         dp = NULL;
4605         for (i = 0; i < n; i++) {
4606                 v = RARRAY_PTR(val)[i];
4607                 if (rb_obj_is_kind_of(v, rb_cFloat))
4608                         break;
4609                 j = NUM2INT(rb_Integer(v));
4610                 if (j < 0 || j >= mol->natoms)
4611                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4612                 for (k = 0; k < i; k++) {
4613                         if (ip[k] == j)
4614                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4615                 }
4616                 ip[i] = j;
4617         }
4618         if (i < n) {
4619                 if (i < 2)
4620                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4621                 else if (i * 2 != n)
4622                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4623                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4624                 for (i = 0; i < n / 2; i++) {
4625                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4626                         if (dp[i] <= 0.0)
4627                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4628                 }
4629                 n /= 2;
4630         }
4631         nUndoActions = 0;
4632         undoActions = NULL;
4633         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4634         free(dp);
4635         free(ip);
4636         if (i != 0)
4637                 rb_raise(rb_eMolbyError, "invalid argument");
4638         if (nUndoActions > 0) {
4639                 for (i = 0; i < nUndoActions; i++) {
4640                         MolActionCallback_registerUndo(mol, undoActions[i]);
4641                         MolActionRelease(undoActions[i]);
4642                 }
4643                 free(undoActions);
4644         }
4645         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4646         return val;
4647 }
4648
4649 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4650         Atom *ap = s_AtomFromValue(self);
4651         char *p = StringValuePtr(val);
4652         VALUE oval = s_AtomRef_GetUFFType(self);
4653         strncpy(ap->uff_type, p, 5);
4654         ap->uff_type[5] = 0;
4655         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4656         return val;
4657 }
4658
4659 static struct s_AtomAttrDef {
4660         char *name;
4661         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4662         ID id;                  /*  Will be set within InitMolby()  */
4663         VALUE (*getter)(VALUE);
4664         VALUE (*setter)(VALUE, VALUE);
4665 } s_AtomAttrDefTable[] = {
4666         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4667         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4668         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4669         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4670         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4671         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4672         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4673         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4674         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4675         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4676         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4677         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4678         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4679         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4680         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4681     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4682         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4683         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4684         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4685         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4686         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4687         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4688         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4689         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4690         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4691         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4692         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4693         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4694         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4695     {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues,        s_AtomRef_SetAnisoEigenValues},
4696         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4697         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4698         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4699         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4700         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4701         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4702         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4703         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4704         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4705         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4706         {NULL} /* Sentinel */
4707 };
4708
4709 static VALUE
4710 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4711 {
4712         int i;
4713         ID kid;
4714         if (TYPE(key) != T_SYMBOL) {
4715                 kid = rb_intern(StringValuePtr(key));
4716                 key = ID2SYM(kid);
4717         } else kid = SYM2ID(key);
4718         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4719                 if (s_AtomAttrDefTable[i].id == kid) {
4720                         if (value == Qundef)
4721                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4722                         else
4723                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4724                 }
4725         }
4726         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4727         return Qnil; /* not reached */
4728 }
4729
4730 static VALUE
4731 s_AtomRef_GetAttr(VALUE self, VALUE key)
4732 {
4733         return s_AtomRef_SetAttr(self, key, Qundef);
4734 }
4735
4736 /*
4737  *  call-seq:
4738  *     self == atomRef -> boolean
4739  *
4740  *  True if the two references point to the same atom.
4741  */
4742 static VALUE
4743 s_AtomRef_Equal(VALUE self, VALUE val)
4744 {
4745         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4746                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4747         } else return Qfalse;
4748 }
4749
4750 #pragma mark ====== MolEnumerable Class ======
4751
4752 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4753
4754 /*
4755  *  call-seq:
4756  *     self[idx] -> AtomRef or Array of Integers
4757  *  
4758  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4759  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4760  *  value is a String. Otherwise, the return value is an Array of Integers.
4761  */
4762 static VALUE
4763 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4764 {
4765         MolEnumerable *mseq;
4766         Molecule *mol;
4767         int idx1, idx2;
4768     Data_Get_Struct(self, MolEnumerable, mseq);
4769         mol = mseq->mol;
4770         if (mseq->kind == kAtomKind) {
4771                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4772         }
4773         idx1 = NUM2INT(arg1);
4774         switch (mseq->kind) {
4775                 case kBondKind: {
4776                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4777                         if (idx2 < 0 || idx2 >= mol->nbonds)
4778                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4779                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4780                 }
4781                 case kAngleKind: {
4782                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4783                         if (idx2 < 0 || idx2 >= mol->nangles)
4784                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4785                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4786                 }
4787                 case kDihedralKind: {
4788                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4789                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4790                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4791                         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]));
4792                 }
4793                 case kImproperKind: {
4794                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4795                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4796                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4797                         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]));
4798                 }
4799                 case kResidueKind: {
4800                         char *p;
4801                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4802                         if (idx2 < 0 || idx2 >= mol->nresidues)
4803                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4804                         p = mol->residues[idx2];
4805                         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4806                 }
4807         }
4808         return Qnil;
4809 }
4810
4811 /*
4812  *  call-seq:
4813  *     length          -> Integer
4814  *  
4815  *  Returns the number of objects included in this enumerable.
4816  */
4817 static VALUE
4818 s_MolEnumerable_Length(VALUE self)
4819 {
4820         MolEnumerable *mseq;
4821     Data_Get_Struct(self, MolEnumerable, mseq);
4822         switch (mseq->kind) {
4823                 case kAtomKind:
4824                         return INT2NUM(mseq->mol->natoms);
4825                 case kBondKind:
4826                         return INT2NUM(mseq->mol->nbonds);
4827                 case kAngleKind:
4828                         return INT2NUM(mseq->mol->nangles);
4829                 case kDihedralKind:
4830                         return INT2NUM(mseq->mol->ndihedrals);
4831                 case kImproperKind:
4832                         return INT2NUM(mseq->mol->nimpropers);
4833                 case kResidueKind:
4834                         return INT2NUM(mseq->mol->nresidues);
4835         }
4836         return INT2NUM(-1);
4837 }
4838
4839 /*
4840  *  call-seq:
4841  *     each {|obj| ...}
4842  *  
4843  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4844  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4845  *  For the atoms, a same AtomRef object is passed (with different internal information)
4846  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4847  *  for each iteration.
4848  */
4849 VALUE
4850 s_MolEnumerable_Each(VALUE self)
4851 {
4852         MolEnumerable *mseq;
4853         int i;
4854         int len = NUM2INT(s_MolEnumerable_Length(self));
4855     Data_Get_Struct(self, MolEnumerable, mseq);
4856         if (mseq->kind == kAtomKind) {
4857                 /*  The same AtomRef object will be used during the loop  */
4858                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4859                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4860                 for (i = 0; i < len; i++) {
4861                         aref->idx = i;
4862                         rb_yield(arval);
4863                 }
4864     } else {
4865                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4866                 for (i = 0; i < len; i++) {
4867                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4868                 }
4869         }
4870     return self;
4871 }
4872
4873 /*
4874  *  call-seq:
4875  *     self == molEnumerable -> boolean
4876  *
4877  *  True if the two arguments point to the same molecule and enumerable type.
4878  */
4879 static VALUE
4880 s_MolEnumerable_Equal(VALUE self, VALUE val)
4881 {
4882         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4883                 MolEnumerable *mseq1, *mseq2;
4884                 Data_Get_Struct(self, MolEnumerable, mseq1);
4885                 Data_Get_Struct(val, MolEnumerable, mseq2);
4886                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4887         } else return Qfalse;
4888 }
4889
4890
4891 #pragma mark ====== Molecule Class ======
4892
4893 #pragma mark ------ Allocate/Release/Accessor ------
4894
4895 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method.  */
4896 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4897 char *gLoadSaveErrorMessage = NULL;
4898
4899 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4900
4901 Molecule *
4902 MoleculeFromValue(VALUE val)
4903 {
4904         Molecule *mol;
4905         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4906                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4907     Data_Get_Struct(val, Molecule, mol);
4908         return mol;
4909 }
4910
4911 static VALUE sMoleculeRetainArray = Qnil;
4912
4913 /*  The function is called from MoleculeRelease()  */
4914 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4915 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4916 /*  object is always returned for the same Molecule.  */
4917 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4918 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4919 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4920 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4921 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4922
4923 /*  Register/unregister the exmolobj Ruby object  */
4924 void
4925 MoleculeReleaseExternalObj(Molecule *mol)
4926 {
4927         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4928                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4929                 mol->exmolobjProtected = 0;
4930         }
4931 }
4932
4933 void
4934 MoleculeRetainExternalObj(Molecule *mol)
4935 {
4936         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4937                 if (sMoleculeRetainArray == Qnil) {
4938                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4939                         sMoleculeRetainArray = rb_ary_new();
4940                 }
4941                 
4942                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4943                 mol->exmolobjProtected = 1;
4944         }
4945 }
4946
4947 /*  Release hook function for Ruby  */
4948 void
4949 MoleculeReleaseHook(Molecule *mol)
4950 {
4951         if (mol->exmolobj != NULL) {
4952                 /*  No need to remove from sMoleculeRetainArray  */
4953                 mol->exmolobj = NULL;
4954                 mol->exmolobjProtected = 0;
4955         }
4956         MoleculeRelease(mol);
4957 }
4958
4959 VALUE
4960 ValueFromMolecule(Molecule *mol)
4961 {
4962         if (mol == NULL)
4963                 return Qnil;
4964         if (mol->exmolobj != NULL)
4965                 return (VALUE)mol->exmolobj;
4966         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4967         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4968         return (VALUE)mol->exmolobj;
4969 }
4970
4971 /*  Allocator  */
4972 static VALUE
4973 s_Molecule_Alloc(VALUE klass)
4974 {
4975         VALUE val;
4976         Molecule *mol = MoleculeNew();
4977         val = ValueFromMolecule(mol);
4978         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4979         return val;
4980 }
4981
4982 static int
4983 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4984 {
4985         int n;
4986         char *p = "";
4987         if (FIXNUM_P(val)) {
4988                 n = FIX2INT(val);
4989                 if (n >= 0 && n < mol->natoms)
4990                         return n;
4991                 n = -1; /*  No such atom  */
4992                 val = rb_inspect(val);
4993         } else {
4994                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4995         }
4996         if (n >= 0 && n < mol->natoms)
4997                 return n;
4998         p = StringValuePtr(val);
4999         if (n == -1)
5000                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
5001         else if (n == -2)
5002                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
5003         else
5004                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
5005         return 0; /* Not reached */
5006 }
5007
5008 static IntGroup *
5009 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
5010 {
5011         IntGroup *ig;
5012     Molecule *mp1;
5013     Data_Get_Struct(self, Molecule, mp1);
5014         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
5015         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
5016                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5017         Data_Get_Struct(val, IntGroup, ig);
5018     IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
5019         IntGroupRetain(ig);
5020         return ig;
5021 }
5022
5023 /*
5024  *  call-seq:
5025  *     dup          -> Molecule
5026  *
5027  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
5028  *  created object does not affect the old object in any sense.
5029  */
5030 static VALUE
5031 s_Molecule_InitCopy(VALUE self, VALUE arg)
5032 {
5033         Molecule *mp1, *mp2;
5034         Data_Get_Struct(self, Molecule, mp1);
5035         mp2 = MoleculeFromValue(arg);
5036         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
5037                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
5038         return self;
5039 }
5040
5041 /*
5042  *  call-seq:
5043  *     atom_index(val)       -> Integer
5044  *
5045  *  Returns the atom index represented by val. val can be either a non-negative integer
5046  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5047  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
5048  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
5049  *  If val is a string and multiple atoms match the description, the atom with the lowest index
5050  *  is returned.
5051  */
5052 static VALUE
5053 s_Molecule_AtomIndex(VALUE self, VALUE val)
5054 {
5055     Molecule *mol;
5056     Data_Get_Struct(self, Molecule, mol);
5057         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5058 }
5059
5060 /*
5061  *  call-seq:
5062  *     self == Molecule -> boolean
5063  *
5064  *  True if the two arguments point to the same molecule.
5065  */
5066 static VALUE
5067 s_Molecule_Equal(VALUE self, VALUE val)
5068 {
5069         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
5070                 Molecule *mol1, *mol2;
5071                 Data_Get_Struct(self, Molecule, mol1);
5072                 Data_Get_Struct(val, Molecule, mol2);
5073                 return (mol1 == mol2 ? Qtrue : Qfalse);
5074         } else return Qfalse;
5075 }
5076
5077 #pragma mark ------ Load/Save ------
5078
5079 static void
5080 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5081 {
5082         if (gLoadSaveErrorMessage != NULL) {
5083                 MyAppCallback_setConsoleColor(1);
5084                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5085                 MyAppCallback_setConsoleColor(0);
5086         }
5087         if (status != 0)
5088                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5089 }
5090
5091 /*
5092  *  call-seq:
5093  *     loadmbsf(file)       -> bool
5094  *
5095  *  Read a structure from a mbsf file.
5096  *  Return true if successful.
5097  */
5098 static VALUE
5099 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5100 {
5101         VALUE fname;
5102         char *fstr;
5103         Molecule *mol;
5104         int retval;
5105         MoleculeClearLoadSaveErrorMessage();
5106         Data_Get_Struct(self, Molecule, mol);
5107         rb_scan_args(argc, argv, "1", &fname);
5108         fstr = FileStringValuePtr(fname);
5109         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5110         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5111         return Qtrue;   
5112 }
5113
5114 /*
5115  *  call-seq:
5116  *     loadpsf(file, pdbfile = nil)       -> bool
5117  *
5118  *  Read a structure from a psf file. molecule must be empty. The psf may be
5119  *  an "extended" version, which also contains coordinates. If pdbfile 
5120  *  is given, then atomic coordinates are read from that file.
5121  *  Return true if successful.
5122  */
5123 static VALUE
5124 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5125 {
5126         VALUE fname, pdbname;
5127         char *fstr, *pdbstr;
5128         Molecule *mol;
5129         int retval;
5130         Data_Get_Struct(self, Molecule, mol);
5131         if (mol->natoms > 0)
5132                 return Qnil;  /*  Must be a new molecule  */
5133         MoleculeClearLoadSaveErrorMessage();
5134         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5135         fstr = FileStringValuePtr(fname);
5136         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5137         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5138         pdbstr = NULL;
5139         if (!NIL_P(pdbname)) {
5140                 pdbstr = strdup(FileStringValuePtr(pdbname));
5141                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5142                 free(pdbstr);
5143                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5144         }
5145         return Qtrue;
5146 }
5147
5148 /*
5149  *  call-seq:
5150  *     loadpdb(file)       -> bool
5151  *
5152  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5153  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5154  *  Return true if successful.
5155  */
5156 static VALUE
5157 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5158 {
5159         VALUE fname;
5160         char *fstr;
5161         Molecule *mol;
5162         int retval;
5163         Data_Get_Struct(self, Molecule, mol);
5164         rb_scan_args(argc, argv, "1", &fname);
5165         MoleculeClearLoadSaveErrorMessage();
5166         fstr = FileStringValuePtr(fname);
5167         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5168         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5169         return Qtrue;   
5170 }
5171
5172 /*
5173  *  call-seq:
5174  *     loaddcd(file)       -> bool
5175  *
5176  *  Read coordinates from a dcd file. The molecule should not empty.
5177  *  Return true if successful.
5178  */
5179 static VALUE
5180 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5181 {
5182         VALUE fname;
5183         char *fstr;
5184         Molecule *mol;
5185         int retval;
5186         Data_Get_Struct(self, Molecule, mol);
5187         rb_scan_args(argc, argv, "1", &fname);
5188         MoleculeClearLoadSaveErrorMessage();
5189         fstr = FileStringValuePtr(fname);
5190         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5191         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5192         return Qtrue;   
5193 }
5194
5195 /*
5196  *  call-seq:
5197  *     loadtep(file)       -> bool
5198  *
5199  *  Read coordinates from an ortep .tep file.
5200  *  Return true if successful.
5201  */
5202 static VALUE
5203 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5204 {
5205         VALUE fname;
5206         char *fstr;
5207         Molecule *mol;
5208         int retval;
5209         Data_Get_Struct(self, Molecule, mol);
5210         rb_scan_args(argc, argv, "1", &fname);
5211         MoleculeClearLoadSaveErrorMessage();
5212         fstr = FileStringValuePtr(fname);
5213         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5214         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5215         return Qtrue;   
5216 }
5217
5218 /*
5219  *  call-seq:
5220  *     loadres(file)       -> bool
5221  *
5222  *  Read coordinates from a shelx .res file.
5223  *  Return true if successful.
5224  */
5225 static VALUE
5226 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5227 {
5228         VALUE fname;
5229         char *fstr;
5230         Molecule *mol;
5231         int retval;
5232         Data_Get_Struct(self, Molecule, mol);
5233         rb_scan_args(argc, argv, "1", &fname);
5234         MoleculeClearLoadSaveErrorMessage();
5235         fstr = FileStringValuePtr(fname);
5236         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5237         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5238         return Qtrue;   
5239 }
5240
5241 /*
5242  *  call-seq:
5243  *     loadfchk(file)       -> bool
5244  *
5245  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5246  *  Return true if successful.
5247  */
5248 static VALUE
5249 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5250 {
5251         VALUE fname;
5252         char *fstr;
5253         Molecule *mol;
5254         int retval;
5255         Data_Get_Struct(self, Molecule, mol);
5256         rb_scan_args(argc, argv, "1", &fname);
5257         MoleculeClearLoadSaveErrorMessage();
5258         fstr = FileStringValuePtr(fname);
5259         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5260         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5261         return Qtrue;   
5262 }
5263
5264 /*
5265  *  call-seq:
5266  *     loaddat(file)       -> bool
5267  *
5268  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5269  *  Return true if successful.
5270  */
5271 static VALUE
5272 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5273 {
5274         VALUE fname;
5275         char *fstr;
5276         Molecule *mol;
5277         int retval;
5278         Data_Get_Struct(self, Molecule, mol);
5279         rb_scan_args(argc, argv, "1", &fname);
5280         MoleculeClearLoadSaveErrorMessage();
5281         fstr = FileStringValuePtr(fname);
5282         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5283         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5284         MyAppCallback_hideProgressPanel();
5285         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5286         return Qtrue;   
5287 }
5288
5289 /*
5290  *  call-seq:
5291  *     savembsf(file)       -> bool
5292  *
5293  *  Write structure as a mbsf file. Returns true if successful.
5294  */
5295 static VALUE
5296 s_Molecule_Savembsf(VALUE self, VALUE fname)
5297 {
5298         char *fstr;
5299     Molecule *mol;
5300         int retval;
5301     Data_Get_Struct(self, Molecule, mol);
5302         MoleculeClearLoadSaveErrorMessage();
5303         fstr = FileStringValuePtr(fname);
5304         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5305         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5306         return Qtrue;
5307 }
5308
5309 /*
5310  *  call-seq:
5311  *     savepsf(file)       -> bool
5312  *
5313  *  Write structure as a psf file. Returns true if successful.
5314  */
5315 static VALUE
5316 s_Molecule_Savepsf(VALUE self, VALUE fname)
5317 {
5318         char *fstr;
5319     Molecule *mol;
5320         int retval;
5321     Data_Get_Struct(self, Molecule, mol);
5322         MoleculeClearLoadSaveErrorMessage();
5323         fstr = FileStringValuePtr(fname);
5324         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5325         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5326         return Qtrue;
5327 }
5328
5329 /*
5330  *  call-seq:
5331  *     savepdb(file)       -> bool
5332  *
5333  *  Write coordinates as a pdb file. Returns true if successful.
5334  */
5335 static VALUE
5336 s_Molecule_Savepdb(VALUE self, VALUE fname)
5337 {
5338         char *fstr;
5339     Molecule *mol;
5340         int retval;
5341     Data_Get_Struct(self, Molecule, mol);
5342         MoleculeClearLoadSaveErrorMessage();
5343         fstr = FileStringValuePtr(fname);
5344         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5345         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5346         return Qtrue;
5347 }
5348
5349 /*
5350  *  call-seq:
5351  *     savedcd(file)       -> bool
5352  *
5353  *  Write coordinates as a dcd file. Returns true if successful.
5354  */
5355 static VALUE
5356 s_Molecule_Savedcd(VALUE self, VALUE fname)
5357 {
5358         char *fstr;
5359     Molecule *mol;
5360         int retval;
5361     Data_Get_Struct(self, Molecule, mol);
5362         MoleculeClearLoadSaveErrorMessage();
5363         fstr = FileStringValuePtr(fname);
5364         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5365         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5366         return Qtrue;
5367 }
5368
5369 /*  load([ftype, ] fname, ...)  */
5370 static VALUE
5371 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5372 {
5373         VALUE rval;
5374         char *argstr, *methname, *p, *type = "";
5375         ID mid = 0;
5376         int i;
5377         const char *ls = (loadFlag ? "load" : "save");
5378         int lslen = strlen(ls);
5379
5380         if (argc == 0)
5381                 return Qnil;
5382
5383         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5384                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5385         if (argstr[0] == ':') {
5386                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5387                 methname = ALLOC_N(char, lslen + strlen(argstr));
5388                 strcpy(methname, ls);
5389                 strcat(methname, argstr + 1);
5390                 type = argstr + 1;
5391                 for (i = lslen; methname[i] != 0; i++)
5392                         methname[i] = tolower(methname[i]);
5393                 mid = rb_intern(methname);
5394                 xfree(methname);
5395                 argc--;
5396                 argv++;
5397                 rval = rb_funcall2(self, mid, argc, argv);
5398                 if (rval == Qnil)
5399                         goto failure;
5400                 else
5401                         goto success;
5402         }
5403         /*  Guess file type from extension  */
5404         p = strrchr(argstr, '.');
5405         if (p != NULL) {
5406                 p++;
5407                 type = p;
5408                 for (methname = p; *methname != 0; methname++) {
5409                         if (!isalpha(*methname))
5410                                 break;
5411                 }
5412                 if (*methname == 0) {
5413                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5414                         if (methname == NULL)
5415                                 rb_raise(rb_eMolbyError, "Low memory");
5416                         strcpy(methname, ls);
5417                         strcat(methname, p);
5418                         for (i = lslen; methname[i] != 0; i++)
5419                                 methname[i] = tolower(methname[i]);
5420                         mid = rb_intern(methname);
5421                         xfree(methname);
5422                         if (loadFlag) {
5423                                 if (rb_respond_to(self, mid)) {
5424                                         /*  Load: try to call the load procedure only if it is available  */
5425                                         rval = rb_funcall2(self, mid, argc, argv);
5426                                         if (rval != Qnil)
5427                                                 goto success;
5428                                 }
5429                         } else {
5430                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5431                                 rval = rb_funcall2(self, mid, argc, argv);
5432                                 if (rval != Qnil)
5433                                         goto success;
5434                         }
5435                 }
5436         }
5437 failure:
5438         rval = rb_str_to_str(argv[0]);
5439         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5440         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5441         return Qnil;  /*  Does not reach here  */
5442
5443 success:
5444         {
5445                 /*  Register the path  */
5446                 Molecule *mol;
5447         /*      Atom *ap; */
5448                 Data_Get_Struct(self, Molecule, mol);
5449                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5450                 
5451                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5452         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5453                         if (ap->occupancy != 0.0)
5454                                 break;
5455                 }
5456                 if (i == mol->natoms) {
5457                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5458                                 ap->occupancy = 1.0;
5459                         }
5460                 } */
5461         }
5462         return rval;
5463 }
5464
5465 /*
5466  *  call-seq:
5467  *     molload(file, *args)       -> bool
5468  *
5469  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5470  *  file type given by the extension). If this method fails, then all defined (public)
5471  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5472  */
5473 static VALUE
5474 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5475 {
5476         return s_Molecule_LoadSave(argc, argv, self, 1);
5477 }
5478
5479 /*
5480  *  call-seq:
5481  *     molsave(file, *args)       -> bool
5482  *
5483  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5484  *  (XXX is the file type given by the extension).
5485  */
5486 static VALUE
5487 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5488 {
5489         return s_Molecule_LoadSave(argc, argv, self, 0);
5490 }
5491
5492 /*
5493  *  call-seq:
5494  *     open        -> Molecule
5495  *     open(file)  -> Molecule
5496  *
5497  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5498  */
5499 static VALUE
5500 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5501 {
5502         VALUE fname;
5503         const char *p;
5504         Molecule *mp;
5505         VALUE iflag;
5506     
5507     if (!gUseGUI) {
5508         rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5509     }
5510     
5511         rb_scan_args(argc, argv, "01", &fname);
5512         if (NIL_P(fname))
5513                 p = NULL;
5514         else
5515                 p = FileStringValuePtr(fname);
5516         iflag = Ruby_SetInterruptFlag(Qfalse);
5517         mp = MoleculeCallback_openNewMolecule(p);
5518         Ruby_SetInterruptFlag(iflag);
5519         if (mp == NULL) {
5520                 if (p == NULL)
5521                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5522                 else
5523                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5524         }
5525         return ValueFromMolecule(mp);
5526 }
5527
5528 /*
5529  *  call-seq:
5530  *     new  -> Molecule
5531  *     new(file, *args)  -> Molecule
5532  *
5533  *  Create a new molecule and call "load" method with the same arguments.
5534  */
5535 static VALUE
5536 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5537 {
5538         if (argc > 0)
5539                 return s_Molecule_Load(argc, argv, self);
5540         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5541 }
5542
5543 /*
5544  *  call-seq:
5545  *     error_message       -> String
5546  *
5547  *  Get the error_message from the last load/save method. If no error, returns nil.
5548  */
5549 static VALUE
5550 s_Molecule_ErrorMessage(VALUE klass)
5551 {
5552         if (gLoadSaveErrorMessage == NULL)
5553                 return Qnil;
5554         else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5555 }
5556
5557 /*
5558  *  call-seq:
5559  *     set_error_message(String)
5560  *     Molecule.error_message = String
5561  *
5562  *  Set the error_message for the present load/save method.
5563  */
5564 static VALUE
5565 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5566 {
5567         if (gLoadSaveErrorMessage != NULL) {
5568                 free(gLoadSaveErrorMessage);
5569                 gLoadSaveErrorMessage = NULL;
5570         }
5571         if (sval != Qnil) {
5572                 sval = rb_str_to_str(sval);
5573                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5574         }
5575         return sval;
5576 }
5577
5578 /*
5579  *  call-seq:
5580  *     set_molecule(Molecule)
5581  *
5582  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5583  *  This method is exclusively used for associating a new document with an existing molecule.
5584  */
5585 static VALUE
5586 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5587 {
5588         Molecule *mp1, *mp2;
5589         Data_Get_Struct(self, Molecule, mp1);
5590         mp2 = MoleculeFromValue(mval);
5591         MoleculeInitWithMolecule(mp1, mp2);
5592         MoleculeCallback_notifyModification(mp1, 1);
5593         return self;
5594 }
5595
5596 #pragma mark ------ Name attributes ------
5597
5598 /*
5599  *  call-seq:
5600  *     name       -> String
5601  *
5602  *  Returns the display name of the molecule. If the molecule has no associated
5603  *  document, then returns nil.
5604  */
5605 static VALUE
5606 s_Molecule_Name(VALUE self)
5607 {
5608     Molecule *mol;
5609         char buf[1024];
5610     Data_Get_Struct(self, Molecule, mol);
5611         MoleculeCallback_displayName(mol, buf, sizeof buf);
5612         if (buf[0] == 0)
5613                 return Qnil;
5614         else
5615                 return Ruby_NewEncodedStringValue2(buf);
5616 }
5617
5618 /*
5619  *  call-seq:
5620  *     set_name(string) -> self
5621  *
5622  *  Set the name of an untitled molecule. If the molecule is not associated with window
5623  *  or it already has an associated file, then exception is thrown.
5624  */
5625 static VALUE
5626 s_Molecule_SetName(VALUE self, VALUE nval)
5627 {
5628     Molecule *mol;
5629     Data_Get_Struct(self, Molecule, mol);
5630         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5631                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5632         return self;
5633 }
5634
5635
5636 /*
5637  *  call-seq:
5638  *     path       -> String
5639  *
5640  *  Returns the full path name of the molecule, if it is associated with a file.
5641  *  If the molecule has no associated file, then returns nil.
5642  */
5643 static VALUE
5644 s_Molecule_Path(VALUE self)
5645 {
5646     Molecule *mol;
5647         char buf[1024];
5648     Data_Get_Struct(self, Molecule, mol);
5649         MoleculeCallback_pathName(mol, buf, sizeof buf);
5650         if (buf[0] == 0)
5651                 return Qnil;
5652         else
5653                 return Ruby_NewFileStringValue(buf);
5654 }
5655
5656 /*
5657  *  call-seq:
5658  *     dir       -> String
5659  *
5660  *  Returns the full path name of the directory in which the file associated with the
5661  *  molecule is located. If the molecule has no associated file, then returns nil.
5662  */
5663 static VALUE
5664 s_Molecule_Dir(VALUE self)
5665 {
5666     Molecule *mol;
5667         char buf[1024], *p;
5668     Data_Get_Struct(self, Molecule, mol);
5669         MoleculeCallback_pathName(mol, buf, sizeof buf);
5670 #if __WXMSW__
5671         translate_char(buf, '\\', '/');
5672 #endif
5673         if (buf[0] == 0)
5674                 return Qnil;
5675         else {
5676                 p = strrchr(buf, '/');
5677                 if (p != NULL)
5678                         *p = 0;
5679                 return Ruby_NewEncodedStringValue2(buf);
5680         }
5681 }
5682
5683 /*
5684  *  call-seq:
5685  *     inspect       -> String
5686  *
5687  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5688  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5689  *  the Molecule structure) is returned.
5690  */
5691 static VALUE
5692 s_Molecule_Inspect(VALUE self)
5693 {
5694     Molecule *mol;
5695         char buf[256];
5696     Data_Get_Struct(self, Molecule, mol);
5697         MoleculeCallback_displayName(mol, buf, sizeof buf);
5698         if (buf[0] == 0) {
5699                 /*  No associated document  */
5700                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5701                 return Ruby_NewEncodedStringValue2(buf);
5702         } else {
5703                 /*  Check whether the document name is duplicate  */
5704                 char buf2[256];
5705                 int idx, k, k2;
5706                 Molecule *mol2;
5707                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5708                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5709                         if (strcmp(buf, buf2) == 0) {
5710                                 k++;
5711                                 if (mol == mol2)
5712                                         k2 = k;
5713                         }
5714                 }
5715                 if (k > 1) {
5716                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5717                 } else {
5718                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5719                 }
5720                 return Ruby_NewEncodedStringValue2(buf2);
5721         }
5722 }
5723
5724 #pragma mark ------ MolEnumerables ------
5725
5726 static VALUE
5727 s_Molecule_MolEnumerable(VALUE self, int kind)
5728 {
5729     Molecule *mol;
5730         MolEnumerable *mseq;
5731     Data_Get_Struct(self, Molecule, mol);
5732         mseq = MolEnumerableNew(mol, kind);
5733         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5734 }
5735
5736 /*
5737  *  call-seq:
5738  *     atoms       -> MolEnumerable
5739  *
5740  *  Returns a MolEnumerable object representing the array of atoms.
5741  */
5742 static VALUE
5743 s_Molecule_Atoms(VALUE self)
5744 {
5745         return s_Molecule_MolEnumerable(self, kAtomKind);
5746 }
5747
5748 /*
5749  *  call-seq:
5750  *     bonds       -> MolEnumerable
5751  *
5752  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5753  *  by an array of two atom indices.
5754  */
5755 static VALUE
5756 s_Molecule_Bonds(VALUE self)
5757 {
5758         return s_Molecule_MolEnumerable(self, kBondKind);
5759 }
5760
5761 /*
5762  *  call-seq:
5763  *     angles       -> MolEnumerable
5764  *
5765  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5766  *  by an array of three atom indices.
5767  */
5768 static VALUE
5769 s_Molecule_Angles(VALUE self)
5770 {
5771         return s_Molecule_MolEnumerable(self, kAngleKind);
5772 }
5773
5774 /*
5775  *  call-seq:
5776  *     dihedrals       -> MolEnumerable
5777  *
5778  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5779  *  by an array of four atom indices.
5780  */
5781 static VALUE
5782 s_Molecule_Dihedrals(VALUE self)
5783 {
5784         return s_Molecule_MolEnumerable(self, kDihedralKind);
5785 }
5786
5787 /*
5788  *  call-seq:
5789  *     impropers       -> MolEnumerable
5790  *
5791  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5792  *  by an array of four atom indices.
5793  */
5794 static VALUE
5795 s_Molecule_Impropers(VALUE self)
5796 {
5797         return s_Molecule_MolEnumerable(self, kImproperKind);
5798 }
5799
5800 /*
5801  *  call-seq:
5802  *     residues       -> MolEnumerable
5803  *
5804  *  Returns a MolEnumerable object representing the array of residue names.
5805  */
5806 static VALUE
5807 s_Molecule_Residues(VALUE self)
5808 {
5809         return s_Molecule_MolEnumerable(self, kResidueKind);
5810 }
5811
5812 /*
5813  *  call-seq:
5814  *     natoms       -> Integer
5815  *
5816  *  Returns the number of atoms.
5817  */
5818 static VALUE
5819 s_Molecule_Natoms(VALUE self)
5820 {
5821     Molecule *mol;
5822     Data_Get_Struct(self, Molecule, mol);
5823         return INT2NUM(mol->natoms);
5824 }
5825
5826 /*
5827  *  call-seq:
5828  *     nbonds       -> Integer
5829  *
5830  *  Returns the number of bonds.
5831  */
5832 static VALUE
5833 s_Molecule_Nbonds(VALUE self)
5834 {
5835     Molecule *mol;
5836     Data_Get_Struct(self, Molecule, mol);
5837         return INT2NUM(mol->nbonds);
5838 }
5839
5840 /*
5841  *  call-seq:
5842  *     nangles       -> Integer
5843  *
5844  *  Returns the number of angles.
5845  */
5846 static VALUE
5847 s_Molecule_Nangles(VALUE self)
5848 {
5849     Molecule *mol;
5850     Data_Get_Struct(self, Molecule, mol);
5851         return INT2NUM(mol->nangles);
5852 }
5853
5854 /*
5855  *  call-seq:
5856  *     ndihedrals       -> Integer
5857  *
5858  *  Returns the number of dihedrals.
5859  */
5860 static VALUE
5861 s_Molecule_Ndihedrals(VALUE self)
5862 {
5863     Molecule *mol;
5864     Data_Get_Struct(self, Molecule, mol);
5865         return INT2NUM(mol->ndihedrals);
5866 }
5867
5868 /*
5869  *  call-seq:
5870  *     nimpropers       -> Integer
5871  *
5872  *  Returns the number of impropers.
5873  */
5874 static VALUE
5875 s_Molecule_Nimpropers(VALUE self)
5876 {
5877     Molecule *mol;
5878     Data_Get_Struct(self, Molecule, mol);
5879         return INT2NUM(mol->nimpropers);
5880 }
5881
5882 /*
5883  *  call-seq:
5884  *     nresidues       -> Integer
5885  *
5886  *  Returns the number of residues.
5887  */
5888 static VALUE
5889 s_Molecule_Nresidues(VALUE self)
5890 {
5891     Molecule *mol;
5892     Data_Get_Struct(self, Molecule, mol);
5893         return INT2NUM(mol->nresidues);
5894 }
5895
5896 /*
5897  *  call-seq:
5898  *     nresidues = Integer
5899  *
5900  *  Change the number of residues.
5901  */
5902 static VALUE
5903 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5904 {
5905     Molecule *mol;
5906         int ival = NUM2INT(val);
5907     Data_Get_Struct(self, Molecule, mol);
5908         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5909         if (ival != mol->nresidues)
5910                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5911         return val;
5912 }
5913
5914 /*
5915  *  call-seq:
5916  *     max_residue_number(atom_group = nil)     -> Integer
5917  *
5918  *  Returns the maximum residue number actually used. If an atom group is given, only
5919  *  these atoms are examined. If no atom is present, nil is returned.
5920  */
5921 static VALUE
5922 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5923 {
5924     Molecule *mol;
5925         VALUE gval;
5926         int maxSeq;
5927         IntGroup *ig;
5928     Data_Get_Struct(self, Molecule, mol);
5929         rb_scan_args(argc, argv, "01", &gval);
5930         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5931         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5932         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5933 }
5934
5935 /*
5936  *  call-seq:
5937  *     min_residue_number(atom_group = nil)     -> Integer
5938  *
5939  *  Returns the minimum residue number actually used. If an atom group is given, only
5940  *  these atoms are examined. If no atom is present, nil is returned.
5941  */
5942 static VALUE
5943 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5944 {
5945     Molecule *mol;
5946         VALUE gval;
5947         int minSeq;
5948         IntGroup *ig;
5949     Data_Get_Struct(self, Molecule, mol);
5950         rb_scan_args(argc, argv, "01", &gval);
5951         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5952         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5953         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5954 }
5955
5956 /*
5957  *  call-seq:
5958  *     each_atom(atom_group = nil) {|aref| ...}
5959  *
5960  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5961  *  group is given, only these atoms are processed.
5962  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5963  *  is self (a Molecule object).
5964  */
5965 static VALUE
5966 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5967 {
5968         int i;
5969     Molecule *mol;
5970         AtomRef *aref;
5971         VALUE arval;
5972         VALUE gval;
5973         IntGroup *ig;
5974     Data_Get_Struct(self, Molecule, mol);
5975         rb_scan_args(argc, argv, "01", &gval);
5976         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5977         arval = ValueFromMoleculeAndIndex(mol, 0);
5978         Data_Get_Struct(arval, AtomRef, aref);
5979         for (i = 0; i < mol->natoms; i++) {
5980                 aref->idx = i;
5981                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5982                         rb_yield(arval);
5983         }
5984         if (ig != NULL)
5985                 IntGroupRelease(ig);
5986     return self;
5987 }
5988
5989 #pragma mark ------ Atom Group ------
5990
5991 static VALUE
5992 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5993 {
5994         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5995         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5996         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5997         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5998         return Qnil;
5999 }
6000
6001 /*
6002  *  call-seq:
6003  *     atom_group
6004  *     atom_group {|aref| ...}
6005  *     atom_group(arg1, arg2, ...)
6006  *     atom_group(arg1, arg2, ...) {|aref| ...}
6007  *
6008  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6009  *  If arguments are given, then the atoms reprensented by the arguments are added to the
6010  *  group. For a conversion of a string to an atom index, see the description
6011  *  of Molecule#atom_index.
6012  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
6013  *  representing each atom, and the atoms are removed from the result if the block returns false.
6014  *
6015  */
6016 static VALUE
6017 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6018 {
6019         IntGroup *ig1, *ig2;
6020     Molecule *mol;
6021         Int i, startPt, interval;
6022         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6023         Data_Get_Struct(retval, IntGroup, ig1);
6024     Data_Get_Struct(self, Molecule, mol);
6025         if (argc == 0) {
6026                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6027         } else {
6028                 while (argc > 0) {
6029                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6030                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6031                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6032                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6033                                 ig2 = IntGroupFromValue(*argv);
6034                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6035                                         interval = IntGroupGetInterval(ig2, i);
6036                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6037                                 }
6038                                 IntGroupRelease(ig2);
6039                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
6040                                 VALUE values[2];
6041                                 values[0] = (VALUE)mol;
6042                                 values[1] = (VALUE)ig1;
6043                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6044                         } else
6045                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6046                         argc--;
6047                         argv++;
6048                 }
6049         }
6050         if (rb_block_given_p()) {
6051                 /*  Evaluate the given block with an AtomRef as the argument, and delete
6052                         the index if the block returns false  */
6053                 AtomRef *aref = AtomRefNew(mol, 0);
6054                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6055                 ig2 = IntGroupNew();
6056                 IntGroupCopy(ig2, ig1);
6057                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6058                         VALUE resval;
6059                         if (startPt >= mol->natoms)
6060                                 break;
6061                         aref->idx = startPt;
6062                         resval = rb_yield(arval);
6063                         if (!RTEST(resval))
6064                                 IntGroupRemove(ig1, startPt, 1);
6065                 }
6066                 IntGroupRelease(ig2);
6067         }
6068         
6069         /*  Remove points that are out of bounds */
6070         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6071
6072         return retval;                  
6073 }
6074
6075 /*
6076  *  call-seq:
6077  *     selection       -> IntGroup
6078  *
6079  *  Returns the current selection.
6080  */
6081 static VALUE
6082 s_Molecule_Selection(VALUE self)
6083 {
6084     Molecule *mol;
6085         IntGroup *ig;
6086         VALUE val;
6087     Data_Get_Struct(self, Molecule, mol);
6088         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6089                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
6090                 val = ValueFromIntGroup(ig);
6091                 IntGroupRelease(ig);
6092         } else {
6093                 val = IntGroup_Alloc(rb_cIntGroup);
6094         }
6095         return val;
6096 }
6097
6098 static VALUE
6099 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6100 {
6101     Molecule *mol;
6102         IntGroup *ig;
6103     Data_Get_Struct(self, Molecule, mol);
6104         if (val == Qnil)
6105                 ig = NULL;
6106         else
6107                 ig = s_Molecule_AtomGroupFromValue(self, val);
6108         if (undoable)
6109                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6110         else
6111                 MoleculeSetSelection(mol, ig);
6112         if (ig != NULL)
6113                 IntGroupRelease(ig);
6114         return val;
6115 }
6116
6117 /*
6118  *  call-seq:
6119  *     selection = IntGroup
6120  *
6121  *  Set the current selection. The right-hand operand may be nil.
6122  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6123  */
6124 static VALUE
6125 s_Molecule_SetSelection(VALUE self, VALUE val)
6126 {
6127         return s_Molecule_SetSelectionSub(self, val, 0);
6128 }
6129
6130 /*
6131  *  call-seq:
6132  *     set_undoable_selection(IntGroup)
6133  *
6134  *  Set the current selection with undo registration. The right-hand operand may be nil.
6135  *  This operation is undoable.
6136  */
6137 static VALUE
6138 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6139 {
6140         return s_Molecule_SetSelectionSub(self, val, 1);
6141 }
6142
6143 #pragma mark ------ Editing ------
6144
6145 /*
6146  *  call-seq:
6147  *     extract(group, dummy_flag = nil)       -> Molecule
6148  *
6149  *  Extract the atoms given by group and return as a new molecule object.
6150  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6151  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6152  *  names beginning with an underscore) and included in the new molecule object.
6153  */
6154 static VALUE
6155 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6156 {
6157     Molecule *mol1, *mol2;
6158         IntGroup *ig;
6159         VALUE group, dummy_flag, retval;
6160     Data_Get_Struct(self, Molecule, mol1);
6161         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6162         ig = s_Molecule_AtomGroupFromValue(self, group);
6163         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6164                 retval = Qnil;
6165         } else {
6166                 retval = ValueFromMolecule(mol2);
6167         }
6168         IntGroupRelease(ig);
6169         return retval;
6170 }
6171
6172 /*
6173  *  call-seq:
6174  *     add(molecule2)       -> self
6175  *
6176  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6177     conflicts.
6178     This operation is undoable.
6179  */
6180 static VALUE
6181 s_Molecule_Add(VALUE self, VALUE val)
6182 {
6183     Molecule *mol1, *mol2;
6184     Data_Get_Struct(self, Molecule, mol1);
6185         mol2 = MoleculeFromValue(val);
6186         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6187         return self; 
6188 }
6189
6190 /*
6191  *  call-seq:
6192  *     remove(group)       -> Molecule
6193  *
6194  *  The atoms designated by the given group are removed from the molecule.
6195  *  This operation is undoable.
6196  */
6197 static VALUE
6198 s_Molecule_Remove(VALUE self, VALUE group)
6199 {
6200     Molecule *mol1;
6201         IntGroup *ig, *bg;
6202         Int i;
6203         IntGroupIterator iter;
6204
6205     ig = s_Molecule_AtomGroupFromValue(self, group);
6206 /*    Data_Get_Struct(self, Molecule, mol1);
6207         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6208         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6209                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6210         Data_Get_Struct(group, IntGroup, ig); */
6211     Data_Get_Struct(self, Molecule, mol1);
6212     
6213         /*  Remove the bonds between the two fragments  */
6214         /*  (This is necessary for undo to work correctly)  */
6215         IntGroupIteratorInit(ig, &iter);
6216         bg = NULL;
6217         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6218                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6219                 Int j, *cp;
6220                 cp = AtomConnectData(&ap->connect);
6221                 for (j = 0; j < ap->connect.count; j++) {
6222                         int n = cp[j];
6223                         if (!IntGroupLookup(ig, n, NULL)) {
6224                                 /*  bond i-n, i is in ig and n is not  */
6225                                 int k = MoleculeLookupBond(mol1, i, n);
6226                                 if (k >= 0) {
6227                                         if (bg == NULL)
6228                                                 bg = IntGroupNew();
6229                                         IntGroupAdd(bg, k, 1);
6230                                 }
6231                         }
6232                 }
6233         }
6234         IntGroupIteratorRelease(&iter);
6235         if (bg != NULL) {
6236                 /*  Remove bonds  */
6237                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6238                 IntGroupRelease(bg);
6239         }
6240         /*  Remove atoms  */
6241         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6242                 return Qnil;
6243         return self;
6244 }
6245
6246 /*
6247  *  call-seq:
6248  *     create_atom(name, pos = -1)  -> AtomRef
6249  *
6250  *  Create a new atom with the specified name (may contain residue 
6251  *  information) and position (if position is out of range, the atom is appended at
6252  *  the end). Returns the reference to the new atom.
6253  *  This operation is undoable.
6254  */
6255 static VALUE
6256 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6257 {
6258     Molecule *mol;
6259     Int i, pos;
6260         VALUE name, ival;
6261     Atom arec;
6262     AtomRef *aref;
6263         char *p, resName[6], atomName[6];
6264         int resSeq;
6265     Data_Get_Struct(self, Molecule, mol);
6266         rb_scan_args(argc, argv, "02", &name, &ival);
6267         if (ival != Qnil)
6268                 pos = NUM2INT(rb_Integer(ival));
6269         else pos = -1;
6270         if (name != Qnil) {
6271                 p = StringValuePtr(name);
6272                 if (p[0] != 0) {
6273                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6274                         if (atomName[0] == 0)
6275                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6276                 }
6277         } else p = NULL;
6278         if (p == NULL || p[0] == 0) {
6279                 memset(atomName, 0, 4);
6280                 resSeq = -1;
6281         }
6282     memset(&arec, 0, sizeof(arec));
6283     strncpy(arec.aname, atomName, 4);
6284     if (resSeq >= 0) {
6285       strncpy(arec.resName, resName, 4);
6286       arec.resSeq = resSeq;
6287     }
6288         arec.occupancy = 1.0;
6289 //    i = MoleculeCreateAnAtom(mol, &arec);
6290         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6291                 return Qnil;
6292     aref = AtomRefNew(mol, pos);
6293     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6294 }
6295
6296 /*
6297  *  call-seq:
6298  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6299  *
6300  *  Create a new atom with the same attributes (but no bonding information)
6301  *  with the specified atom. Returns the reference to the new atom.
6302  *  This operation is undoable.
6303  */
6304 static VALUE
6305 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6306 {
6307     Molecule *mol;
6308         const Atom *apsrc;
6309     Atom arec;
6310         AtomRef *aref;
6311         VALUE retval, aval, ival;
6312         Int pos;
6313     Data_Get_Struct(self, Molecule, mol);
6314         rb_scan_args(argc, argv, "11", &aval, &ival);
6315         if (FIXNUM_P(aval)) {
6316                 int idx = NUM2INT(aval);
6317                 if (idx < 0 || idx >= mol->natoms)
6318                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6319                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6320         } else {
6321                 apsrc = s_AtomFromValue(aval);
6322         }
6323         if (apsrc == NULL)
6324                 rb_raise(rb_eMolbyError, "bad atom specification");
6325         if (ival != Qnil)
6326                 pos = NUM2INT(rb_Integer(ival));
6327         else pos = -1;
6328         AtomDuplicate(&arec, apsrc);
6329         arec.connect.count = 0;
6330         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6331                 retval = Qnil;
6332         else {
6333                 aref = AtomRefNew(mol, pos);
6334                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6335         }
6336         AtomClean(&arec);
6337         return retval;
6338 }
6339
6340 /*
6341  *  call-seq:
6342  *     create_bond(n1, n2, ...)       -> Integer
6343  *
6344  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6345  *  do nothing for that pair. Returns the number of bonds actually created.
6346  *  This operation is undoable.
6347  */
6348 static VALUE
6349 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6350 {
6351     Molecule *mol;
6352         Int i, j, k, *ip, old_nbonds;
6353         if (argc == 0)
6354                 rb_raise(rb_eMolbyError, "missing arguments");
6355         if (argc % 2 != 0)
6356                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6357     Data_Get_Struct(self, Molecule, mol);
6358         ip = ALLOC_N(Int, argc + 1);
6359         for (i = j = 0; i < argc; i++, j++) {
6360                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6361                 if (i % 2 == 1) {
6362                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6363                                 j -= 2;  /*  This bond is already present: skip it  */
6364                         else {
6365                                 for (k = 0; k < j - 1; k += 2) {
6366                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6367                                                 j -= 2;   /*  The same entry is already in the argument  */
6368                                                 break;
6369                                         }
6370                                 }
6371                         }
6372                 }
6373         }
6374         old_nbonds = mol->nbonds;
6375         if (j > 0) {
6376                 ip[j] = kInvalidIndex;
6377                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6378         } else i = 0;
6379         xfree(ip);
6380         if (i == -1)
6381                 rb_raise(rb_eMolbyError, "atom index out of range");
6382         else if (i == -2)
6383                 rb_raise(rb_eMolbyError, "too many bonds");
6384         else if (i == -3)
6385                 rb_raise(rb_eMolbyError, "duplicate bonds");
6386         else if (i == -5)
6387                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6388         else if (i != 0)
6389                 rb_raise(rb_eMolbyError, "error in creating bonds");
6390         return INT2NUM(mol->nbonds - old_nbonds);
6391 }
6392
6393 /*
6394  *  call-seq:
6395  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6396  *
6397  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6398  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6399  *  This operation is undoable.
6400  */
6401 static VALUE
6402 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6403 {
6404     Molecule *mol;
6405         Int i, j, n[2];
6406         IntGroup *bg;
6407         if (argc == 0)
6408                 rb_raise(rb_eMolbyError, "missing arguments");
6409         if (argc % 2 != 0)
6410                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6411     Data_Get_Struct(self, Molecule, mol);
6412         bg = NULL;
6413         for (i = j = 0; i < argc; i++, j = 1 - j) {
6414                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6415                 if (j == 1) {
6416                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6417                         if (k >= 0) {
6418                                 if (bg == NULL)
6419                                         bg = IntGroupNew();
6420                                 IntGroupAdd(bg, k, 1);
6421                         }
6422                 }
6423         }
6424         if (bg != NULL) {
6425                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6426                 i = IntGroupGetCount(bg);
6427                 IntGroupRelease(bg);
6428         } else i = 0;
6429         return INT2NUM(i);
6430 }
6431
6432 /*
6433  *  call-seq:
6434  *     assign_bond_order(idx, d1)
6435  *     assign_bond_orders(group, [d1, d2, ...])
6436  *
6437  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6438  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6439  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6440  *  (This may change in the future)
6441  *  This operation is undoable.
6442  */
6443 static VALUE
6444 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6445 {
6446     Molecule *mol;
6447         IntGroup *ig;
6448     Data_Get_Struct(self, Molecule, mol);
6449         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6450                 /*  The first form  */
6451                 Int idx = NUM2INT(rb_Integer(idxval));
6452                 Double d1 = NUM2DBL(rb_Float(dval));
6453                 if (idx < 0 || idx >= mol->nbonds)
6454                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6455                 ig = IntGroupNewWithPoints(idx, 1, -1);
6456                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6457                 IntGroupRelease(ig);
6458         } else {
6459                 Int i, n;
6460                 Double *dp;
6461                 ig = IntGroupFromValue(idxval);
6462                 n = IntGroupGetCount(ig);
6463                 if (n == 0)
6464                         rb_raise(rb_eMolbyError, "the bond index is empty");
6465                 dval = rb_ary_to_ary(dval);
6466                 dp = (Double *)calloc(sizeof(Double), n);
6467                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6468                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6469                 }
6470                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6471                 free(dp);
6472                 IntGroupRelease(ig);
6473         }
6474         return self;
6475 }
6476
6477 /*
6478  *  call-seq:
6479  *     get_bond_order(idx) -> Float
6480  *     get_bond_orders(group) -> Array
6481  *
6482  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6483  *  In the second form, the bond orders at the indices in the group are returned as an array.
6484  *  If no bond order information have been assigned, returns nil (the first form)
6485  *  or an empty array (the second form).
6486  */
6487 static VALUE
6488 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6489 {
6490     Molecule *mol;
6491         IntGroup *ig;
6492         Double *dp;
6493         VALUE retval;
6494         Int i, n, numericArg;
6495     Data_Get_Struct(self, Molecule, mol);
6496         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6497                 /*  The first form  */
6498                 Int idx = NUM2INT(rb_Integer(idxval));
6499                 if (idx < 0 || idx >= mol->nbonds)
6500                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6501                 if (mol->bondOrders == NULL)
6502                         return Qnil;
6503                 ig = IntGroupNewWithPoints(idx, 1, -1);
6504                 n = 1;
6505                 numericArg = 1;
6506         } else {
6507                 if (mol->bondOrders == NULL)
6508                         return rb_ary_new();
6509                 ig = IntGroupFromValue(idxval);
6510                 n = IntGroupGetCount(ig);
6511                 if (n == 0)
6512                         rb_raise(rb_eMolbyError, "the bond index is empty");
6513                 numericArg = 0;
6514         }
6515         dp = (Double *)calloc(sizeof(Double), n);
6516         MoleculeGetBondOrders(mol, dp, ig);
6517         if (numericArg)
6518                 retval = rb_float_new(dp[0]);
6519         else {
6520                 retval = rb_ary_new();
6521                 for (i = 0; i < n; i++)
6522                         rb_ary_push(retval, rb_float_new(dp[i]));
6523         }
6524         free(dp);
6525         IntGroupRelease(ig);
6526         return retval;
6527 }
6528
6529 /*
6530  *  call-seq:
6531  *     bond_exist?(idx1, idx2) -> bool
6532  *
6533  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6534  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6535  */
6536 static VALUE
6537 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6538 {
6539         Molecule *mol;
6540         Int idx1, idx2, i;
6541         Atom *ap;
6542         Int *cp;
6543     Data_Get_Struct(self, Molecule, mol);
6544         idx1 = NUM2INT(rb_Integer(ival1));
6545         idx2 = NUM2INT(rb_Integer(ival2));
6546         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6547                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6548         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6549         cp = AtomConnectData(&ap->connect);
6550         for (i = 0; i < ap->connect.count; i++) {
6551                 if (cp[i] == idx2)
6552                         return Qtrue;
6553         }
6554         return Qfalse;
6555 }
6556
6557 /*
6558  *  call-seq:
6559  *     add_angle(n1, n2, n3)       -> Molecule
6560  *
6561  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6562  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6563  *  This operation is undoable.
6564  */
6565 static VALUE
6566 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6567 {
6568         Int n[4];
6569     Molecule *mol;
6570     Data_Get_Struct(self, Molecule, mol);
6571         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6572         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6573         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6574         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6575                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6576         n[3] = kInvalidIndex;
6577         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6578         return self;
6579 }
6580
6581 /*
6582  *  call-seq:
6583  *     remove_angle(n1, n2, n3)       -> Molecule
6584  *
6585  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6586  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6587  *  This operation is undoable.
6588  */
6589 static VALUE
6590 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6591 {
6592         Int n[4];
6593     Molecule *mol;
6594         IntGroup *ig;
6595     Data_Get_Struct(self, Molecule, mol);
6596         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6597         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6598         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6599         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6600                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6601         ig = IntGroupNewWithPoints(n[3], 1, -1);
6602         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6603         IntGroupRelease(ig);
6604         return self;
6605 }
6606
6607 /*
6608  *  call-seq:
6609  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6610  *
6611  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6612  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6613  *  This operation is undoable.
6614  */
6615 static VALUE
6616 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6617 {
6618         Int n[5];
6619     Molecule *mol;
6620     Data_Get_Struct(self, Molecule, mol);
6621         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6622         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6623         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6624         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6625         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6626                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6627         n[4] = kInvalidIndex;
6628         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6629         return self;
6630 }
6631
6632 /*
6633  *  call-seq:
6634  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6635  *
6636  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6637  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6638  *  This operation is undoable.
6639  */
6640 static VALUE
6641 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6642 {
6643         Int n[5];
6644     Molecule *mol;
6645         IntGroup *ig;
6646     Data_Get_Struct(self, Molecule, mol);
6647         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6648         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6649         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6650         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6651         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6652                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6653         ig = IntGroupNewWithPoints(n[4], 1, -1);
6654         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6655         IntGroupRelease(ig);
6656         return self;
6657 }
6658
6659 /*
6660  *  call-seq:
6661  *     add_improper(n1, n2, n3, n4)       -> Molecule
6662  *
6663  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6664  *  not automatically added when a new bond is created, so this method is more useful than
6665  *  the angle/dihedral counterpart.
6666  *  This operation is undoable.
6667  */
6668 static VALUE
6669 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6670 {
6671         Int n[5];
6672     Molecule *mol;
6673     Data_Get_Struct(self, Molecule, mol);
6674         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6675         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6676         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6677         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6678         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6679                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6680         n[4] = kInvalidIndex;
6681         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6682         return self;
6683 }
6684
6685 /*
6686  *  call-seq:
6687  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6688  *     remove_improper(intgroup)             -> Molecule
6689  *
6690  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6691  *  Returns self. Unlike angles and dihedrals, impropers are
6692  *  not automatically added when a new bond is created, so this method is more useful than
6693  *  the angle/dihedral counterpart.
6694  *  This operation is undoable.
6695  */
6696 static VALUE
6697 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6698 {
6699         Int n[5];
6700         VALUE v1, v2, v3, v4;
6701     Molecule *mol;
6702         IntGroup *ig;
6703     Data_Get_Struct(self, Molecule, mol);
6704         if (argc == 1) {
6705                 ig = IntGroupFromValue(argv[0]);
6706         } else {
6707                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6708                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6709                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6710                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6711                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6712                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6713                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6714                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6715         }
6716         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6717         IntGroupRelease(ig);
6718         return self;
6719 }
6720
6721 /*
6722  *  call-seq:
6723  *     assign_residue(group, res)       -> Molecule
6724  *
6725  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6726  *  or "resname.resno". When the residue number is not specified, the residue number of
6727  *  the first atom in the group is used.
6728  *  This operation is undoable.
6729  */
6730 static VALUE
6731 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6732 {
6733     Molecule *mol;
6734         IntGroup *ig;
6735         char *p, *pp, buf[16];
6736         Int resid, n;
6737         Atom *ap;
6738     Data_Get_Struct(self, Molecule, mol);
6739         
6740         /*  Parse the argument res  */
6741         if (FIXNUM_P(res)) {
6742                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6743                 resid = NUM2INT(res);
6744                 buf[0] = 0;
6745         } else {
6746                 p = StringValuePtr(res);
6747                 pp = strchr(p, '.');
6748                 if (pp != NULL) {
6749                         resid = atoi(pp + 1);
6750                         n = pp - p;
6751                 } else {
6752                         resid = -1;
6753                         n = strlen(p);
6754                 }
6755                 if (n > sizeof buf - 1)
6756                         n = sizeof buf - 1;
6757                 strncpy(buf, p, n);
6758                 buf[n] = 0;
6759         }
6760         ig = s_Molecule_AtomGroupFromValue(self, range);
6761         if (ig == NULL || IntGroupGetCount(ig) == 0)
6762                 return Qnil;
6763
6764         if (resid < 0) {
6765                 /*  Use the residue number of the first specified atom  */
6766                 n = IntGroupGetNthPoint(ig, 0);
6767                 if (n >= mol->natoms)
6768                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6769                 ap = ATOM_AT_INDEX(mol->atoms, n);
6770                 resid = ap->resSeq;
6771         }
6772         /*  Change the residue number  */
6773         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6774         /*  Change the residue name if necessary  */
6775         if (buf[0] != 0) {
6776         /*      Int seqs[2];
6777                 seqs[0] = resid;
6778                 seqs[1] = kInvalidIndex; */
6779                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6780         }
6781         IntGroupRelease(ig);
6782         return self;
6783 }
6784
6785 /*
6786  *  call-seq:
6787  *     offset_residue(group, offset)       -> Molecule
6788  *
6789  *  Offset the residue number of the specified atoms. If any of the residue number gets
6790  *  negative, then exception is thrown.
6791  *  This operation is undoable.
6792  */
6793 static VALUE
6794 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6795 {
6796     Molecule *mol;
6797         IntGroup *ig;
6798         int ofs, result;
6799     Data_Get_Struct(self, Molecule, mol);
6800         ig = s_Molecule_AtomGroupFromValue(self, range);
6801         ofs = NUM2INT(offset);
6802         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6803         if (result > 0)
6804                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6805         IntGroupRelease(ig);
6806         return self;
6807 }
6808
6809 /*
6810  *  call-seq:
6811  *     renumber_atoms(array)       -> IntGroup
6812  *
6813  *  Change the order of atoms so that the atoms specified in the array argument appear
6814  *  in this order from the top of the molecule. The atoms that are not included in array
6815  *  are placed after these atoms, and these atoms are returned as an intGroup.
6816  *  This operation is undoable.
6817  */
6818 static VALUE
6819 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6820 {
6821     Molecule *mol;
6822         Int *new2old;
6823         IntGroup *ig;
6824         int i, n;
6825         VALUE *valp, retval;
6826     Data_Get_Struct(self, Molecule, mol);
6827         if (TYPE(array) != T_ARRAY)
6828                 array = rb_funcall(array, rb_intern("to_a"), 0);
6829         n = RARRAY_LEN(array);
6830         valp = RARRAY_PTR(array);
6831         new2old = ALLOC_N(Int, n + 1);
6832         for (i = 0; i < n; i++)
6833                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6834         new2old[i] = kInvalidIndex;
6835         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6836         if (i == 1)
6837                 rb_raise(rb_eMolbyError, "Atom index out of range");
6838         else if (i == 2)
6839                 rb_raise(rb_eMolbyError, "Duplicate entry");
6840         else if (i == 3)
6841                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6842         retval = IntGroup_Alloc(rb_cIntGroup);
6843         Data_Get_Struct(retval, IntGroup, ig);
6844         if (mol->natoms > n)
6845                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6846         xfree(new2old);
6847         return retval;
6848 }
6849
6850 /*
6851  *  call-seq:
6852  *     set_atom_attr(index, key, value)
6853  *
6854  *  Set the atom attribute for the specified atom.
6855  *  This operation is undoable.
6856  */
6857 static VALUE
6858 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6859 {
6860         Molecule *mol;
6861         VALUE aref, oldval;
6862     Data_Get_Struct(self, Molecule, mol);
6863         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6864         oldval = s_AtomRef_GetAttr(aref, key);
6865         if (val == Qundef)
6866                 return oldval;
6867         s_AtomRef_SetAttr(aref, key, val);
6868         return val;
6869 }
6870
6871 /*
6872  *  call-seq:
6873  *     get_atom_attr(index, key)
6874  *
6875  *  Get the atom attribute for the specified atom.
6876  */
6877 static VALUE
6878 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6879 {
6880         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6881 }
6882
6883 #pragma mark ------ Undo Support ------
6884
6885 /*
6886  *  call-seq:
6887  *     register_undo(script, *args)
6888  *
6889  *  Register an undo operation with the current molecule.
6890  */
6891 static VALUE
6892 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6893 {
6894         Molecule *mol;
6895         VALUE script, args;
6896         MolAction *act;
6897     Data_Get_Struct(self, Molecule, mol);
6898         rb_scan_args(argc, argv, "1*", &script, &args);
6899         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6900         MolActionCallback_registerUndo(mol, act);
6901         return script;
6902 }
6903
6904 /*
6905  *  call-seq:
6906  *     undo_enabled? -> bool
6907  *
6908  *  Returns true if undo is enabled for this molecule; otherwise no.
6909  */
6910 static VALUE
6911 s_Molecule_UndoEnabled(VALUE self)
6912 {
6913     Molecule *mol;
6914     Data_Get_Struct(self, Molecule, mol);
6915         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6916                 return Qtrue;
6917         else return Qfalse;
6918 }
6919
6920 /*
6921  *  call-seq:
6922  *     undo_enabled = bool
6923  *
6924  *  Enable or disable undo.
6925  */
6926 static VALUE
6927 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6928 {
6929     Molecule *mol;
6930     Data_Get_Struct(self, Molecule, mol);
6931         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6932         return val;
6933 }
6934
6935 #pragma mark ------ Measure ------
6936
6937 static void
6938 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6939 {
6940         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6941                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6942                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6943                 case 0: break;
6944                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6945         }
6946 }
6947
6948 /*
6949  *  call-seq:
6950  *     center_of_mass(group = nil)       -> Vector3D
6951  *
6952  *  Calculate the center of mass for the given set of atoms. The argument
6953  *  group is null, then all atoms are considered.
6954  */
6955 static VALUE
6956 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6957 {
6958     Molecule *mol;
6959         VALUE group;
6960         IntGroup *ig;
6961         Vector v;
6962     Data_Get_Struct(self, Molecule, mol);
6963         rb_scan_args(argc, argv, "01", &group);
6964         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6965         s_Molecule_DoCenterOfMass(mol, &v, ig);
6966         if (ig != NULL)
6967                 IntGroupRelease(ig);
6968         return ValueFromVector(&v);
6969 }
6970
6971 /*
6972  *  call-seq:
6973  *     centralize(group = nil)       -> self
6974  *
6975  *  Translate the molecule so that the center of mass of the given group is located
6976  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6977  */
6978 static VALUE
6979 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6980 {
6981     Molecule *mol;
6982         VALUE group;
6983         IntGroup *ig;
6984         Vector v;
6985     Data_Get_Struct(self, Molecule, mol);
6986         rb_scan_args(argc, argv, "01", &group);
6987         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6988         s_Molecule_DoCenterOfMass(mol, &v, ig);
6989         if (ig != NULL)
6990                 IntGroupRelease(ig);
6991         v.x = -v.x;
6992         v.y = -v.y;
6993         v.z = -v.z;
6994         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6995         return self;
6996 }
6997
6998 /*
6999  *  call-seq:
7000  *     bounds(group = nil)       -> [min, max]
7001  *
7002  *  Calculate the boundary. The return value is an array of two Vector3D objects.
7003  */
7004 static VALUE
7005 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7006 {
7007     Molecule *mol;
7008         VALUE group;
7009         IntGroup *ig;
7010         Vector vmin, vmax;
7011         int n;
7012         Atom *ap;
7013     Data_Get_Struct(self, Molecule, mol);
7014         rb_scan_args(argc, argv, "01", &group);
7015         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7016         if (ig != NULL && IntGroupGetCount(ig) == 0)
7017                 rb_raise(rb_eMolbyError, "atom group is empty");
7018         vmin.x = vmin.y = vmin.z = 1e30;
7019         vmax.x = vmax.y = vmax.z = -1e30;
7020         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7021                 Vector r;
7022                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7023                         continue;
7024                 r = ap->r;
7025                 if (r.x < vmin.x)
7026                         vmin.x = r.x;
7027                 if (r.y < vmin.y)
7028                         vmin.y = r.y;
7029                 if (r.z < vmin.z)
7030                         vmin.z = r.z;
7031                 if (r.x > vmax.x)
7032                         vmax.x = r.x;
7033                 if (r.y > vmax.y)
7034                         vmax.y = r.y;
7035                 if (r.z > vmax.z)
7036                         vmax.z = r.z;
7037         }
7038         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7039 }
7040
7041 /*  Get atom position or a vector  */
7042 static void
7043 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7044 {
7045         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7046                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7047                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7048         } else {
7049                 VectorFromValue(val, vp);
7050         }
7051 }
7052
7053 /*
7054  *  call-seq:
7055  *     measure_bond(n1, n2)       -> Float
7056  *
7057  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
7058  *  or Vector3D values.
7059  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7060  */
7061 static VALUE
7062 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7063 {
7064     Molecule *mol;
7065         Vector v1, v2;
7066     Data_Get_Struct(self, Molecule, mol);
7067         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7068         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7069         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7070 }
7071
7072 /*
7073  *  call-seq:
7074  *     measure_angle(n1, n2, n3)       -> Float
7075  *
7076  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
7077  *  or Vector3D values. The return value is in degree.
7078  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7079  */
7080 static VALUE
7081 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7082 {
7083     Molecule *mol;
7084         Vector v1, v2, v3;
7085         Double d;
7086     Data_Get_Struct(self, Molecule, mol);
7087         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7088         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7089         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7090         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7091         if (isnan(d))
7092                 return Qnil;  /*  Cannot define  */
7093         else return rb_float_new(d);
7094 }
7095
7096 /*
7097  *  call-seq:
7098  *     measure_dihedral(n1, n2, n3, n4)       -> Float
7099  *
7100  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
7101  *  or Vector3D values. The return value is in degree.
7102  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7103  */
7104 static VALUE
7105 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7106 {
7107     Molecule *mol;
7108         Vector v1, v2, v3, v4;
7109         Double d;
7110     Data_Get_Struct(self, Molecule, mol);
7111         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7112         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7113         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7114         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
7115         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7116         if (isnan(d))
7117                 return Qnil;  /*  Cannot define  */
7118         else return rb_float_new(d);
7119 }
7120
7121 /*
7122  *  call-seq:
7123  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7124  *
7125  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7126  *  first and second atom in the pair should belong to group1 and group2, respectively.
7127  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7128  */
7129 static VALUE
7130 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7131 {
7132     Molecule *mol;
7133         VALUE limval, gval1, gval2, rval, igval;
7134         IntGroup *ig1, *ig2;
7135         IntGroupIterator iter1, iter2;
7136         Int npairs, *pairs;
7137         Int n[2], i;
7138         Double lim;
7139         Vector r1;
7140         Atom *ap1, *ap2;
7141         MDExclusion *exinfo;
7142         Int *exlist;
7143         
7144     Data_Get_Struct(self, Molecule, mol);
7145         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7146         lim = NUM2DBL(rb_Float(limval));
7147         if (lim <= 0.0)
7148                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7149         if (gval1 != Qnil)
7150                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7151         else
7152                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7153         if (gval2 != Qnil)
7154                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7155         else
7156                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7157         
7158         if (!RTEST(igval)) {
7159                 /*  Use the exclusion table in MDArena  */
7160                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7161                         VALUE mval = ValueFromMolecule(mol);
7162                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7163                 }
7164                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7165                 exlist = mol->arena->exlist;    
7166         } else {
7167                 exinfo = NULL;
7168                 exlist = NULL;
7169         }
7170         IntGroupIteratorInit(ig1, &iter1);
7171         IntGroupIteratorInit(ig2, &iter2);
7172         npairs = 0;
7173         pairs = NULL;
7174         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7175                 Int exn1, exn2;
7176                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7177                 r1 = ap1->r;
7178                 if (exinfo != NULL) {
7179                         exn1 = exinfo[n[0]].index1;
7180                         exn2 = exinfo[n[0] + 1].index1;
7181                 } else exn1 = exn2 = -1;
7182                 IntGroupIteratorReset(&iter2);
7183                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7184                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7185                         if (n[0] == n[1])
7186                                 continue;  /*  Same atom  */
7187                         if (exinfo != NULL) {
7188                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7189                                 for (i = exn1; i < exn2; i++) {
7190                                         if (exlist[i] == n[1])
7191                                                 break;
7192                                 }
7193                                 if (i < exn2)
7194                                         continue;  /*  Should be excluded  */
7195                         }
7196                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7197                                 /*  Is this pair already registered?  */
7198                                 Int *ip;
7199                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7200                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7201                                                 break;
7202                                 }
7203                                 if (i >= npairs) {
7204                                         /*  Not registered yet  */
7205                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7206                                 }
7207                         }
7208                 }
7209         }
7210         IntGroupIteratorRelease(&iter2);
7211         IntGroupIteratorRelease(&iter1);
7212         IntGroupRelease(ig2);
7213         IntGroupRelease(ig1);
7214         rval = rb_ary_new2(npairs);
7215         if (pairs != NULL) {
7216                 for (i = 0; i < npairs; i++) {
7217                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7218                 }
7219                 free(pairs);
7220         }
7221         return rval;
7222 }
7223
7224 /*
7225  *  call-seq:
7226  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7227  *
7228  *  Find atoms that are within the threshold distance from the given atom.
7229  *  (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.)
7230  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7231  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7232  *  If limit is not given, a default value of 1.2 is used.
7233  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7234  */
7235 static VALUE
7236 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7237 {
7238     Molecule *mol;
7239         VALUE aval, limval, radval;
7240         double limit, radius;
7241         Int n1, nbonds, *bonds, an;
7242         Vector v;
7243     Data_Get_Struct(self, Molecule, mol);
7244         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7245         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)) {
7246                 VectorFromValue(aval, &v);
7247                 if (radval == Qnil)
7248                         radius = gElementParameters[6].radius;
7249                 else
7250                         radius = NUM2DBL(rb_Float(radval));
7251                 n1 = mol->natoms;
7252         } else {
7253                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7254                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7255                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7256                 if (an >= 0 && an < gCountElementParameters)
7257                         radius = gElementParameters[an].radius;
7258                 else radius = gElementParameters[6].radius;
7259         }
7260         if (limval == Qnil)
7261                 limit = 1.2;
7262         else
7263                 limit = NUM2DBL(rb_Float(limval));
7264         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7265         bonds = NULL;
7266         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7267         aval = rb_ary_new();
7268         if (nbonds > 0) {
7269                 for (n1 = 0; n1 < nbonds; n1++)
7270                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7271                 free(bonds);
7272         }
7273         return aval;
7274 }
7275
7276 /*
7277  *  call-seq:
7278  *     guess_bonds(limit = 1.2)       -> Integer
7279  *
7280  *  Create bonds between atoms that are within the threshold distance.
7281  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7282  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7283  *  If limit is not given, a default value of 1.2 is used.
7284  *  The number of the newly created bonds is returned.
7285  *  This operation is undoable.
7286  */
7287 static VALUE
7288 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7289 {
7290     Molecule *mol;
7291         VALUE limval;
7292         double limit;
7293         Int nbonds, *bonds;
7294     Data_Get_Struct(self, Molecule, mol);
7295         rb_scan_args(argc, argv, "01", &limval);
7296         if (limval == Qnil)
7297                 limit = 1.2;
7298         else
7299                 limit = NUM2DBL(rb_Float(limval));
7300         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7301         if (nbonds > 0) {
7302                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7303                 free(bonds);
7304         }
7305         return INT2NUM(nbonds);
7306 }
7307
7308 #pragma mark ------ Cell and Symmetry ------
7309
7310 /*
7311  *  call-seq:
7312  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7313  *
7314  *  Returns the unit cell parameters. If cell is not set, returns nil.
7315  */
7316 static VALUE
7317 s_Molecule_Cell(VALUE self)
7318 {
7319     Molecule *mol;
7320         int i;
7321         VALUE val;
7322     Data_Get_Struct(self, Molecule, mol);
7323         if (mol->cell == NULL)
7324                 return Qnil;
7325         val = rb_ary_new2(6);
7326         for (i = 0; i < 6; i++)
7327                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7328         if (mol->cell->has_sigma) {
7329                 for (i = 0; i < 6; i++) {
7330                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7331                 }
7332         }
7333         return val;
7334 }
7335
7336 /*
7337  *  call-seq:
7338  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7339  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7340  *
7341  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7342  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7343  This operation is undoable.
7344  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7345  */
7346 static VALUE
7347 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7348 {
7349     Molecule *mol;
7350         VALUE val, cval;
7351         int i, convert_coord, n;
7352         double d[12];
7353     Data_Get_Struct(self, Molecule, mol);
7354         rb_scan_args(argc, argv, "11", &val, &cval);
7355         if (val == Qnil) {
7356                 n = 0;
7357         } else {
7358                 int len;
7359                 val = rb_ary_to_ary(val);
7360                 len = RARRAY_LEN(val);
7361                 if (len >= 12) {
7362                         n = 12;
7363                 } else if (len >= 6) {
7364                         n = 6;
7365                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7366                 for (i = 0; i < n; i++)
7367                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7368         }
7369         convert_coord = (RTEST(cval) ? 1 : 0);
7370         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7371         return val;
7372 }
7373
7374 /*
7375  *  call-seq:
7376  *     box -> [avec, bvec, cvec, origin, flags]
7377  *
7378  *  Get the unit cell information in the form of a periodic bounding box.
7379  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7380  *  Integers which define whether the system is periodic along the axis.
7381  *  If no unit cell is defined, nil is returned.
7382  */
7383 static VALUE
7384 s_Molecule_Box(VALUE self)
7385 {
7386     Molecule *mol;
7387         VALUE v[5], val;
7388     Data_Get_Struct(self, Molecule, mol);
7389         if (mol == NULL || mol->cell == NULL)
7390                 return Qnil;
7391         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7392         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7393         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7394         v[3] = ValueFromVector(&(mol->cell->origin));
7395         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7396         val = rb_ary_new4(5, v);
7397         return val;
7398 }
7399
7400 /*
7401  *  call-seq:
7402  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7403  *     set_box(d, origin = [0, 0, 0])
7404  *     set_box
7405  *
7406  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7407  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7408  as the box vector.
7409  Flags, if present, is a 3-member array of Integers defining whether the system is
7410  periodic along the axis.
7411  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7412  In the second form, an isotropic box with cell-length d is set.
7413  In the third form, the existing box is cleared.
7414  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7415  */
7416 static VALUE
7417 s_Molecule_SetBox(VALUE self, VALUE aval)
7418 {
7419     Molecule *mol;
7420         VALUE v[6];
7421         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7422         Vector vv[3];
7423         Vector origin = {0, 0, 0};
7424         char flags[3];
7425         Double d;
7426         int i, convertCoordinates = 0;
7427     Data_Get_Struct(self, Molecule, mol);
7428         if (aval == Qnil) {
7429                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7430                 return self;
7431         }
7432         aval = rb_ary_to_ary(aval);
7433         for (i = 0; i < 6; i++) {
7434                 if (i < RARRAY_LEN(aval))
7435                         v[i] = (RARRAY_PTR(aval))[i];
7436                 else v[i] = Qnil;
7437         }
7438         if (v[0] == Qnil) {
7439                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7440                 return self;
7441         }
7442         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7443                 d = NUM2DBL(rb_Float(v[0]));
7444                 for (i = 0; i < 3; i++)
7445                         VecScale(vv[i], ax[i], d);
7446                 if (v[1] != Qnil)
7447                         VectorFromValue(v[1], &origin);
7448                 flags[0] = flags[1] = flags[2] = 1;
7449         } else {
7450                 for (i = 0; i < 3; i++) {
7451                         if (v[i] == Qnil) {
7452                                 VecZero(vv[i]);
7453                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7454                                 d = NUM2DBL(rb_Float(v[i]));
7455                                 VecScale(vv[i], ax[i], d);
7456                         } else {
7457                                 VectorFromValue(v[i], &vv[i]);
7458                         }
7459                         flags[i] = (VecLength2(vv[i]) > 0.0);
7460                 }
7461                 if (v[3] != Qnil)
7462                         VectorFromValue(v[3], &origin);
7463                 if (v[4] != Qnil) {
7464                         for (i = 0; i < 3; i++) {
7465                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7466                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7467                         }
7468                 }
7469                 if (RTEST(v[5]))
7470                         convertCoordinates = 1;
7471         }
7472         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7473         return self;
7474 }
7475
7476 /*
7477  *  call-seq:
7478  *     cell_periodicity -> [n1, n2, n3]
7479  *
7480  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7481  *  nil is returned.
7482  */
7483 static VALUE
7484 s_Molecule_CellPeriodicity(VALUE self)
7485 {
7486     Molecule *mol;
7487     Data_Get_Struct(self, Molecule, mol);
7488         if (mol->cell == NULL)
7489                 return Qnil;
7490         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7491 }
7492
7493 /*
7494  *  call-seq:
7495  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7496  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7497  *
7498  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7499  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7500  *  If cell is not defined, exception is raised.
7501  *  This operation is undoable.
7502  */
7503 static VALUE
7504 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7505 {
7506     Molecule *mol;
7507         Int flag;
7508     Data_Get_Struct(self, Molecule, mol);
7509         if (mol->cell == NULL)
7510                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7511         if (arg == Qnil)
7512                 flag = 0;
7513         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7514                 flag = NUM2INT(rb_Integer(arg));
7515         else {
7516                 Int i;
7517                 VALUE arg0;
7518                 arg = rb_ary_to_ary(arg);
7519                 flag = 0;
7520                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7521                         arg0 = RARRAY_PTR(arg)[i];
7522                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7523                                 flag |= (1 << (2 - i));
7524                 }
7525         }
7526         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7527         return arg;
7528 }
7529
7530 /*
7531  *  call-seq:
7532  *     cell_flexibility -> bool
7533  *
7534  *  Returns the unit cell is flexible or not
7535  */
7536 static VALUE
7537 s_Molecule_CellFlexibility(VALUE self)
7538 {
7539         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7540         return Qtrue;
7541         /*    Molecule *mol;
7542          Data_Get_Struct(self, Molecule, mol);
7543          if (mol->cell == NULL)
7544          return Qfalse;
7545          if (mol->useFlexibleCell)
7546          return Qtrue;
7547          else return Qfalse; */
7548 }
7549
7550 /*
7551  *  call-seq:
7552  *     self.cell_flexibility = bool
7553  *     set_cell_flexibility(bool)
7554  *
7555  *  Change the unit cell is flexible or not
7556  */
7557 static VALUE
7558 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7559 {
7560         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7561         return self;
7562         /*    Molecule *mol;
7563          Data_Get_Struct(self, Molecule, mol);
7564          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7565          return self; */
7566 }
7567
7568 /*
7569  *  call-seq:
7570  *     cell_transform -> Transform
7571  *
7572  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7573  *  If cell is not defined, nil is returned.
7574  */
7575 static VALUE
7576 s_Molecule_CellTransform(VALUE self)
7577 {
7578     Molecule *mol;
7579     Data_Get_Struct(self, Molecule, mol);
7580         if (mol == NULL || mol->cell == NULL)
7581                 return Qnil;
7582         return ValueFromTransform(&(mol->cell->tr));
7583 }
7584
7585 /*
7586  *  call-seq:
7587  *     symmetry -> Array of Transforms
7588  *     symmetries -> Array of Transforms
7589  *
7590  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7591  *  returns an empty array.
7592  */
7593 static VALUE
7594 s_Molecule_Symmetry(VALUE self)
7595 {
7596     Molecule *mol;
7597         VALUE val;
7598         int i;
7599     Data_Get_Struct(self, Molecule, mol);
7600         if (mol->nsyms <= 0)
7601                 return rb_ary_new();
7602         val = rb_ary_new2(mol->nsyms);
7603         for (i = 0; i < mol->nsyms; i++) {
7604                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7605         }
7606         return val;
7607 }
7608
7609 /*
7610  *  call-seq:
7611  *     nsymmetries -> Integer
7612  *
7613  *  Get the number of currently defined symmetry operations.
7614  */
7615 static VALUE
7616 s_Molecule_Nsymmetries(VALUE self)
7617 {
7618     Molecule *mol;
7619     Data_Get_Struct(self, Molecule, mol);
7620         return INT2NUM(mol->nsyms);
7621 }
7622
7623 /*
7624  *  call-seq:
7625  *     add_symmetry(Transform) -> Integer
7626  *
7627  *  Add a new symmetry operation. If no symmetry operation is defined and the
7628  *  given argument is not an identity transform, then also add an identity
7629  *  transform at the index 0.
7630  *  Returns the total number of symmetries after operation.
7631  */
7632 static VALUE
7633 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7634 {
7635     Molecule *mol;
7636         Transform tr;
7637     Data_Get_Struct(self, Molecule, mol);
7638         TransformFromValue(trans, &tr);
7639         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7640         return INT2NUM(mol->nsyms);
7641 }
7642
7643 /*
7644  *  call-seq:
7645  *     remove_symmetry(count = nil) -> Integer
7646  *     remove_symmetries(count = nil) -> Integer
7647  *
7648  *  Remove the specified number of symmetry operations. The last added ones are removed
7649  *  first. If count is nil, then all symmetry operations are removed. Returns the
7650  *  number of leftover symmetries.
7651  */
7652 static VALUE
7653 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7654 {
7655     Molecule *mol;
7656         VALUE cval;
7657         int i, n;
7658     Data_Get_Struct(self, Molecule, mol);
7659         rb_scan_args(argc, argv, "01", &cval);
7660         if (cval == Qnil)
7661                 n = mol->nsyms - 1;
7662         else {
7663                 n = NUM2INT(rb_Integer(cval));
7664                 if (n < 0 || n > mol->nsyms)
7665                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7666                 if (n == mol->nsyms)
7667                         n = mol->nsyms - 1;
7668         }
7669         for (i = 0; i < n; i++)
7670                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7671         return INT2NUM(mol->nsyms);
7672 }
7673
7674 /*
7675  *  call-seq:
7676  *     wrap_unit_cell(group) -> Vector3D
7677  *
7678  *  Move the specified group so that the center of mass of the group is within the
7679  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7680  *  exception is raised.
7681  */
7682 static VALUE
7683 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7684 {
7685     Molecule *mol;
7686         IntGroup *ig;
7687         Vector v, cv, dv;
7688     Data_Get_Struct(self, Molecule, mol);
7689         if (mol->cell == NULL)
7690                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7691         ig = s_Molecule_AtomGroupFromValue(self, gval);
7692         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7693         TransformVec(&v, mol->cell->rtr, &cv);
7694         if (mol->cell->flags[0])
7695                 v.x -= floor(v.x);
7696         if (mol->cell->flags[1])
7697                 v.y -= floor(v.y);
7698         if (mol->cell->flags[2])
7699                 v.z -= floor(v.z);
7700         TransformVec(&dv, mol->cell->tr, &v);
7701         VecDec(dv, cv);
7702         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7703         IntGroupRelease(ig);
7704         return ValueFromVector(&dv);
7705 }
7706
7707 /*
7708  *  call-seq:
7709  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7710  *
7711  *  Expand the specified part of the molecule by the given symmetry operation.
7712  *  Returns the array of atom indices corresponding to the expanded atoms.
7713  *  If allow_overlap is true, then new atoms are created even when the
7714  *  coordinates coincide with the some other atom (special position) of the
7715  *  same element; otherwise, such atom will not be created and the index of the
7716  *  existing atom is given in the returned array.
7717  */
7718 static VALUE
7719 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7720 {
7721     Molecule *mol;
7722         VALUE gval, sval, xval, yval, zval, rval, oval;
7723         IntGroup *ig;
7724         Int n[4], allow_overlap;
7725         Int natoms;
7726         Int nidx, *idx;
7727         
7728     Data_Get_Struct(self, Molecule, mol);
7729         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7730         n[0] = NUM2INT(rb_Integer(sval));
7731         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7732         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7733         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7734         allow_overlap = (RTEST(oval) ? 1 : 0);
7735         ig = s_Molecule_AtomGroupFromValue(self, gval);
7736         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7737                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7738         natoms = mol->natoms;
7739         
7740         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7741         
7742         rval = rb_ary_new2(nidx);
7743         while (--nidx >= 0) {
7744                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7745         }
7746         /*      if (natoms == mol->natoms)
7747          rval = Qnil;
7748          else {
7749          rval = IntGroup_Alloc(rb_cIntGroup);
7750          Data_Get_Struct(rval, IntGroup, ig);
7751          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7752          } */
7753         return rval;
7754 }
7755
7756 /*
7757  *  call-seq:
7758  *     amend_by_symmetry(group = nil) -> IntGroup
7759  *
7760  *  Expand the specified part of the molecule by the given symmetry operation.
7761  *  Returns an IntGroup containing the added atoms.
7762  */
7763 static VALUE
7764 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7765 {
7766     Molecule *mol;
7767         IntGroup *ig, *ig2;
7768         VALUE rval, gval;
7769     Data_Get_Struct(self, Molecule, mol);
7770         rb_scan_args(argc, argv, "01", &gval);
7771         if (gval != Qnil)
7772                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7773         else ig = NULL;
7774         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7775         rval = ValueFromIntGroup(ig2);
7776         IntGroupRelease(ig2);
7777         return rval;
7778 }
7779
7780 #pragma mark ------ Transforms ------
7781
7782 /*
7783  *  call-seq:
7784  *     translate(vec, group = nil)       -> Molecule
7785  *
7786  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7787  *  This operation is undoable.
7788  */
7789 static VALUE
7790 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7791 {
7792     Molecule *mol;
7793         VALUE vec, group;
7794         Vector v;
7795         IntGroup *ig;
7796     Data_Get_Struct(self, Molecule, mol);
7797         rb_scan_args(argc, argv, "11", &vec, &group);
7798         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7799         VectorFromValue(vec, &v);
7800         //      MoleculeTranslate(mol, &v, ig);
7801         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7802         if (ig != NULL)
7803                 IntGroupRelease(ig);
7804         return self;
7805 }
7806
7807 /*
7808  *  call-seq:
7809  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7810  *
7811  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7812  *  If group is given, only atoms in the group are moved.
7813  *  This operation is undoable.
7814  */
7815 static VALUE
7816 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7817 {
7818     Molecule *mol;
7819         volatile VALUE aval, anval, cval, gval;
7820         Double angle;
7821         Vector av, cv;
7822         Transform tr;
7823         IntGroup *ig;
7824     Data_Get_Struct(self, Molecule, mol);
7825         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7826         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7827         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7828         VectorFromValue(aval, &av);
7829         if (NIL_P(cval))
7830                 cv.x = cv.y = cv.z = 0.0;
7831         else
7832                 VectorFromValue(cval, &cv);
7833         if (TransformForRotation(tr, &av, angle, &cv))
7834                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7835         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7836         if (ig != NULL)
7837                 IntGroupRelease(ig);
7838         return self;
7839 }
7840
7841 /*
7842  *  call-seq:
7843  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7844  *
7845  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7846  *  axis must not be a zero vector.
7847  *  If group is given, only atoms in the group are moved.
7848  *  This operation is undoable.
7849  */
7850 static VALUE
7851 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7852 {
7853     Molecule *mol;
7854         volatile VALUE aval, cval, gval;
7855         Vector av, cv;
7856         Transform tr;
7857         IntGroup *ig;
7858     Data_Get_Struct(self, Molecule, mol);
7859         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7860         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7861         VectorFromValue(aval, &av);
7862         if (NIL_P(cval))
7863                 cv.x = cv.y = cv.z = 0.0;
7864         else
7865                 VectorFromValue(cval, &cv);
7866         if (TransformForReflection(tr, &av, &cv))
7867                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7868         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7869         if (ig != NULL)
7870                 IntGroupRelease(ig);
7871         return self;
7872 }
7873
7874 /*
7875  *  call-seq:
7876  *     invert(center = [0,0,0], group = nil)       -> Molecule
7877  *
7878  *  Invert the molecule with the given center.
7879  *  If group is given, only atoms in the group are moved.
7880  *  This operation is undoable.
7881  */
7882 static VALUE
7883 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7884 {
7885         Molecule *mol;
7886         volatile VALUE cval, gval;
7887         Vector cv;
7888         Transform tr;
7889         IntGroup *ig;
7890     Data_Get_Struct(self, Molecule, mol);
7891         rb_scan_args(argc, argv, "02", &cval, &gval);
7892         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7893         if (NIL_P(cval))
7894                 cv.x = cv.y = cv.z = 0.0;
7895         else
7896                 VectorFromValue(cval, &cv);
7897         TransformForInversion(tr, &cv);
7898         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7899         if (ig != NULL)
7900                 IntGroupRelease(ig);
7901         return self;
7902 }
7903
7904 /*
7905  *  call-seq:
7906  *     transform(transform, group = nil)       -> Molecule
7907  *
7908  *  Transform the molecule by the given Transform object.
7909  *  If group is given, only atoms in the group are moved.
7910  *  This operation is undoable.
7911  */
7912 static VALUE
7913 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7914 {
7915     Molecule *mol;
7916         VALUE trans, group;
7917         Transform tr;
7918         IntGroup *ig;
7919     Data_Get_Struct(self, Molecule, mol);
7920         rb_scan_args(argc, argv, "11", &trans, &group);
7921         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7922         TransformFromValue(trans, &tr);
7923         /*      MoleculeTransform(mol, tr, ig); */
7924         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7925         if (ig != NULL)
7926                 IntGroupRelease(ig);
7927         return self;
7928 }
7929
7930 /*
7931  *  call-seq:
7932  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7933  *
7934  *  Get the transform corresponding to the symmetry operation. The symop can either be
7935  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7936  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7937  *  Otherwise, the returned transform is for fractional coordinates.
7938  *  Raises exception when no cell or no transform are defined.
7939  */
7940 static VALUE
7941 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7942 {
7943     Molecule *mol;
7944         VALUE sval, fval;
7945         Symop symop;
7946         Transform tr;
7947     Data_Get_Struct(self, Molecule, mol);
7948         if (mol->cell == NULL)
7949                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7950         if (mol->nsyms == 0)
7951                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7952         rb_scan_args(argc, argv, "11", &sval, &fval);
7953         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7954                 symop.sym = NUM2INT(rb_Integer(sval));
7955                 symop.dx = symop.dy = symop.dz = 0;
7956         } else {
7957                 sval = rb_ary_to_ary(sval);
7958                 if (RARRAY_LEN(sval) < 4)
7959                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7960                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7961                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7962                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7963                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7964         }
7965         if (symop.sym >= mol->nsyms)
7966                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7967         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7968         return ValueFromTransform(&tr);
7969 }
7970
7971 /*
7972  *  call-seq:
7973  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7974  *
7975  *  Get the symmetry operation corresponding to the given transform.
7976  *  If is_cartesian is true, the given transform is for cartesian coordinates.
7977  *  Otherwise, the given transform is for fractional coordinates.
7978  *  Raises exception when no cell or no transform are defined.
7979  */
7980 static VALUE
7981 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7982 {
7983     Molecule *mol;
7984         VALUE tval, fval;
7985         Symop symop;
7986         Transform tr;
7987         int n;
7988     Data_Get_Struct(self, Molecule, mol);
7989         if (mol->cell == NULL)
7990                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7991         if (mol->nsyms == 0)
7992                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7993         rb_scan_args(argc, argv, "11", &tval, &fval);
7994         TransformFromValue(tval, &tr);
7995         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7996         if (n == 0) {
7997                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7998         } else {
7999                 return Qnil;  /*  Not found  */
8000         }
8001 }
8002
8003 #pragma mark ------ Frames ------
8004
8005 /*
8006  *  call-seq:
8007  *     select_frame(index)
8008  *     frame = index
8009  *
8010  *  Select the specified frame. If successful, returns true, otherwise returns false.
8011  */
8012 static VALUE
8013 s_Molecule_SelectFrame(VALUE self, VALUE val)
8014 {
8015     Molecule *mol;
8016         int ival = NUM2INT(val);
8017     Data_Get_Struct(self, Molecule, mol);
8018         ival = MoleculeSelectFrame(mol, ival, 1);
8019         if (ival >= 0)
8020                 return Qtrue;
8021         else return Qfalse;
8022 }
8023
8024 /*
8025  *  call-seq:
8026  *     frame -> Integer
8027  *
8028  *  Get the current frame.
8029  */
8030 static VALUE
8031 s_Molecule_Frame(VALUE self)
8032 {
8033     Molecule *mol;
8034     Data_Get_Struct(self, Molecule, mol);
8035         return INT2NUM(mol->cframe);
8036 }
8037
8038 /*
8039  *  call-seq:
8040  *     nframes -> Integer
8041  *
8042  *  Get the number of frames.
8043  */
8044 static VALUE
8045 s_Molecule_Nframes(VALUE self)
8046 {
8047     Molecule *mol;
8048     Data_Get_Struct(self, Molecule, mol);
8049         return INT2NUM(MoleculeGetNumberOfFrames(mol));
8050 }
8051
8052 /*
8053  *  call-seq:
8054  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8055  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8056  *
8057  *  Insert new frames at the indices specified by the intGroup. If the first argument is
8058  *  an integer, a single new frame is inserted at that index. If the first argument is 
8059  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8060  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
8061  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
8062  *  to the new frame.
8063  *  Returns an intGroup representing the inserted frames if successful, nil if not.
8064  */
8065 static VALUE
8066 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8067 {
8068         VALUE val, coords, cells;
8069     Molecule *mol;
8070         IntGroup *ig;
8071         int count, ival, i, j, len, len_c, len2, nframes;
8072         VALUE *ptr, *ptr2;
8073         Vector *vp, *vp2;
8074     Data_Get_Struct(self, Molecule, mol);
8075         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8076         if (coords != Qnil) {
8077                 if (TYPE(coords) != T_ARRAY)
8078                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8079                 len = RARRAY_LEN(coords);
8080         } else len = 0;
8081         if (cells != Qnil) {
8082                 if (mol->cell == NULL)
8083                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8084                 if (TYPE(cells) != T_ARRAY)
8085                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8086                 len_c = RARRAY_LEN(cells);
8087         } else len_c = 0;
8088         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
8089         nframes = MoleculeGetNumberOfFrames(mol);
8090         if (val == Qnil) {
8091                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8092                 val = ValueFromIntGroup(ig);
8093         } else {
8094                 ig = IntGroupFromValue(val);
8095         }
8096         count = IntGroupGetCount(ig);  /*  Count is updated here  */
8097         vp = ALLOC_N(Vector, mol->natoms * count);
8098         if (cells != Qnil)
8099                 vp2 = ALLOC_N(Vector, 4 * count);
8100         else vp2 = NULL;
8101         if (len > 0) {
8102                 if (len < count)
8103                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8104                 ptr = RARRAY_PTR(coords);
8105                 for (i = 0; i < count; i++) {
8106                         if (TYPE(ptr[i]) != T_ARRAY)
8107                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8108                         len2 = RARRAY_LEN(ptr[i]);
8109                         if (len2 < mol->natoms)
8110                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8111                         ptr2 = RARRAY_PTR(ptr[i]);
8112                         for (j = 0; j < mol->natoms; j++)
8113                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8114                 }
8115         } else {
8116                 Atom *ap;
8117                 for (i = 0; i < count; i++) {
8118                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8119                                 vp[i * mol->natoms + j] = ap->r;
8120                         }
8121                 }
8122         }
8123         if (len_c > 0) {
8124                 if (len_c < count)
8125                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8126                 ptr = RARRAY_PTR(cells);
8127                 for (i = 0; i < count; i++) {
8128                         if (TYPE(ptr[i]) != T_ARRAY)
8129                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8130                         len2 = RARRAY_LEN(ptr[i]);
8131                         if (len2 < 4)
8132                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8133                         ptr2 = RARRAY_PTR(ptr[i]);
8134                         for (j = 0; j < 4; j++)
8135                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8136                 }
8137         }
8138         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8139         IntGroupRelease(ig);
8140         xfree(vp);
8141         if (vp2 != NULL)
8142                 xfree(vp2);
8143         return (ival >= 0 ? val : Qnil);
8144 }
8145
8146 /*
8147  *  call-seq:
8148  *     create_frame(coordinates = nil) -> Integer
8149  *     create_frames(coordinates = nil) -> Integer
8150  *
8151  *  Same as molecule.insert_frames(nil, coordinates).
8152  */
8153 static VALUE
8154 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8155 {
8156         VALUE vals[3];
8157         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8158         vals[0] = Qnil;
8159         return s_Molecule_InsertFrames(3, vals, self);
8160 }
8161
8162 /*
8163  *  call-seq:
8164  *     remove_frames(IntGroup, wantCoordinates = false)
8165  *
8166  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8167  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8168  *  removed frames is returned if operation is successful.
8169  */
8170 static VALUE
8171 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8172 {
8173         VALUE val, flag;
8174         VALUE retval;
8175     Molecule *mol;
8176         IntGroup *ig;
8177         int count;
8178     Data_Get_Struct(self, Molecule, mol);
8179         rb_scan_args(argc, argv, "11", &val, &flag);
8180         ig = IntGroupFromValue(val);
8181         count = IntGroupGetCount(ig);
8182         if (RTEST(flag)) {
8183                 /*  Create return value before removing frames  */
8184                 VALUE coords;
8185                 int i, j, n;
8186                 Atom *ap;
8187                 Vector v;
8188                 retval = rb_ary_new2(count);
8189                 for (i = 0; i < count; i++) {
8190                         n = IntGroupGetNthPoint(ig, i);
8191                         coords = rb_ary_new2(mol->natoms);
8192                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8193                                 if (n < ap->nframes && n != mol->cframe)
8194                                         v = ap->frames[n];
8195                                 else v = ap->r;
8196                                 rb_ary_push(coords, ValueFromVector(&v));
8197                         }
8198                         rb_ary_push(retval, coords);
8199                 }
8200         } else retval = Qtrue;
8201         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8202                 return retval;
8203         else return Qnil;
8204 }
8205
8206 /*
8207  *  call-seq:
8208  *     each_frame {|n| ...}
8209  *
8210  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8211  *  the frame number. After completion, the original frame number is restored.
8212  */
8213 static VALUE
8214 s_Molecule_EachFrame(VALUE self)
8215 {
8216         int i, cframe, nframes;
8217     Molecule *mol;
8218     Data_Get_Struct(self, Molecule, mol);
8219         cframe = mol->cframe;
8220         nframes = MoleculeGetNumberOfFrames(mol);
8221         if (nframes > 0) {
8222                 for (i = 0; i < nframes; i++) {
8223                         MoleculeSelectFrame(mol, i, 1);
8224                         rb_yield(INT2NUM(i));
8225                 }
8226                 MoleculeSelectFrame(mol, cframe, 1);
8227         }
8228     return self;
8229 }
8230
8231 /*
8232  *  call-seq:
8233  *     get_coord_from_frame(index, group = nil)
8234  *
8235  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8236  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8237  *  copied; now they are always copied)
8238  */
8239 static VALUE
8240 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8241 {
8242         Molecule *mol;
8243         VALUE ival, gval, cval;
8244         Int index, i, j, n, nn;
8245         IntGroup *ig;
8246         IntGroupIterator iter;
8247         Atom *ap;
8248         Vector *vp;
8249     Data_Get_Struct(self, Molecule, mol);
8250         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8251         if (argc == 3)
8252                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8253         index = NUM2INT(rb_Integer(ival));
8254         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8255                 if (n == 0)
8256                         rb_raise(rb_eMolbyError, "No frame is present");
8257                 else
8258                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8259         }
8260         if (gval == Qnil) {
8261                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8262         } else {
8263                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8264         }
8265         n = IntGroupGetCount(ig);
8266         if (n > 0) {
8267                 vp = (Vector *)calloc(sizeof(Vector), n);
8268                 IntGroupIteratorInit(ig, &iter);
8269                 j = 0;
8270                 nn = 0;
8271                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8272                         ap = ATOM_AT_INDEX(mol->atoms, i);
8273                         if (index < ap->nframes) {
8274                                 vp[j] = ap->frames[index];
8275                                 nn++;
8276                         } else {
8277                                 vp[j] = ap->r;
8278                         }
8279                         j++;
8280                 }
8281                 if (nn > 0)
8282                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8283                 free(vp);
8284                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8285                         vp = mol->frame_cells + index * 4;
8286                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8287                 }
8288                 IntGroupIteratorRelease(&iter);
8289         }
8290         /*  Copy the extra properties  */
8291         IntGroupRelease(ig);
8292         for (i = 0; i < mol->nmolprops; i++) {
8293                 Double *dp = (Double *)malloc(sizeof(Double));
8294                 ig = IntGroupNew();
8295                 IntGroupAdd(ig, mol->cframe, 1);
8296                 *dp = mol->molprops[i].propvals[index];
8297                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8298                 free(dp);
8299                 IntGroupRelease(ig);
8300         }
8301         
8302         return self;
8303 }
8304
8305 /*
8306  *  call-seq:
8307  *     reorder_frames(old_indices)
8308  *
8309  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8310  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8311  *  same as the old frames 2/0/1, respectively.
8312  *  The argument must have the same number of integers as the number of frames.
8313  */
8314 static VALUE
8315 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8316 {
8317         Molecule *mol;
8318         Int *ip, *ip2, i, n, nframes;
8319     Data_Get_Struct(self, Molecule, mol);
8320         aval = rb_ary_to_ary(aval);
8321         nframes = MoleculeGetNumberOfFrames(mol);
8322         if (RARRAY_LEN(aval) != nframes)
8323                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8324         ip2 = (Int *)calloc(sizeof(Int), nframes);
8325         ip = (Int *)calloc(sizeof(Int), nframes);
8326         for (i = 0; i < nframes; i++) {
8327                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8328                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8329                         free(ip2);
8330                         free(ip);
8331                         if (n < 0 || n >= nframes)
8332                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8333                         else
8334                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8335                 }
8336                 ip2[n] = 1;
8337                 ip[i] = n;
8338         }
8339         free(ip2);
8340         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8341         free(ip);
8342         return self;
8343 }
8344
8345 #pragma mark ------ Fragments ------
8346
8347 /*
8348  *  call-seq:
8349  *     fragment(n1, *exatoms)  -> IntGroup
8350  *     fragment(group, *exatoms)  -> IntGroup
8351  *
8352  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8353  *  those atoms will not be counted during the search.
8354  */
8355 static VALUE
8356 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8357 {
8358     Molecule *mol;
8359         IntGroup *baseg, *ig, *exatoms;
8360         int n;
8361         volatile VALUE nval, exval;
8362     Data_Get_Struct(self, Molecule, mol);
8363         rb_scan_args(argc, argv, "1*", &nval, &exval);
8364         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8365                 baseg = NULL;
8366                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8367         } else {
8368                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8369         }
8370         if (RARRAY_LEN(exval) == 0) {
8371                 exatoms = NULL;
8372         } else {
8373                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8374                 Data_Get_Struct(exval, IntGroup, exatoms);
8375         }
8376         if (baseg == NULL) {
8377                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8378         } else {
8379                 IntGroupIterator iter;
8380                 IntGroupIteratorInit(baseg, &iter);
8381                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8382                         ig = IntGroupNew();
8383                 } else {
8384                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8385                         if (ig != NULL) {
8386                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8387                                         IntGroup *subg;
8388                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8389                                         if (subg != NULL) {
8390                                                 IntGroupAddIntGroup(ig, subg);
8391                                                 IntGroupRelease(subg);
8392                                         }
8393                                 }
8394                         }
8395                 }
8396                 IntGroupIteratorRelease(&iter);
8397                 IntGroupRelease(baseg);
8398         }
8399         if (ig == NULL)
8400                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8401         nval = ValueFromIntGroup(ig);
8402         IntGroupRelease(ig);
8403         return nval;
8404 }
8405
8406 /*
8407  *  call-seq:
8408  *     fragments(exclude = nil)
8409  *
8410  *  Returns the fragments as an array of IntGroups. 
8411  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8412  *  in defining the fragment.
8413  */
8414 static VALUE
8415 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8416 {
8417     Molecule *mol;
8418         IntGroup *ag, *fg, *eg;
8419         VALUE gval, exval, retval;
8420     Data_Get_Struct(self, Molecule, mol);
8421         if (mol == NULL)
8422                 return Qnil;
8423         if (mol->natoms == 0)
8424                 return rb_ary_new();
8425         rb_scan_args(argc, argv, "01", &exval);
8426         if (exval == Qnil)
8427                 eg = NULL;
8428         else
8429                 eg = IntGroupFromValue(exval);
8430         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8431         if (eg != NULL)
8432                 IntGroupRemoveIntGroup(ag, eg);
8433         retval = rb_ary_new();
8434         while (IntGroupGetCount(ag) > 0) {
8435                 int n = IntGroupGetNthPoint(ag, 0);
8436                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8437                 if (fg == NULL)
8438                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8439                 gval = ValueFromIntGroup(fg);
8440                 rb_ary_push(retval, gval);
8441                 IntGroupRemoveIntGroup(ag, fg);
8442                 IntGroupRelease(fg);
8443         }
8444         IntGroupRelease(ag);
8445         if (eg != NULL)
8446                 IntGroupRelease(eg);
8447         return retval;
8448 }
8449
8450 /*
8451  *  call-seq:
8452  *     each_fragment(exclude = nil) {|group| ...}
8453  *
8454  *  Execute the block, with the IntGroup object for each fragment as the argument.
8455  *  Atoms or bonds should not be added or removed during the execution of the block.
8456  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8457  *  in defining the fragment.
8458  */
8459 static VALUE
8460 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8461 {
8462     Molecule *mol;
8463         IntGroup *ag, *fg, *eg;
8464         VALUE gval, exval;
8465     Data_Get_Struct(self, Molecule, mol);
8466         if (mol == NULL || mol->natoms == 0)
8467                 return self;
8468         rb_scan_args(argc, argv, "01", &exval);
8469         if (exval == Qnil)
8470                 eg = NULL;
8471         else
8472                 eg = IntGroupFromValue(exval);
8473         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8474         if (eg != NULL)
8475                 IntGroupRemoveIntGroup(ag, eg);
8476         while (IntGroupGetCount(ag) > 0) {
8477                 int n = IntGroupGetNthPoint(ag, 0);
8478                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8479                 if (fg == NULL)
8480                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8481                 gval = ValueFromIntGroup(fg);
8482                 rb_yield(gval);
8483                 IntGroupRemoveIntGroup(ag, fg);
8484                 IntGroupRelease(fg);
8485         }
8486         IntGroupRelease(ag);
8487         if (eg != NULL)
8488                 IntGroupRelease(eg);
8489         return self;
8490 }
8491
8492 /*
8493  *  call-seq:
8494  *     detachable?(group)  -> [n1, n2]
8495  *
8496  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8497  *  of the molecule via only one bond. If it is, then the indices of the atoms
8498  *  belonging to the bond is returned, the first element being the atom included
8499  *  in the fragment. Otherwise, Qnil is returned.
8500  */
8501 static VALUE
8502 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8503 {
8504         Molecule *mol;
8505         IntGroup *ig;
8506         int n1, n2;
8507         VALUE retval;
8508     Data_Get_Struct(self, Molecule, mol);
8509         ig = s_Molecule_AtomGroupFromValue(self, gval);
8510         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8511                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8512         } else retval = Qnil;
8513         IntGroupRelease(ig);
8514         return retval;
8515 }
8516
8517 /*
8518  *  call-seq:
8519  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8520  *
8521  *  Returns an array of bonds that connect an atom in the group and an atom out
8522  *  of the group. The first atom in the bond always belongs to the group. If no
8523  *  such bonds are present, an empty array is returned.
8524  */
8525 static VALUE
8526 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8527 {
8528         Molecule *mol;
8529         IntGroup *ig, *bg;
8530         VALUE gval, retval;
8531     Data_Get_Struct(self, Molecule, mol);
8532         rb_scan_args(argc, argv, "01", &gval);
8533         if (gval == Qnil) {
8534                 ig = MoleculeGetSelection(mol);
8535                 if (ig != NULL)
8536                         IntGroupRetain(ig);
8537         } else {
8538                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8539         }
8540         retval = rb_ary_new();
8541         if (ig == NULL)
8542                 return retval;
8543         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8544         if (bg != NULL) {
8545                 IntGroupIterator iter;
8546                 Int i;
8547                 IntGroupIteratorInit(bg, &iter);
8548                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8549                         /*  The atoms at the border  */
8550                         Int n1, n2;
8551                         n1 = mol->bonds[i * 2];
8552                         n2 = mol->bonds[i * 2 + 1];
8553                         if (IntGroupLookupPoint(ig, n1) < 0) {
8554                                 int w = n1;
8555                                 n1 = n2;
8556                                 n2 = w;
8557                                 if (IntGroupLookupPoint(ig, n1) < 0)
8558                                         continue;  /*  Actually this is an internal error  */
8559                         }
8560                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8561                 }
8562                 IntGroupIteratorRelease(&iter);
8563         }
8564         IntGroupRelease(bg);
8565         IntGroupRelease(ig);
8566         return retval;
8567 }
8568
8569 /*  Calculate the transform that moves the current coordinates to the reference
8570  coordinates with least displacements.   */
8571 static Double
8572 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8573 {
8574         Atom *ap, *ap1;
8575         Int natoms, nn;
8576         Vector org1, org2;
8577         Int i, in, j, k;
8578         Double w, w1;
8579         Mat33 r, q, u;
8580         Double eigen_val[3];
8581         Vector eigen_vec[3];
8582         Vector s[3];
8583         IntGroupIterator iter;
8584
8585         natoms = mol->natoms;
8586         ap = mol->atoms;
8587         IntGroupIteratorInit(ig, &iter);
8588         
8589         /*  Calculate the weighted center  */
8590         VecZero(org1);
8591         VecZero(org2);
8592         w = 0.0;
8593         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8594                 ap1 = ATOM_AT_INDEX(ap, in);
8595                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8596                 VecScaleInc(org1, ap1->r, w1);
8597                 VecScaleInc(org2, ref[i], w1);
8598                 w += w1;
8599         }
8600         w = 1.0 / w;
8601         VecScaleSelf(org1, w);
8602         VecScaleSelf(org2, w);
8603
8604     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8605     /*  Matrix to diagonalize = R * tR    */
8606         memset(r, 0, sizeof(Mat33));
8607         memset(q, 0, sizeof(Mat33));
8608         memset(u, 0, sizeof(Mat33));
8609         nn = 0;
8610         IntGroupIteratorReset(&iter);
8611         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8612                 Vector v1, v2;
8613                 ap1 = ATOM_AT_INDEX(ap, in);
8614                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8615                 w1 *= w1;
8616                 VecSub(v1, ap1->r, org1);
8617                 VecSub(v2, ref[i], org2);
8618                 r[0] += w1 * v1.x * v2.x;
8619                 r[1] += w1 * v1.y * v2.x;
8620                 r[2] += w1 * v1.z * v2.x;
8621                 r[3] += w1 * v1.x * v2.y;
8622                 r[4] += w1 * v1.y * v2.y;
8623                 r[5] += w1 * v1.z * v2.y;
8624                 r[6] += w1 * v1.x * v2.z;
8625                 r[7] += w1 * v1.y * v2.z;
8626                 r[8] += w1 * v1.z * v2.z;
8627                 nn++;
8628         }
8629         for (i = 0; i < 9; i++)
8630                 r[i] /= (nn * nn);
8631         for (i = 0; i < 3; i++) {
8632                 for (j = 0; j < 3; j++) {
8633                         for (k = 0; k < 3; k++) {
8634                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8635                         }
8636                 }
8637         }
8638         
8639         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8640                 IntGroupIteratorRelease(&iter);
8641                 return -1.0;  /*  Cannot determine the eigenvector  */
8642         }
8643
8644     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8645     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8646         MatrixTranspose(r, r);
8647         for (i = 0; i < 3; i++) {
8648                 MatrixVec(&s[i], r, &eigen_vec[i]);
8649                 w1 = 1.0 / sqrt(eigen_val[i]);
8650                 VecScaleSelf(s[i], w1);
8651         }
8652         for (k = 0; k < 3; k++) {
8653                 u[0] += s[k].x * eigen_vec[k].x;
8654                 u[1] += s[k].y * eigen_vec[k].x;
8655                 u[2] += s[k].z * eigen_vec[k].x;
8656                 u[3] += s[k].x * eigen_vec[k].y;
8657                 u[4] += s[k].y * eigen_vec[k].y;
8658                 u[5] += s[k].z * eigen_vec[k].y;
8659                 u[6] += s[k].x * eigen_vec[k].z;
8660                 u[7] += s[k].y * eigen_vec[k].z;
8661                 u[8] += s[k].z * eigen_vec[k].z;
8662         }
8663         
8664         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8665         MatrixVec(&org1, u, &org1);
8666         VecDec(org2, org1);
8667         for (i = 0; i < 9; i++)
8668                 trans[i] = u[i];
8669         trans[9] = org2.x;
8670         trans[10] = org2.y;
8671         trans[11] = org2.z;
8672         
8673         /*  Calculate rmsd  */
8674         IntGroupIteratorReset(&iter);
8675         w = 0.0;
8676         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8677                 Vector tv;
8678                 ap1 = ATOM_AT_INDEX(ap, in);
8679                 TransformVec(&tv, trans, &ap1->r);
8680                 VecDec(tv, ref[i]);
8681                 w += VecLength2(tv);
8682         }
8683         w = sqrt(w / nn);
8684         IntGroupIteratorRelease(&iter);
8685         return w;
8686 }
8687
8688 /*
8689  *  call-seq:
8690  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8691  *
8692  *  Calculate the transform to fit the given group to the set of reference coordinates.
8693  *  The reference coordinates ref is given as either a frame number, an array of
8694  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8695  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8696  *  Return values are the transform (that converts the present coordinates to the
8697  *  target coordinates) and root mean square deviation (without weight).
8698  */
8699 static VALUE
8700 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8701 {
8702         Molecule *mol;
8703         Atom *ap;
8704         VALUE gval, rval, wval;
8705         IntGroup *ig;
8706         IntGroupIterator iter;
8707         int nn, errnum, i, j, in, status;
8708         Vector *ref;
8709         Double *weights, dval[3];
8710         Transform tr;
8711
8712         Data_Get_Struct(self, Molecule, mol);
8713         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8714         if (gval == Qnil)
8715                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8716         else
8717                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8718         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8719                 IntGroupRelease(ig);
8720                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8721         }
8722         ref = (Vector *)calloc(sizeof(Vector), nn);
8723         weights = (Double *)calloc(sizeof(Double), nn);
8724         IntGroupIteratorInit(ig, &iter);
8725         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8726                 int fn = NUM2INT(rb_Integer(rval));
8727                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8728                         errnum = 1;
8729                         status = fn;
8730                         goto err;
8731                 }
8732                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8733                         ap = ATOM_AT_INDEX(mol->atoms, in);
8734                         if (fn < ap->nframes)
8735                                 ref[i] = ap->frames[fn];
8736                         else ref[i] = ap->r;
8737                 }
8738         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8739                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8740                 if (m->row * m->column < nn * 3) {
8741                         errnum = 2;
8742                         goto err;
8743                 }
8744                 for (i = 0; i < nn; i++) {
8745                         ref[i].x = m->data[i * 3];
8746                         ref[i].y = m->data[i * 3 + 1];
8747                         ref[i].z = m->data[i * 3 + 2];
8748                 }
8749         } else {
8750                 VALUE aval;
8751                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8752                 if (status != 0) {
8753                         errnum = 3;
8754                         goto err;
8755                 }
8756                 if (RARRAY_LEN(rval) < nn) {
8757                         errnum = 2;
8758                         goto err;
8759                 }
8760                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8761                         /*  Array of 3*nn numbers  */
8762                         if (RARRAY_LEN(rval) < nn * 3) {
8763                                 errnum = 2;
8764                                 goto err;
8765                         }
8766                         for (i = 0; i < nn; i++) {
8767                                 for (j = 0; j < 3; j++) {
8768                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8769                                         if (status != 0) {
8770                                                 errnum = 3;
8771                                                 goto err;
8772                                         }
8773                                         dval[j] = NUM2DBL(aval);
8774                                 }
8775                                 ref[i].x = dval[0];
8776                                 ref[i].y = dval[1];
8777                                 ref[i].z = dval[2];
8778                         }
8779                 } else {
8780                         /*  Array of nn Vector3Ds or Arrays  */
8781                         for (i = 0; i < nn; i++) {
8782                                 aval = (RARRAY_PTR(rval))[i];
8783                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8784                                         VectorFromValue(aval, &ref[i]);
8785                                 } else {
8786                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8787                                         if (status != 0) {
8788                                                 errnum = 3;
8789                                                 goto err;
8790                                         }
8791                                         if (RARRAY_LEN(aval) < 3) {
8792                                                 errnum = 4;
8793                                                 status = i;
8794                                                 goto err;
8795                                         }
8796                                         for (j = 0; j < 3; j++) {
8797                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8798                                                 if (status != 0) {
8799                                                         errnum = 3;
8800                                                         goto err;
8801                                                 }
8802                                                 dval[j] = NUM2DBL(aaval);
8803                                         }
8804                                         ref[i].x = dval[0];
8805                                         ref[i].y = dval[1];
8806                                         ref[i].z = dval[2];
8807                                 }
8808                         }
8809                 }
8810         }
8811         if (wval == Qnil) {
8812                 /*  Use atomic weights  */
8813                 IntGroupIteratorReset(&iter);
8814                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8815                         ap = ATOM_AT_INDEX(mol->atoms, in);
8816                         weights[i] = ap->weight;
8817                 }
8818         } else {
8819                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8820                 if (status != 0) {
8821                         errnum = 3;
8822                         goto err;
8823                 }
8824                 if (RARRAY_LEN(wval) < nn) {
8825                         errnum = 5;
8826                         goto err;
8827                 }
8828                 for (i = 0; i < nn; i++) {
8829                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8830                         if (status != 0) {
8831                                 errnum = 3;
8832                                 goto err;
8833                         }
8834                         weights[i] = NUM2DBL(wwval);
8835                 }
8836         }
8837         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8838         if (dval[0] < 0) {
8839                 errnum = 6;
8840                 goto err;
8841         }
8842         errnum = 0;
8843 err:
8844         IntGroupIteratorRelease(&iter);
8845         free(ref);
8846         free(weights);
8847         if (errnum == 0) {
8848                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8849         } else if (errnum == 1) {
8850                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8851         } else if (errnum == 2) {
8852                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8853         } else if (errnum == 3) {
8854                 rb_jump_tag(status);
8855         } else if (errnum == 4) {
8856                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8857         } else if (errnum == 5) {
8858                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8859         } else if (errnum == 6) {
8860                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8861         }
8862         return Qnil;  /*  Not reached  */
8863 }
8864
8865 #pragma mark ------ Screen Display ------
8866
8867 /*
8868  *  call-seq:
8869  *     display
8870  *
8871  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8872  */
8873 static VALUE
8874 s_Molecule_Display(VALUE self)
8875 {
8876     Molecule *mol;
8877     Data_Get_Struct(self, Molecule, mol);
8878         if (mol->mview != NULL)
8879                 MainViewCallback_display(mol->mview);
8880         return Qnil;
8881 }
8882
8883 /*
8884  *  call-seq:
8885  *     make_front
8886  *
8887  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8888  */
8889 static VALUE
8890 s_Molecule_MakeFront(VALUE self)
8891 {
8892     Molecule *mol;
8893     Data_Get_Struct(self, Molecule, mol);
8894         if (mol->mview != NULL)
8895                 MainViewCallback_makeFront(mol->mview);
8896         return Qnil;
8897 }
8898
8899 /*
8900  *  call-seq:
8901  *     update_enabled? -> bool
8902  *
8903  *  Returns true if screen update is enabled; otherwise no.
8904  */
8905 static VALUE
8906 s_Molecule_UpdateEnabled(VALUE self)
8907 {
8908     Molecule *mol;
8909     Data_Get_Struct(self, Molecule, mol);
8910         if (mol->mview != NULL && !mol->mview->freezeScreen)
8911                 return Qtrue;
8912         else return Qfalse;
8913 }
8914
8915 /*
8916  *  call-seq:
8917  *     update_enabled = bool
8918  *
8919  *  Enable or disable screen update. This is effective for automatic update on modification.
8920  *  Explicit call to molecule.display() always updates the screen.
8921  */
8922 static VALUE
8923 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8924 {
8925     Molecule *mol;
8926     Data_Get_Struct(self, Molecule, mol);
8927         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8928         if (mol->mview != NULL)
8929                 mol->mview->freezeScreen = (val == Qfalse);
8930         else val = Qfalse;
8931         return val;
8932 }
8933
8934 /*
8935  *  call-seq:
8936  *     show_unitcell
8937  *     show_unitcell(bool)
8938  *     show_unitcell = bool
8939  *
8940  *  Set the flag whether to show the unit cell. If no argument is given, the
8941  *  current flag is returned.
8942  */
8943 static VALUE
8944 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8945 {
8946     Molecule *mol;
8947     Data_Get_Struct(self, Molecule, mol);
8948         if (mol->mview == NULL)
8949                 return Qnil;
8950         if (argc > 0) {
8951                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8952                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8953         }
8954         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8955 }
8956
8957 /*
8958  *  call-seq:
8959  *     show_hydrogens
8960  *     show_hydrogens(bool)
8961  *     show_hydrogens = bool
8962  *
8963  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8964  *  current flag is returned.
8965  */
8966 static VALUE
8967 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8968 {
8969     Molecule *mol;
8970     Data_Get_Struct(self, Molecule, mol);
8971         if (mol->mview == NULL)
8972                 return Qnil;
8973         if (argc > 0) {
8974                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8975                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8976         }
8977         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8978 }
8979
8980 /*
8981  *  call-seq:
8982  *     show_dummy_atoms
8983  *     show_dummy_atoms(bool)
8984  *     show_dummy_atoms = bool
8985  *
8986  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8987  *  current flag is returned.
8988  */
8989 static VALUE
8990 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8991 {
8992     Molecule *mol;
8993     Data_Get_Struct(self, Molecule, mol);
8994         if (mol->mview == NULL)
8995                 return Qnil;
8996         if (argc > 0) {
8997                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8998                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8999         }
9000         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9001 }
9002
9003 /*
9004  *  call-seq:
9005  *     show_expanded
9006  *     show_expanded(bool)
9007  *     show_expanded = bool
9008  *
9009  *  Set the flag whether to show the expanded atoms. If no argument is given, the
9010  *  current flag is returned.
9011  */
9012 static VALUE
9013 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9014 {
9015     Molecule *mol;
9016     Data_Get_Struct(self, Molecule, mol);
9017         if (mol->mview == NULL)
9018                 return Qnil;
9019         if (argc > 0) {
9020                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9021                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9022         }
9023         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9024 }
9025
9026 /*
9027  *  call-seq:
9028  *     show_ellipsoids
9029  *     show_ellipsoids(bool)
9030  *     show_ellipsoids = bool
9031  *
9032  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9033  *  current flag is returned.
9034  */
9035 static VALUE
9036 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9037 {
9038     Molecule *mol;
9039     Data_Get_Struct(self, Molecule, mol);
9040         if (mol->mview == NULL)
9041                 return Qnil;
9042         if (argc > 0) {
9043                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9044                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9045         }
9046         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9047 }
9048
9049 /*
9050  *  call-seq:
9051  *     is_atom_visible(index)  -> Boolean
9052  *
9053  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9054  *  as well as the molecule attributes (showHydrogens, etc.)
9055  */
9056 static VALUE
9057 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9058 {
9059         Molecule *mol;
9060         Int idx;
9061         Atom *ap;
9062     Data_Get_Struct(self, Molecule, mol);
9063         idx = s_Molecule_AtomIndexFromValue(mol, ival);
9064         if (idx < 0 || idx >= mol->natoms)
9065                 return Qnil;
9066         ap = ATOM_AT_INDEX(mol->atoms, idx);
9067         if (mol->mview != NULL) {
9068                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9069                         return Qfalse;
9070                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9071                         return Qfalse;
9072                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9073                         return Qfalse;
9074         }
9075         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9076 }
9077
9078 /*
9079  *  call-seq:
9080  *     hidden_atoms       -> IntGroup
9081  *
9082  *  Returns the currently hidden atoms.
9083  */
9084 static VALUE
9085 s_Molecule_HiddenAtoms(VALUE self)
9086 {
9087         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9088         return Qnil;  /*  Not reached  */
9089 }
9090
9091 /*
9092  *  call-seq:
9093  *     set_hidden_atoms(IntGroup)
9094  *     self.hidden_atoms = IntGroup
9095  *
9096  *  Hide the specified atoms. This operation is _not_ undoable.
9097  */
9098 static VALUE
9099 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9100 {
9101         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9102         return Qnil;  /*  Not reached  */
9103 }
9104
9105 /*
9106  *  call-seq:
9107  *     show_graphite -> Integer
9108  *     show_graphite = Integer
9109  *     show_graphite = boolean
9110  *
9111  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9112  *  number of rings to display for each direction.
9113  *  If the argument is boolean, only the show/hide flag is set.
9114  */
9115 static VALUE
9116 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9117 {
9118     Molecule *mol;
9119     Data_Get_Struct(self, Molecule, mol);
9120         if (mol->mview == NULL)
9121                 return Qnil;
9122         if (argc > 0) {
9123                 if (argv[0] == Qnil || argv[0] == Qfalse)
9124                         mol->mview->showGraphiteFlag = 0;
9125                 else if (argv[0] == Qtrue)
9126                         mol->mview->showGraphiteFlag = 1;
9127                 else {
9128                         int n = NUM2INT(rb_Integer(argv[0]));
9129                         if (n < 0)
9130                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9131                         mol->mview->showGraphite = n;
9132                 }
9133                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9134         }
9135         return INT2NUM(mol->mview->showGraphite);
9136 }
9137
9138 /*
9139  *  call-seq:
9140  *     show_graphite? -> boolean
9141  *
9142  *  Return whether the graphite is set visible or not.
9143 */
9144 static VALUE
9145 s_Molecule_ShowGraphiteFlag(VALUE self)
9146 {
9147     Molecule *mol;
9148     Data_Get_Struct(self, Molecule, mol);
9149         if (mol->mview == NULL)
9150                 return Qnil;
9151         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9152 }
9153         
9154 /*
9155  *  call-seq:
9156  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9157  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9158  *     show_periodic_image = boolean
9159  *
9160  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9161  *  set but no visual effects are observed.
9162  *  If the argument is boolean, only the show/hide flag is modified.
9163  */
9164 static VALUE
9165 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9166 {
9167     Molecule *mol;
9168         VALUE val;
9169         int ival[6];
9170         int i;
9171     Data_Get_Struct(self, Molecule, mol);
9172         if (mol->mview == NULL)
9173                 return Qnil;
9174         rb_scan_args(argc, argv, "01", &val);
9175         if (argc > 0) {
9176                 /*  Change current settings  */
9177                 if (val == Qnil || val == Qfalse)
9178                         mol->mview->showPeriodicImageFlag = 0;
9179                 else if (val == Qtrue)
9180                         mol->mview->showPeriodicImageFlag = 1;
9181                 else {
9182                         val = rb_ary_to_ary(val);
9183                         for (i = 0; i < 6; i++) {
9184                                 if (i < RARRAY_LEN(val))
9185                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9186                         }
9187                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9188                                 rb_raise(rb_eMolbyError, "bad arguments");
9189                         for (i = 0; i < 6; i++)
9190                                 mol->mview->showPeriodicImage[i] = ival[i];
9191                 }
9192                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9193         }
9194         val = rb_ary_new();
9195         for (i = 0; i < 6; i++)
9196                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9197         return val;
9198 }
9199
9200 /*
9201  *  call-seq:
9202  *     show_periodic_image? -> boolean
9203  *
9204  *  Return whether the periodic images are set to visible or not. This flag is
9205  *  independent from the show_periodic_image settings.
9206  */
9207 static VALUE
9208 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9209 {
9210     Molecule *mol;
9211     Data_Get_Struct(self, Molecule, mol);
9212         if (mol->mview == NULL)
9213                 return Qnil;
9214         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9215 }
9216
9217 /*
9218  *  call-seq:
9219  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9220  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9221  *     show_rotation_center = boolean
9222  *
9223  *  Set to show the rotation center of the screen.
9224  *  If the argument is boolean, only the show/hide flag is modified.
9225  */
9226 static VALUE
9227 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9228 {
9229     Molecule *mol;
9230     Data_Get_Struct(self, Molecule, mol);
9231         if (mol->mview == NULL)
9232                 return Qnil;
9233         if (argc > 0) {
9234                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9235                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9236         }
9237         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9238 }
9239
9240 /*
9241  *  call-seq:
9242  *     line_mode
9243  *     line_mode(bool)
9244  *     line_mode = bool
9245  *
9246  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9247  *  current flag is returned.
9248  */
9249 static VALUE
9250 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9251 {
9252     Molecule *mol;
9253     Data_Get_Struct(self, Molecule, mol);
9254         if (mol->mview == NULL)
9255                 return Qnil;
9256         if (argc > 0) {
9257                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9258                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9259         }
9260         return (mol->mview->lineMode ? Qtrue : Qfalse);
9261 }
9262
9263 /*
9264  *  call-seq:
9265  *     atom_radius = float
9266  *     atom_radius
9267  *
9268  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9269  *  (Default = 0.4)
9270  *  If no argument is given, the current value is returned.
9271  */
9272 static VALUE
9273 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9274 {
9275     Molecule *mol;
9276     Data_Get_Struct(self, Molecule, mol);
9277         if (mol->mview == NULL)
9278                 return Qnil;
9279         if (argc > 0) {
9280                 double rad = NUM2DBL(rb_Float(argv[0]));
9281                 if (rad > 0.0) {
9282                         mol->mview->atomRadius = rad;
9283                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9284                 }
9285                 return argv[0];
9286         }
9287         return rb_float_new(mol->mview->atomRadius);
9288 }
9289
9290 /*
9291  *  call-seq:
9292  *     bond_radius = float
9293  *     bond_radius
9294  *
9295  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9296  *  If no argument is given, the current value is returned.
9297  */
9298 static VALUE
9299 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9300 {
9301     Molecule *mol;
9302     Data_Get_Struct(self, Molecule, mol);
9303         if (mol->mview == NULL)
9304                 return Qnil;
9305         if (argc > 0) {
9306                 double rad = NUM2DBL(rb_Float(argv[0]));
9307                 if (rad > 0.0) {
9308                         mol->mview->bondRadius = rad;
9309                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9310                 }
9311                 return argv[0];
9312         }
9313         return rb_float_new(mol->mview->bondRadius);
9314 }
9315
9316 /*
9317  *  call-seq:
9318  *     atom_resolution = integer
9319  *     atom_resolution
9320  *
9321  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9322  *  (Default = 12; minimum = 6)
9323  *  If no argument is given, the current value is returned.
9324  */
9325 static VALUE
9326 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9327 {
9328     Molecule *mol;
9329     Data_Get_Struct(self, Molecule, mol);
9330         if (mol->mview == NULL)
9331                 return Qnil;
9332         if (argc > 0) {
9333                 int res = NUM2INT(rb_Integer(argv[0]));
9334                 if (res < 6)
9335                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9336                 mol->mview->atomResolution = res;
9337                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9338                 return INT2NUM(res);
9339         }
9340         return INT2NUM(mol->mview->atomResolution);
9341 }
9342
9343 /*
9344  *  call-seq:
9345  *     bond_resolution = integer
9346  *     bond_resolution
9347  *
9348  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9349  *  (Default = 8; minimum = 4)
9350  *  If no argument is given, the current value is returned.
9351  */
9352 static VALUE
9353 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9354 {
9355     Molecule *mol;
9356     Data_Get_Struct(self, Molecule, mol);
9357         if (mol->mview == NULL)
9358                 return Qnil;
9359         if (argc > 0) {
9360                 int res = NUM2INT(rb_Integer(argv[0]));
9361                 if (res < 4)
9362                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9363                 mol->mview->bondResolution = res;
9364                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9365                 return INT2NUM(res);
9366         }
9367         return INT2NUM(mol->mview->bondResolution);
9368 }
9369
9370 /*
9371  *  call-seq:
9372  *     resize_to_fit
9373  *
9374  *  Resize the model drawing to fit in the window.
9375  */
9376 static VALUE
9377 s_Molecule_ResizeToFit(VALUE self)
9378 {
9379     Molecule *mol;
9380     Data_Get_Struct(self, Molecule, mol);
9381         if (mol->mview != NULL)
9382                 MainView_resizeToFit(mol->mview);
9383         return self;    
9384 }
9385
9386 /*
9387  *  call-seq:
9388  *     get_view_rotation -> [[ax, ay, az], angle]
9389  *
9390  *  Get the current rotation for the view. Angle is in degree, not radian.
9391  */
9392 static VALUE
9393 s_Molecule_GetViewRotation(VALUE self)
9394 {
9395     Molecule *mol;
9396         double f[4];
9397         Vector v;
9398     Data_Get_Struct(self, Molecule, mol);
9399         if (mol->mview == NULL)
9400                 return Qnil;
9401         TrackballGetRotate(mol->mview->track, f);
9402         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9403         v.x = f[1];
9404         v.y = f[2];
9405         v.z = f[3];
9406         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9407 }
9408
9409 /*
9410  *  call-seq:
9411  *     get_view_scale -> float
9412  *
9413  *  Get the current scale for the view.
9414  */
9415 static VALUE
9416 s_Molecule_GetViewScale(VALUE self)
9417 {
9418     Molecule *mol;
9419     Data_Get_Struct(self, Molecule, mol);
9420         if (mol->mview == NULL)
9421                 return Qnil;
9422         return rb_float_new(TrackballGetScale(mol->mview->track));
9423 }
9424
9425 /*
9426  *  call-seq:
9427  *     get_view_center -> Vector
9428  *
9429  *  Get the current center point of the view.
9430  */
9431 static VALUE
9432 s_Molecule_GetViewCenter(VALUE self)
9433 {
9434     Molecule *mol;
9435         double f[4];
9436         Vector v;
9437     Data_Get_Struct(self, Molecule, mol);
9438         if (mol->mview == NULL)
9439                 return Qnil;
9440         TrackballGetTranslate(mol->mview->track, f);
9441         v.x = -f[0] * mol->mview->dimension;
9442         v.y = -f[1] * mol->mview->dimension;
9443         v.z = -f[2] * mol->mview->dimension;
9444         return ValueFromVector(&v);
9445 }
9446
9447 /*
9448  *  call-seq:
9449  *     set_view_rotation([ax, ay, az], angle) -> self
9450  *
9451  *  Set the current rotation for the view. Angle is in degree, not radian.
9452  */
9453 static VALUE
9454 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9455 {
9456     Molecule *mol;
9457         double f[4];
9458         Vector v;
9459     Data_Get_Struct(self, Molecule, mol);
9460         if (mol->mview == NULL)
9461                 return Qnil;
9462         VectorFromValue(aval, &v);
9463         if (NormalizeVec(&v, &v))
9464                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9465         f[1] = v.x;
9466         f[2] = v.y;
9467         f[3] = v.z;
9468         f[0] = -NUM2DBL(rb_Float(angval));
9469         TrackballSetRotate(mol->mview->track, f);
9470         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9471         return self;
9472 }
9473
9474 /*
9475  *  call-seq:
9476  *     set_view_scale(scale) -> self
9477  *
9478  *  Set the current scale for the view.
9479  */
9480 static VALUE
9481 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9482 {
9483     Molecule *mol;
9484     Data_Get_Struct(self, Molecule, mol);
9485         if (mol->mview == NULL)
9486                 return Qnil;
9487         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9488         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9489         return self;
9490 }
9491
9492 /*
9493  *  call-seq:
9494  *     set_view_center(vec) -> self
9495  *
9496  *  Set the current center point of the view.
9497  */
9498 static VALUE
9499 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9500 {
9501     Molecule *mol;
9502         Vector v;
9503         double f[4];
9504     Data_Get_Struct(self, Molecule, mol);
9505         if (mol->mview == NULL)
9506                 return Qnil;
9507         VectorFromValue(aval, &v);
9508         f[0] = -v.x / mol->mview->dimension;
9509         f[1] = -v.y / mol->mview->dimension;
9510         f[2] = -v.z / mol->mview->dimension;
9511         TrackballSetTranslate(mol->mview->track, f);
9512         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9513         return self;
9514 }
9515
9516 /*
9517  *  call-seq:
9518  *     set_background_color(red, green, blue)
9519  *
9520  *  Set the background color of the model window.
9521  */
9522 static VALUE
9523 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9524 {
9525     Molecule *mol;
9526     Data_Get_Struct(self, Molecule, mol);
9527         if (mol->mview != NULL) {
9528                 VALUE rval, gval, bval;
9529                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9530                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9531         }
9532         return self;    
9533 }
9534
9535 /*
9536  *  call-seq:
9537  *     export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9538  *
9539  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9540  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9541  *  If either width or height is not specified, then the screen width/height is used instead.
9542  */
9543 static VALUE
9544 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9545 {
9546         Molecule *mol;
9547         VALUE fval, sval, bval, wval, hval;
9548         char *fname;
9549         float scale;
9550         int bg_color, width, height;
9551     Data_Get_Struct(self, Molecule, mol);
9552         if (mol->mview == NULL)
9553                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9554         rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9555         fname = FileStringValuePtr(fval);
9556         if (sval == Qnil)
9557                 scale = 1.0;
9558         else scale = NUM2DBL(rb_Float(sval));
9559         if (bval == Qnil)
9560                 bg_color = -1;
9561         else bg_color = NUM2INT(rb_Integer(bval));
9562         if (wval == Qnil)
9563                 width = 0;
9564         else width = NUM2INT(rb_Integer(wval));
9565         if (hval == Qnil)
9566                 height = 0;
9567         else height = NUM2INT(rb_Integer(hval));
9568         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9569                 return fval;
9570         else return Qnil;
9571 }
9572
9573 #pragma mark ------ Graphics ------
9574
9575 static void
9576 s_CalculateGraphicNormals(MainViewGraphic *gp)
9577 {
9578         int i;
9579         Vector v1, v2, v3;
9580         if (gp == NULL || gp->npoints < 3)
9581                 return;
9582         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9583         v1.x = gp->points[3] - gp->points[0];
9584         v1.y = gp->points[4] - gp->points[1];
9585         v1.z = gp->points[5] - gp->points[2];
9586         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9587         for (i = 2; i < gp->npoints; i++) {
9588                 v2.x = gp->points[i * 3] - gp->points[0];
9589                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9590                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9591                 VecCross(v3, v1, v2);
9592                 NormalizeVec(&v3, &v3);
9593                 gp->normals[i * 3] = v3.x;
9594                 gp->normals[i * 3 + 1] = v3.y;
9595                 gp->normals[i * 3 + 2] = v3.z;
9596                 v1 = v2;
9597         }
9598         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9599         VecZero(v1);
9600         for (i = 2; i < gp->npoints; i++) {
9601                 v1.x += gp->normals[i * 3];
9602                 v1.y += gp->normals[i * 3 + 1];
9603                 v1.z += gp->normals[i * 3 + 2];
9604         }
9605         NormalizeVec(&v1, &v1);
9606         gp->normals[0] = v1.x;
9607         gp->normals[1] = v1.y;
9608         gp->normals[2] = v1.z;
9609         /*  normals[1] = nv[2].normalize  */
9610         v2.x = gp->normals[6];
9611         v2.y = gp->normals[7];
9612         v2.z = gp->normals[8];
9613         NormalizeVec(&v1, &v2);
9614         gp->normals[3] = v1.x;
9615         gp->normals[4] = v1.y;
9616         gp->normals[5] = v1.z;
9617         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9618         for (i = 2; i < gp->npoints; i++) {
9619                 if (i == gp->npoints - 1)
9620                         VecZero(v3);
9621                 else {
9622                         v3.x = gp->normals[i * 3 + 3];
9623                         v3.y = gp->normals[i * 3 + 4];
9624                         v3.z = gp->normals[i * 3 + 5];
9625                 }
9626                 VecInc(v2, v3);
9627                 NormalizeVec(&v1, &v2);
9628                 gp->normals[i * 3] = v1.x;
9629                 gp->normals[i * 3 + 1] = v1.y;
9630                 gp->normals[i * 3 + 2] = v1.z;
9631                 v2 = v3;
9632         }
9633 }
9634
9635 /*
9636  *  call-seq:
9637  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9638  *
9639  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9640  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9641  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9642  *   points: an array of Vectors
9643  *   
9644  */
9645 static VALUE
9646 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9647 {
9648     Molecule *mol;
9649         MainViewGraphic g;
9650         int i, n, ni, idx;
9651         const char *p;
9652         VALUE kval, cval, pval, fval, ival;
9653     Data_Get_Struct(self, Molecule, mol);
9654         if (mol->mview == NULL)
9655                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9656         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9657         idx = NUM2INT(rb_Integer(ival));
9658         if (idx == -1)
9659                 idx = mol->mview->ngraphics;
9660         else if (idx < 0 || idx > mol->mview->ngraphics)
9661                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9662         memset(&g, 0, sizeof(g));
9663         g.visible = 1;
9664         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9665                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9666         } else {
9667                 kval = rb_obj_as_string(kval);
9668                 p = StringValuePtr(kval);
9669                 if (strcmp(p, "line") == 0)
9670                         g.kind = kMainViewGraphicLine;
9671                 else if (strcmp(p, "poly") == 0)
9672                         g.kind = kMainViewGraphicPoly;
9673                 else if (strcmp(p, "cylinder") == 0)
9674                         g.kind = kMainViewGraphicCylinder;
9675                 else if (strcmp(p, "cone") == 0)
9676                         g.kind = kMainViewGraphicCone;
9677                 else if (strcmp(p, "ellipsoid") == 0)
9678                         g.kind = kMainViewGraphicEllipsoid;
9679                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9680         }
9681         g.closed = (RTEST(fval) ? 1 : 0);
9682         cval = rb_ary_to_ary(cval);
9683         n = RARRAY_LEN(cval);
9684         if (n < 3 || n >= 5)
9685                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9686         if (n == 3)
9687                 g.rgba[3] = 1.0;
9688         for (i = 0; i < n; i++)
9689                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9690         pval = rb_ary_to_ary(pval);
9691         n = RARRAY_LEN(pval);
9692         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9693         if (n <= 0)
9694                 rb_raise(rb_eArgError, "no control points are given");
9695         switch (g.kind) {
9696                 case kMainViewGraphicLine:
9697                         if (n < 2)
9698                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9699                         break;
9700                 case kMainViewGraphicPoly:
9701                         if (n < 3)
9702                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9703                         break;
9704                 case kMainViewGraphicCylinder:
9705                 case kMainViewGraphicCone:
9706                         if (n != 3)
9707                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9708                         ni = 2;
9709                         break;
9710                 case kMainViewGraphicEllipsoid:
9711                         if (n == 2) {
9712                                 ni = 1;
9713                         } else if (n != 4)
9714                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9715                         break;
9716         }
9717         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9718         for (i = 0; i < n; i++) {
9719                 Vector v;
9720                 VALUE rval = RARRAY_PTR(pval)[i];
9721                 if (i == ni) {
9722                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9723                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9724                                 VectorFromValue(rval, &v);
9725                         } else {
9726                                 v.x = NUM2DBL(rb_Float(rval));
9727                         }
9728                         v.y = v.z = 0;
9729                 } else {
9730                         VectorFromValue(rval, &v);
9731                 }
9732                 g.points[i * 3] = v.x;
9733                 g.points[i * 3 + 1] = v.y;
9734                 g.points[i * 3 + 2] = v.z;
9735         }
9736         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9737                 /*  Sphere  */
9738                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9739                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9740                 g.points[7] = g.points[11] = g.points[3];
9741         }
9742         if (g.kind == kMainViewGraphicPoly) {
9743                 /*  Calculate normals  */
9744                 s_CalculateGraphicNormals(&g);
9745         }
9746         MainView_insertGraphic(mol->mview, idx, &g);
9747         
9748         {
9749                 /*  Register undo  */
9750                 MolAction *act;
9751                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9752                 MolActionCallback_registerUndo(mol, act);
9753                 MolActionRelease(act);
9754         }
9755
9756         return INT2NUM(idx);    
9757 }
9758
9759 /*
9760  *  call-seq:
9761  *     create_graphic(kind, color, points, fill = nil) -> integer
9762  *
9763  *  Create a new graphic object. The arguments are similar as insert_graphic.
9764  */
9765 static VALUE
9766 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9767 {
9768         VALUE args[5];
9769         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9770         args[0] = INT2NUM(-1);
9771         return s_Molecule_InsertGraphic(argc + 1, args, self);
9772 }
9773
9774 /*
9775  *  call-seq:
9776  *     remove_graphic(index) -> integer
9777  *
9778  *  Remove a graphic object.
9779  */
9780 static VALUE
9781 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9782 {
9783     Molecule *mol;
9784         int i;
9785     Data_Get_Struct(self, Molecule, mol);
9786         if (mol->mview == NULL)
9787                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9788         i = NUM2INT(rb_Integer(ival));
9789         if (i < 0 || i >= mol->mview->ngraphics)
9790                 rb_raise(rb_eArgError, "graphic index is out of range");
9791         {
9792                 /*  Prepare data for undo  */
9793                 MainViewGraphic *gp;
9794                 Vector *vp;
9795                 MolAction *act;
9796                 double col[4];
9797                 int n;
9798                 gp = mol->mview->graphics + i;
9799                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9800                 for (n = 0; n < gp->npoints; n++) {
9801                         vp[n].x = gp->points[n * 3];
9802                         vp[n].y = gp->points[n * 3 + 1];
9803                         vp[n].z = gp->points[n * 3 + 2];
9804                 }
9805                 col[0] = gp->rgba[0];
9806                 col[1] = gp->rgba[1];
9807                 col[2] = gp->rgba[2];
9808                 col[3] = gp->rgba[3];
9809                 if (gp->visible == 0) {
9810                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9811                         MolActionCallback_registerUndo(mol, act);
9812                         MolActionRelease(act);
9813                 }
9814                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9815                 MolActionCallback_registerUndo(mol, act);
9816                 free(vp);
9817                 MolActionRelease(act);
9818         }
9819         MainView_removeGraphic(mol->mview, i);
9820         return ival;
9821 }
9822
9823 /*
9824  *  call-seq:
9825  *     ngraphics -> integer
9826  *
9827  *  Get the number of graphic objects.
9828  */
9829 static VALUE
9830 s_Molecule_NGraphics(VALUE self)
9831 {
9832     Molecule *mol;
9833     Data_Get_Struct(self, Molecule, mol);
9834         if (mol->mview == NULL)
9835                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9836         return INT2NUM(mol->mview->ngraphics);
9837 }
9838
9839 /*
9840  *  call-seq:
9841  *     get_graphic_point(graphic_index, point_index) -> value
9842  *     get_graphic_points(graphic_index) -> values
9843  *
9844  *  Get the point_index-th control point of graphic_index-th graphic object.
9845  *  Get an array of all control points with the given values.
9846  *   
9847  */
9848 static VALUE
9849 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9850 {
9851         MainViewGraphic *gp;
9852     Molecule *mol;
9853         int index, pindex;
9854         Vector v;
9855         VALUE gval, pval;
9856     Data_Get_Struct(self, Molecule, mol);
9857         if (mol->mview == NULL)
9858                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9859         rb_scan_args(argc, argv, "11", &gval, &pval);
9860         index = NUM2INT(rb_Integer(gval));
9861         if (index < 0 || index >= mol->mview->ngraphics)
9862                 rb_raise(rb_eArgError, "the graphic index is out of range");
9863         gp = mol->mview->graphics + index;
9864         if (pval != Qnil) {
9865                 pindex = NUM2INT(rb_Integer(pval));
9866                 if (pindex < 0 || pindex >= gp->npoints)
9867                         rb_raise(rb_eArgError, "the point index is out of range");
9868                 v.x = gp->points[pindex * 3];
9869                 v.y = gp->points[pindex * 3 + 1];
9870                 v.z = gp->points[pindex * 3 + 2];
9871                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9872                         return rb_float_new(v.x);
9873                 } else {
9874                         return ValueFromVector(&v);
9875                 }
9876         } else {
9877                 pval = rb_ary_new();
9878                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9879                         v.x = gp->points[pindex * 3];
9880                         v.y = gp->points[pindex * 3 + 1];
9881                         v.z = gp->points[pindex * 3 + 2];
9882                         rb_ary_push(pval, ValueFromVector(&v));
9883                 }
9884                 return pval;
9885         }
9886 }
9887
9888 /*
9889  *  call-seq:
9890  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9891  *     set_graphic_points(graphic_index, new_values) -> new_values
9892  *
9893  *  Change the point_index-th control point of graphic_index-th graphic object.
9894  *  Replace the control points with the given values.
9895  *   
9896  */
9897 static VALUE
9898 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9899 {
9900         MainViewGraphic *gp;
9901     Molecule *mol;
9902         int index, pindex;
9903         Vector v, v0;
9904         VALUE gval, pval, nval;
9905         MolAction *act;
9906     Data_Get_Struct(self, Molecule, mol);
9907         if (mol->mview == NULL)
9908                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9909         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9910         index = NUM2INT(rb_Integer(gval));
9911         if (index < 0 || index >= mol->mview->ngraphics)
9912                 rb_raise(rb_eArgError, "the graphic index is out of range");
9913         gp = mol->mview->graphics + index;
9914         if (nval != Qnil) {
9915                 pindex = NUM2INT(rb_Integer(pval));
9916                 if (pindex < 0 || pindex >= gp->npoints)
9917                         rb_raise(rb_eArgError, "the point index is out of range");
9918                 v0.x = gp->points[pindex * 3];
9919                 v0.y = gp->points[pindex * 3 + 1];
9920                 v0.z = gp->points[pindex * 3 + 2];
9921                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9922                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9923                                 v.x = NUM2DBL(rb_Float(nval));
9924                                 v.y = v.z = 0;
9925                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9926                                 v.x = NUM2DBL(rb_Float(nval));
9927                                 v.y = v.z = 0;
9928                                 gp->points[7] = gp->points[11] = v.x;
9929                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9930                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9931                 } else {
9932                         if (nval == Qnil) {
9933                                 v.x = kInvalidFloat;
9934                                 v.y = v.z = 0.0;
9935                         } else VectorFromValue(nval, &v);
9936                 }
9937                 gp->points[pindex * 3] = v.x;
9938                 gp->points[pindex * 3 + 1] = v.y;
9939                 gp->points[pindex * 3 + 2] = v.z;
9940                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9941         } else {
9942                 VALUE aval;
9943                 int len;
9944                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9945                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9946                         vp[pindex].x = gp->points[pindex * 3];
9947                         vp[pindex].y = gp->points[pindex * 3 + 1];
9948                         vp[pindex].z = gp->points[pindex * 3 + 2];
9949                 }
9950                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9951                 free(vp);
9952                 pval = rb_ary_to_ary(pval);
9953                 len = RARRAY_LEN(pval);
9954                 if (gp->npoints < len) {
9955                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9956                         gp->npoints = len;
9957                 } else if (gp->npoints > len) {
9958                         int len2 = 3;
9959                         switch (gp->kind) {
9960                                 case kMainViewGraphicLine: len2 = 2; break;
9961                                 case kMainViewGraphicPoly: len2 = 3; break;
9962                                 case kMainViewGraphicCylinder: len2 = 3; break;
9963                                 case kMainViewGraphicCone: len2 = 3; break;
9964                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9965                         }
9966                         if (len2 < len)
9967                                 len2 = len;
9968                         gp->npoints = len2;
9969                 }
9970                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9971                         aval = RARRAY_PTR(pval)[pindex];
9972                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9973                                 v.x = NUM2DBL(rb_Float(aval));
9974                                 v.y = v.z = 0;
9975                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9976                                 v.x = NUM2DBL(rb_Float(aval));
9977                                 v.y = v.z = 0;
9978                                 gp->points[7] = gp->points[11] = v.x;
9979                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9980                                 break;
9981                         } else VectorFromValue(aval, &v);
9982                         gp->points[pindex * 3] = v.x;
9983                         gp->points[pindex * 3 + 1] = v.y;
9984                         gp->points[pindex * 3 + 2] = v.z;
9985                 }
9986         }
9987         if (gp->kind == kMainViewGraphicPoly) {
9988                 /*  Calculate normals  */
9989                 s_CalculateGraphicNormals(gp);
9990         }
9991         MolActionCallback_registerUndo(mol, act);
9992         MolActionRelease(act);          
9993         MoleculeCallback_notifyModification(mol, 0);
9994         return nval;
9995 }
9996
9997 /*
9998  *  call-seq:
9999  *     get_graphic_color(graphic_index) -> value
10000  *
10001  *  Get the color of graphic_index-th graphic object
10002  */
10003 static VALUE
10004 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10005 {
10006         MainViewGraphic *gp;
10007     Molecule *mol;
10008         int index;
10009     Data_Get_Struct(self, Molecule, mol);
10010         if (mol->mview == NULL)
10011                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10012         index = NUM2INT(rb_Integer(gval));
10013         if (index < 0 || index >= mol->mview->ngraphics)
10014                 rb_raise(rb_eArgError, "the graphic index is out of range");
10015         gp = mol->mview->graphics + index;
10016         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]));
10017 }
10018
10019 /*
10020  *  call-seq:
10021  *     set_graphic_color(graphic_index, new_value) -> new_value
10022  *
10023  *  Change the color of graphic_index-th graphic object
10024  *   
10025  */
10026 static VALUE
10027 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10028 {
10029         MainViewGraphic *gp;
10030     Molecule *mol;
10031         MolAction *act;
10032         double c[4];
10033         int index, i, n;
10034     Data_Get_Struct(self, Molecule, mol);
10035         if (mol->mview == NULL)
10036                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10037         index = NUM2INT(rb_Integer(gval));
10038         if (index < 0 || index >= mol->mview->ngraphics)
10039                 rb_raise(rb_eArgError, "the graphic index is out of range");
10040         gp = mol->mview->graphics + index;
10041         for (i = 0; i < 4; i++)
10042                 c[i] = gp->rgba[i];
10043         cval = rb_ary_to_ary(cval);
10044         n = RARRAY_LEN(cval);
10045         if (n != 3 && n != 4)
10046                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10047
10048         for (i = 0; i < n; i++) {
10049                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10050         }
10051         if (n == 3)
10052                 gp->rgba[3] = 1.0;
10053         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10054         MolActionCallback_registerUndo(mol, act);
10055         MolActionRelease(act);          
10056         MoleculeCallback_notifyModification(mol, 0);
10057         return cval;
10058 }
10059
10060 /*
10061  *  call-seq:
10062  *     show_graphic(graphic_index) -> self
10063  *
10064  *  Enable the visible flag of the graphic_index-th graphic object
10065  *   
10066  */
10067 static VALUE
10068 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10069 {
10070         MainViewGraphic *gp;
10071     Molecule *mol;
10072         int index;
10073     Data_Get_Struct(self, Molecule, mol);
10074         if (mol->mview == NULL)
10075                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10076         index = NUM2INT(rb_Integer(gval));
10077         if (index < 0 || index >= mol->mview->ngraphics)
10078                 rb_raise(rb_eArgError, "the graphic index is out of range");
10079         gp = mol->mview->graphics + index;
10080         gp->visible = 1;
10081         MoleculeCallback_notifyModification(mol, 0);
10082         return self;
10083 }
10084
10085 /*
10086  *  call-seq:
10087  *     hide_graphic(graphic_index) -> self
10088  *
10089  *  Disable the visible flag of the graphic_index-th graphic object
10090  *   
10091  */
10092 static VALUE
10093 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10094 {
10095         MainViewGraphic *gp;
10096     Molecule *mol;
10097         int index;
10098     Data_Get_Struct(self, Molecule, mol);
10099         if (mol->mview == NULL)
10100                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10101         index = NUM2INT(rb_Integer(gval));
10102         if (index < 0 || index >= mol->mview->ngraphics)
10103                 rb_raise(rb_eArgError, "the graphic index is out of range");
10104         gp = mol->mview->graphics + index;
10105         gp->visible = 0;
10106         MoleculeCallback_notifyModification(mol, 0);
10107         return self;
10108 }
10109
10110 /*
10111  *  call-seq:
10112  *     show_text(string)
10113  *
10114  *  Show the string in the info text box.
10115  */
10116 static VALUE
10117 s_Molecule_ShowText(VALUE self, VALUE arg)
10118 {
10119     Molecule *mol;
10120     Data_Get_Struct(self, Molecule, mol);
10121         if (mol->mview != NULL)
10122                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10123         return Qnil;
10124 }
10125
10126 #pragma mark ------ MD Support ------
10127
10128 /*
10129  *  call-seq:
10130  *     md_arena -> MDArena
10131  *
10132  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10133  *  this molecule, a new arena is created.
10134  */
10135 static VALUE
10136 s_Molecule_MDArena(VALUE self)
10137 {
10138     Molecule *mol;
10139         VALUE retval;
10140     Data_Get_Struct(self, Molecule, mol);
10141         if (mol->arena == NULL)
10142                 md_arena_new(mol);
10143         retval = ValueFromMDArena(mol->arena);
10144         return retval;
10145 }
10146
10147 /*
10148  *  call-seq:
10149  *     set_parameter_attr(type, index, key, value, src) -> value
10150  *
10151  *  This method is used only internally.
10152  */
10153 static VALUE
10154 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10155 {
10156         /*  This method is called from MolAction to change a MM parameter attribute.  */
10157     Molecule *mol;
10158         VALUE pval;
10159         ParameterRef *pref;
10160         UnionPar *up;
10161     Data_Get_Struct(self, Molecule, mol);
10162         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10163         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10164         
10165         /*  This is the special part of this method; it allows modification of the src field. */
10166         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10167         Data_Get_Struct(pval, ParameterRef, pref);
10168         up = ParameterRefGetPar(pref);
10169         up->bond.src = FIX2INT(sval);
10170         
10171         return vval;
10172 }
10173
10174 /*
10175  *  call-seq:
10176  *     parameter -> Parameter
10177  *
10178  *  Get the local parameter of this molecule. If not defined, returns nil.
10179  */
10180 static VALUE
10181 s_Molecule_Parameter(VALUE self)
10182 {
10183     Molecule *mol;
10184     Data_Get_Struct(self, Molecule, mol);
10185 /*      if (mol->par == NULL)
10186                 return Qnil; */
10187         return s_NewParameterValueFromValue(self);
10188 }
10189
10190 /*
10191  *  call-seq:
10192  *     start_step       -> Integer
10193  *
10194  *  Returns the start step (defined by dcd format).
10195  */
10196 static VALUE
10197 s_Molecule_StartStep(VALUE self)
10198 {
10199     Molecule *mol;
10200     Data_Get_Struct(self, Molecule, mol);
10201         return INT2NUM(mol->startStep);
10202 }
10203
10204 /*
10205  *  call-seq:
10206  *     start_step = Integer
10207  *
10208  *  Set the start step (defined by dcd format).
10209  */
10210 static VALUE
10211 s_Molecule_SetStartStep(VALUE self, VALUE val)
10212 {
10213     Molecule *mol;
10214     Data_Get_Struct(self, Molecule, mol);
10215         mol->startStep = NUM2INT(rb_Integer(val));
10216         return val;
10217 }
10218
10219 /*
10220  *  call-seq:
10221  *     steps_per_frame       -> Integer
10222  *
10223  *  Returns the number of steps between frames (defined by dcd format).
10224  */
10225 static VALUE
10226 s_Molecule_StepsPerFrame(VALUE self)
10227 {
10228     Molecule *mol;
10229     Data_Get_Struct(self, Molecule, mol);
10230         return INT2NUM(mol->stepsPerFrame);
10231 }
10232
10233 /*
10234  *  call-seq:
10235  *     steps_per_frame = Integer
10236  *
10237  *  Set the number of steps between frames (defined by dcd format).
10238  */
10239 static VALUE
10240 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10241 {
10242     Molecule *mol;
10243     Data_Get_Struct(self, Molecule, mol);
10244         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10245         return val;
10246 }
10247
10248 /*
10249  *  call-seq:
10250  *     ps_per_step       -> Float
10251  *
10252  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10253  */
10254 static VALUE
10255 s_Molecule_PsPerStep(VALUE self)
10256 {
10257     Molecule *mol;
10258     Data_Get_Struct(self, Molecule, mol);
10259         return rb_float_new(mol->psPerStep);
10260 }
10261
10262 /*
10263  *  call-seq:
10264  *     ps_per_step = Float
10265  *
10266  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10267  */
10268 static VALUE
10269 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10270 {
10271     Molecule *mol;
10272     Data_Get_Struct(self, Molecule, mol);
10273         mol->psPerStep = NUM2DBL(rb_Float(val));
10274         return val;
10275 }
10276
10277 static VALUE
10278 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10279 {
10280         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.");
10281 }
10282
10283 #pragma mark ------ MO Handling ------
10284
10285 /*
10286  *  call-seq:
10287  *     selectedMO -> IntGroup
10288  *
10289  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10290  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10291  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10292  */
10293 static VALUE
10294 s_Molecule_SelectedMO(VALUE self)
10295 {
10296     Molecule *mol;
10297         IntGroup *ig;
10298         VALUE val;
10299     Data_Get_Struct(self, Molecule, mol);
10300         if (mol->mview == NULL)
10301                 return Qnil;
10302         ig = MainView_selectedMO(mol->mview);
10303         if (ig == NULL)
10304                 return Qnil;
10305         IntGroupOffset(ig, 1);
10306         val = ValueFromIntGroup(ig);
10307         IntGroupRelease(ig);
10308         return val;
10309 }
10310
10311 /*
10312  *  call-seq:
10313  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10314  *
10315  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10316  *  If the molecule does not contain a basis set information, then returns nil.
10317  */
10318 static VALUE
10319 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10320 {
10321     Molecule *mol;
10322         Vector o, dx, dy, dz;
10323         Int nx, ny, nz;
10324         VALUE nval;
10325         Int npoints = 80 * 80 * 80;
10326     Data_Get_Struct(self, Molecule, mol);
10327         if (mol->bset == NULL)
10328                 return Qnil;
10329         rb_scan_args(argc, argv, "01", &nval);
10330         if (nval != Qnil)
10331                 npoints = NUM2INT(rb_Integer(nval));
10332         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10333                 return Qnil;
10334         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));
10335 }
10336
10337 static int
10338 s_Cubegen_callback(double progress, void *ref)
10339 {
10340         MyAppCallback_setProgressValue(progress);
10341         if (MyAppCallback_checkInterrupt())
10342                 return 1;
10343         else return 0;
10344 }
10345
10346 /*
10347  *  call-seq:
10348  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10349  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10350  *
10351  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10352  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10353  *  second form, the cube dimension is explicitly given.
10354  *  Returns fname when successful, nil otherwise.
10355  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10356  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10357  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10358  */
10359 static VALUE
10360 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10361 {
10362         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10363     Molecule *mol;
10364         Int mono, nx, ny, nz, npoints;
10365         Vector o, dx, dy, dz;
10366         int index, n;
10367         char buf[1024];
10368     Data_Get_Struct(self, Molecule, mol);
10369         if (mol->bset == NULL)
10370                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10371         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10372         
10373         /*  Set up parameters  */
10374         mono = NUM2INT(rb_Integer(mval));
10375         if (mono < 0 || mono > mol->bset->ncomps)
10376                 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);
10377         if (RTEST(bval)) {
10378                 if (mol->bset->rflag != 0)
10379                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10380                 mono += mol->bset->ncomps;
10381         }
10382                 
10383         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10384                 /*  Automatic grid formation  */
10385                 if (oval != Qnil)
10386                         npoints = NUM2INT(rb_Integer(oval));
10387                 else npoints = 0;
10388                 if (npoints == 0)
10389                         npoints = 1000000;
10390                 else if (npoints < 8)
10391                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10392                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10393                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10394                 ival = dxval;
10395                 bval = dyval;
10396         } else {
10397                 VectorFromValue(oval, &o);
10398                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10399                         VectorFromValue(dxval, &dx);
10400                 else {
10401                         dx.x = NUM2DBL(rb_Float(dxval));
10402                         dx.y = dx.z = 0.0;
10403                 }
10404                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10405                         VectorFromValue(dyval, &dy);
10406                 else {
10407                         dy.y = NUM2DBL(rb_Float(dyval));
10408                         dy.x = dy.z = 0.0;
10409                 }
10410                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10411                         VectorFromValue(dzval, &dz);
10412                 else {
10413                         dz.z = NUM2DBL(rb_Float(dzval));
10414                         dz.x = dz.y = 0.0;
10415                 }
10416                 nx = NUM2INT(rb_Integer(nxval));
10417                 ny = NUM2INT(rb_Integer(nyval));
10418                 nz = NUM2INT(rb_Integer(nzval));
10419                 if (nx <= 0 || ny <= 0 || nz <= 0)
10420                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10421                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10422                         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);
10423         }
10424         
10425         /*  Calc MO  */
10426         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10427         if (index == -2)
10428                 rb_interrupt();
10429         else if (index < 0)
10430                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10431         
10432         /*  Output to file  */
10433         MoleculeCallback_displayName(mol, buf, sizeof buf);
10434         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10435         if (n != 0)
10436                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10437         
10438         /*  Discard the cube  */
10439         MoleculeClearCubeAtIndex(mol, index);
10440         return fval;
10441 }
10442
10443 /*
10444  *  call-seq:
10445  *     clear_surface
10446  *
10447  *  Clear the MO surface if present.
10448  */
10449 static VALUE
10450 s_Molecule_ClearSurface(VALUE self)
10451 {
10452     Molecule *mol;
10453     Data_Get_Struct(self, Molecule, mol);
10454         if (mol->mcube != NULL)
10455                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10456         return self;
10457 }
10458
10459 /*
10460  *  call-seq:
10461  *     hide_surface
10462  *
10463  *  Hide the MO surface if present.
10464  */
10465 static VALUE
10466 s_Molecule_HideSurface(VALUE self)
10467 {
10468     Molecule *mol;
10469     Data_Get_Struct(self, Molecule, mol);
10470         if (mol->mcube != NULL) {
10471                 mol->mcube->hidden = 1;
10472                 MoleculeCallback_notifyModification(mol, 0);
10473         }
10474         return self;
10475 }
10476
10477 /*
10478  *  call-seq:
10479  *     show_surface
10480  *
10481  *  Show the MO surface if present.
10482  */
10483 static VALUE
10484 s_Molecule_ShowSurface(VALUE self)
10485 {
10486     Molecule *mol;
10487     Data_Get_Struct(self, Molecule, mol);
10488         if (mol->mcube != NULL) {
10489                 mol->mcube->hidden = 0;
10490                 MoleculeCallback_notifyModification(mol, 0);
10491         }
10492         return self;
10493 }
10494
10495 /*
10496  *  call-seq:
10497  *     create_surface(mo, attr = nil)
10498  *
10499  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10500  *  then it denotes the beta orbital.
10501  *  If mo is nil, then the attributes of the current surface are modified.
10502  *  Attributes:
10503  *    :npoints : the approximate number of grid points
10504  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10505  *    :thres   : the threshold for the isovalue surface
10506  *  If the molecule does not contain MO information, raises exception.
10507  */
10508 static VALUE
10509 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10510 {
10511     Molecule *mol;
10512         Vector o, dx, dy, dz;
10513         Int nmo, nx, ny, nz, i;
10514         Int need_recalc = 0;
10515         VALUE nval, hval, aval;
10516         Int npoints;
10517         Double expand;
10518         Double thres;
10519         Double d[4];
10520     Data_Get_Struct(self, Molecule, mol);
10521         rb_scan_args(argc, argv, "11", &nval, &hval);
10522         if (mol->bset == NULL)
10523                 rb_raise(rb_eMolbyError, "No MO information is given");
10524         if (nval == Qnil) {
10525                 nmo = -1;
10526         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10527                 nmo = mol->bset->nmos + 1;
10528         } else {
10529                 nmo = NUM2INT(rb_Integer(nval));
10530                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10531                         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);
10532                 if (nmo < 0)
10533                         nmo = -nmo + mol->bset->ncomps;
10534         }
10535         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10536                 npoints = NUM2INT(rb_Integer(aval));
10537                 need_recalc = 1;
10538         } else if (mol->mcube != NULL) {
10539                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10540         } else npoints = 80 * 80 * 80;
10541         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10542                 expand = NUM2DBL(rb_Float(aval));
10543         } else if (mol->mcube != NULL) {
10544                 expand = mol->mcube->expand;
10545         } else expand = 1.0;
10546         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10547                 thres = NUM2DBL(rb_Float(aval));
10548         } else if (mol->mcube != NULL) {
10549                 thres = mol->mcube->thres;
10550         } else thres = 0.05;
10551         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10552                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10553                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10554                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10555                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10556         }
10557         for (nx = 0; nx < 2; nx++) {
10558                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10559                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10560                         aval = rb_ary_to_ary(aval);
10561                         if (RARRAY_LEN(aval) < 3) {
10562                         raise:
10563                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10564                         }
10565                         for (i = 0; i < 4; i++)
10566                                 d[i] = mol->mcube->c[nx].rgba[i];
10567                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10568                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10569                                 if (d[i] < 0.0 && d[i] > 1.0)
10570                                         goto raise;
10571                         }
10572                         for (i = 0; i < 4; i++)
10573                                 mol->mcube->c[nx].rgba[i] = d[i];
10574                 }
10575         }
10576         if (mol->mcube->expand != expand)
10577                 need_recalc = 1;
10578         mol->mcube->thres = thres;
10579         mol->mcube->expand = expand;
10580         if (nmo < 0) {
10581                 if (mol->mcube->idn < 0)
10582                         return self;  /*  Only set attributes for now  */
10583                 if (need_recalc)
10584                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10585         }
10586         if (MoleculeUpdateMCube(mol, nmo) != 0)
10587                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10588         return self;
10589 }
10590
10591 /*
10592  *  call-seq:
10593  *     set_surface_attr(attr = nil)
10594  *
10595  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10596  */
10597 static VALUE
10598 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10599 {
10600         VALUE args[2];
10601         args[0] = Qnil;
10602         args[1] = hval;
10603         return s_Molecule_CreateSurface(2, args, self);
10604 }
10605
10606 /*
10607  *  call-seq:
10608  *     nelpots
10609  *
10610  *  Get the number of electrostatic potential info.
10611  */
10612 static VALUE
10613 s_Molecule_NElpots(VALUE self)
10614 {
10615         Molecule *mol;
10616     Data_Get_Struct(self, Molecule, mol);
10617         return INT2NUM(mol->nelpots);
10618 }
10619
10620 /*
10621  *  call-seq:
10622  *     elpot(idx)
10623  *
10624  *  Get the electrostatic potential info at the given index. If present, then the
10625  *  return value is [Vector, Float] (position and potential). If not present, then
10626  *  returns nil.
10627  */
10628 static VALUE
10629 s_Molecule_Elpot(VALUE self, VALUE ival)
10630 {
10631         Molecule *mol;
10632         int idx;
10633     Data_Get_Struct(self, Molecule, mol);
10634         idx = NUM2INT(rb_Integer(ival));
10635         if (idx < 0 || idx >= mol->nelpots)
10636                 return Qnil;
10637         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10638 }
10639
10640 /*
10641  *  call-seq:
10642  *     clear_basis_set
10643  *
10644  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10645  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10646  */
10647 static VALUE
10648 s_Molecule_ClearBasisSet(VALUE self)
10649 {
10650         Molecule *mol;
10651     Data_Get_Struct(self, Molecule, mol);
10652         if (mol != NULL) {
10653                 if (mol->bset != NULL) {
10654                         BasisSetRelease(mol->bset);
10655                         mol->bset = NULL;
10656                 }
10657                 if (mol->mcube != NULL) {
10658                         MoleculeDeallocateMCube(mol->mcube);
10659                         mol->mcube = NULL;
10660                 }
10661         }
10662         return self;
10663 }
10664
10665 /*
10666  *  call-seq:
10667  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10668  *
10669  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10670  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10671  *  -2, D5-type.
10672  */
10673 static VALUE
10674 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10675 {
10676         Molecule *mol;
10677         int sym, nprims, a_idx, n;
10678     Data_Get_Struct(self, Molecule, mol);
10679         a_idx = NUM2INT(rb_Integer(aval));
10680         sym = NUM2INT(rb_Integer(symval));
10681         nprims = NUM2INT(rb_Integer(npval));
10682         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10683         if (n == -1)
10684                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10685         else if (n == -2)
10686                 rb_raise(rb_eMolbyError, "Low memory");
10687         else if (n == -3)
10688                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10689         else if (n != 0)
10690                 rb_raise(rb_eMolbyError, "Unknown error");
10691         return self;
10692 }
10693
10694 /*
10695  *  call-seq:
10696  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10697  *
10698  *  To be used internally. Add a gaussian primitive coefficients.
10699  */
10700 static VALUE
10701 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10702 {
10703         Molecule *mol;
10704         Int n;
10705         Double exponent, contraction, contraction_sp;
10706     Data_Get_Struct(self, Molecule, mol);
10707         exponent = NUM2DBL(rb_Float(expval));
10708         contraction = NUM2DBL(rb_Float(cval));
10709         contraction_sp = NUM2DBL(rb_Float(cspval));
10710         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10711         if (n == -1)
10712                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10713         else if (n == -2)
10714                 rb_raise(rb_eMolbyError, "Low memory");
10715         else if (n != 0)
10716                 rb_raise(rb_eMolbyError, "Unknown error");
10717         return self;
10718 }
10719
10720 /*
10721  *  call-seq:
10722  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10723  *
10724  *  Get the Gaussian shell information for the given MO coefficient index.
10725  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10726  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10727  *  is the number of MO component belonging to this shell.
10728  */
10729 static VALUE
10730 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10731 {
10732         Molecule *mol;
10733         ShellInfo *sp;
10734         int s_idx, sym;
10735     Data_Get_Struct(self, Molecule, mol);
10736         if (mol->bset == NULL)
10737                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10738         s_idx = NUM2INT(rb_Integer(sval));
10739         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10740                 return Qnil;
10741         sp = mol->bset->shells + s_idx;
10742         sym = sp->sym;
10743         switch (sym) {
10744                 case kGTOType_S:  sym = 0;  break;
10745                 case kGTOType_SP: sym = -1; break;
10746                 case kGTOType_P:  sym = 1;  break;
10747                 case kGTOType_D:  sym = 2;  break;
10748                 case kGTOType_D5: sym = -2; break;
10749                 case kGTOType_F:  sym = 3;  break;
10750                 case kGTOType_F7: sym = -3; break;
10751                 case kGTOType_G:  sym = 4;  break;
10752                 case kGTOType_G9: sym = -4; break;
10753                 default:
10754                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10755         }
10756         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10757 }
10758
10759 /*
10760  *  call-seq:
10761  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10762  *
10763  *  Get the Gaussian primitive coefficients for the given MO component.
10764  */
10765 static VALUE
10766 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10767 {
10768         Molecule *mol;
10769         ShellInfo *sp;
10770         PrimInfo *pp;
10771         int s_idx, i;
10772         VALUE retval, aval;
10773     Data_Get_Struct(self, Molecule, mol);
10774         if (mol->bset == NULL)
10775                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10776         s_idx = NUM2INT(rb_Integer(sval));
10777         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10778                 return Qnil;
10779         sp = mol->bset->shells + s_idx;
10780         pp = mol->bset->priminfos + sp->p_idx;
10781         retval = rb_ary_new2(sp->nprim);
10782         for (i = 0; i < sp->nprim; i++) {
10783                 if (sp->sym == kGTOType_SP) {
10784                         /*  With P contraction coefficient  */
10785                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10786                 } else {
10787                         /*  Without P contraction coefficient  */
10788                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10789                 }
10790                 rb_ary_store(retval, i, aval);
10791         }
10792         return retval;
10793 }
10794
10795 /*
10796  *  call-seq:
10797  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10798  *
10799  *  Get the Gaussian shell information for the given MO coefficient index.
10800  */
10801 static VALUE
10802 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10803 {
10804         Molecule *mol;
10805         Int n, c, atom_idx, shell_idx;
10806         char label[32];
10807     Data_Get_Struct(self, Molecule, mol);
10808         if (mol->bset == NULL)
10809                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10810         c = NUM2INT(rb_Integer(cval));
10811         if (c < 0 || c >= mol->bset->ncomps)
10812                 return Qnil;
10813         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10814         if (n != 0)
10815                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10816         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10817 }
10818
10819 /*
10820  *  call-seq:
10821  *     clear_mo_coefficients
10822  *
10823  *  Clear the existing MO coefficients.
10824  */
10825 static VALUE
10826 s_Molecule_ClearMOCoefficients(VALUE self)
10827 {
10828         Molecule *mol;
10829         Data_Get_Struct(self, Molecule, mol);
10830         if (mol->bset != NULL) {
10831                 if (mol->bset->moenergies != NULL) {
10832                         free(mol->bset->moenergies);
10833                         mol->bset->moenergies = NULL;
10834                 }
10835                 if (mol->bset->mo != NULL) {
10836                         free(mol->bset->mo);
10837                         mol->bset->mo = NULL;
10838                 }
10839                 mol->bset->nmos = 0;
10840         }
10841         return self;
10842 }
10843
10844 /*
10845  *  call-seq:
10846  *     set_mo_coefficients(idx, energy, coefficients)
10847  *
10848  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10849  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10850  *  Energy is the MO energy, and coefficients is an array
10851  *  of MO coefficients.
10852  */
10853 static VALUE
10854 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10855 {
10856         Molecule *mol;
10857         Int idx, ncomps, i;
10858         Double energy;
10859         Double *coeffs;
10860     Data_Get_Struct(self, Molecule, mol);
10861         idx = NUM2INT(rb_Integer(ival));
10862         energy = NUM2DBL(rb_Float(eval));
10863         aval = rb_ary_to_ary(aval);
10864         ncomps = RARRAY_LEN(aval);
10865         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10866         if (coeffs == NULL) {
10867                 i = -2;
10868                 goto end;
10869         }
10870         for (i = 0; i < ncomps; i++)
10871                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10872         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10873 end:
10874         if (i == -1)
10875                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10876         else if (i == -2)
10877                 rb_raise(rb_eMolbyError, "Low memory");
10878         else if (i == -3)
10879                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10880         else if (i == -4)
10881                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10882         else if (i == -5)
10883                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10884         else if (i != 0)
10885                 rb_raise(rb_eMolbyError, "Unknown error");
10886         return self;
10887 }
10888
10889 /*
10890  *  call-seq:
10891  *     get_mo_coefficients(idx)
10892  *
10893  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10894  */
10895 static VALUE
10896 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10897 {
10898         Molecule *mol;
10899         Int idx, ncomps, n;
10900         Double energy;
10901         Double *coeffs;
10902         VALUE retval;
10903     Data_Get_Struct(self, Molecule, mol);
10904         idx = NUM2INT(rb_Integer(ival));
10905         ncomps = 0;
10906         coeffs = NULL;
10907         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10908         if (n == -1)
10909                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10910         else if (n == -2)
10911                 rb_raise(rb_eMolbyError, "No basis set information is present");
10912         else if (n == -3)
10913                 return Qnil;  /*  Silently returns nil  */
10914         retval = rb_ary_new2(ncomps);
10915         for (n = 0; n < ncomps; n++)
10916                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10917         free(coeffs);
10918         return retval;
10919 }
10920
10921 /*
10922  *  call-seq:
10923  *     get_mo_energy(idx)
10924  *
10925  *  To be used internally. Get the MO energy for the given MO index (1-based).
10926  */
10927 static VALUE
10928 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10929 {
10930         Molecule *mol;
10931         Int idx, n;
10932         Double energy;
10933     Data_Get_Struct(self, Molecule, mol);
10934         idx = NUM2INT(rb_Integer(ival));
10935         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10936         if (n == -1)
10937                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10938         else if (n == -2)
10939                 rb_raise(rb_eMolbyError, "No basis set information is present");
10940         else if (n == -3)
10941                 return Qnil;
10942         return rb_float_new(energy);
10943 }
10944
10945 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10946
10947 static inline void
10948 s_InitMOInfoKeys(void)
10949 {
10950         if (sTypeSym == 0) {
10951                 sTypeSym = ID2SYM(rb_intern("type"));
10952                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10953                 sBetaSym = ID2SYM(rb_intern("beta"));
10954                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10955                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10956         }
10957 }
10958
10959 /*
10960  *  call-seq:
10961  *     set_mo_info(hash)
10962  *
10963  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10964  *  :alpha=>integer, :beta=>integer
10965  */
10966 static VALUE
10967 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10968 {
10969         Molecule *mol;
10970         VALUE aval;
10971         Int rflag, na, nb, n;
10972         char *s;
10973     Data_Get_Struct(self, Molecule, mol);
10974         if (mol->bset != NULL) {
10975                 rflag = mol->bset->rflag;
10976                 na = mol->bset->ne_alpha;
10977                 nb = mol->bset->ne_beta;
10978         } else {
10979                 rflag = 1;
10980                 na = 0;
10981                 nb = 0;
10982         }
10983         if (hval != Qnil) {
10984                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10985                         s = StringValuePtr(aval);
10986                         if (strcasecmp(s, "RHF") == 0)
10987                                 rflag = 1;
10988                         else if (strcasecmp(s, "UHF") == 0)
10989                                 rflag = 0;
10990                         else if (strcasecmp(s, "ROHF") == 0)
10991                                 rflag = 2;
10992                 }
10993                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10994                         n = NUM2INT(rb_Integer(aval));
10995                         if (n >= 0)
10996                                 na = n;
10997                 }
10998                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10999                         n = NUM2INT(rb_Integer(aval));
11000                         if (n >= 0)
11001                                 nb = n;
11002                 }
11003                 MoleculeSetMOInfo(mol, rflag, na, nb);
11004         }
11005         return self;
11006 }
11007
11008 /*
11009  *  call-seq:
11010  *     get_mo_info(key)
11011  *
11012  *  Get the MO info. The key is as described in set_mo_info.
11013  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11014  */
11015 static VALUE
11016 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11017 {
11018         Molecule *mol;
11019     Data_Get_Struct(self, Molecule, mol);
11020         if (mol->bset == NULL)
11021                 return Qnil;
11022         if (kval == sTypeSym) {
11023                 switch (mol->bset->rflag) {
11024                         case 0: return Ruby_NewEncodedStringValue2("UHF");
11025                         case 1: return Ruby_NewEncodedStringValue2("RHF");
11026                         case 2: return Ruby_NewEncodedStringValue2("ROHF");
11027                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11028                 }
11029         } else if (kval == sAlphaSym) {
11030                 return INT2NUM(mol->bset->ne_alpha);
11031         } else if (kval == sBetaSym) {
11032                 return INT2NUM(mol->bset->ne_beta);
11033         } else if (kval == sNcompsSym) {
11034                 return INT2NUM(mol->bset->ncomps);
11035         } else if (kval == sNshellsSym) {
11036                 return INT2NUM(mol->bset->nshells);
11037         } else {
11038                 kval = rb_inspect(kval);
11039                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11040                 return Qnil;  /*  Does not reach here  */
11041         }
11042 }
11043
11044 /*
11045  *  call-seq:
11046  *     mo_type
11047  *
11048  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11049  */
11050 static VALUE
11051 s_Molecule_MOType(VALUE self)
11052 {
11053         return s_Molecule_GetMOInfo(self, sTypeSym);
11054 }
11055
11056 #pragma mark ------ Molecular Topology ------
11057
11058 /*
11059  *  call-seq:
11060  *     search_equivalent_atoms(ig = nil)
11061  *
11062  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
11063  */
11064 static VALUE
11065 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11066 {
11067         Molecule *mol;
11068         Int *result, i;
11069         VALUE val;
11070         IntGroup *ig;
11071     Data_Get_Struct(self, Molecule, mol);
11072         if (mol->natoms == 0)
11073                 return Qnil;
11074         rb_scan_args(argc, argv, "01", &val);
11075         if (val != Qnil)
11076                 ig = s_Molecule_AtomGroupFromValue(self, val);
11077         else ig = NULL;
11078         result = MoleculeSearchEquivalentAtoms(mol, ig);
11079         if (result == NULL)
11080                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11081         if (ig != NULL)
11082                 IntGroupRelease(ig);
11083         val = rb_ary_new2(mol->natoms);
11084         for (i = 0; i < mol->natoms; i++)
11085                 rb_ary_push(val, INT2NUM(result[i]));
11086         free(result);
11087         return val;
11088 }
11089
11090 /*
11091  *  call-seq:
11092  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11093  *
11094  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11095  *  Name is the name of the new pi anchor, and group is the atoms that define
11096  *  the pi system. Type (a String) is an atom type for MM implementation.
11097  *  Weights represent the relative significance of the component atoms; if omitted, then
11098  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
11099  *  The weight values will be normalized so that the sum of the weights is 1.0.
11100  *  The weight values must be positive.
11101  *  Index is the atom index where the created pi-anchor is inserted in the 
11102  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
11103  *  having the largest index.
11104  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
11105  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11106  */
11107 static VALUE
11108 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11109 {
11110         Molecule *mol;
11111         VALUE nval, gval;
11112         IntGroup *ig;
11113         Int i, n, idx, last_component;
11114         Atom a, *ap;
11115         PiAnchor an;
11116         AtomRef *aref;
11117         if (argc < 2 || argc >= 6)
11118                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11119         nval = *argv++;
11120         gval = *argv++;
11121         argc -= 2;
11122     Data_Get_Struct(self, Molecule, mol);
11123     if (gval == Qnil)
11124         ig = NULL;
11125     else
11126         ig = s_Molecule_AtomGroupFromValue(self, gval);
11127     if (ig == NULL || IntGroupGetCount(ig) == 0)
11128     rb_raise(rb_eMolbyError, "atom group is not given correctly");
11129         memset(&a, 0, sizeof(a));
11130         memset(&an, 0, sizeof(an));
11131         strncpy(a.aname, StringValuePtr(nval), 4);
11132         if (a.aname[0] == '_')
11133                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11134         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11135         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11136                 if (n >= mol->natoms) {
11137                         AtomConnectResize(&an.connect, 0);
11138                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11139                 }
11140                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11141                 last_component = n;
11142         }
11143         if (an.connect.count == 0)
11144                 rb_raise(rb_eMolbyError, "no atoms are specified");
11145         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11146         for (i = 0; i < an.connect.count; i++) {
11147                 an.coeffs[i] = 1.0 / an.connect.count;
11148         }
11149         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11150                 /*  Atom type  */
11151                 if (argv[0] != Qnil)
11152                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11153                 argc--;
11154                 argv++;
11155         }
11156         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11157                 if (argv[0] != Qnil) {
11158                         VALUE aval = rb_ary_to_ary(argv[0]);
11159                         Double d, sum;
11160                         if (RARRAY_LEN(aval) != an.connect.count)
11161                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11162                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11163                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11164                                 if (d <= 0.0)
11165                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11166                                 sum += d;
11167                                 an.coeffs[i] = d;
11168                         }
11169                         for (i = 0; i < an.connect.count; i++)
11170                                 an.coeffs[i] /= sum;
11171                 }
11172                 argc--;
11173                 argv++;
11174         }
11175         if (argc > 0 && argv[0] != Qnil) {
11176                 /*  Index  */
11177                 idx = NUM2INT(rb_Integer(argv[0]));
11178         } else idx = -1;
11179         if (idx < 0 || idx > mol->natoms) {
11180                 /*  Immediately after the last specified atom  */
11181                 idx = last_component + 1;
11182         }
11183         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11184         memmove(a.anchor, &an, sizeof(PiAnchor));
11185         /*  Use residue information of the last specified atom  */
11186         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11187         a.resSeq = ap->resSeq;
11188         strncpy(a.resName, ap->resName, 4);
11189         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11190                 return Qnil;
11191         MoleculeCalculatePiAnchorPosition(mol, idx);
11192     aref = AtomRefNew(mol, idx);
11193     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11194 }
11195
11196 #pragma mark ------ Molecular Properties ------
11197
11198 /*
11199  *  call-seq:
11200  *     set_property(name, value[, index]) -> value
11201  *     set_property(name, values, group) -> values
11202  *
11203  *  Set molecular property. A property is a floating-point number with a specified name,
11204  *  and can be set for each frame separately. The name of the property is given as a String.
11205  *  The value can be a single floating point number, which is set to the current frame.
11206  *  
11207  */
11208 static VALUE
11209 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11210 {
11211         Molecule *mol;
11212         VALUE nval, vval, ival;
11213         char *name;
11214         IntGroup *ig;
11215         Int i, n, idx, fidx;
11216         Double *dp;
11217         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11218     Data_Get_Struct(self, Molecule, mol);
11219         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11220                 idx = NUM2INT(rb_Integer(nval));
11221                 if (idx < 0 || idx >= mol->nmolprops)
11222                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11223         } else {
11224                 name = StringValuePtr(nval);
11225                 idx = MoleculeLookUpProperty(mol, name);
11226                 if (idx < 0) {
11227                         idx = MoleculeCreateProperty(mol, name);
11228                         if (idx < 0)
11229                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11230                 }
11231         }
11232         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11233                 if (ival == Qnil)
11234                         fidx = mol->cframe;
11235                 else {
11236                         fidx = NUM2INT(rb_Integer(ival));
11237                         n = MoleculeGetNumberOfFrames(mol);
11238                         if (fidx < 0 || fidx >= n)
11239                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11240                 }
11241                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11242                 dp = (Double *)malloc(sizeof(Double));
11243                 *dp = NUM2DBL(rb_Float(vval));
11244                 n = 1;
11245         } else {
11246                 vval = rb_ary_to_ary(vval);
11247                 ig = IntGroupFromValue(ival);
11248                 n = IntGroupGetCount(ig);
11249                 if (n == 0)
11250                         rb_raise(rb_eMolbyError, "No frames are specified");
11251                 if (RARRAY_LEN(vval) < n)
11252                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11253                 dp = (Double *)calloc(sizeof(Double), n);
11254                 for (i = 0; i < n; i++)
11255                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11256         }
11257         
11258         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11259         free(dp);
11260         IntGroupRelease(ig);
11261         return self;
11262 }
11263
11264 /*
11265  *  call-seq:
11266  *     get_property(name[, index]) -> value
11267  *     get_property(name, group) -> values
11268  *
11269  *  Get molecular property. In the first form, a property value for a single frame is returned.
11270  *  (If index is omitted, then the value for the current frame is given)
11271  *  In the second form, an array of property values for the given frames is returned.
11272  *  If name is not one of known properties or a valid index integer, exception is raised.
11273  */
11274 static VALUE
11275 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11276 {
11277         Molecule *mol;
11278         VALUE nval, ival;
11279         char *name;
11280         IntGroup *ig;
11281         Int i, n, idx, fidx;
11282         Double *dp;
11283         rb_scan_args(argc, argv, "11", &nval, &ival);
11284     Data_Get_Struct(self, Molecule, mol);
11285         if (mol->nmolprops == 0)
11286                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11287         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11288                 idx = NUM2INT(rb_Integer(nval));
11289                 if (idx < 0 || idx >= mol->nmolprops)
11290                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11291         } else {
11292                 name = StringValuePtr(nval);
11293                 idx = MoleculeLookUpProperty(mol, name);
11294                 if (idx < 0)
11295                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11296         }
11297         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11298                 if (ival == Qnil)
11299                         fidx = mol->cframe;
11300                 else {
11301                         fidx = NUM2INT(rb_Integer(ival));
11302                         n = MoleculeGetNumberOfFrames(mol);
11303                         if (fidx < 0 || fidx >= n)
11304                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11305                 }
11306                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11307                 ival = INT2FIX(fidx);
11308                 n = 1;
11309         } else {
11310                 ig = IntGroupFromValue(ival);
11311                 n = IntGroupGetCount(ig);
11312                 if (n == 0)
11313                         return rb_ary_new();
11314         }
11315         dp = (Double *)calloc(sizeof(Double), n);
11316         MoleculeGetProperty(mol, idx, ig, dp);  
11317         if (FIXNUM_P(ival))
11318                 ival = rb_float_new(dp[0]);
11319         else {
11320                 ival = rb_ary_new();
11321                 for (i = n - 1; i >= 0; i--) {
11322                         nval = rb_float_new(dp[i]);
11323                         rb_ary_store(ival, i, nval);
11324                 }
11325         }
11326         free(dp);
11327         IntGroupRelease(ig);
11328         return ival;
11329 }
11330
11331 /*
11332  *  call-seq:
11333  *     property_names -> Array
11334  *
11335  *  Get an array of property names.
11336  */
11337 static VALUE
11338 s_Molecule_PropertyNames(VALUE self)
11339 {
11340         Molecule *mol;
11341         VALUE rval, nval;
11342         int i;
11343     Data_Get_Struct(self, Molecule, mol);
11344         rval = rb_ary_new();
11345         for (i = mol->nmolprops - 1; i >= 0; i--) {
11346                 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11347                 rb_ary_store(rval, i, nval);
11348         }
11349         return rval;
11350 }
11351
11352 #pragma mark ------ Class methods ------
11353
11354 /*
11355  *  call-seq:
11356  *     current       -> Molecule
11357  *
11358  *  Get the currently "active" molecule.
11359  */
11360 static VALUE
11361 s_Molecule_Current(VALUE klass)
11362 {
11363         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11364 }
11365
11366 /*
11367  *  call-seq:
11368  *     Molecule[]          -> Molecule
11369  *     Molecule[n]         -> Molecule
11370  *     Molecule[name]      -> Molecule
11371  *     Molecule[name, k]   -> Molecule
11372  *     Molecule[regex]     -> Molecule
11373  *     Molecule[regex, k]  -> Molecule
11374  *
11375  *  Molecule[] is equivalent to Molecule.current.
11376  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11377  *  Molecule[name] gives the first document (in the order of creation time) that has
11378  *  the given name. If a second argument (k) is given, the k-th document that has the
11379  *  given name is returned.
11380  *  Molecule[regex] gives the first document (in the order of creation time) that
11381  *  has a name matching the regular expression. If a second argument (k) is given, 
11382  *  the k-th document that has a name matching the re is returned.
11383  */
11384 static VALUE
11385 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11386 {
11387         VALUE val, kval;
11388         int idx, k;
11389         Molecule *mol;
11390         char buf[1024];
11391         rb_scan_args(argc, argv, "02", &val, &kval);
11392         if (val == Qnil)
11393                 return s_Molecule_Current(klass);
11394         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11395                 idx = NUM2INT(val);
11396                 mol = MoleculeCallback_moleculeAtIndex(idx);
11397         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11398                 char *p = StringValuePtr(val);
11399                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11400                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11401                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11402                         if (strcmp(buf, p) == 0 && --k == 0)
11403                                 break;
11404                 }
11405         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11406                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11407                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11408                         VALUE name;
11409                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11410                         name = Ruby_NewEncodedStringValue2(buf);
11411                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11412                                 break;
11413                 }       
11414         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11415         
11416         if (mol == NULL)
11417                 return Qnil;
11418         else return ValueFromMolecule(mol);
11419 }
11420
11421 /*
11422  *  call-seq:
11423  *     list         -> array of Molecules
11424  *
11425  *  Get the list of molecules associated to the documents, in the order of creation
11426  *  time of the document. If no document is open, returns an empry array.
11427  */
11428 static VALUE
11429 s_Molecule_List(VALUE klass)
11430 {
11431         Molecule *mol;
11432         int i;
11433         VALUE ary;
11434         i = 0;
11435         ary = rb_ary_new();
11436         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11437                 rb_ary_push(ary, ValueFromMolecule(mol));
11438                 i++;
11439         }
11440         return ary;
11441 }
11442
11443 /*
11444  *  call-seq:
11445  *     ordered_list         -> array of Molecules
11446  *
11447  *  Get the list of molecules associated to the documents, in the order of front-to-back
11448  *  ordering of the associated window. If no document is open, returns an empry array.
11449  */
11450 static VALUE
11451 s_Molecule_OrderedList(VALUE klass)
11452 {
11453         Molecule *mol;
11454         int i;
11455         VALUE ary;
11456         i = 0;
11457         ary = rb_ary_new();
11458         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11459                 rb_ary_push(ary, ValueFromMolecule(mol));
11460                 i++;
11461         }
11462         return ary;
11463 }
11464
11465 #pragma mark ------ Call Subprocess ------
11466
11467 /*  The callback functions for call_subprocess_async  */
11468 static int
11469 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11470 {
11471         int ruby_status;
11472         VALUE procval, retval, args[2];
11473         args[0] = ValueFromMolecule(mol);
11474         args[1] = INT2NUM(status);
11475         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11476         if (procval != Qnil) {
11477                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11478                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11479                         return 1;
11480         }
11481         return 0;
11482 }
11483
11484 static int
11485 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11486 {
11487         int ruby_status;
11488         VALUE procval, retval, args[2];
11489         args[0] = ValueFromMolecule(mol);
11490         args[1] = INT2NUM(tcount);
11491         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11492         if (procval != Qnil) {
11493                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11494                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11495                         return 1;
11496         }
11497         return 0;
11498 }
11499
11500 /*
11501  *  call-seq:
11502  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11503  *
11504  *  Call subprocess asynchronically.
11505  *  If end_callback is given, it will be called (with two arguments self and termination status)
11506  *  when the subprocess terminated.
11507  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11508  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11509  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11510  *  filename begins with ">>", then the message will be appended to the file.
11511  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11512  *  If the argument is nil, then the message will be sent to the Ruby console.
11513  *  Returns the process ID as an integer.
11514  */
11515 static VALUE
11516 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11517 {
11518         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11519         Molecule *mol;
11520         char *sout, *serr;
11521         int n;
11522         FILE *fpout, *fperr;
11523         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11524         Data_Get_Struct(self, Molecule, mol);
11525
11526         if (stdout_val == Qnil) {
11527                 fpout = (FILE *)1;
11528         } else {
11529                 sout = StringValuePtr(stdout_val);
11530                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11531                         fpout = NULL;
11532                 else {
11533                         if (strncmp(sout, ">>", 2) == 0) {
11534                                 sout += 2;
11535                                 fpout = fopen(sout, "a");
11536                         } else {
11537                                 if (*sout == '>')
11538                                         sout++;
11539                                 fpout = fopen(sout, "w");
11540                         }
11541                         if (fpout == NULL)
11542                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11543                 }
11544         }
11545         if (stderr_val == Qnil) {
11546                 fperr = (FILE *)1;
11547         } else {
11548                 serr = StringValuePtr(stderr_val);
11549                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11550                         fperr = NULL;
11551                 else {
11552                         if (strncmp(serr, ">>", 2) == 0) {
11553                                 serr += 2;
11554                                 fpout = fopen(serr, "a");
11555                         } else {
11556                                 if (*serr == '>')
11557                                         serr++;
11558                                 fperr = fopen(serr, "w");
11559                         }
11560                         if (fperr == NULL)
11561                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11562                 }
11563         }
11564         
11565         /*  Register procs as instance variables  */
11566         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11567         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11568         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11569         if (fpout != NULL && fpout != (FILE *)1)
11570                 fclose(fpout);
11571         if (fperr != NULL && fperr != (FILE *)1)
11572                 fclose(fperr);
11573         return INT2NUM(n);
11574 }
11575
11576 #pragma mark ====== Define Molby Classes ======
11577
11578 void
11579 Init_Molby(void)
11580 {
11581         int i;
11582         
11583         /*  Define module Molby  */
11584         rb_mMolby = rb_define_module("Molby");
11585         
11586         /*  Define Vector3D, Transform, IntGroup  */
11587         Init_MolbyTypes();
11588         
11589         /*  Define MDArena  */
11590         Init_MolbyMDTypes();
11591
11592         /*  class Molecule  */
11593         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11594
11595         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11596     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11597     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11598         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11599         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11600
11601     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11602     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11603     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11604     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11605     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11606     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11607     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11608     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11609     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11610     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11611         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11612     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11613     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11614     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11615     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11616     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11617     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11618     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11619         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11620         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11621         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11622         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11623         
11624     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11625         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11626     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11627     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11628     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11629
11630     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11631     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11632     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11633     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11634     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11635     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11636         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11637         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11638         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11639         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11640         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11641         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11642         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11643         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11644         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11645         
11646         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11647         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11648         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11649         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11650         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11651         
11652         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11653     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11654         rb_define_alias(rb_cMolecule, "+", "add");
11655     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11656         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11657         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11658         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11659         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11660         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11661         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11662         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11663         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11664         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11665         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11666         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11667         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11668         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11669         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11670         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11671         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11672         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11673         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11674         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11675         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11676
11677         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11678         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11679         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11680         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11681         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11682
11683         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11684         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11685         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11686         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11687         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11688         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11689         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11690         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11691         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11692
11693         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11694         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11695         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11696         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11697         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11698         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11699         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11700         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11701         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11702         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11703         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11704         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11705         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11706         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11707         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11708         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11709         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11710         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11711         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11712         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11713         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11714         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11715
11716         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11717         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11718         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11719         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11720         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11721         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11722         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11723
11724         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11725         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11726         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11727         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11728         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11729         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11730         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11731         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11732         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11733         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11734         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11735         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11736         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11737
11738         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11739         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11740         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11741         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11742         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11743         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11744
11745         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11746         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11747         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11748         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11749         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11750         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11751         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11752         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11753         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11754         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11755         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11756         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11757         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11758         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11759         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11760         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11761         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11762         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11763         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11764         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11765         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11766         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11767         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11768         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11769         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11770         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11771         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11772         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11773         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11774         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11775         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11776         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11777         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11778         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11779         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11780         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11781         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11782         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11783         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11784         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11785         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11786         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11787         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11788         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11789         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11790         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11791         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11792         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11793         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11794         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11795         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11796         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11797         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11798         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11799         
11800         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11801         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11802         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11803         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11804         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11805         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11806         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11807         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11808         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11809         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11810         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11811         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11812         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11813
11814         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11815         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11816         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11817         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11818         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11819         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11820         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11821         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11822         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11823         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11824         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11825         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11826         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11827         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11828                 
11829         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11830         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11831         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11832         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11833         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11834         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11835         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11836         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11837         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11838         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11839         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11840         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11841         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11842         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11843         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11844         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11845         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11846         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11847         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11848         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11849         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11850         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11851         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11852
11853         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11854         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11855         
11856         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11857         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11858         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11859                 
11860         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11861         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11862         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11863         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11864         
11865         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11866         
11867         /*  class MolEnumerable  */
11868         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11869     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11870         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11871         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11872     rb_define_alias(rb_cMolEnumerable, "size", "length");
11873         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11874         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11875
11876         /*  class AtomRef  */
11877         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11878         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11879                 char buf[64];
11880                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11881                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11882                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11883                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11884                 strcat(buf, "=");
11885                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11886         }
11887         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11888         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11889         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11890         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11891         s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11892         rb_global_variable(&s_SetAtomAttrString);
11893         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11894         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11895
11896         /*  class Parameter  */
11897         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11898         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11899         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11900         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11901         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11902         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11903         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11904         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11905         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11906         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11907         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11908         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11909         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11910         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11911         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11912         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11913         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11914         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11915         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11916         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11917         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11918         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11919         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11920         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11921         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11922         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11923         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11924         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11925         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11926         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11927         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11928         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11929         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11930         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11931         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11932         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11933         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11934         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11935         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11936         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11937         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11938         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11939         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11940         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11941         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11942         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11943         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11944         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11945         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11946         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11947         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11948         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11949         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11950         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11951
11952         /*  class ParEnumerable  */
11953         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11954     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11955         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11956         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11957         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11958         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11959         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11960         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11961         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11962         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11963         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11964         
11965         /*  class ParameterRef  */
11966         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11967         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11968                 char buf[64];
11969                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11970                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11971                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11972                 if (s_ParameterAttrDefTable[i].symref != NULL)
11973                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11974                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11975                         strcat(buf, "=");
11976                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11977                 }
11978         }
11979         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11980         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11981         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11982         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11983         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11984         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11985         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11986         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11987
11988         /*  class MolbyError  */
11989         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11990
11991         /*  module Kernel  */
11992         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11993         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11994         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11995         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11996         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11997         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11998         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11999         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12000         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12001         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12002         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12003         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12004         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12005         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12006         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12007         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12008         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12009         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12010         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12011         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12012         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12013         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12014         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12015         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12016     rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12017     rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12018     rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12019     rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12020     rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12021     rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12022
12023         /*  class IO  */
12024         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12025         
12026         s_ID_equal = rb_intern("==");
12027         g_RubyID_call = rb_intern("call");
12028         
12029         s_InitMOInfoKeys();
12030         
12031         /*  Symbols for graphics  */
12032         s_LineSym = ID2SYM(rb_intern("line"));
12033         s_PolySym = ID2SYM(rb_intern("poly"));
12034         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12035         s_ConeSym = ID2SYM(rb_intern("cone"));
12036         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12037 }
12038
12039 #pragma mark ====== Interface with RubyDialog class ======
12040
12041 RubyValue
12042 RubyDialogCallback_parentModule(void)
12043 {
12044         return (RubyValue)rb_mMolby;
12045 }
12046
12047 #pragma mark ====== External functions ======
12048
12049 static VALUE s_ruby_top_self = Qfalse;
12050 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12051 static VALUE s_ruby_export_local_variables = Qfalse;
12052
12053 static VALUE
12054 s_evalRubyScriptOnMoleculeSub(VALUE val)
12055 {
12056         void **ptr = (void **)val;
12057         Molecule *mol = (Molecule *)ptr[1];
12058         VALUE sval, fnval, lnval, retval;
12059         VALUE binding;
12060
12061         /*  Clear the error information (store in the history array if necessary)  */
12062         sval = rb_errinfo();
12063         if (sval != Qnil) {
12064                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12065                 rb_set_errinfo(Qnil);
12066         }
12067
12068         if (s_ruby_top_self == Qfalse) {
12069                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12070         }
12071         if (s_ruby_get_binding_for_molecule == Qfalse) {
12072                 const char *s1 =
12073                  "lambda { |_mol_, _bind_| \n"
12074                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12075                  "  _proc_.call(_mol_) } ";
12076                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12077                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12078         }
12079         if (s_ruby_export_local_variables == Qfalse) {
12080                 const char *s2 =
12081                 "lambda { |_bind_| \n"
12082                 "   # find local variables newly defined in _bind_ \n"
12083                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12084                 " _a_.each { |_vsym_| \n"
12085                 "   _vname_ = _vsym_.to_s \n"
12086                 "   _vval_ = _bind_.eval(_vname_) \n"
12087                 "   #  Define local variable \n"
12088                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12089                 "   #  Then set value  \n"
12090                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12091                 " } \n"
12092                 "}";
12093                 s_ruby_export_local_variables = rb_eval_string(s2);
12094                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12095         }
12096         if (ptr[2] == NULL) {
12097                 char *scr;
12098                 /*  String literal: we need to specify string encoding  */
12099 #if defined(__WXMSW__)
12100                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12101 #else
12102                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12103 #endif
12104                 sval = Ruby_NewEncodedStringValue2(scr);
12105                 free(scr);
12106                 fnval = Ruby_NewEncodedStringValue2("(eval)");
12107                 lnval = INT2FIX(0);
12108         } else {
12109                 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12110                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12111                 lnval = INT2FIX(1);
12112         }
12113         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12114         if (mol != NULL) {
12115                 VALUE mval = ValueFromMolecule(mol);
12116                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12117         }
12118         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12119         if (mol != NULL) {
12120                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12121         }
12122         return retval;
12123 }
12124
12125 RubyValue
12126 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12127 {
12128         RubyValue retval;
12129         void *args[3];
12130         VALUE save_interrupt_flag;
12131 /*      char *save_ruby_sourcefile;
12132         int save_ruby_sourceline; */
12133         if (gMolbyIsCheckingInterrupt) {
12134                 MolActionAlertRubyIsRunning();
12135                 *status = -1;
12136                 return (RubyValue)Qnil;
12137         }
12138         gMolbyRunLevel++;
12139         args[0] = (void *)script;
12140         args[1] = (void *)mol;
12141         args[2] = (void *)fname;
12142         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12143 /*      save_ruby_sourcefile = ruby_sourcefile;
12144         save_ruby_sourceline = ruby_sourceline; */
12145         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12146         if (*status != 0) {
12147                 /*  Is this 'exit' exception?  */
12148                 VALUE last_exception = rb_gv_get("$!");
12149                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12150                         /*  Capture exit and return the status value  */
12151                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12152                         *status = 0;
12153                         rb_set_errinfo(Qnil);
12154                 }
12155         }
12156         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12157 /*      ruby_sourcefile = save_ruby_sourcefile;
12158         ruby_sourceline = save_ruby_sourceline; */
12159         gMolbyRunLevel--;
12160         return retval;
12161 }
12162
12163 /*  For debug  */
12164 char *
12165 Ruby_inspectValue(RubyValue value)
12166 {
12167     int status;
12168     static char buf[256];
12169     VALUE val = (VALUE)value;
12170     gMolbyRunLevel++;
12171     val = rb_protect(rb_inspect, val, &status);
12172     gMolbyRunLevel--;
12173     if (status == 0) {
12174         char *str = StringValuePtr(val);
12175         strncpy(buf, str, sizeof(buf) - 1);
12176         buf[sizeof(buf) - 1] = 0;
12177     } else {
12178         snprintf(buf, sizeof(buf), "Error status = %d", status);
12179     }
12180     return buf;
12181 }
12182
12183 int
12184 Ruby_showValue(RubyValue value, char **outValueString)
12185 {
12186         VALUE val = (VALUE)value;
12187         if (gMolbyIsCheckingInterrupt) {
12188                 MolActionAlertRubyIsRunning();
12189                 return 0;
12190         }
12191         if (val != Qnil) {
12192                 int status;
12193                 char *str;
12194                 gMolbyRunLevel++;
12195                 val = rb_protect(rb_inspect, val, &status);
12196                 gMolbyRunLevel--;
12197                 if (status != 0)
12198                         return status;
12199                 str = StringValuePtr(val);
12200                 if (outValueString != NULL)
12201                         *outValueString = strdup(str);
12202                 MyAppCallback_showScriptMessage("%s", str);
12203         } else {
12204                 if (outValueString != NULL)
12205                         *outValueString = NULL;
12206         }
12207         return 0;
12208 }
12209
12210 void
12211 Ruby_showError(int status)
12212 {
12213         static const int tag_raise = 6;
12214     char *main_message = "Molby script error";
12215         char *msg = NULL, *msg2;
12216         VALUE val, backtrace;
12217         int interrupted = 0;
12218     int exit_status = -1;
12219         if (status == tag_raise) {
12220                 VALUE errinfo = rb_errinfo();
12221                 VALUE eclass = CLASS_OF(errinfo);
12222                 if (eclass == rb_eInterrupt) {
12223             main_message = "Molby script interrupted";
12224             msg = "Interrupt";
12225                         interrupted = 1;
12226         } else if (eclass == rb_eSystemExit) {
12227             main_message = "Molby script exit";
12228             interrupted = 2;
12229             val = rb_eval_string_protect("$!.status", &status);
12230             if (status == 0) {
12231                 exit_status = NUM2INT(rb_Integer(val));
12232                 asprintf(&msg, "Molby script exit with status %d", exit_status);
12233             } else {
12234                 asprintf(&msg, "Molby script exit with unknown status");
12235             }
12236         }
12237         }
12238         gMolbyRunLevel++;
12239     if (exit_status != 0) {
12240         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12241         if (msg == NULL) {
12242             val = rb_eval_string_protect("$!.to_s", &status);
12243             if (status == 0)
12244                 msg = RSTRING_PTR(val);
12245             else
12246                 msg = "(message not available)";
12247         }
12248         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12249     } else {
12250         msg2 = strdup(msg);
12251     }
12252         MyAppCallback_messageBox(msg2, main_message, 0, 3);
12253         free(msg2);
12254     if (interrupted == 2) {
12255         free(msg);
12256         if (!gUseGUI && exit_status == 0)
12257             exit(0);  // Capture exit(0) here and force exit
12258     }
12259         gMolbyRunLevel--;
12260 }
12261
12262 /*  Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode.  */
12263 int
12264 Molby_loadScript(const char *script, int from_file)
12265 {
12266     int status;
12267     gMolbyRunLevel++;
12268     if (from_file)
12269         rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12270     else
12271         rb_eval_string_protect(script, &status);
12272     gMolbyRunLevel--;
12273     return status;
12274 }
12275
12276 void
12277 Molby_getDescription(char **versionString, char **auxString)
12278 {
12279         extern const char *gVersionString, *gCopyrightString;
12280         extern int gRevisionNumber;
12281         extern char *gLastBuildString;
12282     char *s1, *s2;
12283         char *revisionString;
12284         if (gRevisionNumber > 0) {
12285                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12286         } else revisionString = "";
12287
12288     asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12289 #if defined(__WXMSW__)
12290     #if TARGET_ARCH == 64
12291              "Molby (64 bit)",
12292     #else
12293              "Molby (32 bit)",
12294     #endif
12295 #else
12296              "Molby",
12297 #endif
12298              gVersionString, revisionString, gCopyrightString, gLastBuildString);
12299     if (gUseGUI) {
12300         asprintf(&s2,
12301                  "\nIncluding:\n"
12302                  "%s"
12303                  "ruby %s, http://www.ruby-lang.org/\n"
12304                  "%s\n"
12305                  "FFTW 3.3.2, http://www.fftw.org/\n"
12306                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12307                  "  and Massachusetts Institute of Technology",
12308                  MyAppCallback_getGUIDescriptionString(),
12309                  gRubyVersion, gRubyCopyright);
12310     } else {
12311         asprintf(&s2,
12312                  "Including "
12313                  "ruby %s, http://www.ruby-lang.org/\n"
12314                  "%s\n"
12315                  "FFTW 3.3.2, http://www.fftw.org/\n"
12316                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12317                  "  and Massachusetts Institute of Technology",
12318                  gRubyVersion, gRubyCopyright);
12319
12320     }
12321         if (revisionString[0] != 0)
12322                 free(revisionString);
12323         if (versionString != NULL)
12324         *versionString = s1;
12325     if (auxString != NULL)
12326         *auxString = s2;
12327 }
12328
12329 void
12330 Molby_startup(const char *script, const char *dir)
12331 {
12332         VALUE val;
12333         int status;
12334         char *libpath;
12335         char *respath, *p, *wbuf;
12336
12337         /*  Get version/copyright string from Ruby interpreter  */
12338         {
12339                 gRubyVersion = strdup(ruby_version);
12340                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12341                                  "  ",  /*  Indent for displaying in About dialog  */
12342                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12343         }
12344         
12345         /*  Read build and revision information for Molby  */
12346 /*      {
12347                 char buf[200];
12348                 extern int gRevisionNumber;
12349                 extern char *gLastBuildString;
12350                 FILE *fp = fopen("../buildInfo.txt", "r");
12351                 gLastBuildString = "";
12352                 if (fp != NULL) {
12353                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12354                                 char *p1 = strchr(buf, '\"');
12355                                 char *p2 = strrchr(buf, '\"');
12356                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12357                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12358                                         buf[p2 - p1 - 1] = 0;
12359                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12360                                 }
12361                         }
12362                         fclose(fp);
12363                 }
12364                 fp = fopen("../revisionInfo.txt", "r");
12365                 gRevisionNumber = 0;
12366                 if (fp != NULL) {
12367                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12368                                 gRevisionNumber = strtol(buf, NULL, 0);
12369                         }
12370                         fclose(fp);
12371                 }
12372     } */
12373
12374     if (!gUseGUI) {
12375         char *wbuf2;
12376         Molby_getDescription(&wbuf, &wbuf2);
12377         MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12378         free(wbuf);
12379         free(wbuf2);
12380     }
12381         
12382         /*  Read atom display parameters  */
12383         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12384         MyAppCallback_setConsoleColor(1);
12385         MyAppCallback_showScriptMessage("%s", wbuf);
12386         MyAppCallback_setConsoleColor(0);
12387                 free(wbuf);
12388         }
12389         
12390         /*  Read default parameters  */
12391         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12392         if (wbuf != NULL) {
12393         MyAppCallback_setConsoleColor(1);
12394         MyAppCallback_showScriptMessage("%s", wbuf);
12395         MyAppCallback_setConsoleColor(0);
12396                 free(wbuf);
12397         }
12398                 
12399         /*  Initialize Ruby interpreter  */
12400 #if __WXMSW__
12401         if (gUseGUI) {
12402                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12403                     it causes rb_bug() (= fatal error) during ruby_init().
12404                     As a workaround, these standard streams are reopend as
12405                     NUL stream.  */
12406                 freopen("NUL", "r", stdin);
12407                 freopen("NUL", "w", stdout);
12408                 freopen("NUL", "w", stderr);
12409         }
12410 #endif
12411         ruby_init();
12412
12413         {
12414         /*  Initialize CP932/Windows-31J encodings  */
12415                 extern void Init_shift_jis(void), Init_windows_31j(void),  Init_trans_japanese_sjis(void);
12416         extern int rb_enc_alias(const char *, const char *);
12417         Init_shift_jis();
12418         Init_windows_31j();
12419         Init_trans_japanese_sjis();
12420         rb_enc_alias("CP932", "Windows-31J");
12421     }
12422     
12423 #if defined(__WXMSW__)
12424     {
12425         /*  Set default external encoding  */
12426         /*  The following snippet is taken from encoding.c  */
12427         extern void rb_enc_set_default_external(VALUE encoding);
12428         char cp[sizeof(int) * 8 / 3 + 22];
12429         int status;
12430         VALUE enc;
12431         snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12432         enc = rb_eval_string_protect(cp, &status);
12433         if (status == 0 && !NIL_P(enc)) {
12434             rb_enc_set_default_external(enc);
12435         }
12436         }
12437 #endif
12438
12439         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12440         ruby_incpush(".");
12441         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12442         ruby_incpush(libpath);
12443         free(libpath);
12444         ruby_incpush(dir);
12445
12446         ruby_script("Molby");
12447         
12448         /*  Find the resource path (the parent directory of the given directory)  */
12449         respath = strdup(dir);
12450         p = strrchr(respath, '/');
12451         if (p == NULL && PATH_SEPARATOR != '/')
12452                 p = strrchr(respath, PATH_SEPARATOR);
12453         if (p != NULL)
12454                 *p = 0;
12455         val = Ruby_NewFileStringValue(respath);
12456         rb_define_global_const("MolbyResourcePath", val);
12457         free(respath);
12458
12459         /*  Define Molby classes  */
12460         Init_Molby();
12461     if (gUseGUI)
12462         RubyDialogInitClass();
12463
12464         rb_define_const(rb_mMolby, "ResourcePath", val);
12465         val = Ruby_NewFileStringValue(dir);
12466         rb_define_const(rb_mMolby, "ScriptPath", val);
12467         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12468         val = Ruby_NewFileStringValue(p);
12469         rb_define_const(rb_mMolby, "MbsfPath", val);    
12470         free(p);
12471         
12472         p = MyAppCallback_getHomeDir();
12473         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12474         rb_define_const(rb_mMolby, "HomeDirectory", val);
12475         free(p);
12476         p = MyAppCallback_getDocumentHomeDir();
12477         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12478         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12479         free(p);
12480         
12481     if (gUseGUI)
12482         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12483     else
12484         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12485
12486     {
12487         /*  Create objects for stdout and stderr  */
12488         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12489         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12490         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12491         rb_gv_set("$stdout", val);
12492         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12493         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12494         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12495         rb_gv_set("$stderr", val);
12496
12497         /*  Create objects for stdin  */
12498         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12499         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12500         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12501         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12502         rb_gv_set("$stdin", val);
12503     }
12504         
12505         /*  Global variable to hold error information  */
12506         rb_define_variable("$backtrace", &gMolbyBacktrace);
12507         rb_define_variable("$error_history", &gMolbyErrorHistory);
12508         gMolbyErrorHistory = rb_ary_new();
12509         
12510         /*  Global variables for script menus  */
12511         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12512         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12513         gScriptMenuCommands = rb_ary_new();
12514         gScriptMenuEnablers = rb_ary_new();
12515         
12516     if (gUseGUI) {
12517         /*  Register interrupt check code  */
12518         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12519         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12520         s_SetIntervalTimer(0, 50);
12521     }
12522         
12523         /*  Read the startup script  */
12524         if (script != NULL && script[0] != 0) {
12525                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12526                 gMolbyRunLevel++;
12527                 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12528                 gMolbyRunLevel--;
12529                 if (status != 0)
12530                         Ruby_showError(status);
12531                 else
12532                         MyAppCallback_showScriptMessage("Done.\n");
12533         }
12534 }
12535
12536 void
12537 Molby_buildARGV(int argc, const char **argv)
12538 {
12539         int i;
12540     rb_ary_clear(rb_argv);
12541     for (i = 0; i < argc; i++) {
12542                 VALUE arg = rb_tainted_str_new2(argv[i]);
12543                 OBJ_FREEZE(arg);
12544                 rb_ary_push(rb_argv, arg);
12545     }
12546 }