OSDN Git Service

Start implementing 'additional exponent' for JANPA output
[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, retval;
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         retval = MyAppCallback_messageBox(str, title, buttons, icon);
223     return (retval ? Qtrue : Qfalse);
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  *  cmd is either a single string of an array of string. If it is a single string, then
1002  *  it will be given to wxExecute as a single argument. In this case, the string can be
1003  *  split into arguments by whitespace. If this behavior is not intended, then use an array
1004  *  containing a single string.
1005  *  A callback proc can be given, which is called periodically during execution. If the proc returns
1006  *  nil or false, then the execution will be interrupted.
1007  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
1008  *  filename begins with ">>", then the message will be appended to the file.
1009  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
1010  *  If the argument is nil, then the message will be sent to the Ruby console.
1011  */
1012 static VALUE
1013 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
1014 {
1015         VALUE cmd, procname, cproc, stdout_val, stderr_val;
1016     VALUE save_interruptFlag;
1017         int n, exitstatus, pid;
1018         char *sout, *serr;
1019     const char *pnamestr, **cmdargv;
1020         FILE *fpout, *fperr;
1021
1022         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
1023
1024         if (stdout_val == Qnil) {
1025                 fpout = (FILE *)1;
1026         } else {
1027                 sout = StringValuePtr(stdout_val);
1028                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
1029                         fpout = NULL;
1030                 else {
1031                         if (strncmp(sout, ">>", 2) == 0) {
1032                                 sout += 2;
1033                                 fpout = fopen(sout, "a");
1034                         } else {
1035                                 if (*sout == '>')
1036                                         sout++;
1037                                 fpout = fopen(sout, "w");
1038                         }
1039                         if (fpout == NULL)
1040                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
1041                 }
1042         }
1043         if (stderr_val == Qnil) {
1044                 fperr = (FILE *)1;
1045         } else {
1046                 serr = StringValuePtr(stderr_val);
1047                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
1048                         fperr = NULL;
1049                 else {
1050                         if (strncmp(serr, ">>", 2) == 0) {
1051                                 serr += 2;
1052                                 fpout = fopen(serr, "a");
1053                         } else {
1054                                 if (*serr == '>')
1055                                         serr++;
1056                                 fperr = fopen(serr, "w");
1057                         }
1058                         if (fperr == NULL)
1059                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
1060                 }
1061         }
1062     
1063     save_interruptFlag = s_SetInterruptFlag(self, Qnil);
1064     if (procname != Qnil)
1065         pnamestr = StringValuePtr(procname);
1066     else pnamestr = NULL;
1067     if (rb_obj_is_kind_of(cmd, rb_cString)) {
1068         cmdargv = calloc(sizeof(cmdargv[0]), 3);
1069         cmdargv[0] = StringValuePtr(cmd);
1070         cmdargv[1] = "";
1071         cmdargv[2] = NULL;
1072     } else {
1073         cmd = rb_ary_to_ary(cmd);
1074         cmdargv = calloc(sizeof(cmdargv[0]), RARRAY_LEN(cmd) + 1);
1075         for (n = 0; n < RARRAY_LEN(cmd); n++) {
1076             cmdargv[n] = StringValuePtr(RARRAY_PTR(cmd)[n]);
1077         }
1078         cmdargv[n] = NULL;
1079     }
1080         n = MyAppCallback_callSubProcess(cmdargv, pnamestr, (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
1081     s_SetInterruptFlag(self, save_interruptFlag);
1082     free(cmdargv);
1083
1084         if (fpout != NULL && fpout != (FILE *)1)
1085                 fclose(fpout);
1086         if (fperr != NULL && fperr != (FILE *)1)
1087                 fclose(fperr);
1088
1089         return INT2NUM(n);
1090
1091         
1092 }
1093
1094 /*
1095  *  call-seq:
1096  *     backquote(cmd)
1097  *
1098  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
1099  */
1100 static VALUE
1101 s_Kernel_Backquote(VALUE self, VALUE cmd)
1102 {
1103         char *buf;
1104         int n, exitstatus, pid;
1105         VALUE val;
1106     const char *cmdargv[3];
1107     cmdargv[0] = StringValuePtr(cmd);
1108     cmdargv[1] = "";
1109     cmdargv[2] = NULL;
1110         n = MyAppCallback_callSubProcess(cmdargv, NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1111 /*      fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1112         if (n >= 0 && buf != NULL) {
1113                 val = Ruby_NewEncodedStringValue(buf, 0);
1114                 free(buf);
1115         } else {
1116                 val = Ruby_NewEncodedStringValue("", 0);
1117         }
1118         rb_last_status_set(exitstatus, pid);
1119         return val;
1120 }
1121
1122 #pragma mark ====== User defaults ======
1123
1124 /*
1125  *  call-seq:
1126  *     get_global_settings(key)
1127  *
1128  *  Get a setting data for key from the application preferences.
1129  */
1130 static VALUE
1131 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1132 {
1133         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1134         if (p != NULL) {
1135                 VALUE retval = rb_eval_string(p);
1136                 free(p);
1137                 return retval;
1138         } else return Qnil;
1139 }
1140
1141 /*
1142  *  call-seq:
1143  *     set_global_settings(key, value)
1144  *
1145  *  Set a setting data for key to the application preferences.
1146  */
1147 static VALUE
1148 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1149 {
1150         VALUE sval = rb_inspect(value);
1151         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1152         return value;
1153 }
1154
1155 #pragma mark ====== IO extension ======
1156
1157 static VALUE
1158 s_Ruby_str_encode_protected(VALUE val)
1159 {
1160         return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1161                                   ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1162 }
1163
1164 /*
1165  *  call-seq:
1166  *     gets_any_eol
1167  *
1168  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1169  */
1170 static VALUE
1171 s_IO_gets_any_eol(VALUE self)
1172 {
1173         VALUE val, val2, cval;
1174         char buf[1024];
1175         int i, c, status;
1176         static ID id_getbyte = 0, id_ungetbyte;
1177         if (id_getbyte == 0) {
1178                 id_getbyte = rb_intern("getbyte");
1179                 id_ungetbyte = rb_intern("ungetbyte");
1180         }
1181         i = 0;
1182         val = Qnil;
1183         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1184                 c = NUM2INT(rb_Integer(cval));
1185                 if (c == 0x0d) {
1186                         cval = rb_funcall(self, id_getbyte, 0);
1187                         if (cval != Qnil) {
1188                                 c = NUM2INT(rb_Integer(cval));
1189                                 if (c != 0x0a)
1190                                         rb_funcall(self, id_ungetbyte, 1, cval);
1191                         }
1192                         break;
1193                 } else if (c != 0x0a) {
1194                         buf[i++] = c;
1195                         if (i >= 1020) {
1196                                 buf[i] = 0;
1197                                 if (val == Qnil)
1198                                         val = rb_str_new(buf, i);
1199                                 else
1200                                         rb_str_append(val, rb_str_new(buf, i));
1201                                 i = 0;
1202                         }
1203                 } else break;
1204         }
1205         if (cval == Qnil && i == 0 && val == Qnil)
1206                 return Qnil;  /*  End of file  */
1207         buf[i] = 0;
1208         if (val == Qnil)
1209                 val = rb_str_new(buf, i);
1210         else if (i > 0)
1211                 rb_str_append(val, rb_str_new(buf, i));
1212         val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /*  Ignore exception  */
1213         if (status == 0)
1214                 val = val2;
1215         if (cval != Qnil) {
1216                 /*  Needs a end-of-line mark  */
1217                 cval = rb_gv_get("$/");
1218                 rb_str_append(val, cval);
1219         }
1220         rb_gv_set("$_", val);
1221         return val;
1222 }
1223
1224 #pragma mark ====== Utility functions (protected funcall) ======
1225
1226 struct Ruby_funcall2_record {
1227         VALUE recv;
1228         ID mid;
1229         int argc;
1230         VALUE *argv;
1231 };
1232
1233 static VALUE
1234 s_Ruby_funcall2_sub(VALUE data)
1235 {
1236         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1237         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1238 }
1239
1240 VALUE
1241 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1242 {
1243         struct Ruby_funcall2_record rec;
1244         rec.recv = recv;
1245         rec.mid = mid;
1246         rec.argc = argc;
1247         rec.argv = argv;
1248         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1249 }
1250
1251 RubyValue
1252 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1253 {
1254         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1255 }
1256
1257 #pragma mark ====== ParameterRef Class ======
1258
1259 static UnionPar *
1260 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1261 {
1262         UnionPar *up;
1263         ParameterRef *pref;
1264         Data_Get_Struct(self, ParameterRef, pref);
1265         if (typep != NULL)
1266                 *typep = pref->parType;
1267         if (pref->parType == kElementParType) {
1268                 up = (UnionPar *)&gElementParameters[pref->idx];
1269         } else {
1270                 up = ParameterRefGetPar(pref);
1271                 if (checkEditable) {
1272                         if (pref->idx < 0)
1273                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1274                         if (up->bond.src != 0 && up->bond.src != -1)
1275                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1276                 }
1277         }
1278         return up;
1279 }
1280
1281 static void
1282 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1283 {
1284         UnionPar *up;
1285         ParameterRef *pref;
1286         Data_Get_Struct(self, ParameterRef, pref);
1287         if (pref->mol == NULL)
1288                 return;
1289         up = ParameterRefGetPar(pref);
1290         if (key != s_SourceSym)
1291                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1292         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1293                 /*  Register undo  */
1294                 MolAction *act;
1295                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1296                 MolActionCallback_registerUndo(pref->mol, act);
1297                 MoleculeCallback_notifyModification(pref->mol, 0);
1298                 pref->mol->needsMDRebuild = 1;
1299         }
1300 }
1301
1302 VALUE
1303 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1304 {
1305         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1306         if (pref != NULL)
1307                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1308         else
1309                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1310 }
1311
1312 static int
1313 s_AtomTypeIndexFromValue(VALUE val)
1314 {
1315         if (rb_obj_is_kind_of(val, rb_cNumeric))
1316                 return NUM2INT(val);
1317         else
1318                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1319 }
1320
1321 static const char *s_ParameterTypeNames[] = {
1322         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1323 };
1324 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1325
1326 static int
1327 s_ParTypeFromValue(VALUE val)
1328 {
1329         int i, n;
1330         ID valid;
1331         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1332         if (s_ParameterTypeIDs[0] == 0) {
1333                 for (i = 0; i < n; i++)
1334                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1335         }
1336         valid = rb_to_id(val);
1337         for (i = 0; i < n; i++) {
1338                 if (valid == s_ParameterTypeIDs[i]) {
1339                         if (i == 7)
1340                                 return kElementParType;
1341                         else return kFirstParType + i;
1342                 }
1343         }
1344         return kInvalidParType;
1345 }
1346
1347 /*
1348  *  call-seq:
1349  *     index -> Integer
1350  *
1351  *  Get the index in the parameter list.
1352  */
1353 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1354         ParameterRef *pref;
1355         Data_Get_Struct(self, ParameterRef, pref);
1356         return INT2NUM(pref->idx);
1357 }
1358
1359 /*
1360  *  call-seq:
1361  *     par_type -> String
1362  *
1363  *  Get the parameter type, like "bond", "angle", etc.
1364  */
1365 static VALUE s_ParameterRef_GetParType(VALUE self) {
1366         Int tp;
1367         s_UnionParFromValue(self, &tp, 0);
1368         if (tp == kElementParType)
1369                 return Ruby_NewEncodedStringValue2("element");
1370         tp -= kFirstParType;
1371         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1372                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1373         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1374 }
1375
1376 /*
1377  *  call-seq:
1378  *     atom_type -> String or Array of String
1379  *     atom_types -> String or Array of String
1380  *
1381  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1382  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1383  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1384  *  The atom type may be "X", which is a wildcard that matches any atom type.
1385  */
1386 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1387         UnionPar *up;
1388         Int tp, i, n;
1389         UInt types[4];
1390         VALUE vals[4];
1391         up = s_UnionParFromValue(self, &tp, 0);
1392         n = ParameterGetAtomTypes(tp, up, types);
1393         if (n == 0)
1394                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1395         for (i = 0; i < n; i++) {
1396                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1397                         vals[i] = INT2NUM(types[i]);
1398                 else
1399                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1400         }
1401         if (n == 1)
1402                 return vals[0];
1403         else
1404                 return rb_ary_new4(n, vals);
1405 }
1406
1407 /*
1408  *  call-seq:
1409  *     k -> Float
1410  *
1411  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1412  */
1413 static VALUE s_ParameterRef_GetK(VALUE self) {
1414         UnionPar *up;
1415         Int tp, i, n;
1416         VALUE vals[3];
1417         up = s_UnionParFromValue(self, &tp, 0);
1418         switch (tp) {
1419                 case kBondParType:
1420                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1421                 case kAngleParType:
1422                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1423                 case kDihedralParType:
1424                 case kImproperParType:
1425                         if (up->torsion.mult == 1)
1426                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1427                         n = up->torsion.mult;
1428                         if (n > 3)
1429                                 n = 3;
1430                         for (i = 0; i < n; i++)
1431                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1432                         return rb_ary_new4(n, vals);
1433                 default:
1434                         rb_raise(rb_eMolbyError, "invalid member k");
1435         }
1436 }
1437
1438 /*
1439  *  call-seq:
1440  *     r0 -> Float
1441  *
1442  *  Get the equilibrium bond length. Only available for bond parameters.
1443  */
1444 static VALUE s_ParameterRef_GetR0(VALUE self) {
1445         UnionPar *up;
1446         Int tp;
1447         up = s_UnionParFromValue(self, &tp, 0);
1448         if (tp == kBondParType)
1449                 return rb_float_new(up->bond.r0);
1450         else rb_raise(rb_eMolbyError, "invalid member r0");
1451 }
1452
1453 /*
1454  *  call-seq:
1455  *     a0 -> Float
1456  *
1457  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1458  */
1459 static VALUE s_ParameterRef_GetA0(VALUE self) {
1460         UnionPar *up;
1461         Int tp;
1462         up = s_UnionParFromValue(self, &tp, 0);
1463         if (tp == kAngleParType)
1464                 return rb_float_new(up->angle.a0 * kRad2Deg);
1465         else rb_raise(rb_eMolbyError, "invalid member a0");
1466 }
1467
1468 /*
1469  *  call-seq:
1470  *     mult -> Float
1471  *
1472  *  Get the multiplicity. Only available for dihedral and improper parameters.
1473  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1474  */
1475 static VALUE s_ParameterRef_GetMult(VALUE self) {
1476         UnionPar *up;
1477         Int tp;
1478         up = s_UnionParFromValue(self, &tp, 0);
1479         if (tp == kDihedralParType || tp == kImproperParType)
1480                 return rb_float_new(up->torsion.mult);
1481         else rb_raise(rb_eMolbyError, "invalid member mult");
1482 }
1483
1484 /*
1485  *  call-seq:
1486  *     period -> Integer or Array of Integers
1487  *
1488  *  Get the periodicity. Only available for dihedral and improper parameters.
1489  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1490  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1491  */
1492 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1493         UnionPar *up;
1494         Int tp, i, n;
1495         VALUE vals[3];
1496         up = s_UnionParFromValue(self, &tp, 0);
1497         if (tp == kDihedralParType || tp == kImproperParType) {
1498                 if (up->torsion.mult == 1)
1499                         return INT2NUM(up->torsion.period[0]);
1500                 n = up->torsion.mult;
1501                 if (n > 3)
1502                         n = 3;
1503                 for (i = 0; i < n; i++)
1504                         vals[i] = INT2NUM(up->torsion.period[i]);
1505                 return rb_ary_new4(n, vals);
1506         } else rb_raise(rb_eMolbyError, "invalid member period");
1507 }
1508
1509 /*
1510  *  call-seq:
1511  *     phi0 -> Float or Array of Floats
1512  *
1513  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1514  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1515  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1516  */
1517 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1518         UnionPar *up;
1519         Int tp, i, n;
1520         VALUE vals[3];
1521         up = s_UnionParFromValue(self, &tp, 0);
1522         if (tp == kDihedralParType || tp == kImproperParType) {
1523                 if (up->torsion.mult == 1)
1524                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1525                 n = up->torsion.mult;
1526                 if (n > 3)
1527                         n = 3;
1528                 for (i = 0; i < n; i++)
1529                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1530                 return rb_ary_new4(n, vals);
1531         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1532 }
1533
1534 /*
1535  *  call-seq:
1536  *     A -> Float
1537  *
1538  *  Get the "A" value for the van der Waals parameter.
1539  */
1540 /*
1541  static VALUE s_ParameterRef_GetA(VALUE self) {
1542         UnionPar *up;
1543         Int tp;
1544         up = s_UnionParFromValue(self, &tp, 0);
1545         if (tp == kVdwParType)
1546                 return rb_float_new(up->vdw.A);
1547         else if (tp == kVdwPairParType)
1548                 return rb_float_new(up->vdwp.A);
1549         else rb_raise(rb_eMolbyError, "invalid member A");
1550 }
1551 */
1552
1553 /*
1554  *  call-seq:
1555  *     B -> Float
1556  *
1557  *  Get the "B" value for the van der Waals parameter.
1558  */
1559 /*
1560 static VALUE s_ParameterRef_GetB(VALUE self) {
1561         UnionPar *up;
1562         Int tp;
1563         up = s_UnionParFromValue(self, &tp, 0);
1564         if (tp == kVdwParType)
1565                 return rb_float_new(up->vdw.B);
1566         else if (tp == kVdwPairParType)
1567                 return rb_float_new(up->vdwp.B);
1568         else rb_raise(rb_eMolbyError, "invalid member B");
1569 }
1570 */
1571
1572 /*
1573  *  call-seq:
1574  *     r_eq -> Float
1575  *
1576  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1577  */
1578 static VALUE s_ParameterRef_GetReq(VALUE self) {
1579         UnionPar *up;
1580         Int tp;
1581 /*      Double a, b, r; */
1582         Double r;
1583         up = s_UnionParFromValue(self, &tp, 0);
1584         if (tp == kVdwParType) {
1585         /*      a = up->vdw.A;
1586                 b = up->vdw.B;  */
1587                 r = up->vdw.r_eq;
1588         } else if (tp == kVdwPairParType) {
1589         /*      a = up->vdwp.A;
1590                 b = up->vdwp.B;  */
1591                 r = up->vdwp.r_eq;
1592         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1593 /*      if (a == 0.0 || b == 0.0) */
1594         return rb_float_new(r);
1595 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1596 }
1597
1598 /*
1599  *  call-seq:
1600  *     eps -> Float
1601  *
1602  *  Get the minimum energy for the van der Waals parameter.
1603  */
1604 static VALUE s_ParameterRef_GetEps(VALUE self) {
1605         UnionPar *up;
1606         Int tp;
1607 /*      Double a, b; */
1608         Double eps;
1609         up = s_UnionParFromValue(self, &tp, 0);
1610         if (tp == kVdwParType) {
1611         /*      a = up->vdw.A;
1612                 b = up->vdw.B;  */
1613                 eps = up->vdw.eps;
1614         } else if (tp == kVdwPairParType) {
1615         /*      a = up->vdwp.A;
1616                 b = up->vdwp.B; */
1617                 eps = up->vdwp.eps;
1618         } else rb_raise(rb_eMolbyError, "invalid member eps");
1619 /*      if (a == 0.0 || b == 0.0)  */
1620                 return rb_float_new(eps * INTERNAL2KCAL);
1621 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1622 }
1623
1624 /*
1625  *  call-seq:
1626  *     A14 -> Float
1627  *
1628  *  Get the "A" value for the 1-4 van der Waals parameter.
1629  */
1630 /*
1631 static VALUE s_ParameterRef_GetA14(VALUE self) {
1632         UnionPar *up;
1633         Int tp;
1634         up = s_UnionParFromValue(self, &tp, 0);
1635         if (tp == kVdwParType)
1636                 return rb_float_new(up->vdw.A14);
1637         else if (tp == kVdwPairParType)
1638                 return rb_float_new(up->vdwp.A14);
1639         else rb_raise(rb_eMolbyError, "invalid member A14");
1640 }
1641 */
1642
1643 /*
1644  *  call-seq:
1645  *     B14 -> Float
1646  *
1647  *  Get the "B" value for the 1-4 van der Waals parameter.
1648  */
1649 /*
1650 static VALUE s_ParameterRef_GetB14(VALUE self) {
1651         UnionPar *up;
1652         Int tp;
1653         up = s_UnionParFromValue(self, &tp, 0);
1654         if (tp == kVdwParType)
1655                 return rb_float_new(up->vdw.B14);
1656         else if (tp == kVdwPairParType)
1657                 return rb_float_new(up->vdwp.B14);
1658         else rb_raise(rb_eMolbyError, "invalid member B14");
1659 }
1660 */
1661
1662 /*
1663  *  call-seq:
1664  *     r_eq14 -> Float
1665  *
1666  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1667  */
1668 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1669         UnionPar *up;
1670         Int tp;
1671 /*      Double a, b, r; */
1672         Double r;
1673         up = s_UnionParFromValue(self, &tp, 0);
1674         if (tp == kVdwParType) {
1675         /*      a = up->vdw.A14;
1676                 b = up->vdw.B14; */
1677                 r = up->vdw.r_eq14;
1678         } else if (tp == kVdwPairParType) {
1679         /*      a = up->vdwp.A14;
1680                 b = up->vdwp.B14;  */
1681                 r = up->vdwp.r_eq14;
1682         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1683 /*      if (a == 0.0 || b == 0.0)  */
1684         return rb_float_new(r);
1685 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1686 }
1687
1688 /*
1689  *  call-seq:
1690  *     eps14 -> Float
1691  *
1692  *  Get the minimum energy for the 1-4 van der Waals parameter.
1693  */
1694 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1695         UnionPar *up;
1696         Int tp;
1697 /*      Double a, b;  */
1698         Double eps;
1699         up = s_UnionParFromValue(self, &tp, 0);
1700         if (tp == kVdwParType) {
1701         /*      a = up->vdw.A14;
1702                 b = up->vdw.B14;  */
1703                 eps = up->vdw.eps14;
1704         } else if (tp == kVdwPairParType) {
1705         /*      a = up->vdwp.A14;
1706                 b = up->vdwp.B14; */
1707                 eps = up->vdwp.eps14;
1708         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1709 /*      if (a == 0.0 || b == 0.0) */
1710         return rb_float_new(eps * INTERNAL2KCAL);
1711 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1712 }
1713
1714 /*
1715  *  call-seq:
1716  *     cutoff -> Float
1717  *
1718  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1719  */
1720 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1721         UnionPar *up;
1722         Int tp;
1723         up = s_UnionParFromValue(self, &tp, 0);
1724         if (tp == kVdwCutoffParType)
1725                 return rb_float_new(up->vdwcutoff.cutoff);
1726         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1727 }
1728
1729 /*
1730  *  call-seq:
1731  *     radius -> Float
1732  *
1733  *  Get the atomic (covalent) radius for the element parameter.
1734  */
1735 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1736         UnionPar *up;
1737         Int tp;
1738         up = s_UnionParFromValue(self, &tp, 0);
1739         if (tp == kElementParType)
1740                 return rb_float_new(up->atom.radius);
1741         else rb_raise(rb_eMolbyError, "invalid member radius");
1742 }
1743
1744 /*
1745  *  call-seq:
1746  *     vdw_radius -> Float
1747  *
1748  *  Get the van der Waals radius for the element parameter. (0 if not given)
1749  */
1750 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1751         UnionPar *up;
1752         Int tp;
1753         up = s_UnionParFromValue(self, &tp, 0);
1754         if (tp == kElementParType)
1755                 return rb_float_new(up->atom.vdw_radius);
1756         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1757 }
1758
1759 /*
1760  *  call-seq:
1761  *     color -> [Float, Float, Float]
1762  *
1763  *  Get the rgb color for the element parameter.
1764  */
1765 static VALUE s_ParameterRef_GetColor(VALUE self) {
1766         UnionPar *up;
1767         Int tp;
1768         up = s_UnionParFromValue(self, &tp, 0);
1769         if (tp == kElementParType)
1770                 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));
1771         else rb_raise(rb_eMolbyError, "invalid member color");
1772 }
1773
1774 /*
1775  *  call-seq:
1776  *     atomic_number -> Integer
1777  *
1778  *  Get the atomic number for the vdw or element parameter.
1779  */
1780 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1781         UnionPar *up;
1782         Int tp;
1783         up = s_UnionParFromValue(self, &tp, 0);
1784         if (tp == kElementParType)
1785                 return INT2NUM(up->atom.number);
1786         else if (tp == kVdwParType)
1787                 return INT2NUM(up->vdw.atomicNumber);
1788         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1789 }
1790
1791 /*
1792  *  call-seq:
1793  *     name -> String
1794  *
1795  *  Get the name for the element parameter.
1796  */
1797 static VALUE s_ParameterRef_GetName(VALUE self) {
1798         UnionPar *up;
1799         Int tp;
1800         up = s_UnionParFromValue(self, &tp, 0);
1801         if (tp == kElementParType) {
1802                 char name[5];
1803                 strncpy(name, up->atom.name, 4);
1804                 name[4] = 0;
1805                 return Ruby_NewEncodedStringValue2(name);
1806         } else rb_raise(rb_eMolbyError, "invalid member name");
1807 }
1808
1809 /*
1810  *  call-seq:
1811  *     weight -> Float
1812  *
1813  *  Get the atomic weight for the element parameter.
1814  */
1815 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1816         UnionPar *up;
1817         Int tp;
1818         up = s_UnionParFromValue(self, &tp, 0);
1819         if (tp == kElementParType)
1820                 return rb_float_new(up->atom.weight);
1821         else if (tp == kVdwParType)
1822                 return rb_float_new(up->vdw.weight);
1823         else rb_raise(rb_eMolbyError, "invalid member weight");
1824 }
1825
1826 /*
1827  *  call-seq:
1828  *     fullname -> String
1829  *
1830  *  Get the full name for the element parameter.
1831  */
1832 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1833         UnionPar *up;
1834         Int tp;
1835         up = s_UnionParFromValue(self, &tp, 0);
1836         if (tp == kElementParType) {
1837                 char fullname[16];
1838                 strncpy(fullname, up->atom.fullname, 15);
1839                 fullname[15] = 0;
1840                 return Ruby_NewEncodedStringValue2(fullname);
1841         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1842 }
1843
1844 /*
1845  *  call-seq:
1846  *     comment -> String
1847  *
1848  *  Get the comment for the parameter.
1849  */
1850 static VALUE s_ParameterRef_GetComment(VALUE self) {
1851         UnionPar *up;
1852         Int tp, com;
1853         up = s_UnionParFromValue(self, &tp, 0);
1854         com = up->bond.com;
1855         if (com == 0)
1856                 return Qnil;
1857         else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1858 }
1859
1860 /*
1861  *  call-seq:
1862  *     source -> String
1863  *
1864  *  Get the source string for the parameter. Returns false for undefined parameter,
1865  *  and nil for "local" parameter that is specific for the molecule.
1866  */
1867 static VALUE s_ParameterRef_GetSource(VALUE self) {
1868         UnionPar *up;
1869         Int tp, src;
1870         up = s_UnionParFromValue(self, &tp, 0);
1871         src = up->bond.src;
1872         if (src < 0)
1873                 return Qfalse;  /* undefined */
1874         else if (src == 0)
1875                 return Qnil;  /*  local  */
1876         else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1877 }
1878
1879 static void
1880 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1881 {
1882         VALUE *valp;
1883         int i;
1884         if (n == 1)
1885                 valp = &val;
1886         else {
1887                 if (rb_obj_is_kind_of(val, rb_cString)) {
1888                         char *s = StringValuePtr(val);
1889                         char *p;
1890                         for (i = 0; i < n; i++) {
1891                                 char buf[40];
1892                                 int len;
1893                                 /*  Skip leading separaters  */
1894                                 while (*s == '-' || *s == ' ' || *s == '\t')
1895                                         s++;
1896                                 for (p = s; *p != 0; p++) {
1897                                         if (*p == '-' || *p == ' ' || *p == '\t')
1898                                                 break;
1899                                 }
1900                                 len = p - s;
1901                                 if (len >= sizeof(buf))
1902                                         len = sizeof(buf) - 1;
1903                                 strncpy(buf, s, len);
1904                                 buf[len] = 0;
1905                                 /*  Skip trailing blanks  */
1906                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1907                                         buf[len] = 0;
1908                                 if (buf[0] == 0)
1909                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1910                                 if (buf[0] >= '0' && buf[0] <= '9')
1911                                         types[i] = atoi(buf);
1912                                 else
1913                                         types[i] = AtomTypeEncodeToUInt(buf);
1914                                 if (p == NULL || *p == 0) {
1915                                         i++;
1916                                         break;
1917                                 } else s = p + 1;
1918                         }
1919                         if (i < n)
1920                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1921                         return;
1922                 }
1923                 val = rb_ary_to_ary(val);
1924                 if (RARRAY_LEN(val) != n)
1925                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1926                 valp = RARRAY_PTR(val);
1927         }
1928         for (i = 0; i < n; i++) {
1929                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1930                         types[i] = NUM2INT(rb_Integer(valp[i]));
1931                 else {
1932                         VALUE sval = valp[i];
1933                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1934                 }
1935         }
1936 }
1937
1938 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1939         UnionPar *up;
1940         VALUE oldval;
1941         Int oldsrc, tp;
1942         UInt types[4];
1943         up = s_UnionParFromValue(self, &tp, 1);
1944         oldval = s_ParameterRef_GetAtomTypes(self);
1945         oldsrc = up->bond.src;
1946         switch (tp) {
1947                 case kBondParType:
1948                         s_ScanAtomTypes(val, 2, types);
1949                         up->bond.type1 = types[0];
1950                         up->bond.type2 = types[1];
1951                         break;
1952                 case kAngleParType:
1953                         s_ScanAtomTypes(val, 3, types);
1954                         up->angle.type1 = types[0];
1955                         up->angle.type2 = types[1];
1956                         up->angle.type3 = types[2];
1957                         break;
1958                 case kDihedralParType:
1959                 case kImproperParType:
1960                         s_ScanAtomTypes(val, 4, types);
1961                         up->torsion.type1 = types[0];
1962                         up->torsion.type2 = types[1];
1963                         up->torsion.type3 = types[2];
1964                         up->torsion.type4 = types[3];
1965                         break;
1966                 case kVdwParType:
1967                         s_ScanAtomTypes(val, 1, types);
1968                         up->vdw.type1 = types[0];
1969                         break;
1970                 case kVdwPairParType:
1971                         s_ScanAtomTypes(val, 2, types);
1972                         up->vdwp.type1 = types[0];
1973                         up->vdwp.type2 = types[1];
1974                         break;
1975                 case kVdwCutoffParType:
1976                         s_ScanAtomTypes(val, 2, types);
1977                         up->vdwcutoff.type1 = types[0];
1978                         up->vdwcutoff.type2 = types[1];
1979                         break;
1980                 default:
1981                         return Qnil;
1982         }
1983         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1984         return val;
1985 }
1986
1987 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1988         UnionPar *up;
1989         Int tp, i, n, oldsrc;
1990         VALUE *valp, oldval;
1991         up = s_UnionParFromValue(self, &tp, 1);
1992         oldval = s_ParameterRef_GetK(self);
1993         oldsrc = up->bond.src;
1994         switch (tp) {
1995                 case kBondParType:
1996                         val = rb_Float(val);
1997                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1998                         break;
1999                 case kAngleParType:
2000                         val = rb_Float(val);
2001                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
2002                         break;
2003                 case kDihedralParType:
2004                 case kImproperParType:
2005                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2006                                 up->torsion.mult = 1;
2007                                 val = rb_Float(val);
2008                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
2009                                 break;
2010                         }
2011                         n = up->torsion.mult;
2012                         if (n > 3)
2013                                 n = 3;
2014                         val = rb_ary_to_ary(val);
2015                         if (RARRAY_LEN(val) != n)
2016                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2017                         valp = RARRAY_PTR(val);
2018                         for (i = 0; i < n; i++) {
2019                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
2020                         }
2021                         break;
2022                 default:
2023                         rb_raise(rb_eMolbyError, "invalid member k");
2024         }
2025         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
2026         return val;
2027 }
2028
2029 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
2030         UnionPar *up;
2031         Int tp, oldsrc;
2032         VALUE oldval;
2033         up = s_UnionParFromValue(self, &tp, 1);
2034         oldval = s_ParameterRef_GetR0(self);
2035         oldsrc = up->bond.src;
2036         if (tp == kBondParType) {
2037                 val = rb_Float(val);
2038                 up->bond.r0 = NUM2DBL(val);
2039         } else rb_raise(rb_eMolbyError, "invalid member r0");
2040         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
2041         return val;
2042 }
2043
2044 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
2045         UnionPar *up;
2046         Int tp, oldsrc;
2047         VALUE oldval;
2048         up = s_UnionParFromValue(self, &tp, 1);
2049         oldval = s_ParameterRef_GetA0(self);
2050         oldsrc = up->bond.src;
2051         if (tp == kAngleParType) {
2052                 val = rb_Float(val);
2053                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
2054         } else rb_raise(rb_eMolbyError, "invalid member a0");
2055         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
2056         return val;
2057 }
2058
2059 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
2060         UnionPar *up;
2061         Int tp, oldsrc;
2062         VALUE oldval;
2063         up = s_UnionParFromValue(self, &tp, 1);
2064         oldval = s_ParameterRef_GetMult(self);
2065         oldsrc = up->bond.src;
2066         if (tp == kDihedralParType || tp == kImproperParType) {
2067                 int i;
2068                 val = rb_Integer(val);
2069                 i = NUM2INT(val);
2070                 if (i < 0 || i > 3)
2071                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
2072                 up->torsion.mult = i;
2073         } else rb_raise(rb_eMolbyError, "invalid member mult");
2074         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
2075         return val;
2076 }
2077
2078 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
2079         UnionPar *up;
2080         Int tp, i, n, oldsrc;
2081         VALUE *valp, oldval;
2082         up = s_UnionParFromValue(self, &tp, 1);
2083         oldval = s_ParameterRef_GetPeriod(self);
2084         oldsrc = up->bond.src;
2085         if (tp == kDihedralParType || tp == kImproperParType) {
2086                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2087                         up->torsion.mult = 1;
2088                         val = rb_Integer(val);
2089                         up->torsion.period[0] = NUM2INT(val);
2090                 } else {
2091                         n = up->torsion.mult;
2092                         if (n > 3)
2093                                 n = 3;
2094                         val = rb_ary_to_ary(val);
2095                         if (RARRAY_LEN(val) != n)
2096                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
2097                         valp = RARRAY_PTR(val);
2098                         for (i = 0; i < n; i++) {
2099                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
2100                         }
2101                 }
2102         } else rb_raise(rb_eMolbyError, "invalid member period");
2103         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2104         return val;
2105 }
2106
2107 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2108         UnionPar *up;
2109         Int tp, i, n, oldsrc;
2110         VALUE *valp, oldval;
2111         up = s_UnionParFromValue(self, &tp, 1);
2112         oldval = s_ParameterRef_GetPhi0(self);
2113         oldsrc = up->bond.src;
2114         if (tp == kDihedralParType || tp == kImproperParType) {
2115                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2116                         up->torsion.mult = 1;
2117                         val = rb_Float(val);
2118                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2119                 } else {
2120                         n = up->torsion.mult;
2121                         if (n > 3)
2122                                 n = 3;
2123                         val = rb_ary_to_ary(val);
2124                         if (RARRAY_LEN(val) != n)
2125                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2126                         valp = RARRAY_PTR(val);
2127                         for (i = 0; i < n; i++)
2128                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2129                 }
2130         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2131         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2132         return val;
2133 }
2134
2135 /*
2136 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2137         UnionPar *up;
2138         Int tp, oldsrc;
2139         double d;
2140         VALUE oldval;
2141         up = s_UnionParFromValue(self, &tp, 1);
2142         oldval = s_ParameterRef_GetA(self);
2143         oldsrc = up->bond.src;
2144         val = rb_Float(val);
2145         d = NUM2DBL(val);
2146         if (tp == kVdwParType)
2147                 up->vdw.A = d;
2148         else if (tp == kVdwPairParType)
2149                 up->vdwp.A = d;
2150         else rb_raise(rb_eMolbyError, "invalid member A");
2151         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2152         return val;
2153 }
2154
2155 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2156         UnionPar *up;
2157         Int tp, oldsrc;
2158         double d;
2159         VALUE oldval;
2160         up = s_UnionParFromValue(self, &tp, 1);
2161         oldval = s_ParameterRef_GetB(self);
2162         oldsrc = up->bond.src;
2163         val = rb_Float(val);
2164         d = NUM2DBL(val);
2165         if (tp == kVdwParType)
2166                 up->vdw.B = d;
2167         else if (tp == kVdwPairParType)
2168                 up->vdwp.B = d;
2169         else rb_raise(rb_eMolbyError, "invalid member B");
2170         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2171         return val;
2172 }
2173 */
2174
2175 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2176         UnionPar *up;
2177         Int tp, oldsrc;
2178         Double r;
2179         VALUE oldval;
2180         up = s_UnionParFromValue(self, &tp, 1);
2181         oldval = s_ParameterRef_GetReq(self);
2182         oldsrc = up->bond.src;
2183         val = rb_Float(val);
2184         r = NUM2DBL(val);
2185         if (tp == kVdwParType) {
2186                 up->vdw.r_eq = r;
2187                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2188                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2189         } else if (tp == kVdwPairParType) {
2190                 up->vdwp.r_eq = r;
2191                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2192                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2193         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2194         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2195         return val;
2196 }
2197
2198 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2199         UnionPar *up;
2200         Int tp, oldsrc;
2201         Double e;
2202         VALUE oldval;
2203         up = s_UnionParFromValue(self, &tp, 1);
2204         oldval = s_ParameterRef_GetEps(self);
2205         oldsrc = up->bond.src;
2206         val = rb_Float(val);
2207         e = NUM2DBL(val) * KCAL2INTERNAL;
2208         if (tp == kVdwParType) {
2209                 up->vdw.eps = e;
2210                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2211                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2212         } else if (tp == kVdwPairParType) {
2213                 up->vdwp.eps = e;
2214                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2215                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2216         } else rb_raise(rb_eMolbyError, "invalid member eps");
2217         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2218         return val;
2219 }
2220
2221 /*
2222 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2223         UnionPar *up;
2224         Int tp, oldsrc;
2225         double d;
2226         VALUE oldval;
2227         up = s_UnionParFromValue(self, &tp, 1);
2228         oldval = s_ParameterRef_GetA14(self);
2229         oldsrc = up->bond.src;
2230         val = rb_Float(val);
2231         d = NUM2DBL(val);
2232         if (tp == kVdwParType)
2233                 up->vdw.A14 = d;
2234         else if (tp == kVdwPairParType)
2235                 up->vdwp.A14 = d;
2236         else rb_raise(rb_eMolbyError, "invalid member A14");
2237         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2238         return val;
2239 }
2240
2241 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2242         UnionPar *up;
2243         Int tp, oldsrc;
2244         double d;
2245         VALUE oldval;
2246         up = s_UnionParFromValue(self, &tp, 1);
2247         oldval = s_ParameterRef_GetB14(self);
2248         oldsrc = up->bond.src;
2249         val = rb_Float(val);
2250         d = NUM2DBL(val);
2251         if (tp == kVdwParType)
2252                 up->vdw.B14 = d;
2253         else if (tp == kVdwPairParType)
2254                 up->vdwp.B14 = d;
2255         else rb_raise(rb_eMolbyError, "invalid member B14");
2256         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2257         return val;
2258 }
2259 */
2260
2261 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2262         UnionPar *up;
2263         Int tp, oldsrc;
2264         Double r;
2265         VALUE oldval;
2266         up = s_UnionParFromValue(self, &tp, 1);
2267         oldval = s_ParameterRef_GetReq14(self);
2268         oldsrc = up->bond.src;
2269         val = rb_Float(val);
2270         r = NUM2DBL(val);
2271         if (tp == kVdwParType) {
2272                 up->vdw.r_eq14 = r;
2273                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2274                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2275         } else if (tp == kVdwPairParType) {
2276                 up->vdwp.r_eq14 = r;
2277                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2278                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2279         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2280         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2281         return val;
2282 }
2283
2284 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2285         UnionPar *up;
2286         Int tp, oldsrc;
2287         Double e;
2288         VALUE oldval;
2289         up = s_UnionParFromValue(self, &tp, 1);
2290         oldval = s_ParameterRef_GetEps14(self);
2291         oldsrc = up->bond.src;
2292         val = rb_Float(val);
2293         e = NUM2DBL(val) * KCAL2INTERNAL;
2294         if (tp == kVdwParType) {
2295                 up->vdw.eps14 = e;
2296                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2297                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2298         } else if (tp == kVdwPairParType) {
2299                 up->vdwp.eps14 = e;
2300                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2301                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2302         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2303         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2304         return val;
2305 }
2306
2307 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2308         UnionPar *up;
2309         Int tp, oldsrc;
2310         VALUE oldval;
2311         oldval = s_ParameterRef_GetCutoff(self);
2312         oldsrc = up->bond.src;
2313         up = s_UnionParFromValue(self, &tp, 1);
2314         val = rb_Float(val);
2315         if (tp == kVdwCutoffParType) {
2316                 up->vdwcutoff.cutoff = NUM2DBL(val);
2317         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2318         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2319         return val;
2320 }
2321
2322 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2323         UnionPar *up;
2324         Int tp, oldsrc;
2325         VALUE oldval;
2326         up = s_UnionParFromValue(self, &tp, 1);
2327         oldval = s_ParameterRef_GetRadius(self);
2328         oldsrc = up->bond.src;
2329         val = rb_Float(val);
2330         if (tp == kElementParType) {
2331                 up->atom.radius = NUM2DBL(val);
2332         } else rb_raise(rb_eMolbyError, "invalid member radius");
2333         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2334         return val;
2335 }
2336
2337 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2338         UnionPar *up;
2339         Int tp, oldsrc;
2340         VALUE oldval;
2341         up = s_UnionParFromValue(self, &tp, 1);
2342         oldval = s_ParameterRef_GetVdwRadius(self);
2343         oldsrc = up->bond.src;
2344         val = rb_Float(val);
2345         if (tp == kElementParType) {
2346                 up->atom.vdw_radius = NUM2DBL(val);
2347         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2348         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2349         return val;
2350 }
2351
2352 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2353         UnionPar *up;
2354         Int tp, oldsrc;
2355         VALUE *valp, oldval;
2356         up = s_UnionParFromValue(self, &tp, 1);
2357         oldval = s_ParameterRef_GetColor(self);
2358         oldsrc = up->bond.src;
2359         val = rb_ary_to_ary(val);
2360         if (RARRAY_LEN(val) != 3)
2361                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2362         valp = RARRAY_PTR(val);
2363         if (tp == kElementParType) {
2364                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2365                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2366                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2367         } else rb_raise(rb_eMolbyError, "invalid member color");
2368         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2369         return val;
2370 }
2371
2372 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2373         UnionPar *up;
2374         Int tp, oldsrc;
2375         VALUE oldval;
2376         up = s_UnionParFromValue(self, &tp, 1);
2377         oldval = s_ParameterRef_GetAtomicNumber(self);
2378         oldsrc = up->bond.src;
2379         val = rb_Integer(val);
2380         if (tp == kElementParType)
2381                 up->atom.number = NUM2INT(val);
2382         else if (tp == kVdwParType) {
2383                 up->vdw.atomicNumber = NUM2INT(val);
2384                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2385         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2386         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2387         return val;
2388 }
2389
2390 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2391         UnionPar *up;
2392         Int tp, oldsrc;
2393         VALUE oldval;
2394         up = s_UnionParFromValue(self, &tp, 1);
2395         oldval = s_ParameterRef_GetName(self);
2396         oldsrc = up->bond.src;
2397         if (tp == kElementParType) {
2398                 strncpy(up->atom.name, StringValuePtr(val), 4);
2399         } else rb_raise(rb_eMolbyError, "invalid member name");
2400         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2401         return val;
2402 }
2403
2404 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2405         UnionPar *up;
2406         Int tp, oldsrc;
2407         VALUE oldval;
2408         val = rb_Float(val);
2409         oldval = s_ParameterRef_GetWeight(self);
2410         up = s_UnionParFromValue(self, &tp, 1);
2411         oldsrc = up->bond.src;
2412         if (tp == kElementParType)
2413                 up->atom.weight = NUM2DBL(val);
2414         else if (tp == kVdwParType)
2415                 up->vdw.weight = NUM2DBL(val);
2416         else rb_raise(rb_eMolbyError, "invalid member weight");
2417         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2418         return val;
2419 }
2420
2421 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2422         UnionPar *up;
2423         Int tp, oldsrc;
2424         VALUE oldval;
2425         up = s_UnionParFromValue(self, &tp, 1);
2426         oldval = s_ParameterRef_GetFullName(self);
2427         oldsrc = up->bond.src;
2428         if (tp == kElementParType) {
2429                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2430                 up->atom.fullname[15] = 0;
2431         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2432         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2433         return val;
2434 }
2435
2436 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2437         UnionPar *up;
2438         Int tp, com, oldsrc;
2439         VALUE oldval;
2440         up = s_UnionParFromValue(self, &tp, 1);
2441         oldval = s_ParameterRef_GetComment(self);
2442         oldsrc = up->bond.src;
2443         if (val == Qnil)
2444                 up->bond.com = 0;
2445         else {
2446                 com = ParameterCommentIndex(StringValuePtr(val));
2447                 up->bond.com = com;
2448         }
2449         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2450         return val;     
2451 }
2452
2453 /*  Only false (undefined) and nil (local) can be set  */
2454 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2455         UnionPar *up;
2456         Int tp, oldsrc;
2457         VALUE oldval;
2458         up = s_UnionParFromValue(self, &tp, 1);
2459         if (val != Qfalse && val != Qnil)
2460                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2461         oldval = s_ParameterRef_GetSource(self);
2462         oldsrc = up->bond.src;
2463         if (oldsrc != 0 && oldsrc != -1)
2464                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2465         up->bond.src = (val == Qfalse ? -1 : 0);
2466         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2467         return val;     
2468 }
2469
2470 static struct s_ParameterAttrDef {
2471         char *name;
2472         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2473         ID id;                  /*  Will be set within InitMolby()  */
2474         VALUE (*getter)(VALUE);
2475         VALUE (*setter)(VALUE, VALUE);
2476 } s_ParameterAttrDefTable[] = {
2477         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2478         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2479         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2480         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2481         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2482         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2483         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2484         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2485         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2486         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2487 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2488         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2489         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2490         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2491 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2492         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2493         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2494         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2495         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2496         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2497         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2498         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2499         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2500         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2501         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2502         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2503         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2504         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2505         {NULL} /* Sentinel */
2506 };
2507
2508 static VALUE
2509 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2510 {
2511         int i;
2512         ID kid;
2513         if (TYPE(key) != T_SYMBOL) {
2514                 kid = rb_intern(StringValuePtr(key));
2515                 key = ID2SYM(kid);
2516         } else kid = SYM2ID(key);
2517         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2518                 if (s_ParameterAttrDefTable[i].id == kid) {
2519                         if (value == Qundef)
2520                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2521                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2522                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2523                         else
2524                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2525                 }
2526         }
2527         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2528         return Qnil; /* not reached */
2529 }
2530
2531 static VALUE
2532 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2533 {
2534         return s_ParameterRef_SetAttr(self, key, Qundef);
2535 }
2536
2537 /*
2538  *  call-seq:
2539  *     keys(idx)          -> array of valid parameter attributes
2540  *  
2541  *  Returns an array of valid parameter attributes (as Symbols).
2542  */
2543 static VALUE
2544 s_ParameterRef_Keys(VALUE self)
2545 {
2546         ParameterRef *pref;
2547         Data_Get_Struct(self, ParameterRef, pref);
2548         switch (pref->parType) {
2549                 case kBondParType:
2550                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2551                 case kAngleParType:
2552                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2553                 case kDihedralParType:
2554                 case kImproperParType:
2555                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2556                 case kVdwParType:
2557                         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);
2558                 case kVdwPairParType:
2559                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2560                 case kVdwCutoffParType:
2561                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2562                 case kElementParType:
2563                         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);
2564                 default:
2565                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2566         }
2567         return Qnil;  /*  Not reached  */
2568 }
2569
2570 /*
2571  *  call-seq:
2572  *     to_hash(idx)          -> Hash
2573  *  
2574  *  Returns a hash containing valid parameter names and values
2575  */
2576 static VALUE
2577 s_ParameterRef_ToHash(VALUE self)
2578 {
2579         VALUE keys = s_ParameterRef_Keys(self);
2580         VALUE retval;
2581         int i;
2582         if (keys == Qnil)
2583                 return Qnil;
2584         retval = rb_hash_new();
2585         for (i = 0; i < RARRAY_LEN(keys); i++) {
2586                 VALUE key = RARRAY_PTR(keys)[i];
2587                 VALUE val = s_ParameterRef_GetAttr(self, key);
2588                 rb_hash_aset(retval, key, val);
2589         }
2590         return retval;
2591 }
2592
2593 /*
2594  *  call-seq:
2595  *     parameter.to_s(idx)          -> String
2596  *  
2597  *  Returns a string representation of the given parameter
2598  */
2599 static VALUE
2600 s_ParameterRef_ToString(VALUE self)
2601 {
2602         Int tp, i, n;
2603         char buf[1024], types[4][8];
2604         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2605         switch (tp) {
2606                 case kBondParType:
2607                         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);
2608                         break;
2609                 case kAngleParType:
2610                         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);
2611                         break;
2612                 case kDihedralParType:
2613                 case kImproperParType:
2614                         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]));
2615                         n = strlen(buf);
2616                         for (i = 0; i < up->torsion.mult; i++) {
2617                                 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);
2618                                 n = strlen(buf);
2619                         }
2620                         break;
2621                 case kVdwParType:
2622                         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);
2623                         break;
2624                 case kVdwPairParType:
2625                         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);
2626                         break;
2627                 case kVdwCutoffParType:
2628                         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);
2629                         break;
2630                 case kElementParType:
2631                         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);
2632                         break;
2633         }
2634         return Ruby_NewEncodedStringValue2(buf);
2635 }
2636
2637 /*
2638  *  call-seq:
2639  *     self == parameterRef -> boolean
2640  *  
2641  *  True if the parameters point to the same parameter record.
2642  */
2643 static VALUE
2644 s_ParameterRef_Equal(VALUE self, VALUE val)
2645 {
2646         Int tp1, tp2;
2647         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2648                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2649         } else return Qfalse;
2650 }
2651         
2652 #pragma mark ====== Parameter Class ======
2653
2654 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2655  *  is NULL, then the global parameters are looked for.  */
2656
2657 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2658 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2659 static void
2660 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2661 {
2662         Molecule *mol;
2663         Data_Get_Struct(val, Molecule, mol);
2664         if (mol == NULL)
2665                 rb_raise(rb_eMolbyError, "the molecule is empty");
2666         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2667                 /*  Do self.md_arena.prepare  */
2668                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2669                 if (val2 != Qnil)
2670                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2671         }
2672 }
2673
2674 static VALUE
2675 s_NewParameterValueFromValue(VALUE val)
2676 {
2677         Molecule *mol;
2678         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2679                 Data_Get_Struct(val, Molecule, mol);
2680                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2681                 MoleculeRetain(mol);
2682                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2683         } else {
2684                 mol = NULL;
2685                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2686         }
2687 }
2688
2689 static Molecule *
2690 s_MoleculeFromParameterValue(VALUE val)
2691 {
2692         Molecule *mol;
2693         Data_Get_Struct(val, Molecule, mol);
2694         return mol;
2695 }
2696
2697 static Parameter *
2698 s_ParameterFromParameterValue(VALUE val)
2699 {
2700         Molecule *mol;
2701         Data_Get_Struct(val, Molecule, mol);
2702         if (mol != NULL)
2703                 return mol->par;
2704         return gBuiltinParameters;
2705 }
2706
2707 /*  Forward declarations  */
2708 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2709 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2710
2711 static Molecule *
2712 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2713 {
2714         if (val == rb_cParameter) {
2715                 return NULL;  /*  Parameter class method: builtin parameters  */
2716         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2717                 return s_MoleculeFromParameterValue(val);
2718         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2719                 return s_MoleculeFromParEnumerableValue(val);
2720         } else return NULL;
2721 }
2722
2723 /*
2724  *  call-seq:
2725  *     builtin    -> Parameter
2726  *  
2727  *  Returns a parameter value that points to the global (builtin) parameters.
2728  *  Equivalent to Parameter::Builtin (constant).
2729  */
2730 static VALUE
2731 s_Parameter_Builtin(VALUE self)
2732 {
2733         static ID s_builtin_id = 0;
2734         if (s_builtin_id == 0)
2735                 s_builtin_id = rb_intern("Builtin");
2736         return rb_const_get(rb_cParameter, s_builtin_id);
2737 }
2738
2739 /*
2740  *  call-seq:
2741  *     bond(idx)          -> ParameterRef
2742  *  
2743  *  The index-th bond parameter record is returned.
2744  */
2745 static VALUE
2746 s_Parameter_Bond(VALUE self, VALUE ival)
2747 {
2748         Molecule *mol;
2749         int idx, n;
2750         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2751         idx = NUM2INT(rb_Integer(ival));
2752         if (mol == NULL)
2753                 n = gBuiltinParameters->nbondPars;
2754         else if (mol->par != NULL)
2755                 n = mol->par->nbondPars;
2756         else n = 0;
2757         if (idx < -n || idx >= n)
2758                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2759         if (idx < 0)
2760                 idx += n;
2761         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2762 }
2763
2764 /*
2765  *  call-seq:
2766  *     angle(idx)          -> ParameterRef
2767  *  
2768  *  The index-th angle parameter record is returned.
2769  */
2770 static VALUE
2771 s_Parameter_Angle(VALUE self, VALUE ival)
2772 {
2773         Molecule *mol;
2774         int idx, n;
2775         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2776         idx = NUM2INT(rb_Integer(ival));
2777         if (mol == NULL)
2778                 n = gBuiltinParameters->nanglePars;
2779         else if (mol->par != NULL)
2780                 n = mol->par->nanglePars;
2781         else n = 0;
2782         if (idx < -n || idx >= n)
2783                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2784         if (idx < 0)
2785                 idx += n;
2786         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2787 }
2788
2789 /*
2790  *  call-seq:
2791  *     dihedral(idx)          -> ParameterRef
2792  *  
2793  *  The index-th dihedral parameter record is returned.
2794  */
2795 static VALUE
2796 s_Parameter_Dihedral(VALUE self, VALUE ival)
2797 {
2798         Molecule *mol;
2799         int idx, n;
2800         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2801         idx = NUM2INT(rb_Integer(ival));
2802         if (mol == NULL)
2803                 n = gBuiltinParameters->ndihedralPars;
2804         else if (mol->par != NULL)
2805                 n = mol->par->ndihedralPars;
2806         else n = 0;
2807         if (idx < -n || idx >= n)
2808                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2809         if (idx < 0)
2810                 idx += n;
2811         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2812 }
2813
2814 /*
2815  *  call-seq:
2816  *     improper(idx)          -> ParameterRef
2817  *  
2818  *  The index-th improper parameter record is returned.
2819  */
2820 static VALUE
2821 s_Parameter_Improper(VALUE self, VALUE ival)
2822 {
2823         Molecule *mol;
2824         int idx, n;
2825         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2826         idx = NUM2INT(rb_Integer(ival));
2827         if (mol == NULL)
2828                 n = gBuiltinParameters->nimproperPars;
2829         else if (mol->par != NULL)
2830                 n = mol->par->nimproperPars;
2831         else n = 0;
2832         if (idx < -n || idx >= n)
2833                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2834         if (idx < 0)
2835                 idx += n;
2836         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2837 }
2838
2839 /*
2840  *  call-seq:
2841  *     vdw(idx)          -> ParameterRef
2842  *  
2843  *  The index-th vdw parameter record is returned.
2844  */
2845 static VALUE
2846 s_Parameter_Vdw(VALUE self, VALUE ival)
2847 {
2848         Molecule *mol;
2849         int idx, n;
2850         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2851         idx = NUM2INT(rb_Integer(ival));
2852         if (mol == NULL)
2853                 n = gBuiltinParameters->nvdwPars;
2854         else if (mol->par != NULL)
2855                 n = mol->par->nvdwPars;
2856         else n = 0;
2857         if (idx < -n || idx >= n)
2858                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2859         if (idx < 0)
2860                 idx += n;
2861         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2862 }
2863
2864 /*
2865  *  call-seq:
2866  *     vdw_pair(idx)          -> ParameterRef
2867  *  
2868  *  The index-th vdw pair parameter record is returned.
2869  */
2870 static VALUE
2871 s_Parameter_VdwPair(VALUE self, VALUE ival)
2872 {
2873         Molecule *mol;
2874         int idx, n;
2875         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2876         idx = NUM2INT(rb_Integer(ival));
2877         if (mol == NULL)
2878                 n = gBuiltinParameters->nvdwpPars;
2879         else if (mol->par != NULL)
2880                 n = mol->par->nvdwpPars;
2881         else n = 0;
2882         if (idx < -n || idx >= n)
2883                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2884         if (idx < 0)
2885                 idx += n;
2886         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2887 }
2888
2889 /*
2890  *  call-seq:
2891  *     vdw_cutoff(idx)          -> ParameterRef
2892  *  
2893  *  The index-th vdw cutoff parameter record is returned.
2894  */
2895 static VALUE
2896 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2897 {
2898         Molecule *mol;
2899         int idx, n;
2900         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2901         idx = NUM2INT(rb_Integer(ival));
2902         if (mol == NULL)
2903                 n = gBuiltinParameters->nvdwCutoffPars;
2904         else if (mol->par != NULL)
2905                 n = mol->par->nvdwCutoffPars;
2906         else n = 0;
2907         if (idx < -n || idx >= n)
2908                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2909         if (idx < 0)
2910                 idx += n;
2911         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2912 }
2913
2914 /*
2915  *  call-seq:
2916  *     element(idx)            -> ParameterRef
2917  *     element(t1)             -> ParameterRef
2918  *  
2919  *  In the first form, the index-th element parameter record is returned. In the second
2920  *  form, the element parameter for t1 is looked up (the last index first). t1
2921  *  is the element name string (up to 4 characters).
2922  *  Unlike other Parameter methods, this is used only for the global parameter.
2923  */
2924 static VALUE
2925 s_Parameter_Element(VALUE self, VALUE ival)
2926 {
2927         Int idx1;
2928         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2929                 int n = gCountElementParameters;
2930                 idx1 = NUM2INT(rb_Integer(ival));
2931                 if (idx1 < -n || idx1 >= n)
2932                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2933                 if (idx1 < 0)
2934                         idx1 += n;
2935                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2936         } else {
2937                 ElementPar *ep;
2938                 char name[6];
2939                 int i;
2940                 strncpy(name, StringValuePtr(ival), 4);
2941                 name[4] = 0;
2942                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2943                         if (strncmp(ep->name, name, 4) == 0)
2944                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2945                 }
2946                 return Qnil;
2947         }
2948 }
2949
2950 /*
2951  *  call-seq:
2952  *     nbonds          -> Integer
2953  *  
2954  *  Returns the number of bond parameters.
2955  */
2956 static VALUE
2957 s_Parameter_Nbonds(VALUE self)
2958 {
2959         Int n;
2960         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2961         if (mol == NULL)
2962                 n = gBuiltinParameters->nbondPars;
2963         else if (mol->par != NULL)
2964                 n = mol->par->nbondPars;
2965         else n = 0;
2966         return INT2NUM(n);
2967 }
2968
2969 /*
2970  *  call-seq:
2971  *     nangles          -> Integer
2972  *  
2973  *  Returns the number of angle parameters.
2974  */
2975 static VALUE
2976 s_Parameter_Nangles(VALUE self)
2977 {
2978         Int n;
2979         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2980         if (mol == NULL)
2981                 n = gBuiltinParameters->nanglePars;
2982         else if (mol->par != NULL)
2983                 n = mol->par->nanglePars;
2984         else n = 0;
2985         return INT2NUM(n);
2986 }
2987
2988 /*
2989  *  call-seq:
2990  *     ndihedrals          -> Integer
2991  *  
2992  *  Returns the number of dihedral parameters.
2993  */
2994 static VALUE
2995 s_Parameter_Ndihedrals(VALUE self)
2996 {
2997         Int n;
2998         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2999         if (mol == NULL)
3000                 n = gBuiltinParameters->ndihedralPars;
3001         else if (mol->par != NULL)
3002                 n = mol->par->ndihedralPars;
3003         else n = 0;
3004         return INT2NUM(n);
3005 }
3006
3007 /*
3008  *  call-seq:
3009  *     nimpropers          -> Integer
3010  *  
3011  *  Returns the number of improper parameters.
3012  */
3013 static VALUE
3014 s_Parameter_Nimpropers(VALUE self)
3015 {
3016         Int n;
3017         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3018         if (mol == NULL)
3019                 n = gBuiltinParameters->nimproperPars;
3020         else if (mol->par != NULL)
3021                 n = mol->par->nimproperPars;
3022         else n = 0;
3023         return INT2NUM(n);
3024 }
3025
3026 /*
3027  *  call-seq:
3028  *     nvdws          -> Integer
3029  *  
3030  *  Returns the number of vdw parameters.
3031  */
3032 static VALUE
3033 s_Parameter_Nvdws(VALUE self)
3034 {
3035         Int n;
3036         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3037         if (mol == NULL)
3038                 n = gBuiltinParameters->nvdwPars;
3039         else if (mol->par != NULL)
3040                 n = mol->par->nvdwPars;
3041         else n = 0;
3042         return INT2NUM(n);
3043 }
3044
3045 /*
3046  *  call-seq:
3047  *     nvdw_pairs          -> Integer
3048  *  
3049  *  Returns the number of vdw pair parameters.
3050  */
3051 static VALUE
3052 s_Parameter_NvdwPairs(VALUE self)
3053 {
3054         Int n;
3055         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3056         if (mol == NULL)
3057                 n = gBuiltinParameters->nvdwpPars;
3058         else if (mol->par != NULL)
3059                 n = mol->par->nvdwpPars;
3060         else n = 0;
3061         return INT2NUM(n);
3062 }
3063
3064 /*
3065  *  call-seq:
3066  *     nvdw_cutoffs          -> Integer
3067  *  
3068  *  Returns the number of vdw cutoff parameters.
3069  */
3070 static VALUE
3071 s_Parameter_NvdwCutoffs(VALUE self)
3072 {
3073         Int n;
3074         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3075         if (mol == NULL)
3076                 n = gBuiltinParameters->nvdwCutoffPars;
3077         else if (mol->par != NULL)
3078                 n = mol->par->nvdwCutoffPars;
3079         else n = 0;
3080         return INT2NUM(n);
3081 }
3082
3083 /*
3084  *  call-seq:
3085  *     nelements          -> Integer
3086  *  
3087  *  Returns the number of element parameters.
3088  */
3089 static VALUE
3090 s_Parameter_Nelements(VALUE self)
3091 {
3092         return INT2NUM(gCountElementParameters);
3093 }
3094
3095 /*
3096  *  call-seq:
3097  *     bonds          -> ParEnumerable
3098  *  
3099  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
3100  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
3101  *  useful when all accessible parameters should be examined by use of 'each' method.
3102  */
3103 static VALUE
3104 s_Parameter_Bonds(VALUE self)
3105 {
3106         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3107         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3108 }
3109
3110 /*
3111  *  call-seq:
3112  *     angles          -> ParEnumerable
3113  *  
3114  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3115  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3116  *  useful when all accessible parameters should be examined by use of 'each' method.
3117  */
3118 static VALUE
3119 s_Parameter_Angles(VALUE self)
3120 {
3121         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3122         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3123 }
3124
3125 /*
3126  *  call-seq:
3127  *     dihedrals          -> ParEnumerable
3128  *  
3129  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3130  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3131  *  useful when all accessible parameters should be examined by use of 'each' method.
3132  */
3133 static VALUE
3134 s_Parameter_Dihedrals(VALUE self)
3135 {
3136         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3137         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3138 }
3139
3140 /*
3141  *  call-seq:
3142  *     impropers          -> ParEnumerable
3143  *  
3144  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3145  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3146  *  useful when all accessible parameters should be examined by use of 'each' method.
3147  */
3148 static VALUE
3149 s_Parameter_Impropers(VALUE self)
3150 {
3151         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3152         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3153 }
3154
3155 /*
3156  *  call-seq:
3157  *     vdws          -> ParEnumerable
3158  *  
3159  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3160  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3161  *  useful when all accessible parameters should be examined by use of 'each' method.
3162  */
3163 static VALUE
3164 s_Parameter_Vdws(VALUE self)
3165 {
3166         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3167         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3168 }
3169
3170 /*
3171  *  call-seq:
3172  *     vdw_pairs          -> ParEnumerable
3173  *  
3174  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3175  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3176  *  useful when all accessible parameters should be examined by use of 'each' method.
3177  */
3178 static VALUE
3179 s_Parameter_VdwPairs(VALUE self)
3180 {
3181         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3182         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3183 }
3184
3185 /*
3186  *  call-seq:
3187  *     vdw_cutoffs          -> ParEnumerable
3188  *  
3189  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3190  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3191  *  useful when all accessible parameters should be examined by use of 'each' method.
3192  */
3193 static VALUE
3194 s_Parameter_VdwCutoffs(VALUE self)
3195 {
3196         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3197         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3198 }
3199
3200 /*
3201  *  call-seq:
3202  *     elements          -> ParEnumerable
3203  *  
3204  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3205  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3206  *  useful when all accessible parameters should be examined by use of 'each' method.
3207  */
3208 static VALUE
3209 s_Parameter_Elements(VALUE self)
3210 {
3211         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3212         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3213 }
3214
3215 static VALUE
3216 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3217 {
3218         VALUE atval, optval;
3219         UInt t[4];
3220         Int ii[4];
3221         int i, n, idx, flags, is_global;
3222
3223         rb_scan_args(argc, argv, "1*", &atval, &optval);
3224         
3225         /*  Get the atom types  */
3226         switch (parType) {
3227                 case kBondParType: n = 2; break;
3228                 case kAngleParType: n = 3; break;
3229                 case kDihedralParType: n = 4; break;
3230                 case kImproperParType: n = 4; break;
3231                 case kVdwParType: n = 1; break;
3232                 case kVdwPairParType: n = 2; break;
3233                 default: return Qnil;
3234         }
3235         s_ScanAtomTypes(atval, n, t);
3236         for (i = 0; i < n; i++) {
3237                 if (t[i] < kAtomTypeMinimum) {
3238                         /*  Explicit atom index  */
3239                         if (mol == NULL)
3240                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3241                         if (t[i] >= mol->natoms)
3242                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3243                         ii[i] = t[i];
3244                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3245                 } else ii[i] = -1;
3246         }
3247         
3248         /*  Analyze options  */
3249         flags = 0;
3250         n = RARRAY_LEN(optval);
3251         for (i = 0; i < n; i++) {
3252                 VALUE oval = RARRAY_PTR(optval)[i];
3253                 if (oval == ID2SYM(rb_intern("global")))
3254                         flags |= kParameterLookupGlobal;
3255                 else if (oval == ID2SYM(rb_intern("local")))
3256                         flags |= kParameterLookupLocal;
3257                 else if (oval == ID2SYM(rb_intern("missing")))
3258                         flags |= kParameterLookupMissing;
3259                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3260                         flags |= kParameterLookupNoWildcard;
3261                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3262                         flags |= kParameterLookupNoBaseAtomType;
3263                 else if (oval == ID2SYM(rb_intern("create")))
3264                         flags |= 256;
3265         }
3266         if (flags == 0)
3267                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3268         
3269         idx = -1;
3270         is_global = 0;
3271         switch (parType) {
3272                 case kBondParType: {
3273                         BondPar *bp;
3274                         if (mol != NULL) {
3275                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3276                                 if (bp != NULL) {
3277                                         idx = bp - mol->par->bondPars;
3278                                         break;
3279                                 }
3280                         }
3281                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3282                         if (bp != NULL) {
3283                                 idx = bp - gBuiltinParameters->bondPars;
3284                                 is_global = 1;
3285                         }
3286                         break;
3287                 }
3288                 case kAngleParType: {
3289                         AnglePar *ap;
3290                         if (mol != NULL) {
3291                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3292                                 if (ap != NULL) {
3293                                         idx = ap - mol->par->anglePars;
3294                                         break;
3295                                 }
3296                         }
3297                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3298                         if (ap != NULL) {
3299                                 idx = ap - gBuiltinParameters->anglePars;
3300                                 is_global = 1;
3301                         }
3302                         break;
3303                 }
3304                 case kDihedralParType: {
3305                         TorsionPar *tp;
3306                         if (mol != NULL) {
3307                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3308                                 if (tp != NULL) {
3309                                         idx = tp - mol->par->dihedralPars;
3310                                         break;
3311                                 }
3312                         }
3313                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3314                         if (tp != NULL) {
3315                                 idx = tp - gBuiltinParameters->dihedralPars;
3316                                 is_global = 1;
3317                         }
3318                         break;
3319                 }
3320                 case kImproperParType: {
3321                         TorsionPar *tp;
3322                         if (mol != NULL) {
3323                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3324                                 if (tp != NULL) {
3325                                         idx = tp - mol->par->improperPars;
3326                                         break;
3327                                 }
3328                         }
3329                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3330                         if (tp != NULL) {
3331                                 idx = tp - gBuiltinParameters->improperPars;
3332                                 is_global = 1;
3333                         }
3334                         break;
3335                 }       
3336                 case kVdwParType: {
3337                         VdwPar *vp;
3338                         if (mol != NULL) {
3339                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3340                                 if (vp != NULL) {
3341                                         idx = vp - mol->par->vdwPars;
3342                                         break;
3343                                 }
3344                         }
3345                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3346                         if (vp != NULL) {
3347                                 idx = vp - gBuiltinParameters->vdwPars;
3348                                 is_global = 1;
3349                         }
3350                         break;
3351                 }       
3352                 case kVdwPairParType: {
3353                         VdwPairPar *vp;
3354                         if (mol != NULL) {
3355                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3356                                 if (vp != NULL) {
3357                                         idx = vp - mol->par->vdwpPars;
3358                                         break;
3359                                 }
3360                         }
3361                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3362                         if (vp != NULL) {
3363                                 idx = vp - gBuiltinParameters->vdwpPars;
3364                                 is_global = 1;
3365                         }
3366                         break;
3367                 }
3368                 default:
3369                         return Qnil;
3370         }
3371         if (idx < 0) {
3372                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3373                         return Qnil;            
3374                 else {
3375                         /*  Insert a new parameter record  */
3376                         UnionPar *up;
3377                         Int count = ParameterGetCountForType(mol->par, parType);
3378                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3379                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3380                         IntGroupRelease(ig);
3381                         is_global = 0;
3382                         idx = count;
3383                         /*  Set atom types  */
3384                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3385                         if (up == NULL)
3386                                 return Qnil;
3387                         switch (parType) {
3388                                 case kBondParType:
3389                                         up->bond.type1 = t[0];
3390                                         up->bond.type2 = t[1];
3391                                         break;
3392                                 case kAngleParType:
3393                                         up->angle.type1 = t[0];
3394                                         up->angle.type2 = t[1];
3395                                         up->angle.type3 = t[2];
3396                                         break;
3397                                 case kDihedralParType:
3398                                 case kImproperParType:
3399                                         up->torsion.type1 = t[0];
3400                                         up->torsion.type2 = t[1];
3401                                         up->torsion.type3 = t[2];
3402                                         up->torsion.type4 = t[3];
3403                                         break;
3404                                 case kVdwParType:
3405                                         up->vdw.type1 = t[0];
3406                                         break;
3407                                 case kVdwPairParType:
3408                                         up->vdwp.type1 = t[0];
3409                                         up->vdwp.type2 = t[1];
3410                                         break;
3411                                 default:
3412                                         return Qnil;
3413                         }
3414                 }
3415         }
3416         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3417 }
3418
3419 /*
3420  *  call-seq:
3421  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3422  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3423  *
3424  *  Find the parameter record that matches the given atom types. The atom types are given
3425  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3426  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3427  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3428  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3429  */
3430 static VALUE
3431 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3432 {
3433         int parType;
3434         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3435         if (argc == 0)
3436                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3437         parType = s_ParTypeFromValue(argv[0]);
3438         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3439 }
3440
3441 /*
3442  *  call-seq:
3443  *     self == parameter -> boolean
3444  *  
3445  *  True if the parameters point to the same parameter table.
3446  */
3447 static VALUE
3448 s_Parameter_Equal(VALUE self, VALUE val)
3449 {
3450         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3451                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3452         } else return Qfalse;
3453 }
3454
3455 #pragma mark ====== ParEnumerable Class ======
3456
3457 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3458  and the parameter type. If the Molecule is NULL, then it refers to the
3459  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3460  the global parameters are always accessible. */
3461
3462 typedef struct ParEnumerable {
3463         Molecule *mol;
3464         Int parType;   /*  Same as parType in ParameterRef  */
3465 } ParEnumerable;
3466
3467 static ParEnumerable *
3468 s_ParEnumerableNew(Molecule *mol, Int parType)
3469 {
3470         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3471         if (pen != NULL) {
3472                 pen->mol = mol;
3473                 if (mol != NULL)
3474                         MoleculeRetain(mol);
3475                 pen->parType = parType;
3476         }
3477         return pen;
3478 }
3479
3480 static void
3481 s_ParEnumerableRelease(ParEnumerable *pen)
3482 {
3483         if (pen != NULL) {
3484                 if (pen->mol != NULL)
3485                         MoleculeRelease(pen->mol);
3486                 free(pen);
3487         }
3488 }
3489
3490 static Molecule *
3491 s_MoleculeFromParEnumerableValue(VALUE val)
3492 {
3493         ParEnumerable *pen;
3494     Data_Get_Struct(val, ParEnumerable, pen);
3495         return pen->mol;
3496 }
3497
3498 static VALUE
3499 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3500 {
3501         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3502         if (pen == NULL)
3503                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3504         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3505 }
3506
3507 /*
3508  *  call-seq:
3509  *     par_type -> String
3510  *
3511  *  Get the parameter type, like "bond", "angle", etc.
3512  */
3513 static VALUE
3514 s_ParEnumerable_ParType(VALUE self) {
3515         ParEnumerable *pen;
3516         Int tp;
3517     Data_Get_Struct(self, ParEnumerable, pen);
3518         tp = pen->parType;
3519         if (tp == kElementParType)
3520                 return Ruby_NewEncodedStringValue2("element");
3521         tp -= kFirstParType;
3522         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3523                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3524         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3525 }
3526
3527 /*
3528  *  call-seq:
3529  *     self[idx]          -> ParameterRef
3530  *  
3531  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3532  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3533  *  parent Parameter object of self.
3534  *
3535  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3536  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3537  */
3538 static VALUE
3539 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3540 {
3541         ParEnumerable *pen;
3542     Data_Get_Struct(self, ParEnumerable, pen);
3543         switch (pen->parType) {
3544                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3545                 case kBondParType:      return s_Parameter_Bond(self, ival);
3546                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3547                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3548                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3549                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3550                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3551                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3552                 case kElementParType:   return s_Parameter_Element(self, ival);
3553                 default:
3554                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3555         }
3556         return Qnil;  /*  Not reached  */
3557 }
3558
3559 /*
3560  *  call-seq:
3561  *     length          -> Integer
3562  *  
3563  *  Returns the number of parameters included in this enumerable.
3564  */
3565 static VALUE
3566 s_ParEnumerable_Length(VALUE self)
3567 {
3568         ParEnumerable *pen;
3569     Data_Get_Struct(self, ParEnumerable, pen);
3570         switch (pen->parType) {
3571                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3572                 case kBondParType:      return s_Parameter_Nbonds(self);
3573                 case kAngleParType:     return s_Parameter_Nangles(self); 
3574                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3575                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3576                 case kVdwParType:       return s_Parameter_Nvdws(self);
3577                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3578                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3579                 case kElementParType:   return s_Parameter_Nelements(self);
3580                 default:
3581                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3582         }
3583         return Qnil;  /*  Not reached  */
3584 }
3585
3586 /*
3587  *  call-seq:
3588  *     each {|pref| ...}
3589  *  
3590  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3591  */
3592 VALUE
3593 s_ParEnumerable_Each(VALUE self)
3594 {
3595         VALUE aval;
3596         ParEnumerable *pen;
3597         ParameterRef *pref;
3598         int i, ofs, n;
3599     Data_Get_Struct(self, ParEnumerable, pen);
3600         if (pen->parType == kElementParType)
3601                 n = gCountElementParameters;
3602         else {
3603                 switch (pen->parType) {
3604                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3605                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3606                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3607                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3608                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3609                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3610                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3611                         default:
3612                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3613                 }
3614                 if (pen->mol == NULL)
3615                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3616                 else if (pen->mol->par != NULL)
3617                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3618                 else return self;
3619         }               
3620         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3621         Data_Get_Struct(aval, ParameterRef, pref);
3622         for (i = 0; i < n; i++) {
3623                 pref->idx = i;
3624                 rb_yield(aval);
3625         }
3626     return self;
3627 }
3628
3629 /*
3630  *  call-seq:
3631  *     reverse_each {|pref| ...}
3632  *  
3633  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3634  */
3635 VALUE
3636 s_ParEnumerable_ReverseEach(VALUE self)
3637 {
3638         VALUE aval;
3639         ParEnumerable *pen;
3640         ParameterRef *pref;
3641         int i, ofs, n;
3642     Data_Get_Struct(self, ParEnumerable, pen);
3643         if (pen->parType == kElementParType)
3644                 n = gCountElementParameters;
3645         else {
3646                 switch (pen->parType) {
3647                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3648                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3649                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3650                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3651                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3652                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3653                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3654                         default:
3655                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3656                 }
3657                 if (pen->mol == NULL)
3658                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3659                 else if (pen->mol->par != NULL)
3660                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3661                 else return self;
3662         }               
3663         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3664         Data_Get_Struct(aval, ParameterRef, pref);
3665         for (i = n - 1; i >= 0; i--) {
3666                 pref->idx = i;
3667                 rb_yield(aval);
3668         }
3669     return self;
3670 }
3671
3672 /*
3673  *  call-seq:
3674  *     insert(idx = nil, pref = nil)       -> ParameterRef
3675  *  
3676  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3677  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3678  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3679  *  parameter is left undefined.
3680  *  Throws an exception if ParEnumerable points to the global parameter.
3681  */
3682 static VALUE
3683 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3684 {
3685         VALUE ival, pval;
3686         ParEnumerable *pen;
3687         int i, n;
3688         IntGroup *ig;
3689         UnionPar u;
3690         MolAction *act;
3691     Data_Get_Struct(self, ParEnumerable, pen);
3692         if (pen->mol == NULL)
3693                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3694         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3695         rb_scan_args(argc, argv, "02", &ival, &pval);
3696         if (ival != Qnil) {
3697                 i = NUM2INT(rb_Integer(ival));
3698                 if (i < 0 || i > n)
3699                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3700                 n = i;
3701         }
3702         if (pval != Qnil) {
3703                 Int type;
3704                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3705                 if (up == NULL || type != pen->parType)
3706                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3707                 ParameterCopyOneWithType(&u, up, pen->parType);
3708                 u.bond.src = 0;
3709         } else {
3710                 memset(&u, 0, sizeof(u));
3711                 u.bond.src = 0;
3712         }
3713         ig = IntGroupNewWithPoints(n, 1, -1);
3714         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3715
3716         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3717         MolActionCallback_registerUndo(pen->mol, act);
3718         MolActionRelease(act);
3719         
3720         IntGroupRelease(ig);
3721         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3722 }
3723
3724 /*
3725  *  call-seq:
3726  *     delete(Integer)
3727  *     delete(IntGroup)
3728  *  
3729  *  Delete the parameter(s) specified by the argument.
3730  */
3731 static VALUE
3732 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3733 {
3734         ParEnumerable *pen;
3735         int i, n;
3736         IntGroup *ig;
3737     Data_Get_Struct(self, ParEnumerable, pen);
3738         if (pen->mol == NULL)
3739                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3740         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3741         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3742                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3743                 i = 1;
3744         } else {
3745                 ig = IntGroupFromValue(ival);
3746                 if ((i = IntGroupGetCount(ig)) == 0) {
3747                         IntGroupRelease(ig);
3748                         return Qnil;
3749                 }
3750         }
3751         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3752                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3753         n = i;
3754
3755         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3756         IntGroupRelease(ig);
3757         return ival;
3758 }
3759
3760 /*
3761  *  call-seq:
3762  *     lookup(atom_types, options, ...) -> ParameterRef
3763  *     lookup(atom_type_string, options, ...) -> ParameterRef
3764  *
3765  *  Find the parameter record that matches the given atom types. The arguments are
3766  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3767  *  specified.
3768  */
3769 static VALUE
3770 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3771 {
3772         ParEnumerable *pen;
3773     Data_Get_Struct(self, ParEnumerable, pen);
3774         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3775 }
3776
3777 /*
3778  *  call-seq:
3779  *     self == parEnumerable -> boolean
3780  *  
3781  *  True if the arguments point to the same parameter table and type.
3782  */
3783 static VALUE
3784 s_ParEnumerable_Equal(VALUE self, VALUE val)
3785 {
3786         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3787                 ParEnumerable *pen1, *pen2;
3788                 Data_Get_Struct(self, ParEnumerable, pen1);
3789                 Data_Get_Struct(val, ParEnumerable, pen2);
3790                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3791         } else return Qfalse;
3792 }
3793
3794 #pragma mark ====== AtomRef Class ======
3795
3796 /*  Forward declaration for register undo  */
3797 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3798
3799 /*  Ruby string "set_atom_attr"  */
3800 static VALUE s_SetAtomAttrString;
3801
3802 static int
3803 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3804 {
3805         AtomRef *aref;
3806         int idx;
3807         Data_Get_Struct(self, AtomRef, aref);
3808         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3809         if (idx < 0 || idx >= aref->mol->natoms)
3810                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3811         if (app != NULL)
3812                 *app = aref->mol->atoms + idx;
3813         if (mpp != NULL)
3814                 *mpp = aref->mol;
3815         return idx;
3816 }
3817
3818 static Atom *
3819 s_AtomFromValue(VALUE self)
3820 {
3821         Atom *ap;
3822         s_AtomIndexFromValue(self, &ap, NULL);
3823         return ap;
3824 }
3825
3826 static Atom *
3827 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3828 {
3829         Atom *ap;
3830         s_AtomIndexFromValue(self, &ap, mpp);
3831         return ap;
3832 }
3833
3834 static void
3835 s_NotifyModificationForAtomRef(VALUE self)
3836 {
3837         AtomRef *aref;
3838         Data_Get_Struct(self, AtomRef, aref);
3839         MoleculeIncrementModifyCount(aref->mol);
3840 }
3841
3842 static void
3843 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3844 {
3845         AtomRef *aref;
3846         Data_Get_Struct(self, AtomRef, aref);
3847         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3848                 /*  Register undo  */
3849                 MolAction *act;
3850                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3851                 MolActionCallback_registerUndo(aref->mol, act);
3852                 MoleculeCallback_notifyModification(aref->mol, 0);
3853                 /*  Request MD rebuilt if necessary  */
3854                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3855                         aref->mol->needsMDRebuild = 1;
3856         }
3857 }
3858
3859 VALUE
3860 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3861 {
3862         AtomRef *aref;
3863         aref = AtomRefNew(mol, idx);
3864         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3865 }
3866
3867 static VALUE
3868 s_AtomRef_GetMolecule(VALUE self)
3869 {
3870         Molecule *mpp;
3871         s_AtomIndexFromValue(self, NULL, &mpp);
3872         return ValueFromMolecule(mpp);
3873 }
3874
3875 static VALUE s_AtomRef_GetIndex(VALUE self) {
3876         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3877 }
3878
3879 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3880         return INT2NUM(s_AtomFromValue(self)->segSeq);
3881 }
3882
3883 static VALUE s_AtomRef_GetSegName(VALUE self) {
3884         char *p = s_AtomFromValue(self)->segName;
3885         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3886 }
3887
3888 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3889         return INT2NUM(s_AtomFromValue(self)->resSeq);
3890 }
3891
3892 static VALUE s_AtomRef_GetResName(VALUE self) {
3893         char *p = s_AtomFromValue(self)->resName;
3894         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3895 }
3896
3897 static VALUE s_AtomRef_GetName(VALUE self) {
3898         char *p = s_AtomFromValue(self)->aname;
3899         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3900 }
3901
3902 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3903         int type = s_AtomFromValue(self)->type;
3904         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3905         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3906 }
3907
3908 static VALUE s_AtomRef_GetCharge(VALUE self) {
3909         return rb_float_new(s_AtomFromValue(self)->charge);
3910 }
3911
3912 static VALUE s_AtomRef_GetWeight(VALUE self) {
3913         return rb_float_new(s_AtomFromValue(self)->weight);
3914 }
3915
3916 static VALUE s_AtomRef_GetElement(VALUE self) {
3917         char *p = s_AtomFromValue(self)->element;
3918         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3919 }
3920
3921 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3922         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3923 }
3924
3925 static VALUE s_AtomRef_GetConnects(VALUE self) {
3926         VALUE retval;
3927         Int i, *cp;
3928         Atom *ap = s_AtomFromValue(self);
3929         retval = rb_ary_new();
3930         cp = AtomConnectData(&ap->connect);
3931         for (i = 0; i < ap->connect.count; i++)
3932                 rb_ary_push(retval, INT2NUM(cp[i]));
3933         return retval;
3934 }
3935
3936 static VALUE s_AtomRef_GetR(VALUE self) {
3937         return ValueFromVector(&(s_AtomFromValue(self)->r));
3938 }
3939
3940 static VALUE s_AtomRef_GetX(VALUE self) {
3941         return rb_float_new(s_AtomFromValue(self)->r.x);
3942 }
3943
3944 static VALUE s_AtomRef_GetY(VALUE self) {
3945         return rb_float_new(s_AtomFromValue(self)->r.y);
3946 }
3947
3948 static VALUE s_AtomRef_GetZ(VALUE self) {
3949         return rb_float_new(s_AtomFromValue(self)->r.z);
3950 }
3951
3952 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3953         Atom *ap;
3954         Molecule *mp;
3955         Vector r1;
3956         s_AtomIndexFromValue(self, &ap, &mp);
3957         r1 = ap->r;
3958         if (mp->cell != NULL)
3959                 TransformVec(&r1, mp->cell->rtr, &r1);
3960         return r1;
3961 }
3962
3963 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3964         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3965         return ValueFromVector(&r1);
3966 }
3967
3968 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3969         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3970 }
3971
3972 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3973         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3974 }
3975
3976 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3977         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3978 }
3979
3980 static VALUE s_AtomRef_GetSigma(VALUE self) {
3981         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3982 }
3983
3984 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3985         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3986 }
3987
3988 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3989         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3990 }
3991
3992 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3993         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3994 }
3995
3996 static VALUE s_AtomRef_GetV(VALUE self) {
3997         return ValueFromVector(&(s_AtomFromValue(self)->v));
3998 }
3999
4000 static VALUE s_AtomRef_GetF(VALUE self) {
4001         Vector v = s_AtomFromValue(self)->f;
4002         VecScaleSelf(v, INTERNAL2KCAL);
4003         return ValueFromVector(&v);
4004 }
4005
4006 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
4007         return rb_float_new(s_AtomFromValue(self)->occupancy);
4008 }
4009
4010 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
4011         return rb_float_new(s_AtomFromValue(self)->tempFactor);
4012 }
4013
4014 static VALUE s_AtomRef_GetAniso(VALUE self) {
4015         VALUE retval;
4016         int i;
4017         Atom *ap = s_AtomFromValue(self);
4018         if (ap->aniso == NULL)
4019                 return Qnil;
4020         retval = rb_ary_new();
4021         for (i = 0; i < 6; i++)
4022                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
4023         if (ap->aniso->has_bsig) {
4024                 rb_ary_push(retval, INT2NUM(0));
4025                 for (i = 0; i < 6; i++)
4026                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
4027         }
4028         return retval;
4029 }
4030
4031 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
4032     VALUE retval;
4033     int i;
4034     Atom *ap = s_AtomFromValue(self);
4035     if (ap->aniso == NULL)
4036         return Qnil;
4037     retval = rb_ary_new();
4038     for (i = 0; i < 3; i++)
4039         rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
4040     return retval;
4041 }
4042
4043 static VALUE s_AtomRef_GetSymop(VALUE self) {
4044         VALUE retval;
4045         Atom *ap = s_AtomFromValue(self);
4046         if (!ap->symop.alive)
4047                 return Qnil;
4048         retval = rb_ary_new();
4049         rb_ary_push(retval, INT2NUM(ap->symop.sym));
4050         rb_ary_push(retval, INT2NUM(ap->symop.dx));
4051         rb_ary_push(retval, INT2NUM(ap->symop.dy));
4052         rb_ary_push(retval, INT2NUM(ap->symop.dz));
4053         rb_ary_push(retval, INT2NUM(ap->symbase));
4054         return retval;
4055 }
4056
4057 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
4058         return INT2NUM(s_AtomFromValue(self)->intCharge);
4059 }
4060
4061 static VALUE s_AtomRef_GetFixForce(VALUE self) {
4062         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
4063 }
4064
4065 static VALUE s_AtomRef_GetFixPos(VALUE self) {
4066         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
4067 }
4068
4069 static VALUE s_AtomRef_GetExclusion(VALUE self) {
4070         Molecule *mol;
4071         Atom *ap;
4072         int idx, i;
4073         MDExclusion *exinfo;
4074         Int *exlist;
4075         VALUE retval, aval;
4076         idx = s_AtomIndexFromValue(self, &ap, &mol);
4077         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
4078                 VALUE mval = ValueFromMolecule(mol);
4079                 s_RebuildMDParameterIfNecessary(mval, Qnil);
4080         }
4081         if (mol->arena->exinfo == NULL)
4082                 return Qnil;
4083         exinfo = mol->arena->exinfo + idx;
4084         exlist = mol->arena->exlist;
4085         retval = rb_ary_new();
4086         aval = rb_ary_new();
4087         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
4088                 rb_ary_push(aval, INT2NUM(exlist[i]));
4089         rb_ary_push(retval, aval);
4090         aval = rb_ary_new();
4091         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
4092                 rb_ary_push(aval, INT2NUM(exlist[i]));
4093         rb_ary_push(retval, aval);
4094         aval = rb_ary_new();
4095         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
4096                 rb_ary_push(aval, INT2NUM(exlist[i]));
4097         rb_ary_push(retval, aval);
4098         return retval;
4099 }
4100
4101 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
4102         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4103 }
4104
4105 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4106         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4107 }
4108
4109 static VALUE s_AtomRef_GetHidden(VALUE self) {
4110         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4111 }
4112
4113 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4114         VALUE retval;
4115         Int i, count, *cp;
4116         Atom *ap = s_AtomFromValue(self);
4117         if (ap->anchor == NULL)
4118                 return Qnil;
4119         count = ap->anchor->connect.count;
4120         retval = rb_ary_new2(count * 2);
4121         cp = AtomConnectData(&ap->anchor->connect);
4122         for (i = 0; i < count; i++) {
4123                 rb_ary_store(retval, i, INT2NUM(cp[i]));
4124                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4125         }
4126         return retval;
4127 }
4128
4129 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4130         char *p = s_AtomFromValue(self)->uff_type;
4131         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4132 }
4133
4134 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4135         rb_raise(rb_eMolbyError, "index cannot be directly set");
4136         return Qnil;
4137 }
4138
4139 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4140         VALUE oval = s_AtomRef_GetSegSeq(self);
4141         val = rb_Integer(val);
4142         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4143         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4144         return val;
4145 }
4146
4147 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4148         char *p = StringValuePtr(val);
4149         VALUE oval = s_AtomRef_GetSegName(self);
4150         strncpy(s_AtomFromValue(self)->segName, p, 4);
4151         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4152         return val;
4153 }
4154
4155 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4156         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4157         return val; /* Not reached */
4158 }
4159
4160 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4161         Atom *ap = s_AtomFromValue(self);
4162         char *p = StringValuePtr(val);
4163         VALUE oval = s_AtomRef_GetName(self);
4164         if (ap->anchor != NULL && p[0] == '_')
4165                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4166         strncpy(ap->aname, p, 4);
4167         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4168         return val;
4169 }
4170
4171 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4172         Molecule *mp;
4173         char *p = StringValuePtr(val);
4174         VALUE oval = s_AtomRef_GetAtomType(self);
4175         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4176         if (type != 0 && type < kAtomTypeMinimum)
4177                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4178         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4179         mp->needsMDRebuild = 1;
4180         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4181         return val;
4182 }
4183
4184 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4185         Molecule *mp;
4186         VALUE oval = s_AtomRef_GetCharge(self);
4187         val = rb_Float(val);
4188         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4189         mp->needsMDRebuild = 1;
4190         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4191         return val;
4192 }
4193
4194 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4195         Molecule *mp;
4196         VALUE oval = s_AtomRef_GetWeight(self);
4197         val = rb_Float(val);
4198         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4199         mp->needsMDRebuild = 1;
4200         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4201         return val;
4202 }
4203
4204 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4205         Double w;
4206         Molecule *mp;
4207         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4208         char *p = StringValuePtr(val);
4209         VALUE oval = s_AtomRef_GetElement(self);
4210         ap->atomicNumber = ElementToInt(p);
4211         ElementToString(ap->atomicNumber, ap->element);
4212         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4213                 ap->weight = w;
4214         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4215         mp->needsMDRebuild = 1;
4216         return val;
4217 }
4218
4219 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4220         Double w;
4221         Molecule *mp;
4222         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4223         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4224         val = rb_Integer(val);
4225         ap->atomicNumber = NUM2INT(val);
4226         ElementToString(ap->atomicNumber, ap->element);
4227         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4228                 ap->weight = w;
4229         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4230         mp->needsMDRebuild = 1;
4231         return val;
4232 }
4233
4234 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4235         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4236         return val; /* Not reached */
4237 }
4238
4239 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4240         Vector v;
4241         Molecule *mp;
4242         VALUE oval = s_AtomRef_GetR(self);
4243         VectorFromValue(val, &v);
4244         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4245         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4246         mp->needsMDCopyCoordinates = 1;
4247         return val;
4248 }
4249
4250 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4251         Double f;
4252         Molecule *mp;
4253         VALUE oval = s_AtomRef_GetX(self);
4254         val = rb_Float(val);
4255         f = NUM2DBL(val);
4256         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4257         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4258         mp->needsMDCopyCoordinates = 1;
4259         return val;
4260 }
4261
4262 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4263         Double f;
4264         Molecule *mp;
4265         VALUE oval = s_AtomRef_GetY(self);
4266         val = rb_Float(val);
4267         f = NUM2DBL(val);
4268         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4269         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4270         mp->needsMDCopyCoordinates = 1;
4271         return val;
4272 }
4273
4274 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4275         Double f;
4276         Molecule *mp;
4277         VALUE oval = s_AtomRef_GetZ(self);
4278         val = rb_Float(val);
4279         f = NUM2DBL(val);
4280         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4281         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4282         mp->needsMDCopyCoordinates = 1;
4283         return val;
4284 }
4285
4286 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4287         Vector v, ov;
4288         Atom *ap;
4289         Molecule *mp;
4290         s_AtomIndexFromValue(self, &ap, &mp);
4291         ov = ap->r;
4292         VectorFromValue(val, &v);
4293         if (mp->cell != NULL)
4294                 TransformVec(&v, mp->cell->tr, &v);
4295         ap->r = v;
4296         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4297         mp->needsMDCopyCoordinates = 1;
4298         return val;
4299 }
4300
4301 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4302         double f;
4303         Vector v, ov;
4304         Atom *ap;
4305         Molecule *mp;
4306         s_AtomIndexFromValue(self, &ap, &mp);
4307         ov = v = ap->r;
4308         val = rb_Float(val);
4309         f = NUM2DBL(val);
4310         if (mp->cell != NULL) {
4311                 TransformVec(&v, mp->cell->rtr, &v);
4312                 v.x = f;
4313                 TransformVec(&v, mp->cell->tr, &v);
4314         } else v.x = f;
4315         ap->r = v;
4316         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4317         mp->needsMDCopyCoordinates = 1;
4318         return val;
4319 }
4320
4321 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4322         double f;
4323         Vector v, ov;
4324         Atom *ap;
4325         Molecule *mp;
4326         s_AtomIndexFromValue(self, &ap, &mp);
4327         ov = v = ap->r;
4328         val = rb_Float(val);
4329         f = NUM2DBL(val);
4330         if (mp->cell != NULL) {
4331                 TransformVec(&v, mp->cell->rtr, &v);
4332                 v.y = f;
4333                 TransformVec(&v, mp->cell->tr, &v);
4334         } else v.y = f;
4335         ap->r = v;
4336         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4337         mp->needsMDCopyCoordinates = 1;
4338         return val;
4339 }
4340
4341 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4342         double f;
4343         Vector v, ov;
4344         Atom *ap;
4345         Molecule *mp;
4346         s_AtomIndexFromValue(self, &ap, &mp);
4347         ov = v = ap->r;
4348         val = rb_Float(val);
4349         f = NUM2DBL(val);
4350         if (mp->cell != NULL) {
4351                 TransformVec(&v, mp->cell->rtr, &v);
4352                 v.z = f;
4353                 TransformVec(&v, mp->cell->tr, &v);
4354         } else v.z = f;
4355         ap->r = v;
4356         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4357         mp->needsMDCopyCoordinates = 1;
4358         return val;
4359 }
4360
4361 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4362         Vector v;
4363         Molecule *mp;
4364         VALUE oval = s_AtomRef_GetSigma(self);
4365         VectorFromValue(val, &v);
4366         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4367         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4368         mp->needsMDCopyCoordinates = 1;
4369         return val;
4370 }
4371
4372 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4373         Double f;
4374         Molecule *mp;
4375         VALUE oval = s_AtomRef_GetSigmaX(self);
4376         val = rb_Float(val);
4377         f = NUM2DBL(val);
4378         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4379         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4380         mp->needsMDCopyCoordinates = 1;
4381         return val;
4382 }
4383
4384 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4385         Double f;
4386         Molecule *mp;
4387         VALUE oval = s_AtomRef_GetSigmaY(self);
4388         val = rb_Float(val);
4389         f = NUM2DBL(val);
4390         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4391         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4392         mp->needsMDCopyCoordinates = 1;
4393         return val;
4394 }
4395
4396 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4397         Double f;
4398         Molecule *mp;
4399         VALUE oval = s_AtomRef_GetSigmaZ(self);
4400         val = rb_Float(val);
4401         f = NUM2DBL(val);
4402         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4403         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4404         mp->needsMDCopyCoordinates = 1;
4405         return val;
4406 }
4407
4408 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4409         Vector v;
4410         Atom *ap;
4411         Molecule *mp;
4412         VALUE oval = s_AtomRef_GetV(self);
4413         VectorFromValue(val, &v);
4414         s_AtomIndexFromValue(self, &ap, &mp);
4415         ap->v = v;
4416         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4417         mp->needsMDCopyCoordinates = 1;
4418         return val;
4419 }
4420
4421 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4422         Vector v;
4423         Molecule *mp;
4424         VALUE oval = s_AtomRef_GetF(self);
4425         VectorFromValue(val, &v);
4426         VecScaleSelf(v, KCAL2INTERNAL);
4427         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4428         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4429         mp->needsMDCopyCoordinates = 1;
4430         return val;
4431 }
4432
4433 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4434         VALUE oval = s_AtomRef_GetOccupancy(self);
4435         Molecule *mp;
4436         val = rb_Float(val);
4437         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4438         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4439         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4440         return val;
4441 }
4442
4443 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4444         VALUE oval = s_AtomRef_GetTempFactor(self);
4445         val = rb_Float(val);
4446         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4447         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4448         return val;
4449 }
4450
4451 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4452         AtomRef *aref;
4453         int i, n, type;
4454         VALUE *valp;
4455         Double f[12];
4456         VALUE oval = s_AtomRef_GetAniso(self);
4457         Data_Get_Struct(self, AtomRef, aref);
4458         val = rb_funcall(val, rb_intern("to_a"), 0);
4459         n = RARRAY_LEN(val);
4460         valp = RARRAY_PTR(val);
4461         for (i = 0; i < 6; i++) {
4462                 if (i < n)
4463                         f[i] = NUM2DBL(rb_Float(valp[i]));
4464                 else f[i] = 0.0;
4465         }
4466         if (n >= 7)
4467                 type = NUM2INT(rb_Integer(valp[6]));
4468         else type = 0;
4469         if (n >= 13) {
4470                 for (i = 0; i < 6; i++)
4471                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4472         } else {
4473                 for (i = 0; i < 6; i++)
4474                         f[i + 6] = 0.0;
4475         }
4476         i = s_AtomIndexFromValue(self, NULL, NULL);
4477         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4478         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4479         return val;
4480 }
4481
4482 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4483     rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4484     return val; /* Not reached */
4485 }
4486
4487 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4488         Molecule *mol;
4489         Atom *ap;
4490         int i, n;
4491         VALUE *valp;
4492         Int ival[5];
4493         VALUE oval = s_AtomRef_GetSymop(self);
4494         i = s_AtomIndexFromValue(self, &ap, &mol);
4495         if (val == Qnil) {
4496                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4497         } else {
4498                 val = rb_funcall(val, rb_intern("to_a"), 0);
4499                 n = RARRAY_LEN(val);
4500                 valp = RARRAY_PTR(val);
4501                 for (i = 0; i < 5; i++) {
4502                         if (i < n) {
4503                                 if (valp[i] == Qnil)
4504                                         ival[i] = -100000;
4505                                 else 
4506                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4507                         } else ival[i] = -100000;
4508                 }
4509         }
4510         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4511                 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));
4512         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4513                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4514         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4515                 return val;  /*  No need to change  */
4516         if (ival[0] != -100000)
4517                 ap->symop.sym = ival[0];
4518         if (ival[1] != -100000)
4519                 ap->symop.dx = ival[1];
4520         if (ival[2] != -100000)
4521                 ap->symop.dy = ival[2];
4522         if (ival[3] != -100000)
4523                 ap->symop.dz = ival[3];
4524         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4525         if (ival[4] != -100000)
4526                 ap->symbase = ival[4];
4527         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4528                 /*  The anisotropic parameters should be recalculated  */
4529                 VALUE oaval = s_AtomRef_GetAniso(self);
4530                 MoleculeSetAnisoBySymop(mol, i);
4531                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4532         }
4533         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4534         return val;
4535 }
4536
4537 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4538         VALUE oval = s_AtomRef_GetIntCharge(self);
4539         val = rb_Integer(val);
4540         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4541         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4542         return val;
4543 }
4544
4545 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4546         Molecule *mp;
4547         VALUE oval = s_AtomRef_GetFixForce(self);
4548         val = rb_Float(val);
4549         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4550         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4551         mp->needsMDRebuild = 1;
4552         return val;
4553 }
4554
4555 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4556         Vector v;
4557         Molecule *mp;
4558         VALUE oval = s_AtomRef_GetFixPos(self);
4559         VectorFromValue(val, &v);
4560         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4561         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4562         mp->needsMDRebuild = 1;
4563         return val;
4564 }
4565
4566 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4567         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4568         return val; /* Not reached */
4569 }
4570
4571 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4572         VALUE oval = s_AtomRef_GetIntCharge(self);
4573         val = rb_Integer(val);
4574         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4575         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4576         return val;
4577 }
4578
4579 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4580         VALUE oval = s_AtomRef_GetIntCharge(self);
4581         val = rb_Integer(val);
4582         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4583         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4584         return val;
4585 }
4586
4587 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4588         Atom *ap = s_AtomFromValue(self);
4589         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4590         if (RTEST(val)) {
4591                 ap->exflags |= kAtomHiddenFlag;
4592         } else {
4593                 ap->exflags &= ~kAtomHiddenFlag;
4594         }
4595         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4596         return val;
4597 }
4598
4599 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4600         Int idx, i, j, k, n, *ip;
4601         Double *dp;
4602         Atom *ap;
4603         Molecule *mol;
4604         VALUE oval, v;
4605         AtomConnect ac;
4606         Int nUndoActions;
4607         MolAction **undoActions;
4608         memset(&ac, 0, sizeof(ac));
4609         idx = s_AtomIndexFromValue(self, &ap, &mol);
4610         oval = s_AtomRef_GetAnchorList(self);
4611         if (val != Qnil) {
4612                 val = rb_ary_to_ary(val);
4613                 n = RARRAY_LEN(val);
4614         } else n = 0;
4615         if (n == 0) {
4616                 if (ap->anchor != NULL) {
4617                         AtomConnectResize(&ap->anchor->connect, 0);
4618                         free(ap->anchor->coeffs);
4619                         free(ap->anchor);
4620                         ap->anchor = NULL;
4621                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4622                 }
4623                 return val;
4624         }
4625         if (n < 2)
4626                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4627         if (ap->aname[0] == '_')
4628                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4629         ip = (Int *)malloc(sizeof(Int) * n);
4630         dp = NULL;
4631         for (i = 0; i < n; i++) {
4632                 v = RARRAY_PTR(val)[i];
4633                 if (rb_obj_is_kind_of(v, rb_cFloat))
4634                         break;
4635                 j = NUM2INT(rb_Integer(v));
4636                 if (j < 0 || j >= mol->natoms)
4637                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4638                 for (k = 0; k < i; k++) {
4639                         if (ip[k] == j)
4640                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4641                 }
4642                 ip[i] = j;
4643         }
4644         if (i < n) {
4645                 if (i < 2)
4646                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4647                 else if (i * 2 != n)
4648                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4649                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4650                 for (i = 0; i < n / 2; i++) {
4651                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4652                         if (dp[i] <= 0.0)
4653                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4654                 }
4655                 n /= 2;
4656         }
4657         nUndoActions = 0;
4658         undoActions = NULL;
4659         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4660         free(dp);
4661         free(ip);
4662         if (i != 0)
4663                 rb_raise(rb_eMolbyError, "invalid argument");
4664         if (nUndoActions > 0) {
4665                 for (i = 0; i < nUndoActions; i++) {
4666                         MolActionCallback_registerUndo(mol, undoActions[i]);
4667                         MolActionRelease(undoActions[i]);
4668                 }
4669                 free(undoActions);
4670         }
4671         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4672         return val;
4673 }
4674
4675 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4676         Atom *ap = s_AtomFromValue(self);
4677         char *p = StringValuePtr(val);
4678         VALUE oval = s_AtomRef_GetUFFType(self);
4679         strncpy(ap->uff_type, p, 5);
4680         ap->uff_type[5] = 0;
4681         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4682         return val;
4683 }
4684
4685 static struct s_AtomAttrDef {
4686         char *name;
4687         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4688         ID id;                  /*  Will be set within InitMolby()  */
4689         VALUE (*getter)(VALUE);
4690         VALUE (*setter)(VALUE, VALUE);
4691 } s_AtomAttrDefTable[] = {
4692         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4693         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4694         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4695         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4696         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4697         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4698         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4699         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4700         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4701         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4702         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4703         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4704         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4705         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4706         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4707     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4708         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4709         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4710         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4711         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4712         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4713         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4714         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4715         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4716         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4717         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4718         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4719         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4720         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4721     {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues,        s_AtomRef_SetAnisoEigenValues},
4722         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4723         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4724         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4725         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4726         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4727         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4728         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4729         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4730         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4731         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4732         {NULL} /* Sentinel */
4733 };
4734
4735 static VALUE
4736 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4737 {
4738         int i;
4739         ID kid;
4740         if (TYPE(key) != T_SYMBOL) {
4741                 kid = rb_intern(StringValuePtr(key));
4742                 key = ID2SYM(kid);
4743         } else kid = SYM2ID(key);
4744         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4745                 if (s_AtomAttrDefTable[i].id == kid) {
4746                         if (value == Qundef)
4747                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4748                         else
4749                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4750                 }
4751         }
4752         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4753         return Qnil; /* not reached */
4754 }
4755
4756 static VALUE
4757 s_AtomRef_GetAttr(VALUE self, VALUE key)
4758 {
4759         return s_AtomRef_SetAttr(self, key, Qundef);
4760 }
4761
4762 /*
4763  *  call-seq:
4764  *     self == atomRef -> boolean
4765  *
4766  *  True if the two references point to the same atom.
4767  */
4768 static VALUE
4769 s_AtomRef_Equal(VALUE self, VALUE val)
4770 {
4771         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4772                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4773         } else return Qfalse;
4774 }
4775
4776 #pragma mark ====== MolEnumerable Class ======
4777
4778 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4779
4780 /*
4781  *  call-seq:
4782  *     self[idx] -> AtomRef or Array of Integers
4783  *  
4784  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4785  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4786  *  value is a String. Otherwise, the return value is an Array of Integers.
4787  */
4788 static VALUE
4789 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4790 {
4791         MolEnumerable *mseq;
4792         Molecule *mol;
4793         int idx1, idx2;
4794     Data_Get_Struct(self, MolEnumerable, mseq);
4795         mol = mseq->mol;
4796         if (mseq->kind == kAtomKind) {
4797                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4798         }
4799         idx1 = NUM2INT(arg1);
4800         switch (mseq->kind) {
4801                 case kBondKind: {
4802                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4803                         if (idx2 < 0 || idx2 >= mol->nbonds)
4804                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4805                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4806                 }
4807                 case kAngleKind: {
4808                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4809                         if (idx2 < 0 || idx2 >= mol->nangles)
4810                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4811                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4812                 }
4813                 case kDihedralKind: {
4814                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4815                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4816                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4817                         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]));
4818                 }
4819                 case kImproperKind: {
4820                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4821                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4822                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4823                         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]));
4824                 }
4825                 case kResidueKind: {
4826                         char *p;
4827                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4828                         if (idx2 < 0 || idx2 >= mol->nresidues)
4829                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4830                         p = mol->residues[idx2];
4831                         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4832                 }
4833         }
4834         return Qnil;
4835 }
4836
4837 /*
4838  *  call-seq:
4839  *     length          -> Integer
4840  *  
4841  *  Returns the number of objects included in this enumerable.
4842  */
4843 static VALUE
4844 s_MolEnumerable_Length(VALUE self)
4845 {
4846         MolEnumerable *mseq;
4847     Data_Get_Struct(self, MolEnumerable, mseq);
4848         switch (mseq->kind) {
4849                 case kAtomKind:
4850                         return INT2NUM(mseq->mol->natoms);
4851                 case kBondKind:
4852                         return INT2NUM(mseq->mol->nbonds);
4853                 case kAngleKind:
4854                         return INT2NUM(mseq->mol->nangles);
4855                 case kDihedralKind:
4856                         return INT2NUM(mseq->mol->ndihedrals);
4857                 case kImproperKind:
4858                         return INT2NUM(mseq->mol->nimpropers);
4859                 case kResidueKind:
4860                         return INT2NUM(mseq->mol->nresidues);
4861         }
4862         return INT2NUM(-1);
4863 }
4864
4865 /*
4866  *  call-seq:
4867  *     each {|obj| ...}
4868  *  
4869  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4870  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4871  *  For the atoms, a same AtomRef object is passed (with different internal information)
4872  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4873  *  for each iteration.
4874  */
4875 VALUE
4876 s_MolEnumerable_Each(VALUE self)
4877 {
4878         MolEnumerable *mseq;
4879         int i;
4880         int len = NUM2INT(s_MolEnumerable_Length(self));
4881     Data_Get_Struct(self, MolEnumerable, mseq);
4882         if (mseq->kind == kAtomKind) {
4883                 /*  The same AtomRef object will be used during the loop  */
4884                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4885                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4886                 for (i = 0; i < len; i++) {
4887                         aref->idx = i;
4888                         rb_yield(arval);
4889                 }
4890     } else {
4891                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4892                 for (i = 0; i < len; i++) {
4893                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4894                 }
4895         }
4896     return self;
4897 }
4898
4899 /*
4900  *  call-seq:
4901  *     self == molEnumerable -> boolean
4902  *
4903  *  True if the two arguments point to the same molecule and enumerable type.
4904  */
4905 static VALUE
4906 s_MolEnumerable_Equal(VALUE self, VALUE val)
4907 {
4908         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4909                 MolEnumerable *mseq1, *mseq2;
4910                 Data_Get_Struct(self, MolEnumerable, mseq1);
4911                 Data_Get_Struct(val, MolEnumerable, mseq2);
4912                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4913         } else return Qfalse;
4914 }
4915
4916
4917 #pragma mark ====== Molecule Class ======
4918
4919 #pragma mark ------ Allocate/Release/Accessor ------
4920
4921 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method.  */
4922 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4923 char *gLoadSaveErrorMessage = NULL;
4924
4925 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4926
4927 Molecule *
4928 MoleculeFromValue(VALUE val)
4929 {
4930         Molecule *mol;
4931         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4932                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4933     Data_Get_Struct(val, Molecule, mol);
4934         return mol;
4935 }
4936
4937 static VALUE sMoleculeRetainArray = Qnil;
4938
4939 /*  The function is called from MoleculeRelease()  */
4940 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4941 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4942 /*  object is always returned for the same Molecule.  */
4943 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4944 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4945 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4946 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4947 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4948
4949 /*  Register/unregister the exmolobj Ruby object  */
4950 void
4951 MoleculeReleaseExternalObj(Molecule *mol)
4952 {
4953         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4954                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4955                 mol->exmolobjProtected = 0;
4956         }
4957 }
4958
4959 void
4960 MoleculeRetainExternalObj(Molecule *mol)
4961 {
4962         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4963                 if (sMoleculeRetainArray == Qnil) {
4964                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4965                         sMoleculeRetainArray = rb_ary_new();
4966                 }
4967                 
4968                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4969                 mol->exmolobjProtected = 1;
4970         }
4971 }
4972
4973 /*  Release hook function for Ruby  */
4974 void
4975 MoleculeReleaseHook(Molecule *mol)
4976 {
4977         if (mol->exmolobj != NULL) {
4978                 /*  No need to remove from sMoleculeRetainArray  */
4979                 mol->exmolobj = NULL;
4980                 mol->exmolobjProtected = 0;
4981         }
4982         MoleculeRelease(mol);
4983 }
4984
4985 VALUE
4986 ValueFromMolecule(Molecule *mol)
4987 {
4988         if (mol == NULL)
4989                 return Qnil;
4990         if (mol->exmolobj != NULL)
4991                 return (VALUE)mol->exmolobj;
4992         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4993         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4994         return (VALUE)mol->exmolobj;
4995 }
4996
4997 /*  Allocator  */
4998 static VALUE
4999 s_Molecule_Alloc(VALUE klass)
5000 {
5001         VALUE val;
5002         Molecule *mol = MoleculeNew();
5003         val = ValueFromMolecule(mol);
5004         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
5005         return val;
5006 }
5007
5008 static int
5009 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
5010 {
5011         int n;
5012         char *p = "";
5013         if (FIXNUM_P(val)) {
5014                 n = FIX2INT(val);
5015                 if (n >= 0 && n < mol->natoms)
5016                         return n;
5017                 n = -1; /*  No such atom  */
5018                 val = rb_inspect(val);
5019         } else {
5020                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
5021         }
5022         if (n >= 0 && n < mol->natoms)
5023                 return n;
5024         p = StringValuePtr(val);
5025         if (n == -1)
5026                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
5027         else if (n == -2)
5028                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
5029         else
5030                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
5031         return 0; /* Not reached */
5032 }
5033
5034 static IntGroup *
5035 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
5036 {
5037         IntGroup *ig;
5038     Molecule *mp1;
5039     Data_Get_Struct(self, Molecule, mp1);
5040         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
5041         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
5042                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5043         Data_Get_Struct(val, IntGroup, ig);
5044     IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
5045         IntGroupRetain(ig);
5046         return ig;
5047 }
5048
5049 /*
5050  *  call-seq:
5051  *     dup          -> Molecule
5052  *
5053  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
5054  *  created object does not affect the old object in any sense.
5055  */
5056 static VALUE
5057 s_Molecule_InitCopy(VALUE self, VALUE arg)
5058 {
5059         Molecule *mp1, *mp2;
5060         Data_Get_Struct(self, Molecule, mp1);
5061         mp2 = MoleculeFromValue(arg);
5062         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
5063                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
5064         return self;
5065 }
5066
5067 /*
5068  *  call-seq:
5069  *     atom_index(val)       -> Integer
5070  *
5071  *  Returns the atom index represented by val. val can be either a non-negative integer
5072  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5073  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
5074  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
5075  *  If val is a string and multiple atoms match the description, the atom with the lowest index
5076  *  is returned.
5077  */
5078 static VALUE
5079 s_Molecule_AtomIndex(VALUE self, VALUE val)
5080 {
5081     Molecule *mol;
5082     Data_Get_Struct(self, Molecule, mol);
5083         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5084 }
5085
5086 /*
5087  *  call-seq:
5088  *     self == Molecule -> boolean
5089  *
5090  *  True if the two arguments point to the same molecule.
5091  */
5092 static VALUE
5093 s_Molecule_Equal(VALUE self, VALUE val)
5094 {
5095         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
5096                 Molecule *mol1, *mol2;
5097                 Data_Get_Struct(self, Molecule, mol1);
5098                 Data_Get_Struct(val, Molecule, mol2);
5099                 return (mol1 == mol2 ? Qtrue : Qfalse);
5100         } else return Qfalse;
5101 }
5102
5103 #pragma mark ------ Load/Save ------
5104
5105 static void
5106 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5107 {
5108         if (gLoadSaveErrorMessage != NULL) {
5109                 MyAppCallback_setConsoleColor(1);
5110                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5111                 MyAppCallback_setConsoleColor(0);
5112         }
5113         if (status != 0)
5114                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5115 }
5116
5117 /*
5118  *  call-seq:
5119  *     loadmbsf(file)       -> bool
5120  *
5121  *  Read a structure from a mbsf file.
5122  *  Return true if successful.
5123  */
5124 static VALUE
5125 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5126 {
5127         VALUE fname;
5128         char *fstr;
5129         Molecule *mol;
5130         int retval;
5131         MoleculeClearLoadSaveErrorMessage();
5132         Data_Get_Struct(self, Molecule, mol);
5133         rb_scan_args(argc, argv, "1", &fname);
5134         fstr = FileStringValuePtr(fname);
5135         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5136         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5137         return Qtrue;   
5138 }
5139
5140 /*
5141  *  call-seq:
5142  *     loadpsf(file, pdbfile = nil)       -> bool
5143  *
5144  *  Read a structure from a psf file. molecule must be empty. The psf may be
5145  *  an "extended" version, which also contains coordinates. If pdbfile 
5146  *  is given, then atomic coordinates are read from that file.
5147  *  Return true if successful.
5148  */
5149 static VALUE
5150 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5151 {
5152         VALUE fname, pdbname;
5153         char *fstr, *pdbstr;
5154         Molecule *mol;
5155         int retval;
5156         Data_Get_Struct(self, Molecule, mol);
5157         if (mol->natoms > 0)
5158                 return Qnil;  /*  Must be a new molecule  */
5159         MoleculeClearLoadSaveErrorMessage();
5160         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5161         fstr = FileStringValuePtr(fname);
5162         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5163         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5164         pdbstr = NULL;
5165         if (!NIL_P(pdbname)) {
5166                 pdbstr = strdup(FileStringValuePtr(pdbname));
5167                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5168                 free(pdbstr);
5169                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5170         }
5171         return Qtrue;
5172 }
5173
5174 /*
5175  *  call-seq:
5176  *     loadpdb(file)       -> bool
5177  *
5178  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5179  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5180  *  Return true if successful.
5181  */
5182 static VALUE
5183 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5184 {
5185         VALUE fname;
5186         char *fstr;
5187         Molecule *mol;
5188         int retval;
5189         Data_Get_Struct(self, Molecule, mol);
5190         rb_scan_args(argc, argv, "1", &fname);
5191         MoleculeClearLoadSaveErrorMessage();
5192         fstr = FileStringValuePtr(fname);
5193         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5194         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5195         return Qtrue;   
5196 }
5197
5198 /*
5199  *  call-seq:
5200  *     loaddcd(file)       -> bool
5201  *
5202  *  Read coordinates from a dcd file. The molecule should not empty.
5203  *  Return true if successful.
5204  */
5205 static VALUE
5206 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5207 {
5208         VALUE fname;
5209         char *fstr;
5210         Molecule *mol;
5211         int retval;
5212         Data_Get_Struct(self, Molecule, mol);
5213         rb_scan_args(argc, argv, "1", &fname);
5214         MoleculeClearLoadSaveErrorMessage();
5215         fstr = FileStringValuePtr(fname);
5216         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5217         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5218         return Qtrue;   
5219 }
5220
5221 /*
5222  *  call-seq:
5223  *     loadtep(file)       -> bool
5224  *
5225  *  Read coordinates from an ortep .tep file.
5226  *  Return true if successful.
5227  */
5228 static VALUE
5229 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5230 {
5231         VALUE fname;
5232         char *fstr;
5233         Molecule *mol;
5234         int retval;
5235         Data_Get_Struct(self, Molecule, mol);
5236         rb_scan_args(argc, argv, "1", &fname);
5237         MoleculeClearLoadSaveErrorMessage();
5238         fstr = FileStringValuePtr(fname);
5239         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5240         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5241         return Qtrue;   
5242 }
5243
5244 /*
5245  *  call-seq:
5246  *     loadres(file)       -> bool
5247  *
5248  *  Read coordinates from a shelx .res file.
5249  *  Return true if successful.
5250  */
5251 static VALUE
5252 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5253 {
5254         VALUE fname;
5255         char *fstr;
5256         Molecule *mol;
5257         int retval;
5258         Data_Get_Struct(self, Molecule, mol);
5259         rb_scan_args(argc, argv, "1", &fname);
5260         MoleculeClearLoadSaveErrorMessage();
5261         fstr = FileStringValuePtr(fname);
5262         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5263         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5264         return Qtrue;   
5265 }
5266
5267 /*
5268  *  call-seq:
5269  *     loadfchk(file)       -> bool
5270  *
5271  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5272  *  Return true if successful.
5273  */
5274 static VALUE
5275 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5276 {
5277         VALUE fname;
5278         char *fstr;
5279         Molecule *mol;
5280         int retval;
5281         Data_Get_Struct(self, Molecule, mol);
5282         rb_scan_args(argc, argv, "1", &fname);
5283         MoleculeClearLoadSaveErrorMessage();
5284         fstr = FileStringValuePtr(fname);
5285         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5286         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5287         return Qtrue;   
5288 }
5289
5290 /*
5291  *  call-seq:
5292  *     loaddat(file)       -> bool
5293  *
5294  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5295  *  Return true if successful.
5296  */
5297 static VALUE
5298 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5299 {
5300         VALUE fname;
5301         char *fstr;
5302         Molecule *mol;
5303         int retval;
5304         Data_Get_Struct(self, Molecule, mol);
5305         rb_scan_args(argc, argv, "1", &fname);
5306         MoleculeClearLoadSaveErrorMessage();
5307         fstr = FileStringValuePtr(fname);
5308         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5309         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5310         MyAppCallback_hideProgressPanel();
5311         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5312         return Qtrue;   
5313 }
5314
5315 /*
5316  *  call-seq:
5317  *     savembsf(file)       -> bool
5318  *
5319  *  Write structure as a mbsf file. Returns true if successful.
5320  */
5321 static VALUE
5322 s_Molecule_Savembsf(VALUE self, VALUE fname)
5323 {
5324         char *fstr;
5325     Molecule *mol;
5326         int retval;
5327     Data_Get_Struct(self, Molecule, mol);
5328         MoleculeClearLoadSaveErrorMessage();
5329         fstr = FileStringValuePtr(fname);
5330         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5331         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5332         return Qtrue;
5333 }
5334
5335 /*
5336  *  call-seq:
5337  *     savepsf(file)       -> bool
5338  *
5339  *  Write structure as a psf file. Returns true if successful.
5340  */
5341 static VALUE
5342 s_Molecule_Savepsf(VALUE self, VALUE fname)
5343 {
5344         char *fstr;
5345     Molecule *mol;
5346         int retval;
5347     Data_Get_Struct(self, Molecule, mol);
5348         MoleculeClearLoadSaveErrorMessage();
5349         fstr = FileStringValuePtr(fname);
5350         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5351         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5352         return Qtrue;
5353 }
5354
5355 /*
5356  *  call-seq:
5357  *     savepdb(file)       -> bool
5358  *
5359  *  Write coordinates as a pdb file. Returns true if successful.
5360  */
5361 static VALUE
5362 s_Molecule_Savepdb(VALUE self, VALUE fname)
5363 {
5364         char *fstr;
5365     Molecule *mol;
5366         int retval;
5367     Data_Get_Struct(self, Molecule, mol);
5368         MoleculeClearLoadSaveErrorMessage();
5369         fstr = FileStringValuePtr(fname);
5370         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5371         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5372         return Qtrue;
5373 }
5374
5375 /*
5376  *  call-seq:
5377  *     savedcd(file)       -> bool
5378  *
5379  *  Write coordinates as a dcd file. Returns true if successful.
5380  */
5381 static VALUE
5382 s_Molecule_Savedcd(VALUE self, VALUE fname)
5383 {
5384         char *fstr;
5385     Molecule *mol;
5386         int retval;
5387     Data_Get_Struct(self, Molecule, mol);
5388         MoleculeClearLoadSaveErrorMessage();
5389         fstr = FileStringValuePtr(fname);
5390         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5391         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5392         return Qtrue;
5393 }
5394
5395 /*  load([ftype, ] fname, ...)  */
5396 static VALUE
5397 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5398 {
5399         VALUE rval;
5400         char *argstr, *methname, *p, *type = "";
5401         ID mid = 0;
5402         int i;
5403         const char *ls = (loadFlag ? "load" : "save");
5404         int lslen = strlen(ls);
5405
5406         if (argc == 0)
5407                 return Qnil;
5408
5409         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5410                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5411         if (argstr[0] == ':') {
5412                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5413                 methname = ALLOC_N(char, lslen + strlen(argstr));
5414                 strcpy(methname, ls);
5415                 strcat(methname, argstr + 1);
5416                 type = argstr + 1;
5417                 for (i = lslen; methname[i] != 0; i++)
5418                         methname[i] = tolower(methname[i]);
5419                 mid = rb_intern(methname);
5420                 xfree(methname);
5421                 argc--;
5422                 argv++;
5423                 rval = rb_funcall2(self, mid, argc, argv);
5424                 if (rval == Qnil)
5425                         goto failure;
5426                 else
5427                         goto success;
5428         }
5429         /*  Guess file type from extension  */
5430         p = strrchr(argstr, '.');
5431         if (p != NULL) {
5432                 p++;
5433                 type = p;
5434                 for (methname = p; *methname != 0; methname++) {
5435                         if (!isalpha(*methname))
5436                                 break;
5437                 }
5438                 if (*methname == 0) {
5439                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5440                         if (methname == NULL)
5441                                 rb_raise(rb_eMolbyError, "Low memory");
5442                         strcpy(methname, ls);
5443                         strcat(methname, p);
5444                         for (i = lslen; methname[i] != 0; i++)
5445                                 methname[i] = tolower(methname[i]);
5446                         mid = rb_intern(methname);
5447                         xfree(methname);
5448                         if (loadFlag) {
5449                                 if (rb_respond_to(self, mid)) {
5450                                         /*  Load: try to call the load procedure only if it is available  */
5451                                         rval = rb_funcall2(self, mid, argc, argv);
5452                                         if (rval != Qnil)
5453                                                 goto success;
5454                                 }
5455                         } else {
5456                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5457                                 rval = rb_funcall2(self, mid, argc, argv);
5458                                 if (rval != Qnil)
5459                                         goto success;
5460                         }
5461                 }
5462         }
5463 failure:
5464         rval = rb_str_to_str(argv[0]);
5465         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5466         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5467         return Qnil;  /*  Does not reach here  */
5468
5469 success:
5470         {
5471                 /*  Register the path  */
5472                 Molecule *mol;
5473         /*      Atom *ap; */
5474                 Data_Get_Struct(self, Molecule, mol);
5475                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5476                 
5477                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5478         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5479                         if (ap->occupancy != 0.0)
5480                                 break;
5481                 }
5482                 if (i == mol->natoms) {
5483                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5484                                 ap->occupancy = 1.0;
5485                         }
5486                 } */
5487         }
5488         return rval;
5489 }
5490
5491 /*
5492  *  call-seq:
5493  *     molload(file, *args)       -> bool
5494  *
5495  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5496  *  file type given by the extension). If this method fails, then all defined (public)
5497  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5498  */
5499 static VALUE
5500 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5501 {
5502         return s_Molecule_LoadSave(argc, argv, self, 1);
5503 }
5504
5505 /*
5506  *  call-seq:
5507  *     molsave(file, *args)       -> bool
5508  *
5509  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5510  *  (XXX is the file type given by the extension).
5511  */
5512 static VALUE
5513 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5514 {
5515         return s_Molecule_LoadSave(argc, argv, self, 0);
5516 }
5517
5518 /*
5519  *  call-seq:
5520  *     open        -> Molecule
5521  *     open(file)  -> Molecule
5522  *
5523  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5524  */
5525 static VALUE
5526 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5527 {
5528         VALUE fname;
5529         const char *p;
5530         Molecule *mp;
5531         VALUE iflag;
5532     
5533     if (!gUseGUI) {
5534         rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5535     }
5536     
5537         rb_scan_args(argc, argv, "01", &fname);
5538         if (NIL_P(fname))
5539                 p = NULL;
5540         else
5541                 p = FileStringValuePtr(fname);
5542         iflag = Ruby_SetInterruptFlag(Qfalse);
5543         mp = MoleculeCallback_openNewMolecule(p);
5544         Ruby_SetInterruptFlag(iflag);
5545         if (mp == NULL) {
5546                 if (p == NULL)
5547                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5548                 else
5549                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5550         }
5551         return ValueFromMolecule(mp);
5552 }
5553
5554 /*
5555  *  call-seq:
5556  *     new  -> Molecule
5557  *     new(file, *args)  -> Molecule
5558  *
5559  *  Create a new molecule and call "load" method with the same arguments.
5560  */
5561 static VALUE
5562 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5563 {
5564         if (argc > 0)
5565                 return s_Molecule_Load(argc, argv, self);
5566         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5567 }
5568
5569 /*
5570  *  call-seq:
5571  *     error_message       -> String
5572  *
5573  *  Get the error_message from the last load/save method. If no error, returns nil.
5574  */
5575 static VALUE
5576 s_Molecule_ErrorMessage(VALUE klass)
5577 {
5578         if (gLoadSaveErrorMessage == NULL)
5579                 return Qnil;
5580         else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5581 }
5582
5583 /*
5584  *  call-seq:
5585  *     set_error_message(String)
5586  *     Molecule.error_message = String
5587  *
5588  *  Set the error_message for the present load/save method.
5589  */
5590 static VALUE
5591 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5592 {
5593         if (gLoadSaveErrorMessage != NULL) {
5594                 free(gLoadSaveErrorMessage);
5595                 gLoadSaveErrorMessage = NULL;
5596         }
5597         if (sval != Qnil) {
5598                 sval = rb_str_to_str(sval);
5599                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5600         }
5601         return sval;
5602 }
5603
5604 /*
5605  *  call-seq:
5606  *     set_molecule(Molecule)
5607  *
5608  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5609  *  This method is exclusively used for associating a new document with an existing molecule.
5610  */
5611 static VALUE
5612 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5613 {
5614         Molecule *mp1, *mp2;
5615         Data_Get_Struct(self, Molecule, mp1);
5616         mp2 = MoleculeFromValue(mval);
5617         MoleculeInitWithMolecule(mp1, mp2);
5618         MoleculeCallback_notifyModification(mp1, 1);
5619         return self;
5620 }
5621
5622 #pragma mark ------ Name attributes ------
5623
5624 /*
5625  *  call-seq:
5626  *     name       -> String
5627  *
5628  *  Returns the display name of the molecule. If the molecule has no associated
5629  *  document, then returns nil.
5630  */
5631 static VALUE
5632 s_Molecule_Name(VALUE self)
5633 {
5634     Molecule *mol;
5635         char buf[1024];
5636     Data_Get_Struct(self, Molecule, mol);
5637         MoleculeCallback_displayName(mol, buf, sizeof buf);
5638         if (buf[0] == 0)
5639                 return Qnil;
5640         else
5641                 return Ruby_NewEncodedStringValue2(buf);
5642 }
5643
5644 /*
5645  *  call-seq:
5646  *     set_name(string) -> self
5647  *
5648  *  Set the name of an untitled molecule. If the molecule is not associated with window
5649  *  or it already has an associated file, then exception is thrown.
5650  */
5651 static VALUE
5652 s_Molecule_SetName(VALUE self, VALUE nval)
5653 {
5654     Molecule *mol;
5655     Data_Get_Struct(self, Molecule, mol);
5656         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5657                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5658         return self;
5659 }
5660
5661
5662 /*
5663  *  call-seq:
5664  *     path       -> String
5665  *
5666  *  Returns the full path name of the molecule, if it is associated with a file.
5667  *  If the molecule has no associated file, then returns nil.
5668  */
5669 static VALUE
5670 s_Molecule_Path(VALUE self)
5671 {
5672     Molecule *mol;
5673         char buf[1024];
5674     Data_Get_Struct(self, Molecule, mol);
5675         MoleculeCallback_pathName(mol, buf, sizeof buf);
5676         if (buf[0] == 0)
5677                 return Qnil;
5678         else
5679                 return Ruby_NewFileStringValue(buf);
5680 }
5681
5682 /*
5683  *  call-seq:
5684  *     dir       -> String
5685  *
5686  *  Returns the full path name of the directory in which the file associated with the
5687  *  molecule is located. If the molecule has no associated file, then returns nil.
5688  */
5689 static VALUE
5690 s_Molecule_Dir(VALUE self)
5691 {
5692     Molecule *mol;
5693         char buf[1024], *p;
5694     Data_Get_Struct(self, Molecule, mol);
5695         MoleculeCallback_pathName(mol, buf, sizeof buf);
5696 #if __WXMSW__
5697         translate_char(buf, '\\', '/');
5698 #endif
5699         if (buf[0] == 0)
5700                 return Qnil;
5701         else {
5702                 p = strrchr(buf, '/');
5703                 if (p != NULL)
5704                         *p = 0;
5705                 return Ruby_NewEncodedStringValue2(buf);
5706         }
5707 }
5708
5709 /*
5710  *  call-seq:
5711  *     inspect       -> String
5712  *
5713  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5714  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5715  *  the Molecule structure) is returned.
5716  */
5717 static VALUE
5718 s_Molecule_Inspect(VALUE self)
5719 {
5720     Molecule *mol;
5721         char buf[256];
5722     Data_Get_Struct(self, Molecule, mol);
5723         MoleculeCallback_displayName(mol, buf, sizeof buf);
5724         if (buf[0] == 0) {
5725                 /*  No associated document  */
5726                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5727                 return Ruby_NewEncodedStringValue2(buf);
5728         } else {
5729                 /*  Check whether the document name is duplicate  */
5730                 char buf2[256];
5731                 int idx, k, k2;
5732                 Molecule *mol2;
5733                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5734                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5735                         if (strcmp(buf, buf2) == 0) {
5736                                 k++;
5737                                 if (mol == mol2)
5738                                         k2 = k;
5739                         }
5740                 }
5741                 if (k > 1) {
5742                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5743                 } else {
5744                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5745                 }
5746                 return Ruby_NewEncodedStringValue2(buf2);
5747         }
5748 }
5749
5750 #pragma mark ------ MolEnumerables ------
5751
5752 static VALUE
5753 s_Molecule_MolEnumerable(VALUE self, int kind)
5754 {
5755     Molecule *mol;
5756         MolEnumerable *mseq;
5757     Data_Get_Struct(self, Molecule, mol);
5758         mseq = MolEnumerableNew(mol, kind);
5759         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5760 }
5761
5762 /*
5763  *  call-seq:
5764  *     atoms       -> MolEnumerable
5765  *
5766  *  Returns a MolEnumerable object representing the array of atoms.
5767  */
5768 static VALUE
5769 s_Molecule_Atoms(VALUE self)
5770 {
5771         return s_Molecule_MolEnumerable(self, kAtomKind);
5772 }
5773
5774 /*
5775  *  call-seq:
5776  *     bonds       -> MolEnumerable
5777  *
5778  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5779  *  by an array of two atom indices.
5780  */
5781 static VALUE
5782 s_Molecule_Bonds(VALUE self)
5783 {
5784         return s_Molecule_MolEnumerable(self, kBondKind);
5785 }
5786
5787 /*
5788  *  call-seq:
5789  *     angles       -> MolEnumerable
5790  *
5791  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5792  *  by an array of three atom indices.
5793  */
5794 static VALUE
5795 s_Molecule_Angles(VALUE self)
5796 {
5797         return s_Molecule_MolEnumerable(self, kAngleKind);
5798 }
5799
5800 /*
5801  *  call-seq:
5802  *     dihedrals       -> MolEnumerable
5803  *
5804  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5805  *  by an array of four atom indices.
5806  */
5807 static VALUE
5808 s_Molecule_Dihedrals(VALUE self)
5809 {
5810         return s_Molecule_MolEnumerable(self, kDihedralKind);
5811 }
5812
5813 /*
5814  *  call-seq:
5815  *     impropers       -> MolEnumerable
5816  *
5817  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5818  *  by an array of four atom indices.
5819  */
5820 static VALUE
5821 s_Molecule_Impropers(VALUE self)
5822 {
5823         return s_Molecule_MolEnumerable(self, kImproperKind);
5824 }
5825
5826 /*
5827  *  call-seq:
5828  *     residues       -> MolEnumerable
5829  *
5830  *  Returns a MolEnumerable object representing the array of residue names.
5831  */
5832 static VALUE
5833 s_Molecule_Residues(VALUE self)
5834 {
5835         return s_Molecule_MolEnumerable(self, kResidueKind);
5836 }
5837
5838 /*
5839  *  call-seq:
5840  *     natoms       -> Integer
5841  *
5842  *  Returns the number of atoms.
5843  */
5844 static VALUE
5845 s_Molecule_Natoms(VALUE self)
5846 {
5847     Molecule *mol;
5848     Data_Get_Struct(self, Molecule, mol);
5849         return INT2NUM(mol->natoms);
5850 }
5851
5852 /*
5853  *  call-seq:
5854  *     nbonds       -> Integer
5855  *
5856  *  Returns the number of bonds.
5857  */
5858 static VALUE
5859 s_Molecule_Nbonds(VALUE self)
5860 {
5861     Molecule *mol;
5862     Data_Get_Struct(self, Molecule, mol);
5863         return INT2NUM(mol->nbonds);
5864 }
5865
5866 /*
5867  *  call-seq:
5868  *     nangles       -> Integer
5869  *
5870  *  Returns the number of angles.
5871  */
5872 static VALUE
5873 s_Molecule_Nangles(VALUE self)
5874 {
5875     Molecule *mol;
5876     Data_Get_Struct(self, Molecule, mol);
5877         return INT2NUM(mol->nangles);
5878 }
5879
5880 /*
5881  *  call-seq:
5882  *     ndihedrals       -> Integer
5883  *
5884  *  Returns the number of dihedrals.
5885  */
5886 static VALUE
5887 s_Molecule_Ndihedrals(VALUE self)
5888 {
5889     Molecule *mol;
5890     Data_Get_Struct(self, Molecule, mol);
5891         return INT2NUM(mol->ndihedrals);
5892 }
5893
5894 /*
5895  *  call-seq:
5896  *     nimpropers       -> Integer
5897  *
5898  *  Returns the number of impropers.
5899  */
5900 static VALUE
5901 s_Molecule_Nimpropers(VALUE self)
5902 {
5903     Molecule *mol;
5904     Data_Get_Struct(self, Molecule, mol);
5905         return INT2NUM(mol->nimpropers);
5906 }
5907
5908 /*
5909  *  call-seq:
5910  *     nresidues       -> Integer
5911  *
5912  *  Returns the number of residues.
5913  */
5914 static VALUE
5915 s_Molecule_Nresidues(VALUE self)
5916 {
5917     Molecule *mol;
5918     Data_Get_Struct(self, Molecule, mol);
5919         return INT2NUM(mol->nresidues);
5920 }
5921
5922 /*
5923  *  call-seq:
5924  *     nresidues = Integer
5925  *
5926  *  Change the number of residues.
5927  */
5928 static VALUE
5929 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5930 {
5931     Molecule *mol;
5932         int ival = NUM2INT(val);
5933     Data_Get_Struct(self, Molecule, mol);
5934         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5935         if (ival != mol->nresidues)
5936                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5937         return val;
5938 }
5939
5940 /*
5941  *  call-seq:
5942  *     max_residue_number(atom_group = nil)     -> Integer
5943  *
5944  *  Returns the maximum residue number actually used. If an atom group is given, only
5945  *  these atoms are examined. If no atom is present, nil is returned.
5946  */
5947 static VALUE
5948 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5949 {
5950     Molecule *mol;
5951         VALUE gval;
5952         int maxSeq;
5953         IntGroup *ig;
5954     Data_Get_Struct(self, Molecule, mol);
5955         rb_scan_args(argc, argv, "01", &gval);
5956         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5957         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5958         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5959 }
5960
5961 /*
5962  *  call-seq:
5963  *     min_residue_number(atom_group = nil)     -> Integer
5964  *
5965  *  Returns the minimum residue number actually used. If an atom group is given, only
5966  *  these atoms are examined. If no atom is present, nil is returned.
5967  */
5968 static VALUE
5969 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5970 {
5971     Molecule *mol;
5972         VALUE gval;
5973         int minSeq;
5974         IntGroup *ig;
5975     Data_Get_Struct(self, Molecule, mol);
5976         rb_scan_args(argc, argv, "01", &gval);
5977         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5978         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5979         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5980 }
5981
5982 /*
5983  *  call-seq:
5984  *     each_atom(atom_group = nil) {|aref| ...}
5985  *
5986  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5987  *  group is given, only these atoms are processed.
5988  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5989  *  is self (a Molecule object).
5990  */
5991 static VALUE
5992 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5993 {
5994         int i;
5995     Molecule *mol;
5996         AtomRef *aref;
5997         VALUE arval;
5998         VALUE gval;
5999         IntGroup *ig;
6000     Data_Get_Struct(self, Molecule, mol);
6001         rb_scan_args(argc, argv, "01", &gval);
6002         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6003         arval = ValueFromMoleculeAndIndex(mol, 0);
6004         Data_Get_Struct(arval, AtomRef, aref);
6005         for (i = 0; i < mol->natoms; i++) {
6006                 aref->idx = i;
6007                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
6008                         rb_yield(arval);
6009         }
6010         if (ig != NULL)
6011                 IntGroupRelease(ig);
6012     return self;
6013 }
6014
6015 #pragma mark ------ Atom Group ------
6016
6017 static VALUE
6018 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6019 {
6020         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6021         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6022         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6023         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6024         return Qnil;
6025 }
6026
6027 /*
6028  *  call-seq:
6029  *     atom_group
6030  *     atom_group {|aref| ...}
6031  *     atom_group(arg1, arg2, ...)
6032  *     atom_group(arg1, arg2, ...) {|aref| ...}
6033  *
6034  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6035  *  If arguments are given, then the atoms reprensented by the arguments are added to the
6036  *  group. For a conversion of a string to an atom index, see the description
6037  *  of Molecule#atom_index.
6038  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
6039  *  representing each atom, and the atoms are removed from the result if the block returns false.
6040  *
6041  */
6042 static VALUE
6043 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6044 {
6045         IntGroup *ig1, *ig2;
6046     Molecule *mol;
6047         Int i, startPt, interval;
6048         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6049         Data_Get_Struct(retval, IntGroup, ig1);
6050     Data_Get_Struct(self, Molecule, mol);
6051         if (argc == 0) {
6052                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6053         } else {
6054                 while (argc > 0) {
6055                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6056                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6057                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6058                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6059                                 ig2 = IntGroupFromValue(*argv);
6060                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6061                                         interval = IntGroupGetInterval(ig2, i);
6062                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6063                                 }
6064                                 IntGroupRelease(ig2);
6065                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
6066                                 VALUE values[2];
6067                                 values[0] = (VALUE)mol;
6068                                 values[1] = (VALUE)ig1;
6069                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6070                         } else
6071                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6072                         argc--;
6073                         argv++;
6074                 }
6075         }
6076         if (rb_block_given_p()) {
6077                 /*  Evaluate the given block with an AtomRef as the argument, and delete
6078                         the index if the block returns false  */
6079                 AtomRef *aref = AtomRefNew(mol, 0);
6080                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6081                 ig2 = IntGroupNew();
6082                 IntGroupCopy(ig2, ig1);
6083                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6084                         VALUE resval;
6085                         if (startPt >= mol->natoms)
6086                                 break;
6087                         aref->idx = startPt;
6088                         resval = rb_yield(arval);
6089                         if (!RTEST(resval))
6090                                 IntGroupRemove(ig1, startPt, 1);
6091                 }
6092                 IntGroupRelease(ig2);
6093         }
6094         
6095         /*  Remove points that are out of bounds */
6096         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6097
6098         return retval;                  
6099 }
6100
6101 /*
6102  *  call-seq:
6103  *     selection       -> IntGroup
6104  *
6105  *  Returns the current selection.
6106  */
6107 static VALUE
6108 s_Molecule_Selection(VALUE self)
6109 {
6110     Molecule *mol;
6111         IntGroup *ig;
6112         VALUE val;
6113     Data_Get_Struct(self, Molecule, mol);
6114         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6115                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
6116                 val = ValueFromIntGroup(ig);
6117                 IntGroupRelease(ig);
6118         } else {
6119                 val = IntGroup_Alloc(rb_cIntGroup);
6120         }
6121         return val;
6122 }
6123
6124 static VALUE
6125 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6126 {
6127     Molecule *mol;
6128         IntGroup *ig;
6129     Data_Get_Struct(self, Molecule, mol);
6130         if (val == Qnil)
6131                 ig = NULL;
6132         else
6133                 ig = s_Molecule_AtomGroupFromValue(self, val);
6134         if (undoable)
6135                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6136         else
6137                 MoleculeSetSelection(mol, ig);
6138         if (ig != NULL)
6139                 IntGroupRelease(ig);
6140         return val;
6141 }
6142
6143 /*
6144  *  call-seq:
6145  *     selection = IntGroup
6146  *
6147  *  Set the current selection. The right-hand operand may be nil.
6148  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6149  */
6150 static VALUE
6151 s_Molecule_SetSelection(VALUE self, VALUE val)
6152 {
6153         return s_Molecule_SetSelectionSub(self, val, 0);
6154 }
6155
6156 /*
6157  *  call-seq:
6158  *     set_undoable_selection(IntGroup)
6159  *
6160  *  Set the current selection with undo registration. The right-hand operand may be nil.
6161  *  This operation is undoable.
6162  */
6163 static VALUE
6164 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6165 {
6166         return s_Molecule_SetSelectionSub(self, val, 1);
6167 }
6168
6169 #pragma mark ------ Editing ------
6170
6171 /*
6172  *  call-seq:
6173  *     extract(group, dummy_flag = nil)       -> Molecule
6174  *
6175  *  Extract the atoms given by group and return as a new molecule object.
6176  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6177  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6178  *  names beginning with an underscore) and included in the new molecule object.
6179  */
6180 static VALUE
6181 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6182 {
6183     Molecule *mol1, *mol2;
6184         IntGroup *ig;
6185         VALUE group, dummy_flag, retval;
6186     Data_Get_Struct(self, Molecule, mol1);
6187         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6188         ig = s_Molecule_AtomGroupFromValue(self, group);
6189         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6190                 retval = Qnil;
6191         } else {
6192                 retval = ValueFromMolecule(mol2);
6193         }
6194         IntGroupRelease(ig);
6195         return retval;
6196 }
6197
6198 /*
6199  *  call-seq:
6200  *     add(molecule2)       -> self
6201  *
6202  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6203     conflicts.
6204     This operation is undoable.
6205  */
6206 static VALUE
6207 s_Molecule_Add(VALUE self, VALUE val)
6208 {
6209     Molecule *mol1, *mol2;
6210     Data_Get_Struct(self, Molecule, mol1);
6211         mol2 = MoleculeFromValue(val);
6212         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6213         return self; 
6214 }
6215
6216 /*
6217  *  call-seq:
6218  *     remove(group)       -> Molecule
6219  *
6220  *  The atoms designated by the given group are removed from the molecule.
6221  *  This operation is undoable.
6222  */
6223 static VALUE
6224 s_Molecule_Remove(VALUE self, VALUE group)
6225 {
6226     Molecule *mol1;
6227         IntGroup *ig, *bg;
6228         Int i;
6229         IntGroupIterator iter;
6230
6231     ig = s_Molecule_AtomGroupFromValue(self, group);
6232 /*    Data_Get_Struct(self, Molecule, mol1);
6233         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6234         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6235                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6236         Data_Get_Struct(group, IntGroup, ig); */
6237     Data_Get_Struct(self, Molecule, mol1);
6238     
6239         /*  Remove the bonds between the two fragments  */
6240         /*  (This is necessary for undo to work correctly)  */
6241         IntGroupIteratorInit(ig, &iter);
6242         bg = NULL;
6243         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6244                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6245                 Int j, *cp;
6246                 cp = AtomConnectData(&ap->connect);
6247                 for (j = 0; j < ap->connect.count; j++) {
6248                         int n = cp[j];
6249                         if (!IntGroupLookup(ig, n, NULL)) {
6250                                 /*  bond i-n, i is in ig and n is not  */
6251                                 int k = MoleculeLookupBond(mol1, i, n);
6252                                 if (k >= 0) {
6253                                         if (bg == NULL)
6254                                                 bg = IntGroupNew();
6255                                         IntGroupAdd(bg, k, 1);
6256                                 }
6257                         }
6258                 }
6259         }
6260         IntGroupIteratorRelease(&iter);
6261         if (bg != NULL) {
6262                 /*  Remove bonds  */
6263                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6264                 IntGroupRelease(bg);
6265         }
6266         /*  Remove atoms  */
6267         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6268                 return Qnil;
6269         return self;
6270 }
6271
6272 /*
6273  *  call-seq:
6274  *     create_atom(name, pos = -1)  -> AtomRef
6275  *
6276  *  Create a new atom with the specified name (may contain residue 
6277  *  information) and position (if position is out of range, the atom is appended at
6278  *  the end). Returns the reference to the new atom.
6279  *  This operation is undoable.
6280  */
6281 static VALUE
6282 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6283 {
6284     Molecule *mol;
6285     Int i, pos;
6286         VALUE name, ival;
6287     Atom arec;
6288     AtomRef *aref;
6289         char *p, resName[6], atomName[6];
6290         int resSeq;
6291     Data_Get_Struct(self, Molecule, mol);
6292         rb_scan_args(argc, argv, "02", &name, &ival);
6293         if (ival != Qnil)
6294                 pos = NUM2INT(rb_Integer(ival));
6295         else pos = -1;
6296         if (name != Qnil) {
6297                 p = StringValuePtr(name);
6298                 if (p[0] != 0) {
6299                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6300                         if (atomName[0] == 0)
6301                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6302                 }
6303         } else p = NULL;
6304         if (p == NULL || p[0] == 0) {
6305                 memset(atomName, 0, 4);
6306                 resSeq = -1;
6307         }
6308     memset(&arec, 0, sizeof(arec));
6309     strncpy(arec.aname, atomName, 4);
6310     if (resSeq >= 0) {
6311       strncpy(arec.resName, resName, 4);
6312       arec.resSeq = resSeq;
6313     }
6314         arec.occupancy = 1.0;
6315 //    i = MoleculeCreateAnAtom(mol, &arec);
6316         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6317                 return Qnil;
6318     aref = AtomRefNew(mol, pos);
6319     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6320 }
6321
6322 /*
6323  *  call-seq:
6324  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6325  *
6326  *  Create a new atom with the same attributes (but no bonding information)
6327  *  with the specified atom. Returns the reference to the new atom.
6328  *  This operation is undoable.
6329  */
6330 static VALUE
6331 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6332 {
6333     Molecule *mol;
6334         const Atom *apsrc;
6335     Atom arec;
6336         AtomRef *aref;
6337         VALUE retval, aval, ival;
6338         Int pos;
6339     Data_Get_Struct(self, Molecule, mol);
6340         rb_scan_args(argc, argv, "11", &aval, &ival);
6341         if (FIXNUM_P(aval)) {
6342                 int idx = NUM2INT(aval);
6343                 if (idx < 0 || idx >= mol->natoms)
6344                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6345                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6346         } else {
6347                 apsrc = s_AtomFromValue(aval);
6348         }
6349         if (apsrc == NULL)
6350                 rb_raise(rb_eMolbyError, "bad atom specification");
6351         if (ival != Qnil)
6352                 pos = NUM2INT(rb_Integer(ival));
6353         else pos = -1;
6354         AtomDuplicate(&arec, apsrc);
6355         arec.connect.count = 0;
6356         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6357                 retval = Qnil;
6358         else {
6359                 aref = AtomRefNew(mol, pos);
6360                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6361         }
6362         AtomClean(&arec);
6363         return retval;
6364 }
6365
6366 /*
6367  *  call-seq:
6368  *     create_bond(n1, n2, ...)       -> Integer
6369  *
6370  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6371  *  do nothing for that pair. Returns the number of bonds actually created.
6372  *  This operation is undoable.
6373  */
6374 static VALUE
6375 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6376 {
6377     Molecule *mol;
6378         Int i, j, k, *ip, old_nbonds;
6379         if (argc == 0)
6380                 rb_raise(rb_eMolbyError, "missing arguments");
6381         if (argc % 2 != 0)
6382                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6383     Data_Get_Struct(self, Molecule, mol);
6384         ip = ALLOC_N(Int, argc + 1);
6385         for (i = j = 0; i < argc; i++, j++) {
6386                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6387                 if (i % 2 == 1) {
6388                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6389                                 j -= 2;  /*  This bond is already present: skip it  */
6390                         else {
6391                                 for (k = 0; k < j - 1; k += 2) {
6392                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6393                                                 j -= 2;   /*  The same entry is already in the argument  */
6394                                                 break;
6395                                         }
6396                                 }
6397                         }
6398                 }
6399         }
6400         old_nbonds = mol->nbonds;
6401         if (j > 0) {
6402                 ip[j] = kInvalidIndex;
6403                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6404         } else i = 0;
6405         xfree(ip);
6406         if (i == -1)
6407                 rb_raise(rb_eMolbyError, "atom index out of range");
6408         else if (i == -2)
6409                 rb_raise(rb_eMolbyError, "too many bonds");
6410         else if (i == -3)
6411                 rb_raise(rb_eMolbyError, "duplicate bonds");
6412         else if (i == -5)
6413                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6414         else if (i != 0)
6415                 rb_raise(rb_eMolbyError, "error in creating bonds");
6416         return INT2NUM(mol->nbonds - old_nbonds);
6417 }
6418
6419 /*
6420  *  call-seq:
6421  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6422  *
6423  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6424  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6425  *  This operation is undoable.
6426  */
6427 static VALUE
6428 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6429 {
6430     Molecule *mol;
6431         Int i, j, n[2];
6432         IntGroup *bg;
6433         if (argc == 0)
6434                 rb_raise(rb_eMolbyError, "missing arguments");
6435         if (argc % 2 != 0)
6436                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6437     Data_Get_Struct(self, Molecule, mol);
6438         bg = NULL;
6439         for (i = j = 0; i < argc; i++, j = 1 - j) {
6440                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6441                 if (j == 1) {
6442                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6443                         if (k >= 0) {
6444                                 if (bg == NULL)
6445                                         bg = IntGroupNew();
6446                                 IntGroupAdd(bg, k, 1);
6447                         }
6448                 }
6449         }
6450         if (bg != NULL) {
6451                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6452                 i = IntGroupGetCount(bg);
6453                 IntGroupRelease(bg);
6454         } else i = 0;
6455         return INT2NUM(i);
6456 }
6457
6458 /*
6459  *  call-seq:
6460  *     assign_bond_order(idx, d1)
6461  *     assign_bond_orders(group, [d1, d2, ...])
6462  *
6463  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6464  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6465  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6466  *  (This may change in the future)
6467  *  This operation is undoable.
6468  */
6469 static VALUE
6470 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6471 {
6472     Molecule *mol;
6473         IntGroup *ig;
6474     Data_Get_Struct(self, Molecule, mol);
6475         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6476                 /*  The first form  */
6477                 Int idx = NUM2INT(rb_Integer(idxval));
6478                 Double d1 = NUM2DBL(rb_Float(dval));
6479                 if (idx < 0 || idx >= mol->nbonds)
6480                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6481                 ig = IntGroupNewWithPoints(idx, 1, -1);
6482                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6483                 IntGroupRelease(ig);
6484         } else {
6485                 Int i, n;
6486                 Double *dp;
6487                 ig = IntGroupFromValue(idxval);
6488                 n = IntGroupGetCount(ig);
6489                 if (n == 0)
6490                         rb_raise(rb_eMolbyError, "the bond index is empty");
6491                 dval = rb_ary_to_ary(dval);
6492                 dp = (Double *)calloc(sizeof(Double), n);
6493                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6494                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6495                 }
6496                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6497                 free(dp);
6498                 IntGroupRelease(ig);
6499         }
6500         return self;
6501 }
6502
6503 /*
6504  *  call-seq:
6505  *     get_bond_order(idx) -> Float
6506  *     get_bond_orders(group) -> Array
6507  *
6508  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6509  *  In the second form, the bond orders at the indices in the group are returned as an array.
6510  *  If no bond order information have been assigned, returns nil (the first form)
6511  *  or an empty array (the second form).
6512  */
6513 static VALUE
6514 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6515 {
6516     Molecule *mol;
6517         IntGroup *ig;
6518         Double *dp;
6519         VALUE retval;
6520         Int i, n, numericArg;
6521     Data_Get_Struct(self, Molecule, mol);
6522         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6523                 /*  The first form  */
6524                 Int idx = NUM2INT(rb_Integer(idxval));
6525                 if (idx < 0 || idx >= mol->nbonds)
6526                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6527                 if (mol->bondOrders == NULL)
6528                         return Qnil;
6529                 ig = IntGroupNewWithPoints(idx, 1, -1);
6530                 n = 1;
6531                 numericArg = 1;
6532         } else {
6533                 if (mol->bondOrders == NULL)
6534                         return rb_ary_new();
6535                 ig = IntGroupFromValue(idxval);
6536                 n = IntGroupGetCount(ig);
6537                 if (n == 0)
6538                         rb_raise(rb_eMolbyError, "the bond index is empty");
6539                 numericArg = 0;
6540         }
6541         dp = (Double *)calloc(sizeof(Double), n);
6542         MoleculeGetBondOrders(mol, dp, ig);
6543         if (numericArg)
6544                 retval = rb_float_new(dp[0]);
6545         else {
6546                 retval = rb_ary_new();
6547                 for (i = 0; i < n; i++)
6548                         rb_ary_push(retval, rb_float_new(dp[i]));
6549         }
6550         free(dp);
6551         IntGroupRelease(ig);
6552         return retval;
6553 }
6554
6555 /*
6556  *  call-seq:
6557  *     bond_exist?(idx1, idx2) -> bool
6558  *
6559  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6560  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6561  */
6562 static VALUE
6563 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6564 {
6565         Molecule *mol;
6566         Int idx1, idx2, i;
6567         Atom *ap;
6568         Int *cp;
6569     Data_Get_Struct(self, Molecule, mol);
6570         idx1 = NUM2INT(rb_Integer(ival1));
6571         idx2 = NUM2INT(rb_Integer(ival2));
6572         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6573                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6574         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6575         cp = AtomConnectData(&ap->connect);
6576         for (i = 0; i < ap->connect.count; i++) {
6577                 if (cp[i] == idx2)
6578                         return Qtrue;
6579         }
6580         return Qfalse;
6581 }
6582
6583 /*
6584  *  call-seq:
6585  *     add_angle(n1, n2, n3)       -> Molecule
6586  *
6587  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6588  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6589  *  This operation is undoable.
6590  */
6591 static VALUE
6592 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6593 {
6594         Int n[4];
6595     Molecule *mol;
6596     Data_Get_Struct(self, Molecule, mol);
6597         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6598         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6599         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6600         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6601                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6602         n[3] = kInvalidIndex;
6603         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6604         return self;
6605 }
6606
6607 /*
6608  *  call-seq:
6609  *     remove_angle(n1, n2, n3)       -> Molecule
6610  *
6611  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6612  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6613  *  This operation is undoable.
6614  */
6615 static VALUE
6616 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6617 {
6618         Int n[4];
6619     Molecule *mol;
6620         IntGroup *ig;
6621     Data_Get_Struct(self, Molecule, mol);
6622         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6623         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6624         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6625         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6626                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6627         ig = IntGroupNewWithPoints(n[3], 1, -1);
6628         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6629         IntGroupRelease(ig);
6630         return self;
6631 }
6632
6633 /*
6634  *  call-seq:
6635  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6636  *
6637  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6638  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6639  *  This operation is undoable.
6640  */
6641 static VALUE
6642 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6643 {
6644         Int n[5];
6645     Molecule *mol;
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 (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6652                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6653         n[4] = kInvalidIndex;
6654         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6655         return self;
6656 }
6657
6658 /*
6659  *  call-seq:
6660  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6661  *
6662  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6663  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6664  *  This operation is undoable.
6665  */
6666 static VALUE
6667 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6668 {
6669         Int n[5];
6670     Molecule *mol;
6671         IntGroup *ig;
6672     Data_Get_Struct(self, Molecule, mol);
6673         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6674         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6675         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6676         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6677         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6678                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6679         ig = IntGroupNewWithPoints(n[4], 1, -1);
6680         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6681         IntGroupRelease(ig);
6682         return self;
6683 }
6684
6685 /*
6686  *  call-seq:
6687  *     add_improper(n1, n2, n3, n4)       -> Molecule
6688  *
6689  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6690  *  not automatically added when a new bond is created, so this method is more useful than
6691  *  the angle/dihedral counterpart.
6692  *  This operation is undoable.
6693  */
6694 static VALUE
6695 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6696 {
6697         Int n[5];
6698     Molecule *mol;
6699     Data_Get_Struct(self, Molecule, mol);
6700         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6701         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6702         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6703         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6704         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6705                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6706         n[4] = kInvalidIndex;
6707         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6708         return self;
6709 }
6710
6711 /*
6712  *  call-seq:
6713  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6714  *     remove_improper(intgroup)             -> Molecule
6715  *
6716  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6717  *  Returns self. Unlike angles and dihedrals, impropers are
6718  *  not automatically added when a new bond is created, so this method is more useful than
6719  *  the angle/dihedral counterpart.
6720  *  This operation is undoable.
6721  */
6722 static VALUE
6723 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6724 {
6725         Int n[5];
6726         VALUE v1, v2, v3, v4;
6727     Molecule *mol;
6728         IntGroup *ig;
6729     Data_Get_Struct(self, Molecule, mol);
6730         if (argc == 1) {
6731                 ig = IntGroupFromValue(argv[0]);
6732         } else {
6733                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6734                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6735                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6736                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6737                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6738                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6739                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6740                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6741         }
6742         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6743         IntGroupRelease(ig);
6744         return self;
6745 }
6746
6747 /*
6748  *  call-seq:
6749  *     assign_residue(group, res)       -> Molecule
6750  *
6751  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6752  *  or "resname.resno". When the residue number is not specified, the residue number of
6753  *  the first atom in the group is used.
6754  *  This operation is undoable.
6755  */
6756 static VALUE
6757 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6758 {
6759     Molecule *mol;
6760         IntGroup *ig;
6761         char *p, *pp, buf[16];
6762         Int resid, n;
6763         Atom *ap;
6764     Data_Get_Struct(self, Molecule, mol);
6765         
6766         /*  Parse the argument res  */
6767         if (FIXNUM_P(res)) {
6768                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6769                 resid = NUM2INT(res);
6770                 buf[0] = 0;
6771         } else {
6772                 p = StringValuePtr(res);
6773                 pp = strchr(p, '.');
6774                 if (pp != NULL) {
6775                         resid = atoi(pp + 1);
6776                         n = pp - p;
6777                 } else {
6778                         resid = -1;
6779                         n = strlen(p);
6780                 }
6781                 if (n > sizeof buf - 1)
6782                         n = sizeof buf - 1;
6783                 strncpy(buf, p, n);
6784                 buf[n] = 0;
6785         }
6786         ig = s_Molecule_AtomGroupFromValue(self, range);
6787         if (ig == NULL || IntGroupGetCount(ig) == 0)
6788                 return Qnil;
6789
6790         if (resid < 0) {
6791                 /*  Use the residue number of the first specified atom  */
6792                 n = IntGroupGetNthPoint(ig, 0);
6793                 if (n >= mol->natoms)
6794                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6795                 ap = ATOM_AT_INDEX(mol->atoms, n);
6796                 resid = ap->resSeq;
6797         }
6798         /*  Change the residue number  */
6799         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6800         /*  Change the residue name if necessary  */
6801         if (buf[0] != 0) {
6802         /*      Int seqs[2];
6803                 seqs[0] = resid;
6804                 seqs[1] = kInvalidIndex; */
6805                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6806         }
6807         IntGroupRelease(ig);
6808         return self;
6809 }
6810
6811 /*
6812  *  call-seq:
6813  *     offset_residue(group, offset)       -> Molecule
6814  *
6815  *  Offset the residue number of the specified atoms. If any of the residue number gets
6816  *  negative, then exception is thrown.
6817  *  This operation is undoable.
6818  */
6819 static VALUE
6820 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6821 {
6822     Molecule *mol;
6823         IntGroup *ig;
6824         int ofs, result;
6825     Data_Get_Struct(self, Molecule, mol);
6826         ig = s_Molecule_AtomGroupFromValue(self, range);
6827         ofs = NUM2INT(offset);
6828         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6829         if (result > 0)
6830                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6831         IntGroupRelease(ig);
6832         return self;
6833 }
6834
6835 /*
6836  *  call-seq:
6837  *     renumber_atoms(array)       -> IntGroup
6838  *
6839  *  Change the order of atoms so that the atoms specified in the array argument appear
6840  *  in this order from the top of the molecule. The atoms that are not included in array
6841  *  are placed after these atoms, and these atoms are returned as an intGroup.
6842  *  This operation is undoable.
6843  */
6844 static VALUE
6845 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6846 {
6847     Molecule *mol;
6848         Int *new2old;
6849         IntGroup *ig;
6850         int i, n;
6851         VALUE *valp, retval;
6852     Data_Get_Struct(self, Molecule, mol);
6853         if (TYPE(array) != T_ARRAY)
6854                 array = rb_funcall(array, rb_intern("to_a"), 0);
6855         n = RARRAY_LEN(array);
6856         valp = RARRAY_PTR(array);
6857         new2old = ALLOC_N(Int, n + 1);
6858         for (i = 0; i < n; i++)
6859                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6860         new2old[i] = kInvalidIndex;
6861         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6862         if (i == 1)
6863                 rb_raise(rb_eMolbyError, "Atom index out of range");
6864         else if (i == 2)
6865                 rb_raise(rb_eMolbyError, "Duplicate entry");
6866         else if (i == 3)
6867                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6868         retval = IntGroup_Alloc(rb_cIntGroup);
6869         Data_Get_Struct(retval, IntGroup, ig);
6870         if (mol->natoms > n)
6871                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6872         xfree(new2old);
6873         return retval;
6874 }
6875
6876 /*
6877  *  call-seq:
6878  *     set_atom_attr(index, key, value)
6879  *
6880  *  Set the atom attribute for the specified atom.
6881  *  This operation is undoable.
6882  */
6883 static VALUE
6884 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6885 {
6886         Molecule *mol;
6887         VALUE aref, oldval;
6888     Data_Get_Struct(self, Molecule, mol);
6889         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6890         oldval = s_AtomRef_GetAttr(aref, key);
6891         if (val == Qundef)
6892                 return oldval;
6893         s_AtomRef_SetAttr(aref, key, val);
6894         return val;
6895 }
6896
6897 /*
6898  *  call-seq:
6899  *     get_atom_attr(index, key)
6900  *
6901  *  Get the atom attribute for the specified atom.
6902  */
6903 static VALUE
6904 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6905 {
6906         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6907 }
6908
6909 #pragma mark ------ Undo Support ------
6910
6911 /*
6912  *  call-seq:
6913  *     register_undo(script, *args)
6914  *
6915  *  Register an undo operation with the current molecule.
6916  */
6917 static VALUE
6918 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6919 {
6920         Molecule *mol;
6921         VALUE script, args;
6922         MolAction *act;
6923     Data_Get_Struct(self, Molecule, mol);
6924         rb_scan_args(argc, argv, "1*", &script, &args);
6925         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6926         MolActionCallback_registerUndo(mol, act);
6927         return script;
6928 }
6929
6930 /*
6931  *  call-seq:
6932  *     undo_enabled? -> bool
6933  *
6934  *  Returns true if undo is enabled for this molecule; otherwise no.
6935  */
6936 static VALUE
6937 s_Molecule_UndoEnabled(VALUE self)
6938 {
6939     Molecule *mol;
6940     Data_Get_Struct(self, Molecule, mol);
6941         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6942                 return Qtrue;
6943         else return Qfalse;
6944 }
6945
6946 /*
6947  *  call-seq:
6948  *     undo_enabled = bool
6949  *
6950  *  Enable or disable undo.
6951  */
6952 static VALUE
6953 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6954 {
6955     Molecule *mol;
6956     Data_Get_Struct(self, Molecule, mol);
6957         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6958         return val;
6959 }
6960
6961 #pragma mark ------ Measure ------
6962
6963 static void
6964 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6965 {
6966         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6967                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6968                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6969                 case 0: break;
6970                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6971         }
6972 }
6973
6974 /*
6975  *  call-seq:
6976  *     center_of_mass(group = nil)       -> Vector3D
6977  *
6978  *  Calculate the center of mass for the given set of atoms. The argument
6979  *  group is null, then all atoms are considered.
6980  */
6981 static VALUE
6982 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6983 {
6984     Molecule *mol;
6985         VALUE group;
6986         IntGroup *ig;
6987         Vector v;
6988     Data_Get_Struct(self, Molecule, mol);
6989         rb_scan_args(argc, argv, "01", &group);
6990         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6991         s_Molecule_DoCenterOfMass(mol, &v, ig);
6992         if (ig != NULL)
6993                 IntGroupRelease(ig);
6994         return ValueFromVector(&v);
6995 }
6996
6997 /*
6998  *  call-seq:
6999  *     centralize(group = nil)       -> self
7000  *
7001  *  Translate the molecule so that the center of mass of the given group is located
7002  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
7003  */
7004 static VALUE
7005 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
7006 {
7007     Molecule *mol;
7008         VALUE group;
7009         IntGroup *ig;
7010         Vector v;
7011     Data_Get_Struct(self, Molecule, mol);
7012         rb_scan_args(argc, argv, "01", &group);
7013         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7014         s_Molecule_DoCenterOfMass(mol, &v, ig);
7015         if (ig != NULL)
7016                 IntGroupRelease(ig);
7017         v.x = -v.x;
7018         v.y = -v.y;
7019         v.z = -v.z;
7020         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7021         return self;
7022 }
7023
7024 /*
7025  *  call-seq:
7026  *     bounds(group = nil)       -> [min, max]
7027  *
7028  *  Calculate the boundary. The return value is an array of two Vector3D objects.
7029  */
7030 static VALUE
7031 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7032 {
7033     Molecule *mol;
7034         VALUE group;
7035         IntGroup *ig;
7036         Vector vmin, vmax;
7037         int n;
7038         Atom *ap;
7039     Data_Get_Struct(self, Molecule, mol);
7040         rb_scan_args(argc, argv, "01", &group);
7041         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7042         if (ig != NULL && IntGroupGetCount(ig) == 0)
7043                 rb_raise(rb_eMolbyError, "atom group is empty");
7044         vmin.x = vmin.y = vmin.z = 1e30;
7045         vmax.x = vmax.y = vmax.z = -1e30;
7046         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7047                 Vector r;
7048                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7049                         continue;
7050                 r = ap->r;
7051                 if (r.x < vmin.x)
7052                         vmin.x = r.x;
7053                 if (r.y < vmin.y)
7054                         vmin.y = r.y;
7055                 if (r.z < vmin.z)
7056                         vmin.z = r.z;
7057                 if (r.x > vmax.x)
7058                         vmax.x = r.x;
7059                 if (r.y > vmax.y)
7060                         vmax.y = r.y;
7061                 if (r.z > vmax.z)
7062                         vmax.z = r.z;
7063         }
7064         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7065 }
7066
7067 /*  Get atom position or a vector  */
7068 static void
7069 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7070 {
7071         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7072                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7073                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7074         } else {
7075                 VectorFromValue(val, vp);
7076         }
7077 }
7078
7079 /*
7080  *  call-seq:
7081  *     measure_bond(n1, n2)       -> Float
7082  *
7083  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
7084  *  or Vector3D values.
7085  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7086  */
7087 static VALUE
7088 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7089 {
7090     Molecule *mol;
7091         Vector v1, v2;
7092     Data_Get_Struct(self, Molecule, mol);
7093         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7094         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7095         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7096 }
7097
7098 /*
7099  *  call-seq:
7100  *     measure_angle(n1, n2, n3)       -> Float
7101  *
7102  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
7103  *  or Vector3D values. The return value is in degree.
7104  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7105  */
7106 static VALUE
7107 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7108 {
7109     Molecule *mol;
7110         Vector v1, v2, v3;
7111         Double d;
7112     Data_Get_Struct(self, Molecule, mol);
7113         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7114         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7115         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7116         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7117         if (isnan(d))
7118                 return Qnil;  /*  Cannot define  */
7119         else return rb_float_new(d);
7120 }
7121
7122 /*
7123  *  call-seq:
7124  *     measure_dihedral(n1, n2, n3, n4)       -> Float
7125  *
7126  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
7127  *  or Vector3D values. The return value is in degree.
7128  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7129  */
7130 static VALUE
7131 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7132 {
7133     Molecule *mol;
7134         Vector v1, v2, v3, v4;
7135         Double d;
7136     Data_Get_Struct(self, Molecule, mol);
7137         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7138         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7139         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7140         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
7141         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7142         if (isnan(d))
7143                 return Qnil;  /*  Cannot define  */
7144         else return rb_float_new(d);
7145 }
7146
7147 /*
7148  *  call-seq:
7149  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7150  *
7151  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7152  *  first and second atom in the pair should belong to group1 and group2, respectively.
7153  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7154  */
7155 static VALUE
7156 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7157 {
7158     Molecule *mol;
7159         VALUE limval, gval1, gval2, rval, igval;
7160         IntGroup *ig1, *ig2;
7161         IntGroupIterator iter1, iter2;
7162         Int npairs, *pairs;
7163         Int n[2], i;
7164         Double lim;
7165         Vector r1;
7166         Atom *ap1, *ap2;
7167         MDExclusion *exinfo;
7168         Int *exlist;
7169         
7170     Data_Get_Struct(self, Molecule, mol);
7171         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7172         lim = NUM2DBL(rb_Float(limval));
7173         if (lim <= 0.0)
7174                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7175         if (gval1 != Qnil)
7176                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7177         else
7178                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7179         if (gval2 != Qnil)
7180                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7181         else
7182                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7183         
7184         if (!RTEST(igval)) {
7185                 /*  Use the exclusion table in MDArena  */
7186                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7187                         VALUE mval = ValueFromMolecule(mol);
7188                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7189                 }
7190                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7191                 exlist = mol->arena->exlist;    
7192         } else {
7193                 exinfo = NULL;
7194                 exlist = NULL;
7195         }
7196         IntGroupIteratorInit(ig1, &iter1);
7197         IntGroupIteratorInit(ig2, &iter2);
7198         npairs = 0;
7199         pairs = NULL;
7200         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7201                 Int exn1, exn2;
7202                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7203                 r1 = ap1->r;
7204                 if (exinfo != NULL) {
7205                         exn1 = exinfo[n[0]].index1;
7206                         exn2 = exinfo[n[0] + 1].index1;
7207                 } else exn1 = exn2 = -1;
7208                 IntGroupIteratorReset(&iter2);
7209                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7210                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7211                         if (n[0] == n[1])
7212                                 continue;  /*  Same atom  */
7213                         if (exinfo != NULL) {
7214                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7215                                 for (i = exn1; i < exn2; i++) {
7216                                         if (exlist[i] == n[1])
7217                                                 break;
7218                                 }
7219                                 if (i < exn2)
7220                                         continue;  /*  Should be excluded  */
7221                         }
7222                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7223                                 /*  Is this pair already registered?  */
7224                                 Int *ip;
7225                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7226                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7227                                                 break;
7228                                 }
7229                                 if (i >= npairs) {
7230                                         /*  Not registered yet  */
7231                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7232                                 }
7233                         }
7234                 }
7235         }
7236         IntGroupIteratorRelease(&iter2);
7237         IntGroupIteratorRelease(&iter1);
7238         IntGroupRelease(ig2);
7239         IntGroupRelease(ig1);
7240         rval = rb_ary_new2(npairs);
7241         if (pairs != NULL) {
7242                 for (i = 0; i < npairs; i++) {
7243                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7244                 }
7245                 free(pairs);
7246         }
7247         return rval;
7248 }
7249
7250 /*
7251  *  call-seq:
7252  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7253  *
7254  *  Find atoms that are within the threshold distance from the given atom.
7255  *  (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.)
7256  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7257  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7258  *  If limit is not given, a default value of 1.2 is used.
7259  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7260  */
7261 static VALUE
7262 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7263 {
7264     Molecule *mol;
7265         VALUE aval, limval, radval;
7266         double limit, radius;
7267         Int n1, nbonds, *bonds, an;
7268         Vector v;
7269     Data_Get_Struct(self, Molecule, mol);
7270         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7271         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)) {
7272                 VectorFromValue(aval, &v);
7273                 if (radval == Qnil)
7274                         radius = gElementParameters[6].radius;
7275                 else
7276                         radius = NUM2DBL(rb_Float(radval));
7277                 n1 = mol->natoms;
7278         } else {
7279                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7280                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7281                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7282                 if (an >= 0 && an < gCountElementParameters)
7283                         radius = gElementParameters[an].radius;
7284                 else radius = gElementParameters[6].radius;
7285         }
7286         if (limval == Qnil)
7287                 limit = 1.2;
7288         else
7289                 limit = NUM2DBL(rb_Float(limval));
7290         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7291         bonds = NULL;
7292         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7293         aval = rb_ary_new();
7294         if (nbonds > 0) {
7295                 for (n1 = 0; n1 < nbonds; n1++)
7296                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7297                 free(bonds);
7298         }
7299         return aval;
7300 }
7301
7302 /*
7303  *  call-seq:
7304  *     guess_bonds(limit = 1.2)       -> Integer
7305  *
7306  *  Create bonds between atoms that are within the threshold distance.
7307  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7308  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7309  *  If limit is not given, a default value of 1.2 is used.
7310  *  The number of the newly created bonds is returned.
7311  *  This operation is undoable.
7312  */
7313 static VALUE
7314 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7315 {
7316     Molecule *mol;
7317         VALUE limval;
7318         double limit;
7319         Int nbonds, *bonds;
7320     Data_Get_Struct(self, Molecule, mol);
7321         rb_scan_args(argc, argv, "01", &limval);
7322         if (limval == Qnil)
7323                 limit = 1.2;
7324         else
7325                 limit = NUM2DBL(rb_Float(limval));
7326         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7327         if (nbonds > 0) {
7328                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7329                 free(bonds);
7330         }
7331         return INT2NUM(nbonds);
7332 }
7333
7334 #pragma mark ------ Cell and Symmetry ------
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  *
7340  *  Returns the unit cell parameters. If cell is not set, returns nil.
7341  */
7342 static VALUE
7343 s_Molecule_Cell(VALUE self)
7344 {
7345     Molecule *mol;
7346         int i;
7347         VALUE val;
7348     Data_Get_Struct(self, Molecule, mol);
7349         if (mol->cell == NULL)
7350                 return Qnil;
7351         val = rb_ary_new2(6);
7352         for (i = 0; i < 6; i++)
7353                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7354         if (mol->cell->has_sigma) {
7355                 for (i = 0; i < 6; i++) {
7356                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7357                 }
7358         }
7359         return val;
7360 }
7361
7362 /*
7363  *  call-seq:
7364  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7365  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7366  *
7367  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7368  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7369  This operation is undoable.
7370  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7371  */
7372 static VALUE
7373 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7374 {
7375     Molecule *mol;
7376         VALUE val, cval;
7377         int i, convert_coord, n;
7378         double d[12];
7379     Data_Get_Struct(self, Molecule, mol);
7380         rb_scan_args(argc, argv, "11", &val, &cval);
7381         if (val == Qnil) {
7382                 n = 0;
7383         } else {
7384                 int len;
7385                 val = rb_ary_to_ary(val);
7386                 len = RARRAY_LEN(val);
7387                 if (len >= 12) {
7388                         n = 12;
7389                 } else if (len >= 6) {
7390                         n = 6;
7391                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7392                 for (i = 0; i < n; i++)
7393                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7394         }
7395         convert_coord = (RTEST(cval) ? 1 : 0);
7396         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7397         return val;
7398 }
7399
7400 /*
7401  *  call-seq:
7402  *     box -> [avec, bvec, cvec, origin, flags]
7403  *
7404  *  Get the unit cell information in the form of a periodic bounding box.
7405  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7406  *  Integers which define whether the system is periodic along the axis.
7407  *  If no unit cell is defined, nil is returned.
7408  */
7409 static VALUE
7410 s_Molecule_Box(VALUE self)
7411 {
7412     Molecule *mol;
7413         VALUE v[5], val;
7414     Data_Get_Struct(self, Molecule, mol);
7415         if (mol == NULL || mol->cell == NULL)
7416                 return Qnil;
7417         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7418         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7419         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7420         v[3] = ValueFromVector(&(mol->cell->origin));
7421         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7422         val = rb_ary_new4(5, v);
7423         return val;
7424 }
7425
7426 /*
7427  *  call-seq:
7428  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7429  *     set_box(d, origin = [0, 0, 0])
7430  *     set_box
7431  *
7432  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7433  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7434  as the box vector.
7435  Flags, if present, is a 3-member array of Integers defining whether the system is
7436  periodic along the axis.
7437  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7438  In the second form, an isotropic box with cell-length d is set.
7439  In the third form, the existing box is cleared.
7440  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7441  */
7442 static VALUE
7443 s_Molecule_SetBox(VALUE self, VALUE aval)
7444 {
7445     Molecule *mol;
7446         VALUE v[6];
7447         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7448         Vector vv[3];
7449         Vector origin = {0, 0, 0};
7450         char flags[3];
7451         Double d;
7452         int i, convertCoordinates = 0;
7453     Data_Get_Struct(self, Molecule, mol);
7454         if (aval == Qnil) {
7455                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7456                 return self;
7457         }
7458         aval = rb_ary_to_ary(aval);
7459         for (i = 0; i < 6; i++) {
7460                 if (i < RARRAY_LEN(aval))
7461                         v[i] = (RARRAY_PTR(aval))[i];
7462                 else v[i] = Qnil;
7463         }
7464         if (v[0] == Qnil) {
7465                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7466                 return self;
7467         }
7468         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7469                 d = NUM2DBL(rb_Float(v[0]));
7470                 for (i = 0; i < 3; i++)
7471                         VecScale(vv[i], ax[i], d);
7472                 if (v[1] != Qnil)
7473                         VectorFromValue(v[1], &origin);
7474                 flags[0] = flags[1] = flags[2] = 1;
7475         } else {
7476                 for (i = 0; i < 3; i++) {
7477                         if (v[i] == Qnil) {
7478                                 VecZero(vv[i]);
7479                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7480                                 d = NUM2DBL(rb_Float(v[i]));
7481                                 VecScale(vv[i], ax[i], d);
7482                         } else {
7483                                 VectorFromValue(v[i], &vv[i]);
7484                         }
7485                         flags[i] = (VecLength2(vv[i]) > 0.0);
7486                 }
7487                 if (v[3] != Qnil)
7488                         VectorFromValue(v[3], &origin);
7489                 if (v[4] != Qnil) {
7490                         for (i = 0; i < 3; i++) {
7491                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7492                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7493                         }
7494                 }
7495                 if (RTEST(v[5]))
7496                         convertCoordinates = 1;
7497         }
7498         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7499         return self;
7500 }
7501
7502 /*
7503  *  call-seq:
7504  *     cell_periodicity -> [n1, n2, n3]
7505  *
7506  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7507  *  nil is returned.
7508  */
7509 static VALUE
7510 s_Molecule_CellPeriodicity(VALUE self)
7511 {
7512     Molecule *mol;
7513     Data_Get_Struct(self, Molecule, mol);
7514         if (mol->cell == NULL)
7515                 return Qnil;
7516         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7517 }
7518
7519 /*
7520  *  call-seq:
7521  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7522  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7523  *
7524  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7525  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7526  *  If cell is not defined, exception is raised.
7527  *  This operation is undoable.
7528  */
7529 static VALUE
7530 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7531 {
7532     Molecule *mol;
7533         Int flag;
7534     Data_Get_Struct(self, Molecule, mol);
7535         if (mol->cell == NULL)
7536                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7537         if (arg == Qnil)
7538                 flag = 0;
7539         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7540                 flag = NUM2INT(rb_Integer(arg));
7541         else {
7542                 Int i;
7543                 VALUE arg0;
7544                 arg = rb_ary_to_ary(arg);
7545                 flag = 0;
7546                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7547                         arg0 = RARRAY_PTR(arg)[i];
7548                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7549                                 flag |= (1 << (2 - i));
7550                 }
7551         }
7552         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7553         return arg;
7554 }
7555
7556 /*
7557  *  call-seq:
7558  *     cell_flexibility -> bool
7559  *
7560  *  Returns the unit cell is flexible or not
7561  */
7562 static VALUE
7563 s_Molecule_CellFlexibility(VALUE self)
7564 {
7565         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7566         return Qtrue;
7567         /*    Molecule *mol;
7568          Data_Get_Struct(self, Molecule, mol);
7569          if (mol->cell == NULL)
7570          return Qfalse;
7571          if (mol->useFlexibleCell)
7572          return Qtrue;
7573          else return Qfalse; */
7574 }
7575
7576 /*
7577  *  call-seq:
7578  *     self.cell_flexibility = bool
7579  *     set_cell_flexibility(bool)
7580  *
7581  *  Change the unit cell is flexible or not
7582  */
7583 static VALUE
7584 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7585 {
7586         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7587         return self;
7588         /*    Molecule *mol;
7589          Data_Get_Struct(self, Molecule, mol);
7590          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7591          return self; */
7592 }
7593
7594 /*
7595  *  call-seq:
7596  *     cell_transform -> Transform
7597  *
7598  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7599  *  If cell is not defined, nil is returned.
7600  */
7601 static VALUE
7602 s_Molecule_CellTransform(VALUE self)
7603 {
7604     Molecule *mol;
7605     Data_Get_Struct(self, Molecule, mol);
7606         if (mol == NULL || mol->cell == NULL)
7607                 return Qnil;
7608         return ValueFromTransform(&(mol->cell->tr));
7609 }
7610
7611 /*
7612  *  call-seq:
7613  *     symmetry -> Array of Transforms
7614  *     symmetries -> Array of Transforms
7615  *
7616  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7617  *  returns an empty array.
7618  */
7619 static VALUE
7620 s_Molecule_Symmetry(VALUE self)
7621 {
7622     Molecule *mol;
7623         VALUE val;
7624         int i;
7625     Data_Get_Struct(self, Molecule, mol);
7626         if (mol->nsyms <= 0)
7627                 return rb_ary_new();
7628         val = rb_ary_new2(mol->nsyms);
7629         for (i = 0; i < mol->nsyms; i++) {
7630                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7631         }
7632         return val;
7633 }
7634
7635 /*
7636  *  call-seq:
7637  *     nsymmetries -> Integer
7638  *
7639  *  Get the number of currently defined symmetry operations.
7640  */
7641 static VALUE
7642 s_Molecule_Nsymmetries(VALUE self)
7643 {
7644     Molecule *mol;
7645     Data_Get_Struct(self, Molecule, mol);
7646         return INT2NUM(mol->nsyms);
7647 }
7648
7649 /*
7650  *  call-seq:
7651  *     add_symmetry(Transform) -> Integer
7652  *
7653  *  Add a new symmetry operation. If no symmetry operation is defined and the
7654  *  given argument is not an identity transform, then also add an identity
7655  *  transform at the index 0.
7656  *  Returns the total number of symmetries after operation.
7657  */
7658 static VALUE
7659 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7660 {
7661     Molecule *mol;
7662         Transform tr;
7663     Data_Get_Struct(self, Molecule, mol);
7664         TransformFromValue(trans, &tr);
7665         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7666         return INT2NUM(mol->nsyms);
7667 }
7668
7669 /*
7670  *  call-seq:
7671  *     remove_symmetry(count = nil) -> Integer
7672  *     remove_symmetries(count = nil) -> Integer
7673  *
7674  *  Remove the specified number of symmetry operations. The last added ones are removed
7675  *  first. If count is nil, then all symmetry operations are removed. Returns the
7676  *  number of leftover symmetries.
7677  */
7678 static VALUE
7679 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7680 {
7681     Molecule *mol;
7682         VALUE cval;
7683         int i, n;
7684     Data_Get_Struct(self, Molecule, mol);
7685         rb_scan_args(argc, argv, "01", &cval);
7686         if (cval == Qnil)
7687                 n = mol->nsyms - 1;
7688         else {
7689                 n = NUM2INT(rb_Integer(cval));
7690                 if (n < 0 || n > mol->nsyms)
7691                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7692                 if (n == mol->nsyms)
7693                         n = mol->nsyms - 1;
7694         }
7695         for (i = 0; i < n; i++)
7696                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7697         return INT2NUM(mol->nsyms);
7698 }
7699
7700 /*
7701  *  call-seq:
7702  *     wrap_unit_cell(group) -> Vector3D
7703  *
7704  *  Move the specified group so that the center of mass of the group is within the
7705  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7706  *  exception is raised.
7707  */
7708 static VALUE
7709 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7710 {
7711     Molecule *mol;
7712         IntGroup *ig;
7713         Vector v, cv, dv;
7714     Data_Get_Struct(self, Molecule, mol);
7715         if (mol->cell == NULL)
7716                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7717         ig = s_Molecule_AtomGroupFromValue(self, gval);
7718         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7719         TransformVec(&v, mol->cell->rtr, &cv);
7720         if (mol->cell->flags[0])
7721                 v.x -= floor(v.x);
7722         if (mol->cell->flags[1])
7723                 v.y -= floor(v.y);
7724         if (mol->cell->flags[2])
7725                 v.z -= floor(v.z);
7726         TransformVec(&dv, mol->cell->tr, &v);
7727         VecDec(dv, cv);
7728         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7729         IntGroupRelease(ig);
7730         return ValueFromVector(&dv);
7731 }
7732
7733 /*
7734  *  call-seq:
7735  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7736  *
7737  *  Expand the specified part of the molecule by the given symmetry operation.
7738  *  Returns the array of atom indices corresponding to the expanded atoms.
7739  *  If allow_overlap is true, then new atoms are created even when the
7740  *  coordinates coincide with the some other atom (special position) of the
7741  *  same element; otherwise, such atom will not be created and the index of the
7742  *  existing atom is given in the returned array.
7743  */
7744 static VALUE
7745 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7746 {
7747     Molecule *mol;
7748         VALUE gval, sval, xval, yval, zval, rval, oval;
7749         IntGroup *ig;
7750         Int n[4], allow_overlap;
7751         Int natoms;
7752         Int nidx, *idx;
7753         
7754     Data_Get_Struct(self, Molecule, mol);
7755         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7756         n[0] = NUM2INT(rb_Integer(sval));
7757         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7758         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7759         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7760         allow_overlap = (RTEST(oval) ? 1 : 0);
7761         ig = s_Molecule_AtomGroupFromValue(self, gval);
7762         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7763                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7764         natoms = mol->natoms;
7765         
7766         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7767         
7768         rval = rb_ary_new2(nidx);
7769         while (--nidx >= 0) {
7770                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7771         }
7772         /*      if (natoms == mol->natoms)
7773          rval = Qnil;
7774          else {
7775          rval = IntGroup_Alloc(rb_cIntGroup);
7776          Data_Get_Struct(rval, IntGroup, ig);
7777          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7778          } */
7779         return rval;
7780 }
7781
7782 /*
7783  *  call-seq:
7784  *     amend_by_symmetry(group = nil) -> IntGroup
7785  *
7786  *  Expand the specified part of the molecule by the given symmetry operation.
7787  *  Returns an IntGroup containing the added atoms.
7788  */
7789 static VALUE
7790 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7791 {
7792     Molecule *mol;
7793         IntGroup *ig, *ig2;
7794         VALUE rval, gval;
7795     Data_Get_Struct(self, Molecule, mol);
7796         rb_scan_args(argc, argv, "01", &gval);
7797         if (gval != Qnil)
7798                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7799         else ig = NULL;
7800         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7801         rval = ValueFromIntGroup(ig2);
7802         IntGroupRelease(ig2);
7803         return rval;
7804 }
7805
7806 #pragma mark ------ Transforms ------
7807
7808 /*
7809  *  call-seq:
7810  *     translate(vec, group = nil)       -> Molecule
7811  *
7812  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7813  *  This operation is undoable.
7814  */
7815 static VALUE
7816 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7817 {
7818     Molecule *mol;
7819         VALUE vec, group;
7820         Vector v;
7821         IntGroup *ig;
7822     Data_Get_Struct(self, Molecule, mol);
7823         rb_scan_args(argc, argv, "11", &vec, &group);
7824         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7825         VectorFromValue(vec, &v);
7826         //      MoleculeTranslate(mol, &v, ig);
7827         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7828         if (ig != NULL)
7829                 IntGroupRelease(ig);
7830         return self;
7831 }
7832
7833 /*
7834  *  call-seq:
7835  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7836  *
7837  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7838  *  If group is given, only atoms in the group are moved.
7839  *  This operation is undoable.
7840  */
7841 static VALUE
7842 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7843 {
7844     Molecule *mol;
7845         volatile VALUE aval, anval, cval, gval;
7846         Double angle;
7847         Vector av, cv;
7848         Transform tr;
7849         IntGroup *ig;
7850     Data_Get_Struct(self, Molecule, mol);
7851         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7852         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7853         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7854         VectorFromValue(aval, &av);
7855         if (NIL_P(cval))
7856                 cv.x = cv.y = cv.z = 0.0;
7857         else
7858                 VectorFromValue(cval, &cv);
7859         if (TransformForRotation(tr, &av, angle, &cv))
7860                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7861         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7862         if (ig != NULL)
7863                 IntGroupRelease(ig);
7864         return self;
7865 }
7866
7867 /*
7868  *  call-seq:
7869  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7870  *
7871  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7872  *  axis must not be a zero vector.
7873  *  If group is given, only atoms in the group are moved.
7874  *  This operation is undoable.
7875  */
7876 static VALUE
7877 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7878 {
7879     Molecule *mol;
7880         volatile VALUE aval, cval, gval;
7881         Vector av, cv;
7882         Transform tr;
7883         IntGroup *ig;
7884     Data_Get_Struct(self, Molecule, mol);
7885         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7886         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7887         VectorFromValue(aval, &av);
7888         if (NIL_P(cval))
7889                 cv.x = cv.y = cv.z = 0.0;
7890         else
7891                 VectorFromValue(cval, &cv);
7892         if (TransformForReflection(tr, &av, &cv))
7893                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7894         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7895         if (ig != NULL)
7896                 IntGroupRelease(ig);
7897         return self;
7898 }
7899
7900 /*
7901  *  call-seq:
7902  *     invert(center = [0,0,0], group = nil)       -> Molecule
7903  *
7904  *  Invert the molecule with the given center.
7905  *  If group is given, only atoms in the group are moved.
7906  *  This operation is undoable.
7907  */
7908 static VALUE
7909 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7910 {
7911         Molecule *mol;
7912         volatile VALUE cval, gval;
7913         Vector cv;
7914         Transform tr;
7915         IntGroup *ig;
7916     Data_Get_Struct(self, Molecule, mol);
7917         rb_scan_args(argc, argv, "02", &cval, &gval);
7918         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7919         if (NIL_P(cval))
7920                 cv.x = cv.y = cv.z = 0.0;
7921         else
7922                 VectorFromValue(cval, &cv);
7923         TransformForInversion(tr, &cv);
7924         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7925         if (ig != NULL)
7926                 IntGroupRelease(ig);
7927         return self;
7928 }
7929
7930 /*
7931  *  call-seq:
7932  *     transform(transform, group = nil)       -> Molecule
7933  *
7934  *  Transform the molecule by the given Transform object.
7935  *  If group is given, only atoms in the group are moved.
7936  *  This operation is undoable.
7937  */
7938 static VALUE
7939 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7940 {
7941     Molecule *mol;
7942         VALUE trans, group;
7943         Transform tr;
7944         IntGroup *ig;
7945     Data_Get_Struct(self, Molecule, mol);
7946         rb_scan_args(argc, argv, "11", &trans, &group);
7947         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7948         TransformFromValue(trans, &tr);
7949         /*      MoleculeTransform(mol, tr, ig); */
7950         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7951         if (ig != NULL)
7952                 IntGroupRelease(ig);
7953         return self;
7954 }
7955
7956 /*
7957  *  call-seq:
7958  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7959  *
7960  *  Get the transform corresponding to the symmetry operation. The symop can either be
7961  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7962  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7963  *  Otherwise, the returned transform is for fractional coordinates.
7964  *  Raises exception when no cell or no transform are defined.
7965  */
7966 static VALUE
7967 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7968 {
7969     Molecule *mol;
7970         VALUE sval, fval;
7971         Symop symop;
7972         Transform tr;
7973     Data_Get_Struct(self, Molecule, mol);
7974         if (mol->cell == NULL)
7975                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7976         if (mol->nsyms == 0)
7977                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7978         rb_scan_args(argc, argv, "11", &sval, &fval);
7979         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7980                 symop.sym = NUM2INT(rb_Integer(sval));
7981                 symop.dx = symop.dy = symop.dz = 0;
7982         } else {
7983                 sval = rb_ary_to_ary(sval);
7984                 if (RARRAY_LEN(sval) < 4)
7985                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7986                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7987                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7988                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7989                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7990         }
7991         if (symop.sym >= mol->nsyms)
7992                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7993         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7994         return ValueFromTransform(&tr);
7995 }
7996
7997 /*
7998  *  call-seq:
7999  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8000  *
8001  *  Get the symmetry operation corresponding to the given transform.
8002  *  If is_cartesian is true, the given transform is for cartesian coordinates.
8003  *  Otherwise, the given transform is for fractional coordinates.
8004  *  Raises exception when no cell or no transform are defined.
8005  */
8006 static VALUE
8007 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8008 {
8009     Molecule *mol;
8010         VALUE tval, fval;
8011         Symop symop;
8012         Transform tr;
8013         int n;
8014     Data_Get_Struct(self, Molecule, mol);
8015         if (mol->cell == NULL)
8016                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8017         if (mol->nsyms == 0)
8018                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8019         rb_scan_args(argc, argv, "11", &tval, &fval);
8020         TransformFromValue(tval, &tr);
8021         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8022         if (n == 0) {
8023                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8024         } else {
8025                 return Qnil;  /*  Not found  */
8026         }
8027 }
8028
8029 #pragma mark ------ Frames ------
8030
8031 /*
8032  *  call-seq:
8033  *     select_frame(index)
8034  *     frame = index
8035  *
8036  *  Select the specified frame. If successful, returns true, otherwise returns false.
8037  */
8038 static VALUE
8039 s_Molecule_SelectFrame(VALUE self, VALUE val)
8040 {
8041     Molecule *mol;
8042         int ival = NUM2INT(val);
8043     Data_Get_Struct(self, Molecule, mol);
8044         ival = MoleculeSelectFrame(mol, ival, 1);
8045         if (ival >= 0)
8046                 return Qtrue;
8047         else return Qfalse;
8048 }
8049
8050 /*
8051  *  call-seq:
8052  *     frame -> Integer
8053  *
8054  *  Get the current frame.
8055  */
8056 static VALUE
8057 s_Molecule_Frame(VALUE self)
8058 {
8059     Molecule *mol;
8060     Data_Get_Struct(self, Molecule, mol);
8061         return INT2NUM(mol->cframe);
8062 }
8063
8064 /*
8065  *  call-seq:
8066  *     nframes -> Integer
8067  *
8068  *  Get the number of frames.
8069  */
8070 static VALUE
8071 s_Molecule_Nframes(VALUE self)
8072 {
8073     Molecule *mol;
8074     Data_Get_Struct(self, Molecule, mol);
8075         return INT2NUM(MoleculeGetNumberOfFrames(mol));
8076 }
8077
8078 /*
8079  *  call-seq:
8080  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8081  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8082  *
8083  *  Insert new frames at the indices specified by the intGroup. If the first argument is
8084  *  an integer, a single new frame is inserted at that index. If the first argument is 
8085  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8086  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
8087  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
8088  *  to the new frame.
8089  *  Returns an intGroup representing the inserted frames if successful, nil if not.
8090  */
8091 static VALUE
8092 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8093 {
8094         VALUE val, coords, cells;
8095     Molecule *mol;
8096         IntGroup *ig;
8097         int count, ival, i, j, len, len_c, len2, nframes;
8098         VALUE *ptr, *ptr2;
8099         Vector *vp, *vp2;
8100     Data_Get_Struct(self, Molecule, mol);
8101         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8102         if (coords != Qnil) {
8103                 if (TYPE(coords) != T_ARRAY)
8104                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8105                 len = RARRAY_LEN(coords);
8106         } else len = 0;
8107         if (cells != Qnil) {
8108                 if (mol->cell == NULL)
8109                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8110                 if (TYPE(cells) != T_ARRAY)
8111                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8112                 len_c = RARRAY_LEN(cells);
8113         } else len_c = 0;
8114         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
8115         nframes = MoleculeGetNumberOfFrames(mol);
8116         if (val == Qnil) {
8117                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8118                 val = ValueFromIntGroup(ig);
8119         } else {
8120                 ig = IntGroupFromValue(val);
8121         }
8122         count = IntGroupGetCount(ig);  /*  Count is updated here  */
8123         vp = ALLOC_N(Vector, mol->natoms * count);
8124         if (cells != Qnil)
8125                 vp2 = ALLOC_N(Vector, 4 * count);
8126         else vp2 = NULL;
8127         if (len > 0) {
8128                 if (len < count)
8129                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8130                 ptr = RARRAY_PTR(coords);
8131                 for (i = 0; i < count; i++) {
8132                         if (TYPE(ptr[i]) != T_ARRAY)
8133                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8134                         len2 = RARRAY_LEN(ptr[i]);
8135                         if (len2 < mol->natoms)
8136                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8137                         ptr2 = RARRAY_PTR(ptr[i]);
8138                         for (j = 0; j < mol->natoms; j++)
8139                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8140                 }
8141         } else {
8142                 Atom *ap;
8143                 for (i = 0; i < count; i++) {
8144                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8145                                 vp[i * mol->natoms + j] = ap->r;
8146                         }
8147                 }
8148         }
8149         if (len_c > 0) {
8150                 if (len_c < count)
8151                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8152                 ptr = RARRAY_PTR(cells);
8153                 for (i = 0; i < count; i++) {
8154                         if (TYPE(ptr[i]) != T_ARRAY)
8155                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8156                         len2 = RARRAY_LEN(ptr[i]);
8157                         if (len2 < 4)
8158                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8159                         ptr2 = RARRAY_PTR(ptr[i]);
8160                         for (j = 0; j < 4; j++)
8161                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8162                 }
8163         }
8164         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8165         IntGroupRelease(ig);
8166         xfree(vp);
8167         if (vp2 != NULL)
8168                 xfree(vp2);
8169         return (ival >= 0 ? val : Qnil);
8170 }
8171
8172 /*
8173  *  call-seq:
8174  *     create_frame(coordinates = nil) -> Integer
8175  *     create_frames(coordinates = nil) -> Integer
8176  *
8177  *  Same as molecule.insert_frames(nil, coordinates).
8178  */
8179 static VALUE
8180 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8181 {
8182         VALUE vals[3];
8183         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8184         vals[0] = Qnil;
8185         return s_Molecule_InsertFrames(3, vals, self);
8186 }
8187
8188 /*
8189  *  call-seq:
8190  *     remove_frames(IntGroup, wantCoordinates = false)
8191  *
8192  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8193  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8194  *  removed frames is returned if operation is successful.
8195  */
8196 static VALUE
8197 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8198 {
8199         VALUE val, flag;
8200         VALUE retval;
8201     Molecule *mol;
8202         IntGroup *ig;
8203         int count;
8204     Data_Get_Struct(self, Molecule, mol);
8205         rb_scan_args(argc, argv, "11", &val, &flag);
8206         ig = IntGroupFromValue(val);
8207         count = IntGroupGetCount(ig);
8208         if (RTEST(flag)) {
8209                 /*  Create return value before removing frames  */
8210                 VALUE coords;
8211                 int i, j, n;
8212                 Atom *ap;
8213                 Vector v;
8214                 retval = rb_ary_new2(count);
8215                 for (i = 0; i < count; i++) {
8216                         n = IntGroupGetNthPoint(ig, i);
8217                         coords = rb_ary_new2(mol->natoms);
8218                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8219                                 if (n < ap->nframes && n != mol->cframe)
8220                                         v = ap->frames[n];
8221                                 else v = ap->r;
8222                                 rb_ary_push(coords, ValueFromVector(&v));
8223                         }
8224                         rb_ary_push(retval, coords);
8225                 }
8226         } else retval = Qtrue;
8227         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8228                 return retval;
8229         else return Qnil;
8230 }
8231
8232 /*
8233  *  call-seq:
8234  *     each_frame {|n| ...}
8235  *
8236  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8237  *  the frame number. After completion, the original frame number is restored.
8238  */
8239 static VALUE
8240 s_Molecule_EachFrame(VALUE self)
8241 {
8242         int i, cframe, nframes;
8243     Molecule *mol;
8244     Data_Get_Struct(self, Molecule, mol);
8245         cframe = mol->cframe;
8246         nframes = MoleculeGetNumberOfFrames(mol);
8247         if (nframes > 0) {
8248                 for (i = 0; i < nframes; i++) {
8249                         MoleculeSelectFrame(mol, i, 1);
8250                         rb_yield(INT2NUM(i));
8251                 }
8252                 MoleculeSelectFrame(mol, cframe, 1);
8253         }
8254     return self;
8255 }
8256
8257 /*
8258  *  call-seq:
8259  *     get_coord_from_frame(index, group = nil)
8260  *
8261  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8262  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8263  *  copied; now they are always copied)
8264  */
8265 static VALUE
8266 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8267 {
8268         Molecule *mol;
8269         VALUE ival, gval, cval;
8270         Int index, i, j, n, nn;
8271         IntGroup *ig;
8272         IntGroupIterator iter;
8273         Atom *ap;
8274         Vector *vp;
8275     Data_Get_Struct(self, Molecule, mol);
8276         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8277         if (argc == 3)
8278                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8279         index = NUM2INT(rb_Integer(ival));
8280         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8281                 if (n == 0)
8282                         rb_raise(rb_eMolbyError, "No frame is present");
8283                 else
8284                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8285         }
8286         if (gval == Qnil) {
8287                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8288         } else {
8289                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8290         }
8291         n = IntGroupGetCount(ig);
8292         if (n > 0) {
8293                 vp = (Vector *)calloc(sizeof(Vector), n);
8294                 IntGroupIteratorInit(ig, &iter);
8295                 j = 0;
8296                 nn = 0;
8297                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8298                         ap = ATOM_AT_INDEX(mol->atoms, i);
8299                         if (index < ap->nframes) {
8300                                 vp[j] = ap->frames[index];
8301                                 nn++;
8302                         } else {
8303                                 vp[j] = ap->r;
8304                         }
8305                         j++;
8306                 }
8307                 if (nn > 0)
8308                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8309                 free(vp);
8310                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8311                         vp = mol->frame_cells + index * 4;
8312                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8313                 }
8314                 IntGroupIteratorRelease(&iter);
8315         }
8316         /*  Copy the extra properties  */
8317         IntGroupRelease(ig);
8318         for (i = 0; i < mol->nmolprops; i++) {
8319                 Double *dp = (Double *)malloc(sizeof(Double));
8320                 ig = IntGroupNew();
8321                 IntGroupAdd(ig, mol->cframe, 1);
8322                 *dp = mol->molprops[i].propvals[index];
8323                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8324                 free(dp);
8325                 IntGroupRelease(ig);
8326         }
8327         
8328         return self;
8329 }
8330
8331 /*
8332  *  call-seq:
8333  *     reorder_frames(old_indices)
8334  *
8335  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8336  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8337  *  same as the old frames 2/0/1, respectively.
8338  *  The argument must have the same number of integers as the number of frames.
8339  */
8340 static VALUE
8341 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8342 {
8343         Molecule *mol;
8344         Int *ip, *ip2, i, n, nframes;
8345     Data_Get_Struct(self, Molecule, mol);
8346         aval = rb_ary_to_ary(aval);
8347         nframes = MoleculeGetNumberOfFrames(mol);
8348         if (RARRAY_LEN(aval) != nframes)
8349                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8350         ip2 = (Int *)calloc(sizeof(Int), nframes);
8351         ip = (Int *)calloc(sizeof(Int), nframes);
8352         for (i = 0; i < nframes; i++) {
8353                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8354                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8355                         free(ip2);
8356                         free(ip);
8357                         if (n < 0 || n >= nframes)
8358                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8359                         else
8360                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8361                 }
8362                 ip2[n] = 1;
8363                 ip[i] = n;
8364         }
8365         free(ip2);
8366         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8367         free(ip);
8368         return self;
8369 }
8370
8371 #pragma mark ------ Fragments ------
8372
8373 /*
8374  *  call-seq:
8375  *     fragment(n1, *exatoms)  -> IntGroup
8376  *     fragment(group, *exatoms)  -> IntGroup
8377  *
8378  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8379  *  those atoms will not be counted during the search.
8380  */
8381 static VALUE
8382 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8383 {
8384     Molecule *mol;
8385         IntGroup *baseg, *ig, *exatoms;
8386         int n;
8387         volatile VALUE nval, exval;
8388     Data_Get_Struct(self, Molecule, mol);
8389         rb_scan_args(argc, argv, "1*", &nval, &exval);
8390         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8391                 baseg = NULL;
8392                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8393         } else {
8394                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8395         }
8396         if (RARRAY_LEN(exval) == 0) {
8397                 exatoms = NULL;
8398         } else {
8399                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8400                 Data_Get_Struct(exval, IntGroup, exatoms);
8401         }
8402         if (baseg == NULL) {
8403                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8404         } else {
8405                 IntGroupIterator iter;
8406                 IntGroupIteratorInit(baseg, &iter);
8407                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8408                         ig = IntGroupNew();
8409                 } else {
8410                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8411                         if (ig != NULL) {
8412                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8413                                         IntGroup *subg;
8414                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8415                                         if (subg != NULL) {
8416                                                 IntGroupAddIntGroup(ig, subg);
8417                                                 IntGroupRelease(subg);
8418                                         }
8419                                 }
8420                         }
8421                 }
8422                 IntGroupIteratorRelease(&iter);
8423                 IntGroupRelease(baseg);
8424         }
8425         if (ig == NULL)
8426                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8427         nval = ValueFromIntGroup(ig);
8428         IntGroupRelease(ig);
8429         return nval;
8430 }
8431
8432 /*
8433  *  call-seq:
8434  *     fragments(exclude = nil)
8435  *
8436  *  Returns the fragments as an array of IntGroups. 
8437  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8438  *  in defining the fragment.
8439  */
8440 static VALUE
8441 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8442 {
8443     Molecule *mol;
8444         IntGroup *ag, *fg, *eg;
8445         VALUE gval, exval, retval;
8446     Data_Get_Struct(self, Molecule, mol);
8447         if (mol == NULL)
8448                 return Qnil;
8449         if (mol->natoms == 0)
8450                 return rb_ary_new();
8451         rb_scan_args(argc, argv, "01", &exval);
8452         if (exval == Qnil)
8453                 eg = NULL;
8454         else
8455                 eg = IntGroupFromValue(exval);
8456         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8457         if (eg != NULL)
8458                 IntGroupRemoveIntGroup(ag, eg);
8459         retval = rb_ary_new();
8460         while (IntGroupGetCount(ag) > 0) {
8461                 int n = IntGroupGetNthPoint(ag, 0);
8462                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8463                 if (fg == NULL)
8464                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8465                 gval = ValueFromIntGroup(fg);
8466                 rb_ary_push(retval, gval);
8467                 IntGroupRemoveIntGroup(ag, fg);
8468                 IntGroupRelease(fg);
8469         }
8470         IntGroupRelease(ag);
8471         if (eg != NULL)
8472                 IntGroupRelease(eg);
8473         return retval;
8474 }
8475
8476 /*
8477  *  call-seq:
8478  *     each_fragment(exclude = nil) {|group| ...}
8479  *
8480  *  Execute the block, with the IntGroup object for each fragment as the argument.
8481  *  Atoms or bonds should not be added or removed during the execution of the block.
8482  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8483  *  in defining the fragment.
8484  */
8485 static VALUE
8486 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8487 {
8488     Molecule *mol;
8489         IntGroup *ag, *fg, *eg;
8490         VALUE gval, exval;
8491     Data_Get_Struct(self, Molecule, mol);
8492         if (mol == NULL || mol->natoms == 0)
8493                 return self;
8494         rb_scan_args(argc, argv, "01", &exval);
8495         if (exval == Qnil)
8496                 eg = NULL;
8497         else
8498                 eg = IntGroupFromValue(exval);
8499         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8500         if (eg != NULL)
8501                 IntGroupRemoveIntGroup(ag, eg);
8502         while (IntGroupGetCount(ag) > 0) {
8503                 int n = IntGroupGetNthPoint(ag, 0);
8504                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8505                 if (fg == NULL)
8506                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8507                 gval = ValueFromIntGroup(fg);
8508                 rb_yield(gval);
8509                 IntGroupRemoveIntGroup(ag, fg);
8510                 IntGroupRelease(fg);
8511         }
8512         IntGroupRelease(ag);
8513         if (eg != NULL)
8514                 IntGroupRelease(eg);
8515         return self;
8516 }
8517
8518 /*
8519  *  call-seq:
8520  *     detachable?(group)  -> [n1, n2]
8521  *
8522  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8523  *  of the molecule via only one bond. If it is, then the indices of the atoms
8524  *  belonging to the bond is returned, the first element being the atom included
8525  *  in the fragment. Otherwise, Qnil is returned.
8526  */
8527 static VALUE
8528 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8529 {
8530         Molecule *mol;
8531         IntGroup *ig;
8532         int n1, n2;
8533         VALUE retval;
8534     Data_Get_Struct(self, Molecule, mol);
8535         ig = s_Molecule_AtomGroupFromValue(self, gval);
8536         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8537                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8538         } else retval = Qnil;
8539         IntGroupRelease(ig);
8540         return retval;
8541 }
8542
8543 /*
8544  *  call-seq:
8545  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8546  *
8547  *  Returns an array of bonds that connect an atom in the group and an atom out
8548  *  of the group. The first atom in the bond always belongs to the group. If no
8549  *  such bonds are present, an empty array is returned.
8550  */
8551 static VALUE
8552 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8553 {
8554         Molecule *mol;
8555         IntGroup *ig, *bg;
8556         VALUE gval, retval;
8557     Data_Get_Struct(self, Molecule, mol);
8558         rb_scan_args(argc, argv, "01", &gval);
8559         if (gval == Qnil) {
8560                 ig = MoleculeGetSelection(mol);
8561                 if (ig != NULL)
8562                         IntGroupRetain(ig);
8563         } else {
8564                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8565         }
8566         retval = rb_ary_new();
8567         if (ig == NULL)
8568                 return retval;
8569         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8570         if (bg != NULL) {
8571                 IntGroupIterator iter;
8572                 Int i;
8573                 IntGroupIteratorInit(bg, &iter);
8574                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8575                         /*  The atoms at the border  */
8576                         Int n1, n2;
8577                         n1 = mol->bonds[i * 2];
8578                         n2 = mol->bonds[i * 2 + 1];
8579                         if (IntGroupLookupPoint(ig, n1) < 0) {
8580                                 int w = n1;
8581                                 n1 = n2;
8582                                 n2 = w;
8583                                 if (IntGroupLookupPoint(ig, n1) < 0)
8584                                         continue;  /*  Actually this is an internal error  */
8585                         }
8586                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8587                 }
8588                 IntGroupIteratorRelease(&iter);
8589         }
8590         IntGroupRelease(bg);
8591         IntGroupRelease(ig);
8592         return retval;
8593 }
8594
8595 /*  Calculate the transform that moves the current coordinates to the reference
8596  coordinates with least displacements.   */
8597 static Double
8598 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8599 {
8600         Atom *ap, *ap1;
8601         Int natoms, nn;
8602         Vector org1, org2;
8603         Int i, in, j, k;
8604         Double w, w1;
8605         Mat33 r, q, u;
8606         Double eigen_val[3];
8607         Vector eigen_vec[3];
8608         Vector s[3];
8609         IntGroupIterator iter;
8610
8611         natoms = mol->natoms;
8612         ap = mol->atoms;
8613         IntGroupIteratorInit(ig, &iter);
8614         
8615         /*  Calculate the weighted center  */
8616         VecZero(org1);
8617         VecZero(org2);
8618         w = 0.0;
8619         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8620                 ap1 = ATOM_AT_INDEX(ap, in);
8621                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8622                 VecScaleInc(org1, ap1->r, w1);
8623                 VecScaleInc(org2, ref[i], w1);
8624                 w += w1;
8625         }
8626         w = 1.0 / w;
8627         VecScaleSelf(org1, w);
8628         VecScaleSelf(org2, w);
8629
8630     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8631     /*  Matrix to diagonalize = R * tR    */
8632         memset(r, 0, sizeof(Mat33));
8633         memset(q, 0, sizeof(Mat33));
8634         memset(u, 0, sizeof(Mat33));
8635         nn = 0;
8636         IntGroupIteratorReset(&iter);
8637         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8638                 Vector v1, v2;
8639                 ap1 = ATOM_AT_INDEX(ap, in);
8640                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8641                 w1 *= w1;
8642                 VecSub(v1, ap1->r, org1);
8643                 VecSub(v2, ref[i], org2);
8644                 r[0] += w1 * v1.x * v2.x;
8645                 r[1] += w1 * v1.y * v2.x;
8646                 r[2] += w1 * v1.z * v2.x;
8647                 r[3] += w1 * v1.x * v2.y;
8648                 r[4] += w1 * v1.y * v2.y;
8649                 r[5] += w1 * v1.z * v2.y;
8650                 r[6] += w1 * v1.x * v2.z;
8651                 r[7] += w1 * v1.y * v2.z;
8652                 r[8] += w1 * v1.z * v2.z;
8653                 nn++;
8654         }
8655         for (i = 0; i < 9; i++)
8656                 r[i] /= (nn * nn);
8657         for (i = 0; i < 3; i++) {
8658                 for (j = 0; j < 3; j++) {
8659                         for (k = 0; k < 3; k++) {
8660                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8661                         }
8662                 }
8663         }
8664         
8665         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8666                 IntGroupIteratorRelease(&iter);
8667                 return -1.0;  /*  Cannot determine the eigenvector  */
8668         }
8669
8670     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8671     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8672         MatrixTranspose(r, r);
8673         for (i = 0; i < 3; i++) {
8674                 MatrixVec(&s[i], r, &eigen_vec[i]);
8675                 w1 = 1.0 / sqrt(eigen_val[i]);
8676                 VecScaleSelf(s[i], w1);
8677         }
8678         for (k = 0; k < 3; k++) {
8679                 u[0] += s[k].x * eigen_vec[k].x;
8680                 u[1] += s[k].y * eigen_vec[k].x;
8681                 u[2] += s[k].z * eigen_vec[k].x;
8682                 u[3] += s[k].x * eigen_vec[k].y;
8683                 u[4] += s[k].y * eigen_vec[k].y;
8684                 u[5] += s[k].z * eigen_vec[k].y;
8685                 u[6] += s[k].x * eigen_vec[k].z;
8686                 u[7] += s[k].y * eigen_vec[k].z;
8687                 u[8] += s[k].z * eigen_vec[k].z;
8688         }
8689         
8690         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8691         MatrixVec(&org1, u, &org1);
8692         VecDec(org2, org1);
8693         for (i = 0; i < 9; i++)
8694                 trans[i] = u[i];
8695         trans[9] = org2.x;
8696         trans[10] = org2.y;
8697         trans[11] = org2.z;
8698         
8699         /*  Calculate rmsd  */
8700         IntGroupIteratorReset(&iter);
8701         w = 0.0;
8702         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8703                 Vector tv;
8704                 ap1 = ATOM_AT_INDEX(ap, in);
8705                 TransformVec(&tv, trans, &ap1->r);
8706                 VecDec(tv, ref[i]);
8707                 w += VecLength2(tv);
8708         }
8709         w = sqrt(w / nn);
8710         IntGroupIteratorRelease(&iter);
8711         return w;
8712 }
8713
8714 /*
8715  *  call-seq:
8716  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8717  *
8718  *  Calculate the transform to fit the given group to the set of reference coordinates.
8719  *  The reference coordinates ref is given as either a frame number, an array of
8720  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8721  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8722  *  Return values are the transform (that converts the present coordinates to the
8723  *  target coordinates) and root mean square deviation (without weight).
8724  */
8725 static VALUE
8726 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8727 {
8728         Molecule *mol;
8729         Atom *ap;
8730         VALUE gval, rval, wval;
8731         IntGroup *ig;
8732         IntGroupIterator iter;
8733         int nn, errnum, i, j, in, status;
8734         Vector *ref;
8735         Double *weights, dval[3];
8736         Transform tr;
8737
8738         Data_Get_Struct(self, Molecule, mol);
8739         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8740         if (gval == Qnil)
8741                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8742         else
8743                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8744         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8745                 IntGroupRelease(ig);
8746                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8747         }
8748         ref = (Vector *)calloc(sizeof(Vector), nn);
8749         weights = (Double *)calloc(sizeof(Double), nn);
8750         IntGroupIteratorInit(ig, &iter);
8751         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8752                 int fn = NUM2INT(rb_Integer(rval));
8753                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8754                         errnum = 1;
8755                         status = fn;
8756                         goto err;
8757                 }
8758                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8759                         ap = ATOM_AT_INDEX(mol->atoms, in);
8760                         if (fn < ap->nframes)
8761                                 ref[i] = ap->frames[fn];
8762                         else ref[i] = ap->r;
8763                 }
8764         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8765                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8766                 if (m->row * m->column < nn * 3) {
8767                         errnum = 2;
8768                         goto err;
8769                 }
8770                 for (i = 0; i < nn; i++) {
8771                         ref[i].x = m->data[i * 3];
8772                         ref[i].y = m->data[i * 3 + 1];
8773                         ref[i].z = m->data[i * 3 + 2];
8774                 }
8775         } else {
8776                 VALUE aval;
8777                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8778                 if (status != 0) {
8779                         errnum = 3;
8780                         goto err;
8781                 }
8782                 if (RARRAY_LEN(rval) < nn) {
8783                         errnum = 2;
8784                         goto err;
8785                 }
8786                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8787                         /*  Array of 3*nn numbers  */
8788                         if (RARRAY_LEN(rval) < nn * 3) {
8789                                 errnum = 2;
8790                                 goto err;
8791                         }
8792                         for (i = 0; i < nn; i++) {
8793                                 for (j = 0; j < 3; j++) {
8794                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8795                                         if (status != 0) {
8796                                                 errnum = 3;
8797                                                 goto err;
8798                                         }
8799                                         dval[j] = NUM2DBL(aval);
8800                                 }
8801                                 ref[i].x = dval[0];
8802                                 ref[i].y = dval[1];
8803                                 ref[i].z = dval[2];
8804                         }
8805                 } else {
8806                         /*  Array of nn Vector3Ds or Arrays  */
8807                         for (i = 0; i < nn; i++) {
8808                                 aval = (RARRAY_PTR(rval))[i];
8809                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8810                                         VectorFromValue(aval, &ref[i]);
8811                                 } else {
8812                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8813                                         if (status != 0) {
8814                                                 errnum = 3;
8815                                                 goto err;
8816                                         }
8817                                         if (RARRAY_LEN(aval) < 3) {
8818                                                 errnum = 4;
8819                                                 status = i;
8820                                                 goto err;
8821                                         }
8822                                         for (j = 0; j < 3; j++) {
8823                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8824                                                 if (status != 0) {
8825                                                         errnum = 3;
8826                                                         goto err;
8827                                                 }
8828                                                 dval[j] = NUM2DBL(aaval);
8829                                         }
8830                                         ref[i].x = dval[0];
8831                                         ref[i].y = dval[1];
8832                                         ref[i].z = dval[2];
8833                                 }
8834                         }
8835                 }
8836         }
8837         if (wval == Qnil) {
8838                 /*  Use atomic weights  */
8839                 IntGroupIteratorReset(&iter);
8840                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8841                         ap = ATOM_AT_INDEX(mol->atoms, in);
8842                         weights[i] = ap->weight;
8843                 }
8844         } else {
8845                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8846                 if (status != 0) {
8847                         errnum = 3;
8848                         goto err;
8849                 }
8850                 if (RARRAY_LEN(wval) < nn) {
8851                         errnum = 5;
8852                         goto err;
8853                 }
8854                 for (i = 0; i < nn; i++) {
8855                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8856                         if (status != 0) {
8857                                 errnum = 3;
8858                                 goto err;
8859                         }
8860                         weights[i] = NUM2DBL(wwval);
8861                 }
8862         }
8863         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8864         if (dval[0] < 0) {
8865                 errnum = 6;
8866                 goto err;
8867         }
8868         errnum = 0;
8869 err:
8870         IntGroupIteratorRelease(&iter);
8871         free(ref);
8872         free(weights);
8873         if (errnum == 0) {
8874                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8875         } else if (errnum == 1) {
8876                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8877         } else if (errnum == 2) {
8878                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8879         } else if (errnum == 3) {
8880                 rb_jump_tag(status);
8881         } else if (errnum == 4) {
8882                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8883         } else if (errnum == 5) {
8884                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8885         } else if (errnum == 6) {
8886                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8887         }
8888         return Qnil;  /*  Not reached  */
8889 }
8890
8891 #pragma mark ------ Screen Display ------
8892
8893 /*
8894  *  call-seq:
8895  *     display
8896  *
8897  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8898  */
8899 static VALUE
8900 s_Molecule_Display(VALUE self)
8901 {
8902     Molecule *mol;
8903     Data_Get_Struct(self, Molecule, mol);
8904         if (mol->mview != NULL)
8905                 MainViewCallback_display(mol->mview);
8906         return Qnil;
8907 }
8908
8909 /*
8910  *  call-seq:
8911  *     make_front
8912  *
8913  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8914  */
8915 static VALUE
8916 s_Molecule_MakeFront(VALUE self)
8917 {
8918     Molecule *mol;
8919     Data_Get_Struct(self, Molecule, mol);
8920         if (mol->mview != NULL)
8921                 MainViewCallback_makeFront(mol->mview);
8922         return Qnil;
8923 }
8924
8925 /*
8926  *  call-seq:
8927  *     update_enabled? -> bool
8928  *
8929  *  Returns true if screen update is enabled; otherwise no.
8930  */
8931 static VALUE
8932 s_Molecule_UpdateEnabled(VALUE self)
8933 {
8934     Molecule *mol;
8935     Data_Get_Struct(self, Molecule, mol);
8936         if (mol->mview != NULL && !mol->mview->freezeScreen)
8937                 return Qtrue;
8938         else return Qfalse;
8939 }
8940
8941 /*
8942  *  call-seq:
8943  *     update_enabled = bool
8944  *
8945  *  Enable or disable screen update. This is effective for automatic update on modification.
8946  *  Explicit call to molecule.display() always updates the screen.
8947  */
8948 static VALUE
8949 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8950 {
8951     Molecule *mol;
8952     Data_Get_Struct(self, Molecule, mol);
8953         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8954         if (mol->mview != NULL)
8955                 mol->mview->freezeScreen = (val == Qfalse);
8956         else val = Qfalse;
8957         return val;
8958 }
8959
8960 /*
8961  *  call-seq:
8962  *     show_unitcell
8963  *     show_unitcell(bool)
8964  *     show_unitcell = bool
8965  *
8966  *  Set the flag whether to show the unit cell. If no argument is given, the
8967  *  current flag is returned.
8968  */
8969 static VALUE
8970 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8971 {
8972     Molecule *mol;
8973     Data_Get_Struct(self, Molecule, mol);
8974         if (mol->mview == NULL)
8975                 return Qnil;
8976         if (argc > 0) {
8977                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8978                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8979         }
8980         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8981 }
8982
8983 /*
8984  *  call-seq:
8985  *     show_hydrogens
8986  *     show_hydrogens(bool)
8987  *     show_hydrogens = bool
8988  *
8989  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8990  *  current flag is returned.
8991  */
8992 static VALUE
8993 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8994 {
8995     Molecule *mol;
8996     Data_Get_Struct(self, Molecule, mol);
8997         if (mol->mview == NULL)
8998                 return Qnil;
8999         if (argc > 0) {
9000                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
9001                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9002         }
9003         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
9004 }
9005
9006 /*
9007  *  call-seq:
9008  *     show_dummy_atoms
9009  *     show_dummy_atoms(bool)
9010  *     show_dummy_atoms = bool
9011  *
9012  *  Set the flag whether to show the dummy atoms. If no argument is given, the
9013  *  current flag is returned.
9014  */
9015 static VALUE
9016 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
9017 {
9018     Molecule *mol;
9019     Data_Get_Struct(self, Molecule, mol);
9020         if (mol->mview == NULL)
9021                 return Qnil;
9022         if (argc > 0) {
9023                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9024                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9025         }
9026         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9027 }
9028
9029 /*
9030  *  call-seq:
9031  *     show_expanded
9032  *     show_expanded(bool)
9033  *     show_expanded = bool
9034  *
9035  *  Set the flag whether to show the expanded atoms. If no argument is given, the
9036  *  current flag is returned.
9037  */
9038 static VALUE
9039 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9040 {
9041     Molecule *mol;
9042     Data_Get_Struct(self, Molecule, mol);
9043         if (mol->mview == NULL)
9044                 return Qnil;
9045         if (argc > 0) {
9046                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9047                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9048         }
9049         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9050 }
9051
9052 /*
9053  *  call-seq:
9054  *     show_ellipsoids
9055  *     show_ellipsoids(bool)
9056  *     show_ellipsoids = bool
9057  *
9058  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9059  *  current flag is returned.
9060  */
9061 static VALUE
9062 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9063 {
9064     Molecule *mol;
9065     Data_Get_Struct(self, Molecule, mol);
9066         if (mol->mview == NULL)
9067                 return Qnil;
9068         if (argc > 0) {
9069                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9070                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9071         }
9072         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9073 }
9074
9075 /*
9076  *  call-seq:
9077  *     is_atom_visible(index)  -> Boolean
9078  *
9079  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9080  *  as well as the molecule attributes (showHydrogens, etc.)
9081  */
9082 static VALUE
9083 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9084 {
9085         Molecule *mol;
9086         Int idx;
9087         Atom *ap;
9088     Data_Get_Struct(self, Molecule, mol);
9089         idx = s_Molecule_AtomIndexFromValue(mol, ival);
9090         if (idx < 0 || idx >= mol->natoms)
9091                 return Qnil;
9092         ap = ATOM_AT_INDEX(mol->atoms, idx);
9093         if (mol->mview != NULL) {
9094                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9095                         return Qfalse;
9096                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9097                         return Qfalse;
9098                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9099                         return Qfalse;
9100         }
9101         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9102 }
9103
9104 /*
9105  *  call-seq:
9106  *     hidden_atoms       -> IntGroup
9107  *
9108  *  Returns the currently hidden atoms.
9109  */
9110 static VALUE
9111 s_Molecule_HiddenAtoms(VALUE self)
9112 {
9113         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9114         return Qnil;  /*  Not reached  */
9115 }
9116
9117 /*
9118  *  call-seq:
9119  *     set_hidden_atoms(IntGroup)
9120  *     self.hidden_atoms = IntGroup
9121  *
9122  *  Hide the specified atoms. This operation is _not_ undoable.
9123  */
9124 static VALUE
9125 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9126 {
9127         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9128         return Qnil;  /*  Not reached  */
9129 }
9130
9131 /*
9132  *  call-seq:
9133  *     show_graphite -> Integer
9134  *     show_graphite = Integer
9135  *     show_graphite = boolean
9136  *
9137  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9138  *  number of rings to display for each direction.
9139  *  If the argument is boolean, only the show/hide flag is set.
9140  */
9141 static VALUE
9142 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9143 {
9144     Molecule *mol;
9145     Data_Get_Struct(self, Molecule, mol);
9146         if (mol->mview == NULL)
9147                 return Qnil;
9148         if (argc > 0) {
9149                 if (argv[0] == Qnil || argv[0] == Qfalse)
9150                         mol->mview->showGraphiteFlag = 0;
9151                 else if (argv[0] == Qtrue)
9152                         mol->mview->showGraphiteFlag = 1;
9153                 else {
9154                         int n = NUM2INT(rb_Integer(argv[0]));
9155                         if (n < 0)
9156                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9157                         mol->mview->showGraphite = n;
9158                 }
9159                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9160         }
9161         return INT2NUM(mol->mview->showGraphite);
9162 }
9163
9164 /*
9165  *  call-seq:
9166  *     show_graphite? -> boolean
9167  *
9168  *  Return whether the graphite is set visible or not.
9169 */
9170 static VALUE
9171 s_Molecule_ShowGraphiteFlag(VALUE self)
9172 {
9173     Molecule *mol;
9174     Data_Get_Struct(self, Molecule, mol);
9175         if (mol->mview == NULL)
9176                 return Qnil;
9177         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9178 }
9179         
9180 /*
9181  *  call-seq:
9182  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9183  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9184  *     show_periodic_image = boolean
9185  *
9186  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9187  *  set but no visual effects are observed.
9188  *  If the argument is boolean, only the show/hide flag is modified.
9189  */
9190 static VALUE
9191 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9192 {
9193     Molecule *mol;
9194         VALUE val;
9195         int ival[6];
9196         int i;
9197     Data_Get_Struct(self, Molecule, mol);
9198         if (mol->mview == NULL)
9199                 return Qnil;
9200         rb_scan_args(argc, argv, "01", &val);
9201         if (argc > 0) {
9202                 /*  Change current settings  */
9203                 if (val == Qnil || val == Qfalse)
9204                         mol->mview->showPeriodicImageFlag = 0;
9205                 else if (val == Qtrue)
9206                         mol->mview->showPeriodicImageFlag = 1;
9207                 else {
9208                         val = rb_ary_to_ary(val);
9209                         for (i = 0; i < 6; i++) {
9210                                 if (i < RARRAY_LEN(val))
9211                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9212                         }
9213                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9214                                 rb_raise(rb_eMolbyError, "bad arguments");
9215                         for (i = 0; i < 6; i++)
9216                                 mol->mview->showPeriodicImage[i] = ival[i];
9217                 }
9218                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9219         }
9220         val = rb_ary_new();
9221         for (i = 0; i < 6; i++)
9222                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9223         return val;
9224 }
9225
9226 /*
9227  *  call-seq:
9228  *     show_periodic_image? -> boolean
9229  *
9230  *  Return whether the periodic images are set to visible or not. This flag is
9231  *  independent from the show_periodic_image settings.
9232  */
9233 static VALUE
9234 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9235 {
9236     Molecule *mol;
9237     Data_Get_Struct(self, Molecule, mol);
9238         if (mol->mview == NULL)
9239                 return Qnil;
9240         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9241 }
9242
9243 /*
9244  *  call-seq:
9245  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9246  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9247  *     show_rotation_center = boolean
9248  *
9249  *  Set to show the rotation center of the screen.
9250  *  If the argument is boolean, only the show/hide flag is modified.
9251  */
9252 static VALUE
9253 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9254 {
9255     Molecule *mol;
9256     Data_Get_Struct(self, Molecule, mol);
9257         if (mol->mview == NULL)
9258                 return Qnil;
9259         if (argc > 0) {
9260                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9261                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9262         }
9263         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9264 }
9265
9266 /*
9267  *  call-seq:
9268  *     line_mode
9269  *     line_mode(bool)
9270  *     line_mode = bool
9271  *
9272  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9273  *  current flag is returned.
9274  */
9275 static VALUE
9276 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9277 {
9278     Molecule *mol;
9279     Data_Get_Struct(self, Molecule, mol);
9280         if (mol->mview == NULL)
9281                 return Qnil;
9282         if (argc > 0) {
9283                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9284                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9285         }
9286         return (mol->mview->lineMode ? Qtrue : Qfalse);
9287 }
9288
9289 /*
9290  *  call-seq:
9291  *     atom_radius = float
9292  *     atom_radius
9293  *
9294  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9295  *  (Default = 0.4)
9296  *  If no argument is given, the current value is returned.
9297  */
9298 static VALUE
9299 s_Molecule_AtomRadius(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->atomRadius = rad;
9309                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9310                 }
9311                 return argv[0];
9312         }
9313         return rb_float_new(mol->mview->atomRadius);
9314 }
9315
9316 /*
9317  *  call-seq:
9318  *     bond_radius = float
9319  *     bond_radius
9320  *
9321  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9322  *  If no argument is given, the current value is returned.
9323  */
9324 static VALUE
9325 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9326 {
9327     Molecule *mol;
9328     Data_Get_Struct(self, Molecule, mol);
9329         if (mol->mview == NULL)
9330                 return Qnil;
9331         if (argc > 0) {
9332                 double rad = NUM2DBL(rb_Float(argv[0]));
9333                 if (rad > 0.0) {
9334                         mol->mview->bondRadius = rad;
9335                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9336                 }
9337                 return argv[0];
9338         }
9339         return rb_float_new(mol->mview->bondRadius);
9340 }
9341
9342 /*
9343  *  call-seq:
9344  *     atom_resolution = integer
9345  *     atom_resolution
9346  *
9347  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9348  *  (Default = 12; minimum = 6)
9349  *  If no argument is given, the current value is returned.
9350  */
9351 static VALUE
9352 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9353 {
9354     Molecule *mol;
9355     Data_Get_Struct(self, Molecule, mol);
9356         if (mol->mview == NULL)
9357                 return Qnil;
9358         if (argc > 0) {
9359                 int res = NUM2INT(rb_Integer(argv[0]));
9360                 if (res < 6)
9361                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9362                 mol->mview->atomResolution = res;
9363                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9364                 return INT2NUM(res);
9365         }
9366         return INT2NUM(mol->mview->atomResolution);
9367 }
9368
9369 /*
9370  *  call-seq:
9371  *     bond_resolution = integer
9372  *     bond_resolution
9373  *
9374  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9375  *  (Default = 8; minimum = 4)
9376  *  If no argument is given, the current value is returned.
9377  */
9378 static VALUE
9379 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9380 {
9381     Molecule *mol;
9382     Data_Get_Struct(self, Molecule, mol);
9383         if (mol->mview == NULL)
9384                 return Qnil;
9385         if (argc > 0) {
9386                 int res = NUM2INT(rb_Integer(argv[0]));
9387                 if (res < 4)
9388                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9389                 mol->mview->bondResolution = res;
9390                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9391                 return INT2NUM(res);
9392         }
9393         return INT2NUM(mol->mview->bondResolution);
9394 }
9395
9396 /*
9397  *  call-seq:
9398  *     resize_to_fit
9399  *
9400  *  Resize the model drawing to fit in the window.
9401  */
9402 static VALUE
9403 s_Molecule_ResizeToFit(VALUE self)
9404 {
9405     Molecule *mol;
9406     Data_Get_Struct(self, Molecule, mol);
9407         if (mol->mview != NULL)
9408                 MainView_resizeToFit(mol->mview);
9409         return self;    
9410 }
9411
9412 /*
9413  *  call-seq:
9414  *     get_view_rotation -> [[ax, ay, az], angle]
9415  *
9416  *  Get the current rotation for the view. Angle is in degree, not radian.
9417  */
9418 static VALUE
9419 s_Molecule_GetViewRotation(VALUE self)
9420 {
9421     Molecule *mol;
9422         double f[4];
9423         Vector v;
9424     Data_Get_Struct(self, Molecule, mol);
9425         if (mol->mview == NULL)
9426                 return Qnil;
9427         TrackballGetRotate(mol->mview->track, f);
9428         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9429         v.x = f[1];
9430         v.y = f[2];
9431         v.z = f[3];
9432         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9433 }
9434
9435 /*
9436  *  call-seq:
9437  *     get_view_scale -> float
9438  *
9439  *  Get the current scale for the view.
9440  */
9441 static VALUE
9442 s_Molecule_GetViewScale(VALUE self)
9443 {
9444     Molecule *mol;
9445     Data_Get_Struct(self, Molecule, mol);
9446         if (mol->mview == NULL)
9447                 return Qnil;
9448         return rb_float_new(TrackballGetScale(mol->mview->track));
9449 }
9450
9451 /*
9452  *  call-seq:
9453  *     get_view_center -> Vector
9454  *
9455  *  Get the current center point of the view.
9456  */
9457 static VALUE
9458 s_Molecule_GetViewCenter(VALUE self)
9459 {
9460     Molecule *mol;
9461         double f[4];
9462         Vector v;
9463     Data_Get_Struct(self, Molecule, mol);
9464         if (mol->mview == NULL)
9465                 return Qnil;
9466         TrackballGetTranslate(mol->mview->track, f);
9467         v.x = -f[0] * mol->mview->dimension;
9468         v.y = -f[1] * mol->mview->dimension;
9469         v.z = -f[2] * mol->mview->dimension;
9470         return ValueFromVector(&v);
9471 }
9472
9473 /*
9474  *  call-seq:
9475  *     set_view_rotation([ax, ay, az], angle) -> self
9476  *
9477  *  Set the current rotation for the view. Angle is in degree, not radian.
9478  */
9479 static VALUE
9480 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9481 {
9482     Molecule *mol;
9483         double f[4];
9484         Vector v;
9485     Data_Get_Struct(self, Molecule, mol);
9486         if (mol->mview == NULL)
9487                 return Qnil;
9488         VectorFromValue(aval, &v);
9489         if (NormalizeVec(&v, &v))
9490                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9491         f[1] = v.x;
9492         f[2] = v.y;
9493         f[3] = v.z;
9494         f[0] = -NUM2DBL(rb_Float(angval));
9495         TrackballSetRotate(mol->mview->track, f);
9496         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9497         return self;
9498 }
9499
9500 /*
9501  *  call-seq:
9502  *     set_view_scale(scale) -> self
9503  *
9504  *  Set the current scale for the view.
9505  */
9506 static VALUE
9507 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9508 {
9509     Molecule *mol;
9510     Data_Get_Struct(self, Molecule, mol);
9511         if (mol->mview == NULL)
9512                 return Qnil;
9513         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9514         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9515         return self;
9516 }
9517
9518 /*
9519  *  call-seq:
9520  *     set_view_center(vec) -> self
9521  *
9522  *  Set the current center point of the view.
9523  */
9524 static VALUE
9525 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9526 {
9527     Molecule *mol;
9528         Vector v;
9529         double f[4];
9530     Data_Get_Struct(self, Molecule, mol);
9531         if (mol->mview == NULL)
9532                 return Qnil;
9533         VectorFromValue(aval, &v);
9534         f[0] = -v.x / mol->mview->dimension;
9535         f[1] = -v.y / mol->mview->dimension;
9536         f[2] = -v.z / mol->mview->dimension;
9537         TrackballSetTranslate(mol->mview->track, f);
9538         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9539         return self;
9540 }
9541
9542 /*
9543  *  call-seq:
9544  *     set_background_color(red, green, blue)
9545  *
9546  *  Set the background color of the model window.
9547  */
9548 static VALUE
9549 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9550 {
9551     Molecule *mol;
9552     Data_Get_Struct(self, Molecule, mol);
9553         if (mol->mview != NULL) {
9554                 VALUE rval, gval, bval;
9555                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9556                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9557         }
9558         return self;    
9559 }
9560
9561 /*
9562  *  call-seq:
9563  *     export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9564  *
9565  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9566  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9567  *  If either width or height is not specified, then the screen width/height is used instead.
9568  */
9569 static VALUE
9570 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9571 {
9572         Molecule *mol;
9573         VALUE fval, sval, bval, wval, hval;
9574         char *fname;
9575         float scale;
9576         int bg_color, width, height;
9577     Data_Get_Struct(self, Molecule, mol);
9578         if (mol->mview == NULL)
9579                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9580         rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9581         fname = FileStringValuePtr(fval);
9582         if (sval == Qnil)
9583                 scale = 1.0;
9584         else scale = NUM2DBL(rb_Float(sval));
9585         if (bval == Qnil)
9586                 bg_color = -1;
9587         else bg_color = NUM2INT(rb_Integer(bval));
9588         if (wval == Qnil)
9589                 width = 0;
9590         else width = NUM2INT(rb_Integer(wval));
9591         if (hval == Qnil)
9592                 height = 0;
9593         else height = NUM2INT(rb_Integer(hval));
9594         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9595                 return fval;
9596         else return Qnil;
9597 }
9598
9599 #pragma mark ------ Graphics ------
9600
9601 static void
9602 s_CalculateGraphicNormals(MainViewGraphic *gp)
9603 {
9604         int i;
9605         Vector v1, v2, v3;
9606         if (gp == NULL || gp->npoints < 3)
9607                 return;
9608         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9609         v1.x = gp->points[3] - gp->points[0];
9610         v1.y = gp->points[4] - gp->points[1];
9611         v1.z = gp->points[5] - gp->points[2];
9612         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9613         for (i = 2; i < gp->npoints; i++) {
9614                 v2.x = gp->points[i * 3] - gp->points[0];
9615                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9616                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9617                 VecCross(v3, v1, v2);
9618                 NormalizeVec(&v3, &v3);
9619                 gp->normals[i * 3] = v3.x;
9620                 gp->normals[i * 3 + 1] = v3.y;
9621                 gp->normals[i * 3 + 2] = v3.z;
9622                 v1 = v2;
9623         }
9624         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9625         VecZero(v1);
9626         for (i = 2; i < gp->npoints; i++) {
9627                 v1.x += gp->normals[i * 3];
9628                 v1.y += gp->normals[i * 3 + 1];
9629                 v1.z += gp->normals[i * 3 + 2];
9630         }
9631         NormalizeVec(&v1, &v1);
9632         gp->normals[0] = v1.x;
9633         gp->normals[1] = v1.y;
9634         gp->normals[2] = v1.z;
9635         /*  normals[1] = nv[2].normalize  */
9636         v2.x = gp->normals[6];
9637         v2.y = gp->normals[7];
9638         v2.z = gp->normals[8];
9639         NormalizeVec(&v1, &v2);
9640         gp->normals[3] = v1.x;
9641         gp->normals[4] = v1.y;
9642         gp->normals[5] = v1.z;
9643         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9644         for (i = 2; i < gp->npoints; i++) {
9645                 if (i == gp->npoints - 1)
9646                         VecZero(v3);
9647                 else {
9648                         v3.x = gp->normals[i * 3 + 3];
9649                         v3.y = gp->normals[i * 3 + 4];
9650                         v3.z = gp->normals[i * 3 + 5];
9651                 }
9652                 VecInc(v2, v3);
9653                 NormalizeVec(&v1, &v2);
9654                 gp->normals[i * 3] = v1.x;
9655                 gp->normals[i * 3 + 1] = v1.y;
9656                 gp->normals[i * 3 + 2] = v1.z;
9657                 v2 = v3;
9658         }
9659 }
9660
9661 /*
9662  *  call-seq:
9663  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9664  *
9665  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9666  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9667  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9668  *   points: an array of Vectors
9669  *   
9670  */
9671 static VALUE
9672 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9673 {
9674     Molecule *mol;
9675         MainViewGraphic g;
9676         int i, n, ni, idx;
9677         const char *p;
9678         VALUE kval, cval, pval, fval, ival;
9679     Data_Get_Struct(self, Molecule, mol);
9680         if (mol->mview == NULL)
9681                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9682         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9683         idx = NUM2INT(rb_Integer(ival));
9684         if (idx == -1)
9685                 idx = mol->mview->ngraphics;
9686         else if (idx < 0 || idx > mol->mview->ngraphics)
9687                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9688         memset(&g, 0, sizeof(g));
9689         g.visible = 1;
9690         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9691                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9692         } else {
9693                 kval = rb_obj_as_string(kval);
9694                 p = StringValuePtr(kval);
9695                 if (strcmp(p, "line") == 0)
9696                         g.kind = kMainViewGraphicLine;
9697                 else if (strcmp(p, "poly") == 0)
9698                         g.kind = kMainViewGraphicPoly;
9699                 else if (strcmp(p, "cylinder") == 0)
9700                         g.kind = kMainViewGraphicCylinder;
9701                 else if (strcmp(p, "cone") == 0)
9702                         g.kind = kMainViewGraphicCone;
9703                 else if (strcmp(p, "ellipsoid") == 0)
9704                         g.kind = kMainViewGraphicEllipsoid;
9705                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9706         }
9707         g.closed = (RTEST(fval) ? 1 : 0);
9708         cval = rb_ary_to_ary(cval);
9709         n = RARRAY_LEN(cval);
9710         if (n < 3 || n >= 5)
9711                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9712         if (n == 3)
9713                 g.rgba[3] = 1.0;
9714         for (i = 0; i < n; i++)
9715                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9716         pval = rb_ary_to_ary(pval);
9717         n = RARRAY_LEN(pval);
9718         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9719         if (n <= 0)
9720                 rb_raise(rb_eArgError, "no control points are given");
9721         switch (g.kind) {
9722                 case kMainViewGraphicLine:
9723                         if (n < 2)
9724                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9725                         break;
9726                 case kMainViewGraphicPoly:
9727                         if (n < 3)
9728                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9729                         break;
9730                 case kMainViewGraphicCylinder:
9731                 case kMainViewGraphicCone:
9732                         if (n != 3)
9733                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9734                         ni = 2;
9735                         break;
9736                 case kMainViewGraphicEllipsoid:
9737                         if (n == 2) {
9738                                 ni = 1;
9739                         } else if (n != 4)
9740                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9741                         break;
9742         }
9743         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9744         for (i = 0; i < n; i++) {
9745                 Vector v;
9746                 VALUE rval = RARRAY_PTR(pval)[i];
9747                 if (i == ni) {
9748                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9749                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9750                                 VectorFromValue(rval, &v);
9751                         } else {
9752                                 v.x = NUM2DBL(rb_Float(rval));
9753                         }
9754                         v.y = v.z = 0;
9755                 } else {
9756                         VectorFromValue(rval, &v);
9757                 }
9758                 g.points[i * 3] = v.x;
9759                 g.points[i * 3 + 1] = v.y;
9760                 g.points[i * 3 + 2] = v.z;
9761         }
9762         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9763                 /*  Sphere  */
9764                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9765                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9766                 g.points[7] = g.points[11] = g.points[3];
9767         }
9768         if (g.kind == kMainViewGraphicPoly) {
9769                 /*  Calculate normals  */
9770                 s_CalculateGraphicNormals(&g);
9771         }
9772         MainView_insertGraphic(mol->mview, idx, &g);
9773         
9774         {
9775                 /*  Register undo  */
9776                 MolAction *act;
9777                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9778                 MolActionCallback_registerUndo(mol, act);
9779                 MolActionRelease(act);
9780         }
9781
9782         return INT2NUM(idx);    
9783 }
9784
9785 /*
9786  *  call-seq:
9787  *     create_graphic(kind, color, points, fill = nil) -> integer
9788  *
9789  *  Create a new graphic object. The arguments are similar as insert_graphic.
9790  */
9791 static VALUE
9792 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9793 {
9794         VALUE args[5];
9795         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9796         args[0] = INT2NUM(-1);
9797         return s_Molecule_InsertGraphic(argc + 1, args, self);
9798 }
9799
9800 /*
9801  *  call-seq:
9802  *     remove_graphic(index) -> integer
9803  *
9804  *  Remove a graphic object.
9805  */
9806 static VALUE
9807 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9808 {
9809     Molecule *mol;
9810         int i;
9811     Data_Get_Struct(self, Molecule, mol);
9812         if (mol->mview == NULL)
9813                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9814         i = NUM2INT(rb_Integer(ival));
9815         if (i < 0 || i >= mol->mview->ngraphics)
9816                 rb_raise(rb_eArgError, "graphic index is out of range");
9817         {
9818                 /*  Prepare data for undo  */
9819                 MainViewGraphic *gp;
9820                 Vector *vp;
9821                 MolAction *act;
9822                 double col[4];
9823                 int n;
9824                 gp = mol->mview->graphics + i;
9825                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9826                 for (n = 0; n < gp->npoints; n++) {
9827                         vp[n].x = gp->points[n * 3];
9828                         vp[n].y = gp->points[n * 3 + 1];
9829                         vp[n].z = gp->points[n * 3 + 2];
9830                 }
9831                 col[0] = gp->rgba[0];
9832                 col[1] = gp->rgba[1];
9833                 col[2] = gp->rgba[2];
9834                 col[3] = gp->rgba[3];
9835                 if (gp->visible == 0) {
9836                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9837                         MolActionCallback_registerUndo(mol, act);
9838                         MolActionRelease(act);
9839                 }
9840                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9841                 MolActionCallback_registerUndo(mol, act);
9842                 free(vp);
9843                 MolActionRelease(act);
9844         }
9845         MainView_removeGraphic(mol->mview, i);
9846         return ival;
9847 }
9848
9849 /*
9850  *  call-seq:
9851  *     ngraphics -> integer
9852  *
9853  *  Get the number of graphic objects.
9854  */
9855 static VALUE
9856 s_Molecule_NGraphics(VALUE self)
9857 {
9858     Molecule *mol;
9859     Data_Get_Struct(self, Molecule, mol);
9860         if (mol->mview == NULL)
9861                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9862         return INT2NUM(mol->mview->ngraphics);
9863 }
9864
9865 /*
9866  *  call-seq:
9867  *     get_graphic_point(graphic_index, point_index) -> value
9868  *     get_graphic_points(graphic_index) -> values
9869  *
9870  *  Get the point_index-th control point of graphic_index-th graphic object.
9871  *  Get an array of all control points with the given values.
9872  *   
9873  */
9874 static VALUE
9875 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9876 {
9877         MainViewGraphic *gp;
9878     Molecule *mol;
9879         int index, pindex;
9880         Vector v;
9881         VALUE gval, pval;
9882     Data_Get_Struct(self, Molecule, mol);
9883         if (mol->mview == NULL)
9884                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9885         rb_scan_args(argc, argv, "11", &gval, &pval);
9886         index = NUM2INT(rb_Integer(gval));
9887         if (index < 0 || index >= mol->mview->ngraphics)
9888                 rb_raise(rb_eArgError, "the graphic index is out of range");
9889         gp = mol->mview->graphics + index;
9890         if (pval != Qnil) {
9891                 pindex = NUM2INT(rb_Integer(pval));
9892                 if (pindex < 0 || pindex >= gp->npoints)
9893                         rb_raise(rb_eArgError, "the point index is out of range");
9894                 v.x = gp->points[pindex * 3];
9895                 v.y = gp->points[pindex * 3 + 1];
9896                 v.z = gp->points[pindex * 3 + 2];
9897                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9898                         return rb_float_new(v.x);
9899                 } else {
9900                         return ValueFromVector(&v);
9901                 }
9902         } else {
9903                 pval = rb_ary_new();
9904                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9905                         v.x = gp->points[pindex * 3];
9906                         v.y = gp->points[pindex * 3 + 1];
9907                         v.z = gp->points[pindex * 3 + 2];
9908                         rb_ary_push(pval, ValueFromVector(&v));
9909                 }
9910                 return pval;
9911         }
9912 }
9913
9914 /*
9915  *  call-seq:
9916  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9917  *     set_graphic_points(graphic_index, new_values) -> new_values
9918  *
9919  *  Change the point_index-th control point of graphic_index-th graphic object.
9920  *  Replace the control points with the given values.
9921  *   
9922  */
9923 static VALUE
9924 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9925 {
9926         MainViewGraphic *gp;
9927     Molecule *mol;
9928         int index, pindex;
9929         Vector v, v0;
9930         VALUE gval, pval, nval;
9931         MolAction *act;
9932     Data_Get_Struct(self, Molecule, mol);
9933         if (mol->mview == NULL)
9934                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9935         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9936         index = NUM2INT(rb_Integer(gval));
9937         if (index < 0 || index >= mol->mview->ngraphics)
9938                 rb_raise(rb_eArgError, "the graphic index is out of range");
9939         gp = mol->mview->graphics + index;
9940         if (nval != Qnil) {
9941                 pindex = NUM2INT(rb_Integer(pval));
9942                 if (pindex < 0 || pindex >= gp->npoints)
9943                         rb_raise(rb_eArgError, "the point index is out of range");
9944                 v0.x = gp->points[pindex * 3];
9945                 v0.y = gp->points[pindex * 3 + 1];
9946                 v0.z = gp->points[pindex * 3 + 2];
9947                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9948                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9949                                 v.x = NUM2DBL(rb_Float(nval));
9950                                 v.y = v.z = 0;
9951                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9952                                 v.x = NUM2DBL(rb_Float(nval));
9953                                 v.y = v.z = 0;
9954                                 gp->points[7] = gp->points[11] = v.x;
9955                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9956                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9957                 } else {
9958                         if (nval == Qnil) {
9959                                 v.x = kInvalidFloat;
9960                                 v.y = v.z = 0.0;
9961                         } else VectorFromValue(nval, &v);
9962                 }
9963                 gp->points[pindex * 3] = v.x;
9964                 gp->points[pindex * 3 + 1] = v.y;
9965                 gp->points[pindex * 3 + 2] = v.z;
9966                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9967         } else {
9968                 VALUE aval;
9969                 int len;
9970                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9971                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9972                         vp[pindex].x = gp->points[pindex * 3];
9973                         vp[pindex].y = gp->points[pindex * 3 + 1];
9974                         vp[pindex].z = gp->points[pindex * 3 + 2];
9975                 }
9976                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9977                 free(vp);
9978                 pval = rb_ary_to_ary(pval);
9979                 len = RARRAY_LEN(pval);
9980                 if (gp->npoints < len) {
9981                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9982                         gp->npoints = len;
9983                 } else if (gp->npoints > len) {
9984                         int len2 = 3;
9985                         switch (gp->kind) {
9986                                 case kMainViewGraphicLine: len2 = 2; break;
9987                                 case kMainViewGraphicPoly: len2 = 3; break;
9988                                 case kMainViewGraphicCylinder: len2 = 3; break;
9989                                 case kMainViewGraphicCone: len2 = 3; break;
9990                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9991                         }
9992                         if (len2 < len)
9993                                 len2 = len;
9994                         gp->npoints = len2;
9995                 }
9996                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9997                         aval = RARRAY_PTR(pval)[pindex];
9998                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9999                                 v.x = NUM2DBL(rb_Float(aval));
10000                                 v.y = v.z = 0;
10001                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
10002                                 v.x = NUM2DBL(rb_Float(aval));
10003                                 v.y = v.z = 0;
10004                                 gp->points[7] = gp->points[11] = v.x;
10005                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
10006                                 break;
10007                         } else VectorFromValue(aval, &v);
10008                         gp->points[pindex * 3] = v.x;
10009                         gp->points[pindex * 3 + 1] = v.y;
10010                         gp->points[pindex * 3 + 2] = v.z;
10011                 }
10012         }
10013         if (gp->kind == kMainViewGraphicPoly) {
10014                 /*  Calculate normals  */
10015                 s_CalculateGraphicNormals(gp);
10016         }
10017         MolActionCallback_registerUndo(mol, act);
10018         MolActionRelease(act);          
10019         MoleculeCallback_notifyModification(mol, 0);
10020         return nval;
10021 }
10022
10023 /*
10024  *  call-seq:
10025  *     get_graphic_color(graphic_index) -> value
10026  *
10027  *  Get the color of graphic_index-th graphic object
10028  */
10029 static VALUE
10030 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10031 {
10032         MainViewGraphic *gp;
10033     Molecule *mol;
10034         int index;
10035     Data_Get_Struct(self, Molecule, mol);
10036         if (mol->mview == NULL)
10037                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10038         index = NUM2INT(rb_Integer(gval));
10039         if (index < 0 || index >= mol->mview->ngraphics)
10040                 rb_raise(rb_eArgError, "the graphic index is out of range");
10041         gp = mol->mview->graphics + index;
10042         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]));
10043 }
10044
10045 /*
10046  *  call-seq:
10047  *     set_graphic_color(graphic_index, new_value) -> new_value
10048  *
10049  *  Change the color of graphic_index-th graphic object
10050  *   
10051  */
10052 static VALUE
10053 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10054 {
10055         MainViewGraphic *gp;
10056     Molecule *mol;
10057         MolAction *act;
10058         double c[4];
10059         int index, i, n;
10060     Data_Get_Struct(self, Molecule, mol);
10061         if (mol->mview == NULL)
10062                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10063         index = NUM2INT(rb_Integer(gval));
10064         if (index < 0 || index >= mol->mview->ngraphics)
10065                 rb_raise(rb_eArgError, "the graphic index is out of range");
10066         gp = mol->mview->graphics + index;
10067         for (i = 0; i < 4; i++)
10068                 c[i] = gp->rgba[i];
10069         cval = rb_ary_to_ary(cval);
10070         n = RARRAY_LEN(cval);
10071         if (n != 3 && n != 4)
10072                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10073
10074         for (i = 0; i < n; i++) {
10075                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10076         }
10077         if (n == 3)
10078                 gp->rgba[3] = 1.0;
10079         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10080         MolActionCallback_registerUndo(mol, act);
10081         MolActionRelease(act);          
10082         MoleculeCallback_notifyModification(mol, 0);
10083         return cval;
10084 }
10085
10086 /*
10087  *  call-seq:
10088  *     show_graphic(graphic_index) -> self
10089  *
10090  *  Enable the visible flag of the graphic_index-th graphic object
10091  *   
10092  */
10093 static VALUE
10094 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10095 {
10096         MainViewGraphic *gp;
10097     Molecule *mol;
10098         int index;
10099     Data_Get_Struct(self, Molecule, mol);
10100         if (mol->mview == NULL)
10101                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10102         index = NUM2INT(rb_Integer(gval));
10103         if (index < 0 || index >= mol->mview->ngraphics)
10104                 rb_raise(rb_eArgError, "the graphic index is out of range");
10105         gp = mol->mview->graphics + index;
10106         gp->visible = 1;
10107         MoleculeCallback_notifyModification(mol, 0);
10108         return self;
10109 }
10110
10111 /*
10112  *  call-seq:
10113  *     hide_graphic(graphic_index) -> self
10114  *
10115  *  Disable the visible flag of the graphic_index-th graphic object
10116  *   
10117  */
10118 static VALUE
10119 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10120 {
10121         MainViewGraphic *gp;
10122     Molecule *mol;
10123         int index;
10124     Data_Get_Struct(self, Molecule, mol);
10125         if (mol->mview == NULL)
10126                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10127         index = NUM2INT(rb_Integer(gval));
10128         if (index < 0 || index >= mol->mview->ngraphics)
10129                 rb_raise(rb_eArgError, "the graphic index is out of range");
10130         gp = mol->mview->graphics + index;
10131         gp->visible = 0;
10132         MoleculeCallback_notifyModification(mol, 0);
10133         return self;
10134 }
10135
10136 /*
10137  *  call-seq:
10138  *     show_text(string)
10139  *
10140  *  Show the string in the info text box.
10141  */
10142 static VALUE
10143 s_Molecule_ShowText(VALUE self, VALUE arg)
10144 {
10145     Molecule *mol;
10146     Data_Get_Struct(self, Molecule, mol);
10147         if (mol->mview != NULL)
10148                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10149         return Qnil;
10150 }
10151
10152 #pragma mark ------ MD Support ------
10153
10154 /*
10155  *  call-seq:
10156  *     md_arena -> MDArena
10157  *
10158  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10159  *  this molecule, a new arena is created.
10160  */
10161 static VALUE
10162 s_Molecule_MDArena(VALUE self)
10163 {
10164     Molecule *mol;
10165         VALUE retval;
10166     Data_Get_Struct(self, Molecule, mol);
10167         if (mol->arena == NULL)
10168                 md_arena_new(mol);
10169         retval = ValueFromMDArena(mol->arena);
10170         return retval;
10171 }
10172
10173 /*
10174  *  call-seq:
10175  *     set_parameter_attr(type, index, key, value, src) -> value
10176  *
10177  *  This method is used only internally.
10178  */
10179 static VALUE
10180 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10181 {
10182         /*  This method is called from MolAction to change a MM parameter attribute.  */
10183     Molecule *mol;
10184         VALUE pval;
10185         ParameterRef *pref;
10186         UnionPar *up;
10187     Data_Get_Struct(self, Molecule, mol);
10188         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10189         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10190         
10191         /*  This is the special part of this method; it allows modification of the src field. */
10192         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10193         Data_Get_Struct(pval, ParameterRef, pref);
10194         up = ParameterRefGetPar(pref);
10195         up->bond.src = FIX2INT(sval);
10196         
10197         return vval;
10198 }
10199
10200 /*
10201  *  call-seq:
10202  *     parameter -> Parameter
10203  *
10204  *  Get the local parameter of this molecule. If not defined, returns nil.
10205  */
10206 static VALUE
10207 s_Molecule_Parameter(VALUE self)
10208 {
10209     Molecule *mol;
10210     Data_Get_Struct(self, Molecule, mol);
10211 /*      if (mol->par == NULL)
10212                 return Qnil; */
10213         return s_NewParameterValueFromValue(self);
10214 }
10215
10216 /*
10217  *  call-seq:
10218  *     start_step       -> Integer
10219  *
10220  *  Returns the start step (defined by dcd format).
10221  */
10222 static VALUE
10223 s_Molecule_StartStep(VALUE self)
10224 {
10225     Molecule *mol;
10226     Data_Get_Struct(self, Molecule, mol);
10227         return INT2NUM(mol->startStep);
10228 }
10229
10230 /*
10231  *  call-seq:
10232  *     start_step = Integer
10233  *
10234  *  Set the start step (defined by dcd format).
10235  */
10236 static VALUE
10237 s_Molecule_SetStartStep(VALUE self, VALUE val)
10238 {
10239     Molecule *mol;
10240     Data_Get_Struct(self, Molecule, mol);
10241         mol->startStep = NUM2INT(rb_Integer(val));
10242         return val;
10243 }
10244
10245 /*
10246  *  call-seq:
10247  *     steps_per_frame       -> Integer
10248  *
10249  *  Returns the number of steps between frames (defined by dcd format).
10250  */
10251 static VALUE
10252 s_Molecule_StepsPerFrame(VALUE self)
10253 {
10254     Molecule *mol;
10255     Data_Get_Struct(self, Molecule, mol);
10256         return INT2NUM(mol->stepsPerFrame);
10257 }
10258
10259 /*
10260  *  call-seq:
10261  *     steps_per_frame = Integer
10262  *
10263  *  Set the number of steps between frames (defined by dcd format).
10264  */
10265 static VALUE
10266 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10267 {
10268     Molecule *mol;
10269     Data_Get_Struct(self, Molecule, mol);
10270         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10271         return val;
10272 }
10273
10274 /*
10275  *  call-seq:
10276  *     ps_per_step       -> Float
10277  *
10278  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10279  */
10280 static VALUE
10281 s_Molecule_PsPerStep(VALUE self)
10282 {
10283     Molecule *mol;
10284     Data_Get_Struct(self, Molecule, mol);
10285         return rb_float_new(mol->psPerStep);
10286 }
10287
10288 /*
10289  *  call-seq:
10290  *     ps_per_step = Float
10291  *
10292  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10293  */
10294 static VALUE
10295 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10296 {
10297     Molecule *mol;
10298     Data_Get_Struct(self, Molecule, mol);
10299         mol->psPerStep = NUM2DBL(rb_Float(val));
10300         return val;
10301 }
10302
10303 static VALUE
10304 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10305 {
10306         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.");
10307 }
10308
10309 #pragma mark ------ MO Handling ------
10310
10311 /*
10312  *  call-seq:
10313  *     selectedMO -> IntGroup
10314  *
10315  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10316  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10317  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10318  */
10319 static VALUE
10320 s_Molecule_SelectedMO(VALUE self)
10321 {
10322     Molecule *mol;
10323         IntGroup *ig;
10324         VALUE val;
10325     Data_Get_Struct(self, Molecule, mol);
10326         if (mol->mview == NULL)
10327                 return Qnil;
10328         ig = MainView_selectedMO(mol->mview);
10329         if (ig == NULL)
10330                 return Qnil;
10331         IntGroupOffset(ig, 1);
10332         val = ValueFromIntGroup(ig);
10333         IntGroupRelease(ig);
10334         return val;
10335 }
10336
10337 /*
10338  *  call-seq:
10339  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10340  *
10341  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10342  *  If the molecule does not contain a basis set information, then returns nil.
10343  */
10344 static VALUE
10345 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10346 {
10347     Molecule *mol;
10348         Vector o, dx, dy, dz;
10349         Int nx, ny, nz;
10350         VALUE nval;
10351         Int npoints = 80 * 80 * 80;
10352     Data_Get_Struct(self, Molecule, mol);
10353         if (mol->bset == NULL)
10354                 return Qnil;
10355         rb_scan_args(argc, argv, "01", &nval);
10356         if (nval != Qnil)
10357                 npoints = NUM2INT(rb_Integer(nval));
10358         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10359                 return Qnil;
10360         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));
10361 }
10362
10363 static int
10364 s_Cubegen_callback(double progress, void *ref)
10365 {
10366         MyAppCallback_setProgressValue(progress);
10367         if (MyAppCallback_checkInterrupt())
10368                 return 1;
10369         else return 0;
10370 }
10371
10372 /*
10373  *  call-seq:
10374  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10375  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10376  *
10377  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10378  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10379  *  second form, the cube dimension is explicitly given.
10380  *  Returns fname when successful, nil otherwise.
10381  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10382  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10383  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10384  */
10385 static VALUE
10386 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10387 {
10388         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10389     Molecule *mol;
10390         Int mono, nx, ny, nz, npoints;
10391         Vector o, dx, dy, dz;
10392         int index, n;
10393         char buf[1024];
10394     Data_Get_Struct(self, Molecule, mol);
10395         if (mol->bset == NULL)
10396                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10397         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10398         
10399         /*  Set up parameters  */
10400         mono = NUM2INT(rb_Integer(mval));
10401         if (mono < 0 || mono > mol->bset->ncomps)
10402                 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);
10403         if (RTEST(bval)) {
10404                 if (mol->bset->rflag != 0)
10405                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10406                 mono += mol->bset->ncomps;
10407         }
10408                 
10409         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10410                 /*  Automatic grid formation  */
10411                 if (oval != Qnil)
10412                         npoints = NUM2INT(rb_Integer(oval));
10413                 else npoints = 0;
10414                 if (npoints == 0)
10415                         npoints = 1000000;
10416                 else if (npoints < 8)
10417                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10418                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10419                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10420                 ival = dxval;
10421                 bval = dyval;
10422         } else {
10423                 VectorFromValue(oval, &o);
10424                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10425                         VectorFromValue(dxval, &dx);
10426                 else {
10427                         dx.x = NUM2DBL(rb_Float(dxval));
10428                         dx.y = dx.z = 0.0;
10429                 }
10430                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10431                         VectorFromValue(dyval, &dy);
10432                 else {
10433                         dy.y = NUM2DBL(rb_Float(dyval));
10434                         dy.x = dy.z = 0.0;
10435                 }
10436                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10437                         VectorFromValue(dzval, &dz);
10438                 else {
10439                         dz.z = NUM2DBL(rb_Float(dzval));
10440                         dz.x = dz.y = 0.0;
10441                 }
10442                 nx = NUM2INT(rb_Integer(nxval));
10443                 ny = NUM2INT(rb_Integer(nyval));
10444                 nz = NUM2INT(rb_Integer(nzval));
10445                 if (nx <= 0 || ny <= 0 || nz <= 0)
10446                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10447                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10448                         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);
10449         }
10450         
10451         /*  Calc MO  */
10452         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10453         if (index == -2)
10454                 rb_interrupt();
10455         else if (index < 0)
10456                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10457         
10458         /*  Output to file  */
10459         MoleculeCallback_displayName(mol, buf, sizeof buf);
10460         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10461         if (n != 0)
10462                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10463         
10464         /*  Discard the cube  */
10465         MoleculeClearCubeAtIndex(mol, index);
10466         return fval;
10467 }
10468
10469 /*
10470  *  call-seq:
10471  *     clear_surface
10472  *
10473  *  Clear the MO surface if present.
10474  */
10475 static VALUE
10476 s_Molecule_ClearSurface(VALUE self)
10477 {
10478     Molecule *mol;
10479     Data_Get_Struct(self, Molecule, mol);
10480         if (mol->mcube != NULL)
10481                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10482         return self;
10483 }
10484
10485 /*
10486  *  call-seq:
10487  *     hide_surface
10488  *
10489  *  Hide the MO surface if present.
10490  */
10491 static VALUE
10492 s_Molecule_HideSurface(VALUE self)
10493 {
10494     Molecule *mol;
10495     Data_Get_Struct(self, Molecule, mol);
10496         if (mol->mcube != NULL) {
10497                 mol->mcube->hidden = 1;
10498                 MoleculeCallback_notifyModification(mol, 0);
10499         }
10500         return self;
10501 }
10502
10503 /*
10504  *  call-seq:
10505  *     show_surface
10506  *
10507  *  Show the MO surface if present.
10508  */
10509 static VALUE
10510 s_Molecule_ShowSurface(VALUE self)
10511 {
10512     Molecule *mol;
10513     Data_Get_Struct(self, Molecule, mol);
10514         if (mol->mcube != NULL) {
10515                 mol->mcube->hidden = 0;
10516                 MoleculeCallback_notifyModification(mol, 0);
10517         }
10518         return self;
10519 }
10520
10521 /*
10522  *  call-seq:
10523  *     create_surface(mo, attr = nil)
10524  *
10525  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10526  *  then it denotes the beta orbital.
10527  *  If mo is nil, then the attributes of the current surface are modified.
10528  *  Attributes:
10529  *    :npoints : the approximate number of grid points
10530  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10531  *    :thres   : the threshold for the isovalue surface
10532  *  If the molecule does not contain MO information, raises exception.
10533  */
10534 static VALUE
10535 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10536 {
10537     Molecule *mol;
10538         Vector o, dx, dy, dz;
10539         Int nmo, nx, ny, nz, i;
10540         Int need_recalc = 0;
10541         VALUE nval, hval, aval;
10542         Int npoints;
10543         Double expand;
10544         Double thres;
10545         Double d[4];
10546     Data_Get_Struct(self, Molecule, mol);
10547         rb_scan_args(argc, argv, "11", &nval, &hval);
10548         if (mol->bset == NULL)
10549                 rb_raise(rb_eMolbyError, "No MO information is given");
10550         if (nval == Qnil) {
10551                 nmo = -1;
10552         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10553                 nmo = mol->bset->nmos + 1;
10554         } else {
10555                 nmo = NUM2INT(rb_Integer(nval));
10556                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10557                         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);
10558                 if (nmo < 0)
10559                         nmo = -nmo + mol->bset->ncomps;
10560         }
10561         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10562                 npoints = NUM2INT(rb_Integer(aval));
10563                 need_recalc = 1;
10564         } else if (mol->mcube != NULL) {
10565                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10566         } else npoints = 80 * 80 * 80;
10567         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10568                 expand = NUM2DBL(rb_Float(aval));
10569         } else if (mol->mcube != NULL) {
10570                 expand = mol->mcube->expand;
10571         } else expand = 1.0;
10572         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10573                 thres = NUM2DBL(rb_Float(aval));
10574         } else if (mol->mcube != NULL) {
10575                 thres = mol->mcube->thres;
10576         } else thres = 0.05;
10577         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10578                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10579                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10580                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10581                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10582         }
10583         for (nx = 0; nx < 2; nx++) {
10584                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10585                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10586                         aval = rb_ary_to_ary(aval);
10587                         if (RARRAY_LEN(aval) < 3) {
10588                         raise:
10589                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10590                         }
10591                         for (i = 0; i < 4; i++)
10592                                 d[i] = mol->mcube->c[nx].rgba[i];
10593                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10594                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10595                                 if (d[i] < 0.0 && d[i] > 1.0)
10596                                         goto raise;
10597                         }
10598                         for (i = 0; i < 4; i++)
10599                                 mol->mcube->c[nx].rgba[i] = d[i];
10600                 }
10601         }
10602         if (mol->mcube->expand != expand)
10603                 need_recalc = 1;
10604         mol->mcube->thres = thres;
10605         mol->mcube->expand = expand;
10606         if (nmo < 0) {
10607                 if (mol->mcube->idn < 0)
10608                         return self;  /*  Only set attributes for now  */
10609                 if (need_recalc)
10610                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10611         }
10612         if (MoleculeUpdateMCube(mol, nmo) != 0)
10613                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10614         return self;
10615 }
10616
10617 /*
10618  *  call-seq:
10619  *     set_surface_attr(attr = nil)
10620  *
10621  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10622  */
10623 static VALUE
10624 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10625 {
10626         VALUE args[2];
10627         args[0] = Qnil;
10628         args[1] = hval;
10629         return s_Molecule_CreateSurface(2, args, self);
10630 }
10631
10632 /*
10633  *  call-seq:
10634  *     nelpots
10635  *
10636  *  Get the number of electrostatic potential info.
10637  */
10638 static VALUE
10639 s_Molecule_NElpots(VALUE self)
10640 {
10641         Molecule *mol;
10642     Data_Get_Struct(self, Molecule, mol);
10643         return INT2NUM(mol->nelpots);
10644 }
10645
10646 /*
10647  *  call-seq:
10648  *     elpot(idx)
10649  *
10650  *  Get the electrostatic potential info at the given index. If present, then the
10651  *  return value is [Vector, Float] (position and potential). If not present, then
10652  *  returns nil.
10653  */
10654 static VALUE
10655 s_Molecule_Elpot(VALUE self, VALUE ival)
10656 {
10657         Molecule *mol;
10658         int idx;
10659     Data_Get_Struct(self, Molecule, mol);
10660         idx = NUM2INT(rb_Integer(ival));
10661         if (idx < 0 || idx >= mol->nelpots)
10662                 return Qnil;
10663         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10664 }
10665
10666 /*
10667  *  call-seq:
10668  *     clear_basis_set
10669  *
10670  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10671  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10672  */
10673 static VALUE
10674 s_Molecule_ClearBasisSet(VALUE self)
10675 {
10676         Molecule *mol;
10677     Data_Get_Struct(self, Molecule, mol);
10678         if (mol != NULL) {
10679                 if (mol->bset != NULL) {
10680                         BasisSetRelease(mol->bset);
10681                         mol->bset = NULL;
10682                 }
10683                 if (mol->mcube != NULL) {
10684                         MoleculeDeallocateMCube(mol->mcube);
10685                         mol->mcube = NULL;
10686                 }
10687         }
10688         return self;
10689 }
10690
10691 /*
10692  *  call-seq:
10693  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives[, additional_exponent])
10694  *
10695  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10696  *  and the number of primitives.
10697  *  Additional exponent is for JANPA only; implements an additinal r^N component that
10698  *  appears in cartesian->spherical conversion.
10699  *  Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type; -2, D5-type;
10700  *                 3, F-type; -3, F7-type; 4, G-type; -4, G9-type.
10701  *  Or: "s", S-type; "p", P-type; "sp", SP-type; "d", D-type; "d5", D5-type;
10702  *      "f", F-type; "f7", F7-type; "g", G-type; "g9", G9-type
10703  */
10704 static VALUE
10705 s_Molecule_AddGaussianOrbitalShell(int argc, VALUE *argv, VALUE self)
10706 {
10707         Molecule *mol;
10708     int sym, nprims, a_idx, n, add_exp;
10709     VALUE aval, symval, npval, addval;
10710     Data_Get_Struct(self, Molecule, mol);
10711     rb_scan_args(argc, argv, "31", &aval, &symval, &npval, &addval);
10712     if (rb_obj_is_kind_of(symval, rb_cString)) {
10713         const char *p = StringValuePtr(symval);
10714         if (strcasecmp(p, "s") == 0)
10715             sym = 0;
10716         else if (strcasecmp(p, "p") == 0)
10717             sym = 1;
10718         else if (strcasecmp(p, "sp") == 0)
10719             sym = -1;
10720         else if (strcasecmp(p, "d") == 0)
10721             sym = 2;
10722         else if (strcasecmp(p, "d5") == 0)
10723             sym = -2;
10724         else if (strcasecmp(p, "f") == 0)
10725             sym = 3;
10726         else if (strcasecmp(p, "f7") == 0)
10727             sym = -3;
10728         else if (strcasecmp(p, "g") == 0)
10729             sym = 4;
10730         else if (strcasecmp(p, "g9") == 0)
10731             sym = -4;
10732         else
10733             rb_raise(rb_eArgError, "Unknown orbital type '%s'", p);
10734     } else {
10735         sym = NUM2INT(rb_Integer(symval));
10736     }
10737         a_idx = NUM2INT(rb_Integer(aval));
10738         nprims = NUM2INT(rb_Integer(npval));
10739     if (addval != Qnil)
10740         add_exp = NUM2INT(rb_Integer(addval));
10741     else add_exp = 0;
10742         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims, add_exp);
10743         if (n == -1)
10744                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10745         else if (n == -2)
10746                 rb_raise(rb_eMolbyError, "Low memory");
10747         else if (n == -3)
10748                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10749         else if (n != 0)
10750                 rb_raise(rb_eMolbyError, "Unknown error");
10751         return self;
10752 }
10753
10754 /*
10755  *  call-seq:
10756  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10757  *
10758  *  To be used internally. Add a gaussian primitive coefficients.
10759  */
10760 static VALUE
10761 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10762 {
10763         Molecule *mol;
10764         Int n;
10765         Double exponent, contraction, contraction_sp;
10766     Data_Get_Struct(self, Molecule, mol);
10767         exponent = NUM2DBL(rb_Float(expval));
10768         contraction = NUM2DBL(rb_Float(cval));
10769         contraction_sp = NUM2DBL(rb_Float(cspval));
10770         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10771         if (n == -1)
10772                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10773         else if (n == -2)
10774                 rb_raise(rb_eMolbyError, "Low memory");
10775         else if (n != 0)
10776                 rb_raise(rb_eMolbyError, "Unknown error");
10777         return self;
10778 }
10779
10780 /*
10781  *  call-seq:
10782  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10783  *
10784  *  Get the Gaussian shell information for the given MO coefficient index.
10785  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10786  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10787  *  is the number of MO component belonging to this shell.
10788  */
10789 static VALUE
10790 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10791 {
10792         Molecule *mol;
10793         ShellInfo *sp;
10794         int s_idx, sym;
10795     Data_Get_Struct(self, Molecule, mol);
10796         if (mol->bset == NULL)
10797                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10798         s_idx = NUM2INT(rb_Integer(sval));
10799         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10800                 return Qnil;
10801         sp = mol->bset->shells + s_idx;
10802         sym = sp->sym;
10803         switch (sym) {
10804                 case kGTOType_S:  sym = 0;  break;
10805                 case kGTOType_SP: sym = -1; break;
10806                 case kGTOType_P:  sym = 1;  break;
10807                 case kGTOType_D:  sym = 2;  break;
10808                 case kGTOType_D5: sym = -2; break;
10809                 case kGTOType_F:  sym = 3;  break;
10810                 case kGTOType_F7: sym = -3; break;
10811                 case kGTOType_G:  sym = 4;  break;
10812                 case kGTOType_G9: sym = -4; break;
10813                 default:
10814                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10815         }
10816         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10817 }
10818
10819 /*
10820  *  call-seq:
10821  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10822  *
10823  *  Get the Gaussian primitive coefficients for the given MO component.
10824  */
10825 static VALUE
10826 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10827 {
10828         Molecule *mol;
10829         ShellInfo *sp;
10830         PrimInfo *pp;
10831         int s_idx, i;
10832         VALUE retval, aval;
10833     Data_Get_Struct(self, Molecule, mol);
10834         if (mol->bset == NULL)
10835                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10836         s_idx = NUM2INT(rb_Integer(sval));
10837         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10838                 return Qnil;
10839         sp = mol->bset->shells + s_idx;
10840         pp = mol->bset->priminfos + sp->p_idx;
10841         retval = rb_ary_new2(sp->nprim);
10842         for (i = 0; i < sp->nprim; i++) {
10843                 if (sp->sym == kGTOType_SP) {
10844                         /*  With P contraction coefficient  */
10845                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10846                 } else {
10847                         /*  Without P contraction coefficient  */
10848                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10849                 }
10850                 rb_ary_store(retval, i, aval);
10851         }
10852         return retval;
10853 }
10854
10855 /*
10856  *  call-seq:
10857  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10858  *
10859  *  Get the Gaussian shell information for the given MO coefficient index.
10860  */
10861 static VALUE
10862 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10863 {
10864         Molecule *mol;
10865         Int n, c, atom_idx, shell_idx;
10866         char label[32];
10867     Data_Get_Struct(self, Molecule, mol);
10868         if (mol->bset == NULL)
10869                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10870         c = NUM2INT(rb_Integer(cval));
10871         if (c < 0 || c >= mol->bset->ncomps)
10872                 return Qnil;
10873         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10874         if (n != 0)
10875                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10876         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10877 }
10878
10879 /*
10880  *  call-seq:
10881  *     clear_mo_coefficients
10882  *
10883  *  Clear the existing MO coefficients.
10884  */
10885 static VALUE
10886 s_Molecule_ClearMOCoefficients(VALUE self)
10887 {
10888         Molecule *mol;
10889         Data_Get_Struct(self, Molecule, mol);
10890         if (mol->bset != NULL) {
10891                 if (mol->bset->moenergies != NULL) {
10892                         free(mol->bset->moenergies);
10893                         mol->bset->moenergies = NULL;
10894                 }
10895                 if (mol->bset->mo != NULL) {
10896                         free(mol->bset->mo);
10897                         mol->bset->mo = NULL;
10898                 }
10899                 mol->bset->nmos = 0;
10900         }
10901         return self;
10902 }
10903
10904 /*
10905  *  call-seq:
10906  *     set_mo_coefficients(idx, energy, coefficients)
10907  *
10908  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10909  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10910  *  Energy is the MO energy, and coefficients is an array
10911  *  of MO coefficients.
10912  */
10913 static VALUE
10914 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10915 {
10916         Molecule *mol;
10917         Int idx, ncomps, i;
10918         Double energy;
10919         Double *coeffs;
10920     Data_Get_Struct(self, Molecule, mol);
10921         idx = NUM2INT(rb_Integer(ival));
10922         energy = NUM2DBL(rb_Float(eval));
10923         aval = rb_ary_to_ary(aval);
10924         ncomps = RARRAY_LEN(aval);
10925         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10926         if (coeffs == NULL) {
10927                 i = -2;
10928                 goto end;
10929         }
10930         for (i = 0; i < ncomps; i++)
10931                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10932         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10933 end:
10934         if (i == -1)
10935                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10936         else if (i == -2)
10937                 rb_raise(rb_eMolbyError, "Low memory");
10938         else if (i == -3)
10939                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10940         else if (i == -4)
10941                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10942         else if (i == -5)
10943                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10944         else if (i != 0)
10945                 rb_raise(rb_eMolbyError, "Unknown error");
10946         return self;
10947 }
10948
10949 /*
10950  *  call-seq:
10951  *     get_mo_coefficients(idx)
10952  *
10953  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10954  */
10955 static VALUE
10956 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10957 {
10958         Molecule *mol;
10959         Int idx, ncomps, n;
10960         Double energy;
10961         Double *coeffs;
10962         VALUE retval;
10963     Data_Get_Struct(self, Molecule, mol);
10964         idx = NUM2INT(rb_Integer(ival));
10965         ncomps = 0;
10966         coeffs = NULL;
10967         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10968         if (n == -1)
10969                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10970         else if (n == -2)
10971                 rb_raise(rb_eMolbyError, "No basis set information is present");
10972         else if (n == -3)
10973                 return Qnil;  /*  Silently returns nil  */
10974         retval = rb_ary_new2(ncomps);
10975         for (n = 0; n < ncomps; n++)
10976                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10977         free(coeffs);
10978         return retval;
10979 }
10980
10981 /*
10982  *  call-seq:
10983  *     get_mo_energy(idx)
10984  *
10985  *  To be used internally. Get the MO energy for the given MO index (1-based).
10986  */
10987 static VALUE
10988 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10989 {
10990         Molecule *mol;
10991         Int idx, n;
10992         Double energy;
10993     Data_Get_Struct(self, Molecule, mol);
10994         idx = NUM2INT(rb_Integer(ival));
10995         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10996         if (n == -1)
10997                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10998         else if (n == -2)
10999                 rb_raise(rb_eMolbyError, "No basis set information is present");
11000         else if (n == -3)
11001                 return Qnil;
11002         return rb_float_new(energy);
11003 }
11004
11005 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
11006
11007 static inline void
11008 s_InitMOInfoKeys(void)
11009 {
11010         if (sTypeSym == 0) {
11011                 sTypeSym = ID2SYM(rb_intern("type"));
11012                 sAlphaSym = ID2SYM(rb_intern("alpha"));
11013                 sBetaSym = ID2SYM(rb_intern("beta"));
11014                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
11015                 sNshellsSym = ID2SYM(rb_intern("nshells"));
11016         }
11017 }
11018
11019 /*
11020  *  call-seq:
11021  *     set_mo_info(hash)
11022  *
11023  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
11024  *  :alpha=>integer, :beta=>integer
11025  */
11026 static VALUE
11027 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
11028 {
11029         Molecule *mol;
11030         VALUE aval;
11031         Int rflag, na, nb, n;
11032         char *s;
11033     Data_Get_Struct(self, Molecule, mol);
11034         if (mol->bset != NULL) {
11035                 rflag = mol->bset->rflag;
11036                 na = mol->bset->ne_alpha;
11037                 nb = mol->bset->ne_beta;
11038         } else {
11039                 rflag = 1;
11040                 na = 0;
11041                 nb = 0;
11042         }
11043         if (hval != Qnil) {
11044                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
11045                         s = StringValuePtr(aval);
11046                         if (strcasecmp(s, "RHF") == 0)
11047                                 rflag = 1;
11048                         else if (strcasecmp(s, "UHF") == 0)
11049                                 rflag = 0;
11050                         else if (strcasecmp(s, "ROHF") == 0)
11051                                 rflag = 2;
11052                 }
11053                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
11054                         n = NUM2INT(rb_Integer(aval));
11055                         if (n >= 0)
11056                                 na = n;
11057                 }
11058                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
11059                         n = NUM2INT(rb_Integer(aval));
11060                         if (n >= 0)
11061                                 nb = n;
11062                 }
11063                 MoleculeSetMOInfo(mol, rflag, na, nb);
11064         }
11065         return self;
11066 }
11067
11068 /*
11069  *  call-seq:
11070  *     get_mo_info(key)
11071  *
11072  *  Get the MO info. The key is as described in set_mo_info.
11073  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11074  */
11075 static VALUE
11076 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11077 {
11078         Molecule *mol;
11079     Data_Get_Struct(self, Molecule, mol);
11080         if (mol->bset == NULL)
11081                 return Qnil;
11082         if (kval == sTypeSym) {
11083                 switch (mol->bset->rflag) {
11084                         case 0: return Ruby_NewEncodedStringValue2("UHF");
11085                         case 1: return Ruby_NewEncodedStringValue2("RHF");
11086                         case 2: return Ruby_NewEncodedStringValue2("ROHF");
11087                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11088                 }
11089         } else if (kval == sAlphaSym) {
11090                 return INT2NUM(mol->bset->ne_alpha);
11091         } else if (kval == sBetaSym) {
11092                 return INT2NUM(mol->bset->ne_beta);
11093         } else if (kval == sNcompsSym) {
11094                 return INT2NUM(mol->bset->ncomps);
11095         } else if (kval == sNshellsSym) {
11096                 return INT2NUM(mol->bset->nshells);
11097         } else {
11098                 kval = rb_inspect(kval);
11099                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11100                 return Qnil;  /*  Does not reach here  */
11101         }
11102 }
11103
11104 /*
11105  *  call-seq:
11106  *     mo_type
11107  *
11108  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11109  */
11110 static VALUE
11111 s_Molecule_MOType(VALUE self)
11112 {
11113         return s_Molecule_GetMOInfo(self, sTypeSym);
11114 }
11115
11116 #pragma mark ------ Molecular Topology ------
11117
11118 /*
11119  *  call-seq:
11120  *     search_equivalent_atoms(ig = nil)
11121  *
11122  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
11123  */
11124 static VALUE
11125 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11126 {
11127         Molecule *mol;
11128         Int *result, i;
11129         VALUE val;
11130         IntGroup *ig;
11131     Data_Get_Struct(self, Molecule, mol);
11132         if (mol->natoms == 0)
11133                 return Qnil;
11134         rb_scan_args(argc, argv, "01", &val);
11135         if (val != Qnil)
11136                 ig = s_Molecule_AtomGroupFromValue(self, val);
11137         else ig = NULL;
11138         result = MoleculeSearchEquivalentAtoms(mol, ig);
11139         if (result == NULL)
11140                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11141         if (ig != NULL)
11142                 IntGroupRelease(ig);
11143         val = rb_ary_new2(mol->natoms);
11144         for (i = 0; i < mol->natoms; i++)
11145                 rb_ary_push(val, INT2NUM(result[i]));
11146         free(result);
11147         return val;
11148 }
11149
11150 /*
11151  *  call-seq:
11152  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11153  *
11154  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11155  *  Name is the name of the new pi anchor, and group is the atoms that define
11156  *  the pi system. Type (a String) is an atom type for MM implementation.
11157  *  Weights represent the relative significance of the component atoms; if omitted, then
11158  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
11159  *  The weight values will be normalized so that the sum of the weights is 1.0.
11160  *  The weight values must be positive.
11161  *  Index is the atom index where the created pi-anchor is inserted in the 
11162  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
11163  *  having the largest index.
11164  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
11165  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11166  */
11167 static VALUE
11168 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11169 {
11170         Molecule *mol;
11171         VALUE nval, gval;
11172         IntGroup *ig;
11173         Int i, n, idx, last_component;
11174         Atom a, *ap;
11175         PiAnchor an;
11176         AtomRef *aref;
11177         if (argc < 2 || argc >= 6)
11178                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11179         nval = *argv++;
11180         gval = *argv++;
11181         argc -= 2;
11182     Data_Get_Struct(self, Molecule, mol);
11183     if (gval == Qnil)
11184         ig = NULL;
11185     else
11186         ig = s_Molecule_AtomGroupFromValue(self, gval);
11187     if (ig == NULL || IntGroupGetCount(ig) == 0)
11188     rb_raise(rb_eMolbyError, "atom group is not given correctly");
11189         memset(&a, 0, sizeof(a));
11190         memset(&an, 0, sizeof(an));
11191         strncpy(a.aname, StringValuePtr(nval), 4);
11192         if (a.aname[0] == '_')
11193                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11194         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11195         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11196                 if (n >= mol->natoms) {
11197                         AtomConnectResize(&an.connect, 0);
11198                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11199                 }
11200                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11201                 last_component = n;
11202         }
11203         if (an.connect.count == 0)
11204                 rb_raise(rb_eMolbyError, "no atoms are specified");
11205         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11206         for (i = 0; i < an.connect.count; i++) {
11207                 an.coeffs[i] = 1.0 / an.connect.count;
11208         }
11209         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11210                 /*  Atom type  */
11211                 if (argv[0] != Qnil)
11212                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11213                 argc--;
11214                 argv++;
11215         }
11216         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11217                 if (argv[0] != Qnil) {
11218                         VALUE aval = rb_ary_to_ary(argv[0]);
11219                         Double d, sum;
11220                         if (RARRAY_LEN(aval) != an.connect.count)
11221                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11222                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11223                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11224                                 if (d <= 0.0)
11225                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11226                                 sum += d;
11227                                 an.coeffs[i] = d;
11228                         }
11229                         for (i = 0; i < an.connect.count; i++)
11230                                 an.coeffs[i] /= sum;
11231                 }
11232                 argc--;
11233                 argv++;
11234         }
11235         if (argc > 0 && argv[0] != Qnil) {
11236                 /*  Index  */
11237                 idx = NUM2INT(rb_Integer(argv[0]));
11238         } else idx = -1;
11239         if (idx < 0 || idx > mol->natoms) {
11240                 /*  Immediately after the last specified atom  */
11241                 idx = last_component + 1;
11242         }
11243         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11244         memmove(a.anchor, &an, sizeof(PiAnchor));
11245         /*  Use residue information of the last specified atom  */
11246         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11247         a.resSeq = ap->resSeq;
11248         strncpy(a.resName, ap->resName, 4);
11249         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11250                 return Qnil;
11251         MoleculeCalculatePiAnchorPosition(mol, idx);
11252     aref = AtomRefNew(mol, idx);
11253     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11254 }
11255
11256 #pragma mark ------ Molecular Properties ------
11257
11258 /*
11259  *  call-seq:
11260  *     set_property(name, value[, index]) -> value
11261  *     set_property(name, values, group) -> values
11262  *
11263  *  Set molecular property. A property is a floating-point number with a specified name,
11264  *  and can be set for each frame separately. The name of the property is given as a String.
11265  *  The value can be a single floating point number, which is set to the current frame.
11266  *  
11267  */
11268 static VALUE
11269 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11270 {
11271         Molecule *mol;
11272         VALUE nval, vval, ival;
11273         char *name;
11274         IntGroup *ig;
11275         Int i, n, idx, fidx;
11276         Double *dp;
11277         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11278     Data_Get_Struct(self, Molecule, mol);
11279         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11280                 idx = NUM2INT(rb_Integer(nval));
11281                 if (idx < 0 || idx >= mol->nmolprops)
11282                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11283         } else {
11284                 name = StringValuePtr(nval);
11285                 idx = MoleculeLookUpProperty(mol, name);
11286                 if (idx < 0) {
11287                         idx = MoleculeCreateProperty(mol, name);
11288                         if (idx < 0)
11289                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11290                 }
11291         }
11292         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11293                 if (ival == Qnil)
11294                         fidx = mol->cframe;
11295                 else {
11296                         fidx = NUM2INT(rb_Integer(ival));
11297                         n = MoleculeGetNumberOfFrames(mol);
11298                         if (fidx < 0 || fidx >= n)
11299                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11300                 }
11301                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11302                 dp = (Double *)malloc(sizeof(Double));
11303                 *dp = NUM2DBL(rb_Float(vval));
11304                 n = 1;
11305         } else {
11306                 vval = rb_ary_to_ary(vval);
11307                 ig = IntGroupFromValue(ival);
11308                 n = IntGroupGetCount(ig);
11309                 if (n == 0)
11310                         rb_raise(rb_eMolbyError, "No frames are specified");
11311                 if (RARRAY_LEN(vval) < n)
11312                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11313                 dp = (Double *)calloc(sizeof(Double), n);
11314                 for (i = 0; i < n; i++)
11315                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11316         }
11317         
11318         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11319         free(dp);
11320         IntGroupRelease(ig);
11321         return self;
11322 }
11323
11324 /*
11325  *  call-seq:
11326  *     get_property(name[, index]) -> value
11327  *     get_property(name, group) -> values
11328  *
11329  *  Get molecular property. In the first form, a property value for a single frame is returned.
11330  *  (If index is omitted, then the value for the current frame is given)
11331  *  In the second form, an array of property values for the given frames is returned.
11332  *  If name is not one of known properties or a valid index integer, exception is raised.
11333  */
11334 static VALUE
11335 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11336 {
11337         Molecule *mol;
11338         VALUE nval, ival;
11339         char *name;
11340         IntGroup *ig;
11341         Int i, n, idx, fidx;
11342         Double *dp;
11343         rb_scan_args(argc, argv, "11", &nval, &ival);
11344     Data_Get_Struct(self, Molecule, mol);
11345         if (mol->nmolprops == 0)
11346                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11347         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11348                 idx = NUM2INT(rb_Integer(nval));
11349                 if (idx < 0 || idx >= mol->nmolprops)
11350                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11351         } else {
11352                 name = StringValuePtr(nval);
11353                 idx = MoleculeLookUpProperty(mol, name);
11354                 if (idx < 0)
11355                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11356         }
11357         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11358                 if (ival == Qnil)
11359                         fidx = mol->cframe;
11360                 else {
11361                         fidx = NUM2INT(rb_Integer(ival));
11362                         n = MoleculeGetNumberOfFrames(mol);
11363                         if (fidx < 0 || fidx >= n)
11364                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11365                 }
11366                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11367                 ival = INT2FIX(fidx);
11368                 n = 1;
11369         } else {
11370                 ig = IntGroupFromValue(ival);
11371                 n = IntGroupGetCount(ig);
11372                 if (n == 0)
11373                         return rb_ary_new();
11374         }
11375         dp = (Double *)calloc(sizeof(Double), n);
11376         MoleculeGetProperty(mol, idx, ig, dp);  
11377         if (FIXNUM_P(ival))
11378                 ival = rb_float_new(dp[0]);
11379         else {
11380                 ival = rb_ary_new();
11381                 for (i = n - 1; i >= 0; i--) {
11382                         nval = rb_float_new(dp[i]);
11383                         rb_ary_store(ival, i, nval);
11384                 }
11385         }
11386         free(dp);
11387         IntGroupRelease(ig);
11388         return ival;
11389 }
11390
11391 /*
11392  *  call-seq:
11393  *     property_names -> Array
11394  *
11395  *  Get an array of property names.
11396  */
11397 static VALUE
11398 s_Molecule_PropertyNames(VALUE self)
11399 {
11400         Molecule *mol;
11401         VALUE rval, nval;
11402         int i;
11403     Data_Get_Struct(self, Molecule, mol);
11404         rval = rb_ary_new();
11405         for (i = mol->nmolprops - 1; i >= 0; i--) {
11406                 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11407                 rb_ary_store(rval, i, nval);
11408         }
11409         return rval;
11410 }
11411
11412 #pragma mark ------ Class methods ------
11413
11414 /*
11415  *  call-seq:
11416  *     current       -> Molecule
11417  *
11418  *  Get the currently "active" molecule.
11419  */
11420 static VALUE
11421 s_Molecule_Current(VALUE klass)
11422 {
11423         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11424 }
11425
11426 /*
11427  *  call-seq:
11428  *     Molecule[]          -> Molecule
11429  *     Molecule[n]         -> Molecule
11430  *     Molecule[name]      -> Molecule
11431  *     Molecule[name, k]   -> Molecule
11432  *     Molecule[regex]     -> Molecule
11433  *     Molecule[regex, k]  -> Molecule
11434  *
11435  *  Molecule[] is equivalent to Molecule.current.
11436  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11437  *  Molecule[name] gives the first document (in the order of creation time) that has
11438  *  the given name. If a second argument (k) is given, the k-th document that has the
11439  *  given name is returned.
11440  *  Molecule[regex] gives the first document (in the order of creation time) that
11441  *  has a name matching the regular expression. If a second argument (k) is given, 
11442  *  the k-th document that has a name matching the re is returned.
11443  */
11444 static VALUE
11445 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11446 {
11447         VALUE val, kval;
11448         int idx, k;
11449         Molecule *mol;
11450         char buf[1024];
11451         rb_scan_args(argc, argv, "02", &val, &kval);
11452         if (val == Qnil)
11453                 return s_Molecule_Current(klass);
11454         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11455                 idx = NUM2INT(val);
11456                 mol = MoleculeCallback_moleculeAtIndex(idx);
11457         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11458                 char *p = StringValuePtr(val);
11459                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11460                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11461                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11462                         if (strcmp(buf, p) == 0 && --k == 0)
11463                                 break;
11464                 }
11465         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11466                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11467                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11468                         VALUE name;
11469                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11470                         name = Ruby_NewEncodedStringValue2(buf);
11471                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11472                                 break;
11473                 }       
11474         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11475         
11476         if (mol == NULL)
11477                 return Qnil;
11478         else return ValueFromMolecule(mol);
11479 }
11480
11481 /*
11482  *  call-seq:
11483  *     list         -> array of Molecules
11484  *
11485  *  Get the list of molecules associated to the documents, in the order of creation
11486  *  time of the document. If no document is open, returns an empry array.
11487  */
11488 static VALUE
11489 s_Molecule_List(VALUE klass)
11490 {
11491         Molecule *mol;
11492         int i;
11493         VALUE ary;
11494         i = 0;
11495         ary = rb_ary_new();
11496         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11497                 rb_ary_push(ary, ValueFromMolecule(mol));
11498                 i++;
11499         }
11500         return ary;
11501 }
11502
11503 /*
11504  *  call-seq:
11505  *     ordered_list         -> array of Molecules
11506  *
11507  *  Get the list of molecules associated to the documents, in the order of front-to-back
11508  *  ordering of the associated window. If no document is open, returns an empry array.
11509  */
11510 static VALUE
11511 s_Molecule_OrderedList(VALUE klass)
11512 {
11513         Molecule *mol;
11514         int i;
11515         VALUE ary;
11516         i = 0;
11517         ary = rb_ary_new();
11518         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11519                 rb_ary_push(ary, ValueFromMolecule(mol));
11520                 i++;
11521         }
11522         return ary;
11523 }
11524
11525 #pragma mark ------ Call Subprocess ------
11526
11527 /*  The callback functions for call_subprocess_async  */
11528 static int
11529 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11530 {
11531         int ruby_status;
11532         VALUE procval, retval, args[2];
11533         args[0] = ValueFromMolecule(mol);
11534         args[1] = INT2NUM(status);
11535         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11536         if (procval != Qnil) {
11537                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11538                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11539                         return 1;
11540         }
11541         return 0;
11542 }
11543
11544 static int
11545 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11546 {
11547         int ruby_status;
11548         VALUE procval, retval, args[2];
11549         args[0] = ValueFromMolecule(mol);
11550         args[1] = INT2NUM(tcount);
11551         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11552         if (procval != Qnil) {
11553                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11554                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11555                         return 1;
11556         }
11557         return 0;
11558 }
11559
11560 /*
11561  *  call-seq:
11562  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11563  *
11564  *  Call subprocess asynchronically.
11565  *  cmd is either a single string of an array of string. If it is a single string, then
11566  *  it will be given to wxExecute as a single argument. In this case, the string can be
11567  *  split into arguments by whitespace. If this behavior is not intended, then use an array
11568  *  containing a single string.
11569  *  If end_callback is given, it will be called (with two arguments self and termination status)
11570  *  when the subprocess terminated.
11571  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11572  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11573  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11574  *  filename begins with ">>", then the message will be appended to the file.
11575  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11576  *  If the argument is nil, then the message will be sent to the Ruby console.
11577  *  Returns the process ID as an integer.
11578  */
11579 static VALUE
11580 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11581 {
11582         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11583         Molecule *mol;
11584         char *sout, *serr;
11585     const char **cmdargv;
11586         int n;
11587         FILE *fpout, *fperr;
11588         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11589         Data_Get_Struct(self, Molecule, mol);
11590
11591         if (stdout_val == Qnil) {
11592                 fpout = (FILE *)1;
11593         } else {
11594                 sout = StringValuePtr(stdout_val);
11595                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11596                         fpout = NULL;
11597                 else {
11598                         if (strncmp(sout, ">>", 2) == 0) {
11599                                 sout += 2;
11600                                 fpout = fopen(sout, "a");
11601                         } else {
11602                                 if (*sout == '>')
11603                                         sout++;
11604                                 fpout = fopen(sout, "w");
11605                         }
11606                         if (fpout == NULL)
11607                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11608                 }
11609         }
11610         if (stderr_val == Qnil) {
11611                 fperr = (FILE *)1;
11612         } else {
11613                 serr = StringValuePtr(stderr_val);
11614                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11615                         fperr = NULL;
11616                 else {
11617                         if (strncmp(serr, ">>", 2) == 0) {
11618                                 serr += 2;
11619                                 fpout = fopen(serr, "a");
11620                         } else {
11621                                 if (*serr == '>')
11622                                         serr++;
11623                                 fperr = fopen(serr, "w");
11624                         }
11625                         if (fperr == NULL)
11626                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11627                 }
11628         }
11629         
11630         /*  Register procs as instance variables  */
11631         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11632         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11633     
11634     if (rb_obj_is_kind_of(cmd, rb_cString)) {
11635         cmdargv = calloc(sizeof(cmdargv[0]), 3);
11636         cmdargv[0] = StringValuePtr(cmd);
11637         cmdargv[1] = "";
11638         cmdargv[2] = NULL;
11639     } else {
11640         cmd = rb_ary_to_ary(cmd);
11641         cmdargv = calloc(sizeof(cmdargv[0]), RARRAY_LEN(cmd) + 1);
11642         for (n = 0; n < RARRAY_LEN(cmd); n++) {
11643             cmdargv[n] = StringValuePtr(RARRAY_PTR(cmd)[n]);
11644         }
11645         cmdargv[n] = NULL;
11646     }
11647         n = MoleculeCallback_callSubProcessAsync(mol, cmdargv, s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11648         if (fpout != NULL && fpout != (FILE *)1)
11649                 fclose(fpout);
11650         if (fperr != NULL && fperr != (FILE *)1)
11651                 fclose(fperr);
11652         return INT2NUM(n);
11653 }
11654
11655 #pragma mark ====== Define Molby Classes ======
11656
11657 void
11658 Init_Molby(void)
11659 {
11660         int i;
11661         
11662         /*  Define module Molby  */
11663         rb_mMolby = rb_define_module("Molby");
11664         
11665         /*  Define Vector3D, Transform, IntGroup  */
11666         Init_MolbyTypes();
11667         
11668         /*  Define MDArena  */
11669         Init_MolbyMDTypes();
11670
11671         /*  class Molecule  */
11672         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11673
11674         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11675     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11676     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11677         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11678         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11679
11680     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11681     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11682     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11683     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11684     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11685     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11686     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11687     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11688     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11689     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11690         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11691     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11692     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11693     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11694     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11695     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11696     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11697     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11698         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11699         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11700         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11701         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11702         
11703     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11704         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11705     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11706     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11707     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11708
11709     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11710     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11711     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11712     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11713     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11714     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11715         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11716         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11717         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11718         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11719         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11720         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11721         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11722         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11723         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11724         
11725         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11726         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11727         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11728         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11729         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11730         
11731         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11732     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11733         rb_define_alias(rb_cMolecule, "+", "add");
11734     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11735         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11736         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11737         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11738         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11739         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11740         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11741         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11742         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11743         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11744         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11745         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11746         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11747         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11748         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11749         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11750         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11751         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11752         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11753         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11754         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11755
11756         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11757         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11758         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11759         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11760         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11761
11762         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11763         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11764         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11765         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11766         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11767         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11768         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11769         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11770         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11771
11772         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11773         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11774         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11775         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11776         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11777         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11778         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11779         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11780         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11781         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11782         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11783         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11784         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11785         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11786         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11787         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11788         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11789         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11790         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11791         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11792         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11793         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11794
11795         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11796         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11797         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11798         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11799         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11800         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11801         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11802
11803         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11804         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11805         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11806         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11807         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11808         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11809         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11810         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11811         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11812         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11813         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11814         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11815         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11816
11817         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11818         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11819         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11820         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11821         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11822         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11823
11824         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11825         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11826         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11827         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11828         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11829         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11830         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11831         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11832         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11833         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11834         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11835         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11836         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11837         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11838         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11839         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11840         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11841         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11842         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11843         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11844         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11845         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11846         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11847         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11848         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11849         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11850         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11851         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11852         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11853         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11854         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11855         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11856         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11857         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11858         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11859         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11860         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11861         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11862         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11863         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11864         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11865         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11866         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11867         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11868         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11869         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11870         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11871         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11872         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11873         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11874         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11875         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11876         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11877         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11878         
11879         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11880         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11881         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11882         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11883         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11884         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11885         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11886         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11887         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11888         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11889         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11890         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11891         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11892
11893         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11894         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11895         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11896         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11897         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11898         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11899         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11900         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11901         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11902         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11903         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11904         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11905         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11906         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11907                 
11908         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11909         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11910         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11911         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11912         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11913         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11914         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11915         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11916         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11917         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11918         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11919         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, -1);
11920         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11921         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11922         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11923         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11924         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11925         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11926         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11927         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11928         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11929         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11930         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11931
11932         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11933         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11934         
11935         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11936         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11937         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11938                 
11939         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11940         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11941         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11942         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11943         
11944         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11945         
11946         /*  class MolEnumerable  */
11947         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11948     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11949         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11950         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11951     rb_define_alias(rb_cMolEnumerable, "size", "length");
11952         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11953         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11954
11955         /*  class AtomRef  */
11956         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11957         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11958                 char buf[64];
11959                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11960                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11961                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11962                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11963                 strcat(buf, "=");
11964                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11965         }
11966         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11967         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11968         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11969         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11970         s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11971         rb_global_variable(&s_SetAtomAttrString);
11972         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11973         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11974
11975         /*  class Parameter  */
11976         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11977         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11978         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11979         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11980         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11981         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11982         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11983         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11984         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11985         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11986         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11987         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11988         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11989         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11990         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11991         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11992         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11993         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11994         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11995         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11996         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11997         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11998         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11999         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12000         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12001         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12002         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
12003         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
12004         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
12005         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
12006         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
12007         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
12008         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
12009         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
12010         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
12011         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
12012         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
12013         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
12014         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
12015         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
12016         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
12017         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
12018         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
12019         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
12020         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
12021         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
12022         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
12023         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
12024         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
12025         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
12026         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12027         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12028         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12029         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
12030
12031         /*  class ParEnumerable  */
12032         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
12033     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
12034         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
12035         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
12036         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
12037         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
12038         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
12039         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
12040         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
12041         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
12042         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
12043         
12044         /*  class ParameterRef  */
12045         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
12046         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
12047                 char buf[64];
12048                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
12049                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
12050                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
12051                 if (s_ParameterAttrDefTable[i].symref != NULL)
12052                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
12053                 if (s_ParameterAttrDefTable[i].setter != NULL) {
12054                         strcat(buf, "=");
12055                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
12056                 }
12057         }
12058         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
12059         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
12060         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
12061         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
12062         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
12063         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
12064         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
12065         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
12066
12067         /*  class MolbyError  */
12068         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
12069
12070         /*  module Kernel  */
12071         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
12072         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
12073         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
12074         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
12075         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
12076         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
12077         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
12078         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12079         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12080         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12081         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12082         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12083         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12084         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12085         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12086         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12087         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12088         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12089         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12090         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12091         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12092         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12093         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12094         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12095     rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12096     rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12097     rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12098     rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12099     rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12100     rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12101
12102         /*  class IO  */
12103         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12104         
12105         s_ID_equal = rb_intern("==");
12106         g_RubyID_call = rb_intern("call");
12107         
12108         s_InitMOInfoKeys();
12109         
12110         /*  Symbols for graphics  */
12111         s_LineSym = ID2SYM(rb_intern("line"));
12112         s_PolySym = ID2SYM(rb_intern("poly"));
12113         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12114         s_ConeSym = ID2SYM(rb_intern("cone"));
12115         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12116 }
12117
12118 #pragma mark ====== Interface with RubyDialog class ======
12119
12120 RubyValue
12121 RubyDialogCallback_parentModule(void)
12122 {
12123         return (RubyValue)rb_mMolby;
12124 }
12125
12126 #pragma mark ====== External functions ======
12127
12128 static VALUE s_ruby_top_self = Qfalse;
12129 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12130 static VALUE s_ruby_export_local_variables = Qfalse;
12131
12132 static VALUE
12133 s_evalRubyScriptOnMoleculeSub(VALUE val)
12134 {
12135         void **ptr = (void **)val;
12136         Molecule *mol = (Molecule *)ptr[1];
12137         VALUE sval, fnval, lnval, retval;
12138         VALUE binding;
12139
12140         /*  Clear the error information (store in the history array if necessary)  */
12141         sval = rb_errinfo();
12142         if (sval != Qnil) {
12143                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12144                 rb_set_errinfo(Qnil);
12145         }
12146
12147         if (s_ruby_top_self == Qfalse) {
12148                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12149         }
12150         if (s_ruby_get_binding_for_molecule == Qfalse) {
12151                 const char *s1 =
12152                  "lambda { |_mol_, _bind_| \n"
12153                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12154                  "  _proc_.call(_mol_) } ";
12155                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12156                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12157         }
12158         if (s_ruby_export_local_variables == Qfalse) {
12159                 const char *s2 =
12160                 "lambda { |_bind_| \n"
12161                 "   # find local variables newly defined in _bind_ \n"
12162                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12163                 " _a_.each { |_vsym_| \n"
12164                 "   _vname_ = _vsym_.to_s \n"
12165                 "   _vval_ = _bind_.eval(_vname_) \n"
12166                 "   #  Define local variable \n"
12167                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12168                 "   #  Then set value  \n"
12169                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12170                 " } \n"
12171                 "}";
12172                 s_ruby_export_local_variables = rb_eval_string(s2);
12173                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12174         }
12175         if (ptr[2] == NULL) {
12176                 char *scr;
12177                 /*  String literal: we need to specify string encoding  */
12178 #if defined(__WXMSW__)
12179                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12180 #else
12181                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12182 #endif
12183                 sval = Ruby_NewEncodedStringValue2(scr);
12184                 free(scr);
12185                 fnval = Ruby_NewEncodedStringValue2("(eval)");
12186                 lnval = INT2FIX(0);
12187         } else {
12188                 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12189                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12190                 lnval = INT2FIX(1);
12191         }
12192         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12193         if (mol != NULL) {
12194                 VALUE mval = ValueFromMolecule(mol);
12195                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12196         }
12197         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12198         if (mol != NULL) {
12199                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12200         }
12201         return retval;
12202 }
12203
12204 RubyValue
12205 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12206 {
12207         RubyValue retval;
12208         void *args[3];
12209         VALUE save_interrupt_flag;
12210 /*      char *save_ruby_sourcefile;
12211         int save_ruby_sourceline; */
12212         if (gMolbyIsCheckingInterrupt) {
12213                 MolActionAlertRubyIsRunning();
12214                 *status = -1;
12215                 return (RubyValue)Qnil;
12216         }
12217         gMolbyRunLevel++;
12218         args[0] = (void *)script;
12219         args[1] = (void *)mol;
12220         args[2] = (void *)fname;
12221         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12222 /*      save_ruby_sourcefile = ruby_sourcefile;
12223         save_ruby_sourceline = ruby_sourceline; */
12224         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12225         if (*status != 0) {
12226                 /*  Is this 'exit' exception?  */
12227                 VALUE last_exception = rb_gv_get("$!");
12228                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12229                         /*  Capture exit and return the status value  */
12230                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12231                         *status = 0;
12232                         rb_set_errinfo(Qnil);
12233                 }
12234         }
12235         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12236 /*      ruby_sourcefile = save_ruby_sourcefile;
12237         ruby_sourceline = save_ruby_sourceline; */
12238         gMolbyRunLevel--;
12239         return retval;
12240 }
12241
12242 /*  For debug  */
12243 char *
12244 Ruby_inspectValue(RubyValue value)
12245 {
12246     int status;
12247     static char buf[256];
12248     VALUE val = (VALUE)value;
12249     gMolbyRunLevel++;
12250     val = rb_protect(rb_inspect, val, &status);
12251     gMolbyRunLevel--;
12252     if (status == 0) {
12253         char *str = StringValuePtr(val);
12254         strncpy(buf, str, sizeof(buf) - 1);
12255         buf[sizeof(buf) - 1] = 0;
12256     } else {
12257         snprintf(buf, sizeof(buf), "Error status = %d", status);
12258     }
12259     return buf;
12260 }
12261
12262 int
12263 Ruby_showValue(RubyValue value, char **outValueString)
12264 {
12265         VALUE val = (VALUE)value;
12266         if (gMolbyIsCheckingInterrupt) {
12267                 MolActionAlertRubyIsRunning();
12268                 return 0;
12269         }
12270         if (val != Qnil) {
12271                 int status;
12272                 char *str;
12273                 gMolbyRunLevel++;
12274                 val = rb_protect(rb_inspect, val, &status);
12275                 gMolbyRunLevel--;
12276                 if (status != 0)
12277                         return status;
12278                 str = StringValuePtr(val);
12279                 if (outValueString != NULL)
12280                         *outValueString = strdup(str);
12281                 MyAppCallback_showScriptMessage("%s", str);
12282         } else {
12283                 if (outValueString != NULL)
12284                         *outValueString = NULL;
12285         }
12286         return 0;
12287 }
12288
12289 void
12290 Ruby_showError(int status)
12291 {
12292         static const int tag_raise = 6;
12293     char *main_message = "Molby script error";
12294         char *msg = NULL, *msg2;
12295         VALUE val, backtrace;
12296         int interrupted = 0;
12297     int exit_status = -1;
12298         if (status == tag_raise) {
12299                 VALUE errinfo = rb_errinfo();
12300                 VALUE eclass = CLASS_OF(errinfo);
12301                 if (eclass == rb_eInterrupt) {
12302             main_message = "Molby script interrupted";
12303             msg = "Interrupt";
12304                         interrupted = 1;
12305         } else if (eclass == rb_eSystemExit) {
12306             main_message = "Molby script exit";
12307             interrupted = 2;
12308             val = rb_eval_string_protect("$!.status", &status);
12309             if (status == 0) {
12310                 exit_status = NUM2INT(rb_Integer(val));
12311                 asprintf(&msg, "Molby script exit with status %d", exit_status);
12312             } else {
12313                 asprintf(&msg, "Molby script exit with unknown status");
12314             }
12315         }
12316         }
12317         gMolbyRunLevel++;
12318     if (exit_status != 0) {
12319         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12320         if (msg == NULL) {
12321             val = rb_eval_string_protect("$!.to_s", &status);
12322             if (status == 0)
12323                 msg = RSTRING_PTR(val);
12324             else
12325                 msg = "(message not available)";
12326         }
12327         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12328     } else {
12329         msg2 = strdup(msg);
12330     }
12331         MyAppCallback_messageBox(msg2, main_message, 0, 3);
12332         free(msg2);
12333     if (interrupted == 2) {
12334         free(msg);
12335         if (!gUseGUI && exit_status == 0)
12336             exit(0);  // Capture exit(0) here and force exit
12337     }
12338         gMolbyRunLevel--;
12339 }
12340
12341 /*  Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode.  */
12342 int
12343 Molby_loadScript(const char *script, int from_file)
12344 {
12345     int status;
12346     gMolbyRunLevel++;
12347     if (from_file)
12348         rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12349     else
12350         rb_eval_string_protect(script, &status);
12351     gMolbyRunLevel--;
12352     return status;
12353 }
12354
12355 void
12356 Molby_getDescription(char **versionString, char **auxString)
12357 {
12358         extern const char *gVersionString, *gCopyrightString;
12359         extern int gRevisionNumber;
12360         extern char *gLastBuildString;
12361     char *s1, *s2;
12362         char *revisionString;
12363         if (gRevisionNumber > 0) {
12364                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12365         } else revisionString = "";
12366
12367     asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12368 #if defined(__WXMSW__)
12369     #if TARGET_ARCH == 64
12370              "Molby (64 bit)",
12371     #else
12372              "Molby (32 bit)",
12373     #endif
12374 #else
12375              "Molby",
12376 #endif
12377              gVersionString, revisionString, gCopyrightString, gLastBuildString);
12378     if (gUseGUI) {
12379         asprintf(&s2,
12380                  "\nIncluding:\n"
12381                  "%s"
12382                  "ruby %s, http://www.ruby-lang.org/\n"
12383                  "%s\n"
12384                  "FFTW 3.3.2, http://www.fftw.org/\n"
12385                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12386                  "  and Massachusetts Institute of Technology",
12387                  MyAppCallback_getGUIDescriptionString(),
12388                  gRubyVersion, gRubyCopyright);
12389     } else {
12390         asprintf(&s2,
12391                  "Including "
12392                  "ruby %s, http://www.ruby-lang.org/\n"
12393                  "%s\n"
12394                  "FFTW 3.3.2, http://www.fftw.org/\n"
12395                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12396                  "  and Massachusetts Institute of Technology",
12397                  gRubyVersion, gRubyCopyright);
12398
12399     }
12400         if (revisionString[0] != 0)
12401                 free(revisionString);
12402         if (versionString != NULL)
12403         *versionString = s1;
12404     if (auxString != NULL)
12405         *auxString = s2;
12406 }
12407
12408 void
12409 Molby_startup(const char *script, const char *dir)
12410 {
12411         VALUE val;
12412         int status;
12413         char *libpath;
12414         char *respath, *p, *wbuf;
12415
12416         /*  Get version/copyright string from Ruby interpreter  */
12417         {
12418                 gRubyVersion = strdup(ruby_version);
12419                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12420                                  "  ",  /*  Indent for displaying in About dialog  */
12421                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12422         }
12423         
12424         /*  Read build and revision information for Molby  */
12425 /*      {
12426                 char buf[200];
12427                 extern int gRevisionNumber;
12428                 extern char *gLastBuildString;
12429                 FILE *fp = fopen("../buildInfo.txt", "r");
12430                 gLastBuildString = "";
12431                 if (fp != NULL) {
12432                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12433                                 char *p1 = strchr(buf, '\"');
12434                                 char *p2 = strrchr(buf, '\"');
12435                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12436                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12437                                         buf[p2 - p1 - 1] = 0;
12438                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12439                                 }
12440                         }
12441                         fclose(fp);
12442                 }
12443                 fp = fopen("../revisionInfo.txt", "r");
12444                 gRevisionNumber = 0;
12445                 if (fp != NULL) {
12446                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12447                                 gRevisionNumber = strtol(buf, NULL, 0);
12448                         }
12449                         fclose(fp);
12450                 }
12451     } */
12452
12453     if (!gUseGUI) {
12454         char *wbuf2;
12455         Molby_getDescription(&wbuf, &wbuf2);
12456         MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12457         free(wbuf);
12458         free(wbuf2);
12459     }
12460         
12461         /*  Read atom display parameters  */
12462         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12463         MyAppCallback_setConsoleColor(1);
12464         MyAppCallback_showScriptMessage("%s", wbuf);
12465         MyAppCallback_setConsoleColor(0);
12466                 free(wbuf);
12467         }
12468         
12469         /*  Read default parameters  */
12470         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12471         if (wbuf != NULL) {
12472         MyAppCallback_setConsoleColor(1);
12473         MyAppCallback_showScriptMessage("%s", wbuf);
12474         MyAppCallback_setConsoleColor(0);
12475                 free(wbuf);
12476         }
12477                 
12478         /*  Initialize Ruby interpreter  */
12479 #if __WXMSW__
12480         if (gUseGUI) {
12481                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12482                     it causes rb_bug() (= fatal error) during ruby_init().
12483                     As a workaround, these standard streams are reopend as
12484                     NUL stream.  */
12485                 freopen("NUL", "r", stdin);
12486                 freopen("NUL", "w", stdout);
12487                 freopen("NUL", "w", stderr);
12488         }
12489 #endif
12490         ruby_init();
12491
12492         {
12493         /*  Initialize CP932/Windows-31J encodings  */
12494                 extern void Init_shift_jis(void), Init_windows_31j(void),  Init_trans_japanese_sjis(void);
12495         extern int rb_enc_alias(const char *, const char *);
12496         Init_shift_jis();
12497         Init_windows_31j();
12498         Init_trans_japanese_sjis();
12499         rb_enc_alias("CP932", "Windows-31J");
12500     }
12501     
12502 #if defined(__WXMSW__)
12503     {
12504         /*  Set default external encoding  */
12505         /*  The following snippet is taken from encoding.c  */
12506         extern void rb_enc_set_default_external(VALUE encoding);
12507         char cp[sizeof(int) * 8 / 3 + 22];
12508         int status;
12509         VALUE enc;
12510         snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12511         enc = rb_eval_string_protect(cp, &status);
12512         if (status == 0 && !NIL_P(enc)) {
12513             rb_enc_set_default_external(enc);
12514         }
12515         }
12516 #endif
12517
12518         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12519         ruby_incpush(".");
12520         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12521         ruby_incpush(libpath);
12522         free(libpath);
12523         ruby_incpush(dir);
12524
12525         ruby_script("Molby");
12526         
12527         /*  Find the resource path (the parent directory of the given directory)  */
12528         respath = strdup(dir);
12529         p = strrchr(respath, '/');
12530         if (p == NULL && PATH_SEPARATOR != '/')
12531                 p = strrchr(respath, PATH_SEPARATOR);
12532         if (p != NULL)
12533                 *p = 0;
12534         val = Ruby_NewFileStringValue(respath);
12535         rb_define_global_const("MolbyResourcePath", val);
12536         free(respath);
12537
12538         /*  Define Molby classes  */
12539         Init_Molby();
12540     if (gUseGUI)
12541         RubyDialogInitClass();
12542
12543         rb_define_const(rb_mMolby, "ResourcePath", val);
12544         val = Ruby_NewFileStringValue(dir);
12545         rb_define_const(rb_mMolby, "ScriptPath", val);
12546         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12547         val = Ruby_NewFileStringValue(p);
12548         rb_define_const(rb_mMolby, "MbsfPath", val);    
12549         free(p);
12550         
12551         p = MyAppCallback_getHomeDir();
12552         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12553         rb_define_const(rb_mMolby, "HomeDirectory", val);
12554         free(p);
12555         p = MyAppCallback_getDocumentHomeDir();
12556         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12557         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12558         free(p);
12559         
12560     if (gUseGUI)
12561         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12562     else
12563         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12564
12565     {
12566         /*  Create objects for stdout and stderr  */
12567         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12568         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12569         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12570         rb_gv_set("$stdout", val);
12571         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12572         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12573         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12574         rb_gv_set("$stderr", val);
12575
12576         /*  Create objects for stdin  */
12577         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12578         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12579         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12580         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12581         rb_gv_set("$stdin", val);
12582     }
12583         
12584         /*  Global variable to hold error information  */
12585         rb_define_variable("$backtrace", &gMolbyBacktrace);
12586         rb_define_variable("$error_history", &gMolbyErrorHistory);
12587         gMolbyErrorHistory = rb_ary_new();
12588         
12589         /*  Global variables for script menus  */
12590         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12591         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12592         gScriptMenuCommands = rb_ary_new();
12593         gScriptMenuEnablers = rb_ary_new();
12594         
12595     if (gUseGUI) {
12596         /*  Register interrupt check code  */
12597         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12598         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12599         s_SetIntervalTimer(0, 50);
12600     }
12601         
12602         /*  Read the startup script  */
12603         if (script != NULL && script[0] != 0) {
12604                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12605                 gMolbyRunLevel++;
12606                 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12607                 gMolbyRunLevel--;
12608                 if (status != 0)
12609                         Ruby_showError(status);
12610                 else
12611                         MyAppCallback_showScriptMessage("Done.\n");
12612         }
12613 }
12614
12615 void
12616 Molby_buildARGV(int argc, const char **argv)
12617 {
12618         int i;
12619     rb_ary_clear(rb_argv);
12620     for (i = 0; i < argc; i++) {
12621                 VALUE arg = rb_tainted_str_new2(argv[i]);
12622                 OBJ_FREEZE(arg);
12623                 rb_ary_push(rb_argv, arg);
12624     }
12625 }