OSDN Git Service

Ruby document is updated.
[molby/Molby.git] / MolLib / Ruby_bind / ruby_bind.c
1 /*
2  *  ruby_bind.c
3  *  Ruby binding
4  *
5  *  Created by Toshi Nagata on 07/11/09.
6  *  Copyright 2007-2008 Toshi Nagata. All rights reserved.
7  *
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation version 2 of the License.
11  
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16 */
17
18 #include "Molby.h"
19 #include "ruby_dialog.h"
20 #include "../MD/MDCore.h"
21
22 #include <stdio.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <limits.h>
27
28 #include "version.h"       /*  for Ruby version  */
29 #include "ruby/version.h"  /*  for RUBY_BIRTH_YEAR etc.  */
30 #include "ruby/encoding.h" /*  for rb_str_encode() etc. */
31 /*#include <node.h>     *//*  for rb_add_event_hook()  */
32
33 #if defined(__WXMAC__) || defined(__CMDMAC__)
34 #define USE_PTHREAD_FOR_TIMER 1
35 #endif
36
37 #if !__WXMSW__
38 #if USE_PTHREAD_FOR_TIMER
39 #include <unistd.h>   /*  for usleep()  */
40 #include <pthread.h>  /*  for pthread  */
41 #else
42 #include <signal.h>   /*  for sigaction()  */
43 #endif
44 #endif
45
46 #include "../Missing.h"
47
48 #pragma mark ====== Global Values ======
49
50 VALUE rb_eMolbyError;
51 VALUE rb_mMolby;
52 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
53 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
54
55 VALUE gMolbyBacktrace;
56 VALUE gMolbyErrorHistory;
57 VALUE gScriptMenuCommands;
58 VALUE gScriptMenuEnablers;
59
60 int gMolbyRunLevel = 0;
61 int gMolbyIsCheckingInterrupt = 0;
62
63 char *gRubyVersion, *gRubyCopyright;
64
65 /*  For convenience  */
66 static ID s_ID_equal;  /*  rb_intern("==")  */
67
68 int g_RubyID_call;
69
70 /*  Symbols for atom attributes  */
71 static VALUE
72         s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
73         s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
74         s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
75         s_RSym, s_XSym, s_YSym, s_ZSym,
76         s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
77         s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
78         s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
79         s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
80         s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
81         s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
82
83 /*  Symbols for parameter attributes  */
84 static VALUE
85         s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
86         s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
87         /* s_ASym, s_BSym, */
88         s_ReqSym, s_EpsSym,
89         /* s_A14Sym, s_B14Sym, */
90         s_Req14Sym, s_Eps14Sym,
91         s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym, s_VdwRadiusSym,
92         s_CommentSym, s_SourceSym;
93
94 /*  Symbols for graphics  */
95 static VALUE
96         s_LineSym, s_PolySym, s_CylinderSym, s_ConeSym, s_EllipsoidSym;
97
98 /*
99  *  Utility function
100  *  Get ary[i] by calling "[]" method
101  */
102 VALUE
103 Ruby_ObjectAtIndex(VALUE ary, int idx)
104 {
105         static ID index_method = 0;
106         if (TYPE(ary) == T_ARRAY) {
107                 int len = RARRAY_LEN(ary);
108                 if (idx >= 0 && idx < len)
109                         return (RARRAY_PTR(ary))[idx];
110                 else return Qnil;
111         }
112         if (index_method == 0)
113                 index_method = rb_intern("[]");
114         return rb_funcall(ary, index_method, 1, INT2NUM(idx));
115 }
116
117 char *
118 Ruby_FileStringValuePtr(VALUE *valp)
119 {
120 #if __WXMSW__
121         char *p = strdup(StringValuePtr(*valp));
122         translate_char(p, '/', '\\');
123         *valp = rb_str_new2(p);
124         free(p);
125         return StringValuePtr(*valp);
126 #else
127         return StringValuePtr(*valp);
128 #endif
129 }
130
131 VALUE
132 Ruby_NewFileStringValue(const char *fstr)
133 {
134 #if __WXMSW__
135         VALUE retval;
136         char *p = strdup(fstr);
137         translate_char(p, '\\', '/');
138         retval = rb_enc_str_new(p, strlen(p), rb_default_external_encoding());
139         free(p);
140         return retval;
141 #else
142         return rb_str_new2(fstr);
143 #endif
144 }
145
146 char *
147 Ruby_EncodedStringValuePtr(VALUE *valp)
148 {
149         rb_string_value(valp);
150         *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
151         return RSTRING_PTR(*valp);
152 }
153
154 VALUE
155 Ruby_NewEncodedStringValue(const char *str, int len)
156 {
157         if (len <= 0)
158                 len = strlen(str);
159         return rb_enc_str_new(str, len, rb_default_external_encoding());
160 }
161
162 VALUE
163 Ruby_ObjToStringObj(VALUE val)
164 {
165         switch (TYPE(val)) {
166                 case T_STRING:
167                         return val;
168                 case T_SYMBOL:
169                         return rb_str_new2(rb_id2name(SYM2ID(val)));
170                 default:
171                         return rb_str_to_str(val);
172         }
173 }
174
175 #pragma mark ====== Message input/output ======
176
177 /*
178  *  call-seq:
179  *     message_box(str, title, button = nil, icon = :info)
180  *
181  *  Show a message box.
182  *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
183  *  Icon: :info, :warning, :error
184  */
185 static VALUE
186 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
187 {
188         char *str, *title, *s;
189         int buttons, icon;
190         VALUE sval, tval, bval, ival;
191         rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
192         str = StringValuePtr(sval);
193         title = StringValuePtr(tval);
194         if (bval != Qnil) {
195                 bval = Ruby_ObjToStringObj(bval);
196                 s = RSTRING_PTR(bval);
197                 if (strncmp(s, "ok", 2) == 0)
198                         buttons = 1;
199                 else if (strncmp(s, "cancel", 6) == 0)
200                         buttons = 2;
201                 else
202                         rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
203         } else buttons = 3;
204         if (ival != Qnil) {
205                 ival = Ruby_ObjToStringObj(ival);
206                 s = RSTRING_PTR(ival);
207                 if (strncmp(s, "info", 4) == 0)
208                         icon = 1;
209                 else if (strncmp(s, "warn", 4) == 0)
210                         icon = 2;
211                 else if (strncmp(s, "err", 3) == 0)
212                         icon = 3;
213                 else
214                         rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
215         } else icon = 1;
216         MyAppCallback_messageBox(str, title, buttons, icon);
217         return Qnil;
218 }
219
220 /*
221  *  call-seq:
222  *     error_message_box(str)
223  *
224  *  Show an error message box.
225  */
226 static VALUE
227 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
228 {
229         char *str = StringValuePtr(sval);
230         MyAppCallback_errorMessageBox("%s", str);
231         return Qnil;
232 }
233
234 /*
235  *  call-seq:
236  *     ask(prompt, default = nil) -> string
237  *
238  *  Open a modal dialog and get a line of text.
239  */
240 static VALUE
241 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
242 {
243         volatile VALUE prompt, message;
244         char buf[1024];
245         int retval;
246         rb_scan_args(argc, argv, "11", &prompt, &message);
247         if (message != Qnil) {
248                 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
249                 buf[sizeof buf - 1] = 0;
250         } else buf[0] = 0;
251         retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
252         if (retval)
253                 return rb_str_new2(buf);
254         else
255                 return Qnil;    
256 }
257
258 /*
259  *  call-seq:
260  *     show_console_window
261  *
262  *  Show the console window and bring to the front.
263  */
264 static VALUE
265 s_Kernel_ShowConsoleWindow(VALUE self)
266 {
267         MyAppCallback_showConsoleWindow();
268         return Qnil;
269 }
270
271 /*
272  *  call-seq:
273  *     hide_console_window
274  *
275  *  Hide the console window.
276  */
277 static VALUE
278 s_Kernel_HideConsoleWindow(VALUE self)
279 {
280         MyAppCallback_hideConsoleWindow();
281         return Qnil;
282 }
283
284 /*
285  *  call-seq:
286  *     bell
287  *
288  *  Ring the system bell.
289  */
290 static VALUE
291 s_Kernel_Bell(VALUE self)
292 {
293         MyAppCallback_bell();
294         return Qnil;
295 }
296
297 /*
298  *  call-seq:
299  *     play_sound(filename, flag = 0)
300  *
301  *  Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
302  *  1, play the sound asynchronously; 3, play the sound with loop asynchronously
303  */
304 static VALUE
305 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
306 {
307         VALUE fnval, flval;
308         int flag, retval;
309         char *fname;
310         rb_scan_args(argc, argv, "11", &fnval, &flval);
311         if (flval == Qnil)
312                 flag = 0;
313         else flag = NUM2INT(rb_Integer(flval));
314         fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
315         fname = StringValuePtr(fnval);
316         retval = MyAppCallback_playSound(fname, flag);
317         return (retval ? Qtrue : Qnil);
318 }
319
320 /*
321  *  call-seq:
322  *     stop_sound
323  *
324  *  Stop the sound if playing.
325  */
326 static VALUE
327 s_Kernel_StopSound(VALUE self)
328 {
329         MyAppCallback_stopSound();
330         return Qnil;
331 }
332
333 /*
334  *  call-seq:
335  *     export_to_clipboard(str)
336  *
337  *  Export the given string to clipboard.
338  */
339 static VALUE
340 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
341 {
342 #if !defined(__CMDMAC__)
343         const char *s = StringValuePtr(sval);
344         char *ns;
345 #if __WXMSW__
346         /*  Convert the end-of-line characters  */
347         {       const char *p; int nc; char *np;
348                 nc = 0;
349                 for (p = s; *p != 0; p++) {
350                         if (*p == '\n')
351                                 nc++;
352                 }       
353                 ns = (char *)malloc(strlen(s) + nc + 1);
354                 for (np = ns, p = s; *p != 0; p++, np++) {
355                         if (*p == '\n')
356                                 *np++ = '\r';
357                         *np = *p;
358                 }
359                 *np = 0;
360         }
361 #else
362         ns = (char *)malloc(strlen(s) + 1);
363         strcpy(ns, s);
364 #if __WXMAC__
365         {       char *np;
366                 /*  wxMac still has Carbon code. Oops.  */
367                 for (np = ns; *np != 0; np++) {
368                         if (*np == '\n')
369                                 *np = '\r';
370                 }
371         }
372 #endif
373 #endif
374         if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
375                 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
376 #endif
377         return Qnil;
378 }
379
380 /*
381  *  call-seq:
382  *     stdout.write(str)
383  *
384  *  Put the message in the main text view in black color.
385  */
386 static VALUE
387 s_StandardOutput(VALUE self, VALUE str)
388 {
389         int n;
390         MyAppCallback_setConsoleColor(0);
391         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
392         return INT2NUM(n);
393 }
394
395 /*
396  *  call-seq:
397  *     stderr.write(str)
398  *
399  *  Put the message in the main text view in red color.
400  */
401 static VALUE
402 s_StandardErrorOutput(VALUE self, VALUE str)
403 {
404         int n;
405         MyAppCallback_setConsoleColor(1);
406         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
407         MyAppCallback_setConsoleColor(0);
408         return INT2NUM(n);
409 }
410
411 /*
412  *  call-seq:
413  *     stdout.flush
414  *     stderr.flush
415  *
416  *  Flush the standard (error) output. Actually do nothing.
417  */
418 static VALUE
419 s_FlushConsoleOutput(VALUE self)
420 {
421         return self;
422 }
423
424 /*
425  *  call-seq:
426  *     stdin.gets(rs = $/)
427  *
428  *  Read one line message via dialog box.
429  */
430 static VALUE
431 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
432 {
433         VALUE pval, rval;
434         pval = rb_str_new2("Enter a line:");
435         rval = s_Kernel_Ask(1, &pval, self);
436         if (rval == Qnil)
437                 rb_interrupt();
438         rb_str_cat2(rval, "\n");
439         return rval;
440 }
441
442 /*
443  *  call-seq:
444  *     stdin.method_missing(name, args, ...)
445  *
446  *  Throw an exception, noting only gets and readline are defined.
447  */
448 static VALUE
449 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
450 {
451         VALUE nval;
452         rb_scan_args(argc, argv, "10", &nval);
453         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
454         return Qnil;  /*  Not reached  */
455 }
456
457 #pragma mark ====== Track key events ======
458
459 /*  User interrupt handling
460  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
461  *  key events. This polling should only be enabled during "normal" execution
462  *  of scripts and must be disabled when the rest of the application (or Ruby
463  *  script itself) is handling GUI. This is ensured by appropriate calls to
464  *  enable_interrupt and disable_interrupt.  */
465
466 static VALUE s_interrupt_flag = Qfalse;
467
468 static VALUE
469 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
470 {
471         volatile VALUE message;
472         const char *p;
473         if (Ruby_GetInterruptFlag() == Qtrue) {
474                 rb_scan_args(argc, argv, "01", &message);
475                 if (message != Qnil)
476                         p = StringValuePtr(message);
477                 else
478                         p = NULL;
479                 MyAppCallback_showProgressPanel(p);
480         }
481         return Qnil;
482 }
483
484 static VALUE
485 s_HideProgressPanel(VALUE self)
486 {
487         MyAppCallback_hideProgressPanel();
488         return Qnil;
489 }
490
491 static VALUE
492 s_SetProgressValue(VALUE self, VALUE val)
493 {
494         double dval = NUM2DBL(rb_Float(val));
495         MyAppCallback_setProgressValue(dval);
496         return Qnil;
497 }
498
499 static VALUE
500 s_SetProgressMessage(VALUE self, VALUE msg)
501 {
502         const char *p;
503         if (msg == Qnil)
504                 p = NULL;
505         else p = StringValuePtr(msg);
506         MyAppCallback_setProgressMessage(p);
507         return Qnil;
508 }
509
510 static VALUE
511 s_SetInterruptFlag(VALUE self, VALUE val)
512 {
513         VALUE oldval;
514         if (val != Qundef) {
515                 if (val == Qfalse || val == Qnil)
516                         val = Qfalse;
517                 else val = Qtrue;
518         }
519         oldval = s_interrupt_flag;
520         if (val != Qundef) {
521                 s_interrupt_flag = val;
522                 if (val == Qfalse) {
523                         s_HideProgressPanel(self);
524                 }
525         }
526         return oldval;
527 }
528
529 static VALUE
530 s_GetInterruptFlag(VALUE self)
531 {
532         return s_SetInterruptFlag(self, Qundef);
533 }
534
535 VALUE
536 Ruby_SetInterruptFlag(VALUE val)
537 {
538         return s_SetInterruptFlag(Qnil, val);
539 }
540
541 VALUE
542 Ruby_GetInterruptFlag(void)
543 {
544         return s_SetInterruptFlag(Qnil, Qundef);
545 }
546
547 /*
548  *  call-seq:
549  *     check_interrupt -> integer
550  *
551  *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
552  */
553 static VALUE
554 s_Kernel_CheckInterrupt(VALUE self)
555 {
556         if (Ruby_GetInterruptFlag() == Qfalse)
557                 return INT2NUM(-1);
558         else if (MyAppCallback_checkInterrupt())
559                 return INT2NUM(1);
560         else return INT2NUM(0);
561 }
562
563 static volatile unsigned long sITimerCount = 0;
564
565 #if __WXMSW__
566 static HANDLE sITimerEvent;
567 static HANDLE sITimerThread;
568 static int sITimerInterval;
569
570 static __stdcall unsigned
571 s_ITimerThreadFunc(void *p)
572 {
573         while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
574                 sITimerCount++;
575         }
576         return 0;
577 }
578
579 #elif USE_PTHREAD_FOR_TIMER
580
581 /*  Timer thread  */
582 static pthread_t sTimerThread;
583
584 /*  -1: uninitiated; 0: active, 1: inactive, -2: request to terminate  */
585 static volatile signed char sTimerFlag = -1;
586 static volatile int sTimerIntervalMicrosec = 0;
587
588 static void *
589 s_TimerThreadEntry(void *param)
590 {
591         while (1) {
592                 usleep(sTimerIntervalMicrosec);
593                 if (sTimerFlag == 0)
594                         sITimerCount++;
595                 else if (sTimerFlag == -2)
596                         break;
597         }
598         return NULL;    
599 }
600
601 #endif
602
603 static void
604 s_SignalAction(int n)
605 {
606         sITimerCount++;
607 }
608
609 static void
610 s_SetIntervalTimer(int n, int msec)
611 {
612 #if __WXMSW__
613         if (n == 0) {
614                 /*  Start interval timer  */
615                 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
616                 sITimerInterval = msec;
617                 if (sITimerEvent) {
618                         sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
619                 }
620         } else {
621                 /*  Stop interval timer  */
622                 if (sITimerEvent)
623                         SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
624                 if (sITimerThread) {
625                         WaitForSingleObject(sITimerThread, 1000);
626                         CloseHandle(sITimerThread);
627                 }
628                 if (sITimerEvent)
629                         CloseHandle(sITimerEvent);
630                 sITimerEvent = NULL;
631                 sITimerThread = NULL;
632         }
633 #elif USE_PTHREAD_FOR_TIMER
634         if (n == 0) {
635                 if (sTimerFlag == -1) {
636                         int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
637                         if (status != 0) {
638                                 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
639                         }
640                 }
641                 sTimerFlag = 0;  /*  Active  */
642                 sTimerIntervalMicrosec = msec * 1000;
643         } else if (sTimerFlag != -1)
644                 sTimerFlag = 1;  /*  Inactive  */       
645 #else
646         static struct itimerval sOldValue;
647         static struct sigaction sOldAction;
648         struct itimerval val;
649         struct sigaction act;
650         if (n == 0) {
651                 sITimerCount = 0;
652                 act.sa_handler = s_SignalAction;
653                 act.sa_mask = 0;
654                 act.sa_flags = 0;
655                 sigaction(SIGALRM, &act, &sOldAction);
656                 val.it_value.tv_sec = 0;
657                 val.it_value.tv_usec = msec * 1000;
658                 val.it_interval.tv_sec = 0;
659                 val.it_interval.tv_usec = msec * 1000;
660                 setitimer(ITIMER_REAL, &val, &sOldValue);
661         } else {
662                 setitimer(ITIMER_REAL, &sOldValue, &val);
663                 sigaction(SIGALRM, &sOldAction, &act);
664         }
665 #endif
666 }
667
668 static unsigned long
669 s_GetTimerCount(void)
670 {
671         return sITimerCount;
672 }
673
674 static void
675 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
676 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
677 {
678         if (s_interrupt_flag != Qfalse) {
679                 static unsigned long sLastTime = 0;
680                 unsigned long currentTime;
681                 int flag;
682                 currentTime = s_GetTimerCount();
683                 if (currentTime != sLastTime) {
684                         sLastTime = currentTime;
685                         gMolbyIsCheckingInterrupt = 1;
686                         flag = MyAppCallback_checkInterrupt();
687                         gMolbyIsCheckingInterrupt = 0;
688                         if (flag) {
689                                 s_SetInterruptFlag(Qnil, Qfalse);
690                                 rb_interrupt();
691                         }
692                 }
693         }
694 }
695
696 #pragma mark ====== Menu handling ======
697
698 /*
699  *  call-seq:
700  *     register_menu(title, method, enable_proc = nil)
701  *
702  *  Register the method (specified as a symbol) in the script menu.
703  *  The method must be either an instance method of Molecule with no argument,
704  *  or a class method of Molecule with one argument (the current molecule),
705  *  or a proc object with one argument (the current molecule).
706  *  The menu associated with the class method can be invoked even when no document
707  *  is open (the argument is set to Qnil in this case). On the other hand, the
708  *  menu associated with the instance method can only be invoked when at least one 
709  *  document is active.
710  *  If enable_proc is non-nil, then it is called whenever the availability of
711  *  the menu command is tested. It is usually a proc object with one argument
712  *  (the current molecule or nil). As a special case, the following symbols can
713  *  be given; :mol (enabled when any molecule is open), :non_empty (enabled when
714  *  the top-level molecule has at least one atom), :selection (enabled when
715  *  the top-level molecule has one or more selected atoms).
716  */
717 static VALUE
718 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
719 {
720         int n, mtype = 0;
721         VALUE tval, mval, pval;
722         static VALUE sMolSym, sNonEmptySym, sSelectionSym;
723         static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
724         rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
725         tval = rb_str_to_str(tval);
726         n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
727         if (n < 0)
728                 return Qnil;
729         if (TYPE(mval) == T_SYMBOL) {
730                 /*  Create an appropriate proc object  */
731                 const char *name = rb_id2name(SYM2ID(mval));
732                 char *s;
733                 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
734                         /*  Defined as a Molecule method  */
735                         asprintf(&s, "lambda { |m| m.%s }", name);
736                         mtype = 1;
737                 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
738                         /*  Defined as a Molecule class method  */
739                         asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
740                         mtype = 2;
741                 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
742                 mval = rb_eval_string(s);
743                 free(s);
744         }
745         if (sMolSym == Qfalse) {
746                 sMolSym = ID2SYM(rb_intern("mol"));
747                 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
748                 sSelectionSym = ID2SYM(rb_intern("selection"));
749                 sMolProc = rb_eval_string("lambda { |m| m != nil }");
750                 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
751                 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
752                 sTrueProc = rb_eval_string("lambda { |m| true }");
753                 rb_global_variable(&sMolProc);
754                 rb_global_variable(&sNonEmptyProc);
755                 rb_global_variable(&sSelectionProc);
756                 rb_global_variable(&sTrueProc);
757         }
758         
759         if (pval == Qnil) {
760                 if (mtype == 1)
761                         pval = sMolProc;
762                 else
763                         pval = sTrueProc;
764         } else if (pval == sMolSym)
765                 pval = sMolProc;
766         else if (pval == sNonEmptySym)
767                 pval = sNonEmptyProc;
768         else if (pval == sSelectionSym)
769                 pval = sSelectionProc;
770         rb_ary_store(gScriptMenuCommands, n, mval);
771         rb_ary_store(gScriptMenuEnablers, n, pval);
772         return INT2NUM(n);
773 }
774
775 static VALUE
776 s_Kernel_LookupMenu(VALUE self, VALUE title)
777 {
778         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
779         return INT2NUM(n);
780 }
781
782 static VALUE
783 s_Ruby_UpdateUI_handler(VALUE data)
784 {
785         void **p = (void **)data;
786         int index = (int)p[0];
787         Molecule *mol = (Molecule *)p[1];
788         int *outChecked = (int *)p[2];
789         char **outTitle = (char **)p[3];
790         VALUE mval = ValueFromMolecule(mol);
791         VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
792         static ID call_id = 0;
793         if (call_id == 0)
794                 call_id = rb_intern("call");
795         if (pval == Qnil)
796                 return Qnil;
797         pval = rb_funcall(pval, call_id, 1, mval);
798         if (rb_obj_is_kind_of(pval, rb_cArray)) {
799                 VALUE val;
800                 if (outChecked != NULL) {
801                         val = rb_ary_entry(pval, 1);  /*  Checked or not  */
802                         *outChecked = (RTEST(val) ? 1 : 0);
803                 }
804                 if (outTitle != NULL) {
805                         val = rb_ary_entry(pval, 2);  /*  Text  */
806                         if (TYPE(val) == T_STRING) {
807                                 *outTitle = strdup(StringValuePtr(val));
808                         }
809                 }
810                 pval = rb_ary_entry(pval, 0);
811         }
812         return pval;
813 }
814
815 int
816 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
817 {
818         int status;
819         void *p[4];
820         VALUE retval;
821         p[0] = (void *)index;
822         p[1] = mol;
823         p[2] = outChecked;
824         p[3] = outTitle;
825         retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
826         return (RTEST(retval) ? 1 : 0);
827 }
828
829 /*
830 static VALUE
831 s_Ruby_methodType_sub(VALUE data)
832 {
833         const char **p = (const char **)data;
834         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
835         ID mid = rb_intern(p[1]);
836         int ival;
837         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
838                 ival = 1;
839         else if (rb_respond_to(klass, mid))
840                 ival = 2;
841         else ival = 0;
842         return INT2FIX(ival);
843 }
844 */      
845 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
846     has the singleton method (class method) with the given name, 0 otherwise.  */
847 /*int
848 Ruby_methodType(const char *className, const char *methodName)
849 {
850         int status;
851         VALUE retval;
852         const char *p[2];
853         p[0] = className;
854         p[1] = methodName;
855         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
856         if (status == 0)
857                 return FIX2INT(retval);
858         else return 0;
859 }
860 */
861
862 /*
863  *  call-seq:
864  *     execute_script_file(fname)
865  *
866  *  Execute the script in the given file. If a molecule is active, then
867  *  the script is evaluated as Molecule.current.instance_eval(script).
868  *  Before entering the script, the current directory is set to the parent
869  *  directory of the script.
870  */
871 static VALUE
872 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
873 {
874         int status;
875         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
876         if (retval == (VALUE)6 && status == -1)
877                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
878         if (status != 0)
879                 rb_jump_tag(status);
880         return retval;
881 }
882
883 /*
884  *  call-seq:
885  *     document_home
886  *
887  *  Get the directory suitable for storing user documents. On Windows
888  *  it is the home directory + "My Documents". On other platforms
889  *  it is the home directory.
890  */
891 static VALUE
892 s_Kernel_DocumentHome(VALUE self)
893 {
894         char *s = MyAppCallback_getDocumentHomeDir();
895         VALUE retval = Ruby_NewFileStringValue(s);
896         free(s);
897         return retval;
898 }
899
900 /*  The callback function for call_subprocess  */
901 static int
902 s_Kernel_CallSubProcess_Callback(void *data)
903 {
904         int status;
905         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
906         if (status != 0 || retval == Qnil || retval == Qfalse)
907                 return 1;
908         else return 0;
909 }
910
911 /*
912  *  call-seq:
913  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
914  *
915  *  Call subprocess. A progress dialog window is displayed, with a message
916  *  "Running #{process_name}...".
917  *  A callback proc can be given, which is called periodically during execution. If the proc returns
918  *  nil or false, then the execution will be interrupted.
919  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
920  *  filename begins with ">>", then the message will be appended to the file.
921  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
922  *  If the argument is nil, then the message will be sent to the Ruby console.
923  */
924 static VALUE
925 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
926 {
927         VALUE cmd, procname, cproc, stdout_val, stderr_val;
928         int n, exitstatus, pid;
929         char *sout, *serr;
930         FILE *fpout, *fperr;
931
932         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
933
934         if (stdout_val == Qnil) {
935                 fpout = (FILE *)1;
936         } else {
937                 sout = StringValuePtr(stdout_val);
938                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
939                         fpout = NULL;
940                 else {
941                         if (strncmp(sout, ">>", 2) == 0) {
942                                 sout += 2;
943                                 fpout = fopen(sout, "a");
944                         } else {
945                                 if (*sout == '>')
946                                         sout++;
947                                 fpout = fopen(sout, "w");
948                         }
949                         if (fpout == NULL)
950                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
951                 }
952         }
953         if (stderr_val == Qnil) {
954                 fperr = (FILE *)1;
955         } else {
956                 serr = StringValuePtr(stderr_val);
957                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
958                         fperr = NULL;
959                 else {
960                         if (strncmp(serr, ">>", 2) == 0) {
961                                 serr += 2;
962                                 fpout = fopen(serr, "a");
963                         } else {
964                                 if (*serr == '>')
965                                         serr++;
966                                 fperr = fopen(serr, "w");
967                         }
968                         if (fperr == NULL)
969                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
970                 }
971         }
972
973         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
974         
975         if (fpout != NULL && fpout != (FILE *)1)
976                 fclose(fpout);
977         if (fperr != NULL && fperr != (FILE *)1)
978                 fclose(fperr);
979
980         return INT2NUM(n);
981
982         
983 }
984
985 /*
986  *  call-seq:
987  *     backquote(cmd)
988  *
989  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
990  */
991 static VALUE
992 s_Kernel_Backquote(VALUE self, VALUE cmd)
993 {
994         char *buf;
995         int n, exitstatus, pid;
996         VALUE val;
997         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
998 /*      fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
999         if (n >= 0 && buf != NULL) {
1000                 val = Ruby_NewEncodedStringValue(buf, 0);
1001                 free(buf);
1002         } else {
1003                 val = Ruby_NewEncodedStringValue("", 0);
1004         }
1005         rb_last_status_set(exitstatus, pid);
1006         return val;
1007 }
1008
1009 #pragma mark ====== User defaults ======
1010
1011 /*
1012  *  call-seq:
1013  *     get_global_settings(key)
1014  *
1015  *  Get a setting data for key from the application preferences.
1016  */
1017 static VALUE
1018 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1019 {
1020         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1021         if (p != NULL) {
1022                 VALUE retval = rb_eval_string(p);
1023                 free(p);
1024                 return retval;
1025         } else return Qnil;
1026 }
1027
1028 /*
1029  *  call-seq:
1030  *     set_global_settings(key, value)
1031  *
1032  *  Set a setting data for key to the application preferences.
1033  */
1034 static VALUE
1035 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1036 {
1037         VALUE sval = rb_inspect(value);
1038         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1039         return value;
1040 }
1041
1042 #pragma mark ====== IO extension ======
1043
1044 static VALUE
1045 s_Ruby_str_encode_protected(VALUE val)
1046 {
1047         return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1048                                   ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1049 }
1050
1051 /*
1052  *  call-seq:
1053  *     gets_any_eol
1054  *
1055  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1056  */
1057 static VALUE
1058 s_IO_gets_any_eol(VALUE self)
1059 {
1060         VALUE val, val2, cval;
1061         char buf[1024];
1062         int i, c, status;
1063         static ID id_getbyte = 0, id_ungetbyte;
1064         if (id_getbyte == 0) {
1065                 id_getbyte = rb_intern("getbyte");
1066                 id_ungetbyte = rb_intern("ungetbyte");
1067         }
1068         i = 0;
1069         val = Qnil;
1070         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1071                 c = NUM2INT(rb_Integer(cval));
1072                 if (c == 0x0d) {
1073                         cval = rb_funcall(self, id_getbyte, 0);
1074                         if (cval != Qnil) {
1075                                 c = NUM2INT(rb_Integer(cval));
1076                                 if (c != 0x0a)
1077                                         rb_funcall(self, id_ungetbyte, 1, cval);
1078                         }
1079                         break;
1080                 } else if (c != 0x0a) {
1081                         buf[i++] = c;
1082                         if (i >= 1020) {
1083                                 buf[i] = 0;
1084                                 if (val == Qnil)
1085                                         val = rb_str_new(buf, i);
1086                                 else
1087                                         rb_str_append(val, rb_str_new(buf, i));
1088                                 i = 0;
1089                         }
1090                 } else break;
1091         }
1092         if (cval == Qnil && i == 0 && val == Qnil)
1093                 return Qnil;  /*  End of file  */
1094         buf[i] = 0;
1095         if (val == Qnil)
1096                 val = rb_str_new(buf, i);
1097         else if (i > 0)
1098                 rb_str_append(val, rb_str_new(buf, i));
1099         val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /*  Ignore exception  */
1100         if (status == 0)
1101                 val = val2;
1102         if (cval != Qnil) {
1103                 /*  Needs a end-of-line mark  */
1104                 cval = rb_gv_get("$/");
1105                 rb_str_append(val, cval);
1106         }
1107         rb_gv_set("$_", val);
1108         return val;
1109 }
1110
1111 #pragma mark ====== Utility functions (protected funcall) ======
1112
1113 struct Ruby_funcall2_record {
1114         VALUE recv;
1115         ID mid;
1116         int argc;
1117         VALUE *argv;
1118 };
1119
1120 static VALUE
1121 s_Ruby_funcall2_sub(VALUE data)
1122 {
1123         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1124         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1125 }
1126
1127 VALUE
1128 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1129 {
1130         struct Ruby_funcall2_record rec;
1131         rec.recv = recv;
1132         rec.mid = mid;
1133         rec.argc = argc;
1134         rec.argv = argv;
1135         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1136 }
1137
1138 RubyValue
1139 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1140 {
1141         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1142 }
1143
1144 #pragma mark ====== ParameterRef Class ======
1145
1146 static UnionPar *
1147 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1148 {
1149         UnionPar *up;
1150         ParameterRef *pref;
1151         Data_Get_Struct(self, ParameterRef, pref);
1152         if (typep != NULL)
1153                 *typep = pref->parType;
1154         if (pref->parType == kElementParType) {
1155                 up = (UnionPar *)&gElementParameters[pref->idx];
1156         } else {
1157                 up = ParameterRefGetPar(pref);
1158                 if (checkEditable) {
1159                         if (pref->idx < 0)
1160                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1161                         if (up->bond.src != 0 && up->bond.src != -1)
1162                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1163                 }
1164         }
1165         return up;
1166 }
1167
1168 static void
1169 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1170 {
1171         UnionPar *up;
1172         ParameterRef *pref;
1173         Data_Get_Struct(self, ParameterRef, pref);
1174         if (pref->mol == NULL)
1175                 return;
1176         up = ParameterRefGetPar(pref);
1177         if (key != s_SourceSym)
1178                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1179         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1180                 /*  Register undo  */
1181                 MolAction *act;
1182                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1183                 MolActionCallback_registerUndo(pref->mol, act);
1184                 MoleculeCallback_notifyModification(pref->mol, 0);
1185                 pref->mol->needsMDRebuild = 1;
1186         }
1187 }
1188
1189 VALUE
1190 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1191 {
1192         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1193         if (pref != NULL)
1194                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1195         else
1196                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1197 }
1198
1199 static int
1200 s_AtomTypeIndexFromValue(VALUE val)
1201 {
1202         if (rb_obj_is_kind_of(val, rb_cNumeric))
1203                 return NUM2INT(val);
1204         else
1205                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1206 }
1207
1208 static const char *s_ParameterTypeNames[] = {
1209         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1210 };
1211 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1212
1213 static int
1214 s_ParTypeFromValue(VALUE val)
1215 {
1216         int i, n;
1217         ID valid;
1218         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1219         if (s_ParameterTypeIDs[0] == 0) {
1220                 for (i = 0; i < n; i++)
1221                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1222         }
1223         valid = rb_to_id(val);
1224         for (i = 0; i < n; i++) {
1225                 if (valid == s_ParameterTypeIDs[i]) {
1226                         if (i == 7)
1227                                 return kElementParType;
1228                         else return kFirstParType + i;
1229                 }
1230         }
1231         return kInvalidParType;
1232 }
1233
1234 /*
1235  *  call-seq:
1236  *     index -> Integer
1237  *
1238  *  Get the index in the parameter list.
1239  */
1240 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1241         ParameterRef *pref;
1242         Data_Get_Struct(self, ParameterRef, pref);
1243         return INT2NUM(pref->idx);
1244 }
1245
1246 /*
1247  *  call-seq:
1248  *     par_type -> String
1249  *
1250  *  Get the parameter type, like "bond", "angle", etc.
1251  */
1252 static VALUE s_ParameterRef_GetParType(VALUE self) {
1253         Int tp;
1254         s_UnionParFromValue(self, &tp, 0);
1255         if (tp == kElementParType)
1256                 return rb_str_new2("element");
1257         tp -= kFirstParType;
1258         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1259                 return rb_str_new2(s_ParameterTypeNames[tp]);
1260         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1261 }
1262
1263 /*
1264  *  call-seq:
1265  *     atom_type -> String or Array of String
1266  *     atom_types -> String or Array of String
1267  *
1268  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1269  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1270  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1271  *  The atom type may be "X", which is a wildcard that matches any atom type.
1272  */
1273 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1274         UnionPar *up;
1275         Int tp, i, n;
1276         UInt types[4];
1277         VALUE vals[4];
1278         up = s_UnionParFromValue(self, &tp, 0);
1279         n = ParameterGetAtomTypes(tp, up, types);
1280         if (n == 0)
1281                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1282         for (i = 0; i < n; i++) {
1283                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1284                         vals[i] = INT2NUM(types[i]);
1285                 else
1286                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1287         }
1288         if (n == 1)
1289                 return vals[0];
1290         else
1291                 return rb_ary_new4(n, vals);
1292 }
1293
1294 /*
1295  *  call-seq:
1296  *     k -> Float
1297  *
1298  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1299  */
1300 static VALUE s_ParameterRef_GetK(VALUE self) {
1301         UnionPar *up;
1302         Int tp, i, n;
1303         VALUE vals[3];
1304         up = s_UnionParFromValue(self, &tp, 0);
1305         switch (tp) {
1306                 case kBondParType:
1307                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1308                 case kAngleParType:
1309                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1310                 case kDihedralParType:
1311                 case kImproperParType:
1312                         if (up->torsion.mult == 1)
1313                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1314                         n = up->torsion.mult;
1315                         if (n > 3)
1316                                 n = 3;
1317                         for (i = 0; i < n; i++)
1318                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1319                         return rb_ary_new4(n, vals);
1320                 default:
1321                         rb_raise(rb_eMolbyError, "invalid member k");
1322         }
1323 }
1324
1325 /*
1326  *  call-seq:
1327  *     r0 -> Float
1328  *
1329  *  Get the equilibrium bond length. Only available for bond parameters.
1330  */
1331 static VALUE s_ParameterRef_GetR0(VALUE self) {
1332         UnionPar *up;
1333         Int tp;
1334         up = s_UnionParFromValue(self, &tp, 0);
1335         if (tp == kBondParType)
1336                 return rb_float_new(up->bond.r0);
1337         else rb_raise(rb_eMolbyError, "invalid member r0");
1338 }
1339
1340 /*
1341  *  call-seq:
1342  *     a0 -> Float
1343  *
1344  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1345  */
1346 static VALUE s_ParameterRef_GetA0(VALUE self) {
1347         UnionPar *up;
1348         Int tp;
1349         up = s_UnionParFromValue(self, &tp, 0);
1350         if (tp == kAngleParType)
1351                 return rb_float_new(up->angle.a0 * kRad2Deg);
1352         else rb_raise(rb_eMolbyError, "invalid member a0");
1353 }
1354
1355 /*
1356  *  call-seq:
1357  *     mult -> Float
1358  *
1359  *  Get the multiplicity. Only available for dihedral and improper parameters.
1360  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1361  */
1362 static VALUE s_ParameterRef_GetMult(VALUE self) {
1363         UnionPar *up;
1364         Int tp;
1365         up = s_UnionParFromValue(self, &tp, 0);
1366         if (tp == kDihedralParType || tp == kImproperParType)
1367                 return rb_float_new(up->torsion.mult);
1368         else rb_raise(rb_eMolbyError, "invalid member mult");
1369 }
1370
1371 /*
1372  *  call-seq:
1373  *     period -> Integer or Array of Integers
1374  *
1375  *  Get the periodicity. Only available for dihedral and improper parameters.
1376  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1377  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1378  */
1379 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1380         UnionPar *up;
1381         Int tp, i, n;
1382         VALUE vals[3];
1383         up = s_UnionParFromValue(self, &tp, 0);
1384         if (tp == kDihedralParType || tp == kImproperParType) {
1385                 if (up->torsion.mult == 1)
1386                         return INT2NUM(up->torsion.period[0]);
1387                 n = up->torsion.mult;
1388                 if (n > 3)
1389                         n = 3;
1390                 for (i = 0; i < n; i++)
1391                         vals[i] = INT2NUM(up->torsion.period[i]);
1392                 return rb_ary_new4(n, vals);
1393         } else rb_raise(rb_eMolbyError, "invalid member period");
1394 }
1395
1396 /*
1397  *  call-seq:
1398  *     phi0 -> Float or Array of Floats
1399  *
1400  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1401  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1402  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1403  */
1404 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1405         UnionPar *up;
1406         Int tp, i, n;
1407         VALUE vals[3];
1408         up = s_UnionParFromValue(self, &tp, 0);
1409         if (tp == kDihedralParType || tp == kImproperParType) {
1410                 if (up->torsion.mult == 1)
1411                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1412                 n = up->torsion.mult;
1413                 if (n > 3)
1414                         n = 3;
1415                 for (i = 0; i < n; i++)
1416                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1417                 return rb_ary_new4(n, vals);
1418         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1419 }
1420
1421 /*
1422  *  call-seq:
1423  *     A -> Float
1424  *
1425  *  Get the "A" value for the van der Waals parameter.
1426  */
1427 /*
1428  static VALUE s_ParameterRef_GetA(VALUE self) {
1429         UnionPar *up;
1430         Int tp;
1431         up = s_UnionParFromValue(self, &tp, 0);
1432         if (tp == kVdwParType)
1433                 return rb_float_new(up->vdw.A);
1434         else if (tp == kVdwPairParType)
1435                 return rb_float_new(up->vdwp.A);
1436         else rb_raise(rb_eMolbyError, "invalid member A");
1437 }
1438 */
1439
1440 /*
1441  *  call-seq:
1442  *     B -> Float
1443  *
1444  *  Get the "B" value for the van der Waals parameter.
1445  */
1446 /*
1447 static VALUE s_ParameterRef_GetB(VALUE self) {
1448         UnionPar *up;
1449         Int tp;
1450         up = s_UnionParFromValue(self, &tp, 0);
1451         if (tp == kVdwParType)
1452                 return rb_float_new(up->vdw.B);
1453         else if (tp == kVdwPairParType)
1454                 return rb_float_new(up->vdwp.B);
1455         else rb_raise(rb_eMolbyError, "invalid member B");
1456 }
1457 */
1458
1459 /*
1460  *  call-seq:
1461  *     r_eq -> Float
1462  *
1463  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1464  */
1465 static VALUE s_ParameterRef_GetReq(VALUE self) {
1466         UnionPar *up;
1467         Int tp;
1468 /*      Double a, b, r; */
1469         Double r;
1470         up = s_UnionParFromValue(self, &tp, 0);
1471         if (tp == kVdwParType) {
1472         /*      a = up->vdw.A;
1473                 b = up->vdw.B;  */
1474                 r = up->vdw.r_eq;
1475         } else if (tp == kVdwPairParType) {
1476         /*      a = up->vdwp.A;
1477                 b = up->vdwp.B;  */
1478                 r = up->vdwp.r_eq;
1479         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1480 /*      if (a == 0.0 || b == 0.0) */
1481         return rb_float_new(r);
1482 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1483 }
1484
1485 /*
1486  *  call-seq:
1487  *     eps -> Float
1488  *
1489  *  Get the minimum energy for the van der Waals parameter.
1490  */
1491 static VALUE s_ParameterRef_GetEps(VALUE self) {
1492         UnionPar *up;
1493         Int tp;
1494 /*      Double a, b; */
1495         Double eps;
1496         up = s_UnionParFromValue(self, &tp, 0);
1497         if (tp == kVdwParType) {
1498         /*      a = up->vdw.A;
1499                 b = up->vdw.B;  */
1500                 eps = up->vdw.eps;
1501         } else if (tp == kVdwPairParType) {
1502         /*      a = up->vdwp.A;
1503                 b = up->vdwp.B; */
1504                 eps = up->vdwp.eps;
1505         } else rb_raise(rb_eMolbyError, "invalid member eps");
1506 /*      if (a == 0.0 || b == 0.0)  */
1507                 return rb_float_new(eps * INTERNAL2KCAL);
1508 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1509 }
1510
1511 /*
1512  *  call-seq:
1513  *     A14 -> Float
1514  *
1515  *  Get the "A" value for the 1-4 van der Waals parameter.
1516  */
1517 /*
1518 static VALUE s_ParameterRef_GetA14(VALUE self) {
1519         UnionPar *up;
1520         Int tp;
1521         up = s_UnionParFromValue(self, &tp, 0);
1522         if (tp == kVdwParType)
1523                 return rb_float_new(up->vdw.A14);
1524         else if (tp == kVdwPairParType)
1525                 return rb_float_new(up->vdwp.A14);
1526         else rb_raise(rb_eMolbyError, "invalid member A14");
1527 }
1528 */
1529
1530 /*
1531  *  call-seq:
1532  *     B14 -> Float
1533  *
1534  *  Get the "B" value for the 1-4 van der Waals parameter.
1535  */
1536 /*
1537 static VALUE s_ParameterRef_GetB14(VALUE self) {
1538         UnionPar *up;
1539         Int tp;
1540         up = s_UnionParFromValue(self, &tp, 0);
1541         if (tp == kVdwParType)
1542                 return rb_float_new(up->vdw.B14);
1543         else if (tp == kVdwPairParType)
1544                 return rb_float_new(up->vdwp.B14);
1545         else rb_raise(rb_eMolbyError, "invalid member B14");
1546 }
1547 */
1548
1549 /*
1550  *  call-seq:
1551  *     r_eq14 -> Float
1552  *
1553  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1554  */
1555 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1556         UnionPar *up;
1557         Int tp;
1558 /*      Double a, b, r; */
1559         Double r;
1560         up = s_UnionParFromValue(self, &tp, 0);
1561         if (tp == kVdwParType) {
1562         /*      a = up->vdw.A14;
1563                 b = up->vdw.B14; */
1564                 r = up->vdw.r_eq14;
1565         } else if (tp == kVdwPairParType) {
1566         /*      a = up->vdwp.A14;
1567                 b = up->vdwp.B14;  */
1568                 r = up->vdwp.r_eq14;
1569         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1570 /*      if (a == 0.0 || b == 0.0)  */
1571         return rb_float_new(r);
1572 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1573 }
1574
1575 /*
1576  *  call-seq:
1577  *     eps14 -> Float
1578  *
1579  *  Get the minimum energy for the 1-4 van der Waals parameter.
1580  */
1581 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1582         UnionPar *up;
1583         Int tp;
1584 /*      Double a, b;  */
1585         Double eps;
1586         up = s_UnionParFromValue(self, &tp, 0);
1587         if (tp == kVdwParType) {
1588         /*      a = up->vdw.A14;
1589                 b = up->vdw.B14;  */
1590                 eps = up->vdw.eps14;
1591         } else if (tp == kVdwPairParType) {
1592         /*      a = up->vdwp.A14;
1593                 b = up->vdwp.B14; */
1594                 eps = up->vdwp.eps14;
1595         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1596 /*      if (a == 0.0 || b == 0.0) */
1597         return rb_float_new(eps * INTERNAL2KCAL);
1598 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1599 }
1600
1601 /*
1602  *  call-seq:
1603  *     cutoff -> Float
1604  *
1605  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1606  */
1607 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1608         UnionPar *up;
1609         Int tp;
1610         up = s_UnionParFromValue(self, &tp, 0);
1611         if (tp == kVdwCutoffParType)
1612                 return rb_float_new(up->vdwcutoff.cutoff);
1613         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1614 }
1615
1616 /*
1617  *  call-seq:
1618  *     radius -> Float
1619  *
1620  *  Get the atomic (covalent) radius for the element parameter.
1621  */
1622 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1623         UnionPar *up;
1624         Int tp;
1625         up = s_UnionParFromValue(self, &tp, 0);
1626         if (tp == kElementParType)
1627                 return rb_float_new(up->atom.radius);
1628         else rb_raise(rb_eMolbyError, "invalid member radius");
1629 }
1630
1631 /*
1632  *  call-seq:
1633  *     vdw_radius -> Float
1634  *
1635  *  Get the van der Waals radius for the element parameter. (0 if not given)
1636  */
1637 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1638         UnionPar *up;
1639         Int tp;
1640         up = s_UnionParFromValue(self, &tp, 0);
1641         if (tp == kElementParType)
1642                 return rb_float_new(up->atom.vdw_radius);
1643         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1644 }
1645
1646 /*
1647  *  call-seq:
1648  *     color -> [Float, Float, Float]
1649  *
1650  *  Get the rgb color for the element parameter.
1651  */
1652 static VALUE s_ParameterRef_GetColor(VALUE self) {
1653         UnionPar *up;
1654         Int tp;
1655         up = s_UnionParFromValue(self, &tp, 0);
1656         if (tp == kElementParType)
1657                 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));
1658         else rb_raise(rb_eMolbyError, "invalid member color");
1659 }
1660
1661 /*
1662  *  call-seq:
1663  *     atomic_number -> Integer
1664  *
1665  *  Get the atomic number for the vdw or element parameter.
1666  */
1667 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1668         UnionPar *up;
1669         Int tp;
1670         up = s_UnionParFromValue(self, &tp, 0);
1671         if (tp == kElementParType)
1672                 return INT2NUM(up->atom.number);
1673         else if (tp == kVdwParType)
1674                 return INT2NUM(up->vdw.atomicNumber);
1675         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1676 }
1677
1678 /*
1679  *  call-seq:
1680  *     name -> String
1681  *
1682  *  Get the name for the element parameter.
1683  */
1684 static VALUE s_ParameterRef_GetName(VALUE self) {
1685         UnionPar *up;
1686         Int tp;
1687         up = s_UnionParFromValue(self, &tp, 0);
1688         if (tp == kElementParType) {
1689                 char name[5];
1690                 strncpy(name, up->atom.name, 4);
1691                 name[4] = 0;
1692                 return rb_str_new2(name);
1693         } else rb_raise(rb_eMolbyError, "invalid member name");
1694 }
1695
1696 /*
1697  *  call-seq:
1698  *     weight -> Float
1699  *
1700  *  Get the atomic weight for the element parameter.
1701  */
1702 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1703         UnionPar *up;
1704         Int tp;
1705         up = s_UnionParFromValue(self, &tp, 0);
1706         if (tp == kElementParType)
1707                 return rb_float_new(up->atom.weight);
1708         else if (tp == kVdwParType)
1709                 return rb_float_new(up->vdw.weight);
1710         else rb_raise(rb_eMolbyError, "invalid member weight");
1711 }
1712
1713 /*
1714  *  call-seq:
1715  *     fullname -> String
1716  *
1717  *  Get the full name for the element parameter.
1718  */
1719 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1720         UnionPar *up;
1721         Int tp;
1722         up = s_UnionParFromValue(self, &tp, 0);
1723         if (tp == kElementParType) {
1724                 char fullname[16];
1725                 strncpy(fullname, up->atom.fullname, 15);
1726                 fullname[15] = 0;
1727                 return rb_str_new2(fullname);
1728         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1729 }
1730
1731 /*
1732  *  call-seq:
1733  *     comment -> String
1734  *
1735  *  Get the comment for the parameter.
1736  */
1737 static VALUE s_ParameterRef_GetComment(VALUE self) {
1738         UnionPar *up;
1739         Int tp, com;
1740         up = s_UnionParFromValue(self, &tp, 0);
1741         com = up->bond.com;
1742         if (com == 0)
1743                 return Qnil;
1744         else return rb_str_new2(ParameterGetComment(com));
1745 }
1746
1747 /*
1748  *  call-seq:
1749  *     source -> String
1750  *
1751  *  Get the source string for the parameter. Returns false for undefined parameter,
1752  *  and nil for "local" parameter that is specific for the molecule.
1753  */
1754 static VALUE s_ParameterRef_GetSource(VALUE self) {
1755         UnionPar *up;
1756         Int tp, src;
1757         up = s_UnionParFromValue(self, &tp, 0);
1758         src = up->bond.src;
1759         if (src < 0)
1760                 return Qfalse;  /* undefined */
1761         else if (src == 0)
1762                 return Qnil;  /*  local  */
1763         else return rb_str_new2(ParameterGetComment(src));
1764 }
1765
1766 static void
1767 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1768 {
1769         VALUE *valp;
1770         int i;
1771         if (n == 1)
1772                 valp = &val;
1773         else {
1774                 if (rb_obj_is_kind_of(val, rb_cString)) {
1775                         char *s = StringValuePtr(val);
1776                         char *p;
1777                         for (i = 0; i < n; i++) {
1778                                 char buf[40];
1779                                 int len;
1780                                 /*  Skip leading separaters  */
1781                                 while (*s == '-' || *s == ' ' || *s == '\t')
1782                                         s++;
1783                                 for (p = s; *p != 0; p++) {
1784                                         if (*p == '-' || *p == ' ' || *p == '\t')
1785                                                 break;
1786                                 }
1787                                 len = p - s;
1788                                 if (len >= sizeof(buf))
1789                                         len = sizeof(buf) - 1;
1790                                 strncpy(buf, s, len);
1791                                 buf[len] = 0;
1792                                 /*  Skip trailing blanks  */
1793                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1794                                         buf[len] = 0;
1795                                 if (buf[0] == 0)
1796                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1797                                 if (buf[0] >= '0' && buf[0] <= '9')
1798                                         types[i] = atoi(buf);
1799                                 else
1800                                         types[i] = AtomTypeEncodeToUInt(buf);
1801                                 if (p == NULL || *p == 0) {
1802                                         i++;
1803                                         break;
1804                                 } else s = p + 1;
1805                         }
1806                         if (i < n)
1807                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1808                         return;
1809                 }
1810                 val = rb_ary_to_ary(val);
1811                 if (RARRAY_LEN(val) != n)
1812                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1813                 valp = RARRAY_PTR(val);
1814         }
1815         for (i = 0; i < n; i++) {
1816                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1817                         types[i] = NUM2INT(rb_Integer(valp[i]));
1818                 else {
1819                         VALUE sval = valp[i];
1820                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1821                 }
1822         }
1823 }
1824
1825 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1826         UnionPar *up;
1827         VALUE oldval;
1828         Int oldsrc, tp;
1829         UInt types[4];
1830         up = s_UnionParFromValue(self, &tp, 1);
1831         oldval = s_ParameterRef_GetAtomTypes(self);
1832         oldsrc = up->bond.src;
1833         switch (tp) {
1834                 case kBondParType:
1835                         s_ScanAtomTypes(val, 2, types);
1836                         up->bond.type1 = types[0];
1837                         up->bond.type2 = types[1];
1838                         break;
1839                 case kAngleParType:
1840                         s_ScanAtomTypes(val, 3, types);
1841                         up->angle.type1 = types[0];
1842                         up->angle.type2 = types[1];
1843                         up->angle.type3 = types[2];
1844                         break;
1845                 case kDihedralParType:
1846                 case kImproperParType:
1847                         s_ScanAtomTypes(val, 4, types);
1848                         up->torsion.type1 = types[0];
1849                         up->torsion.type2 = types[1];
1850                         up->torsion.type3 = types[2];
1851                         up->torsion.type4 = types[3];
1852                         break;
1853                 case kVdwParType:
1854                         s_ScanAtomTypes(val, 1, types);
1855                         up->vdw.type1 = types[0];
1856                         break;
1857                 case kVdwPairParType:
1858                         s_ScanAtomTypes(val, 2, types);
1859                         up->vdwp.type1 = types[0];
1860                         up->vdwp.type2 = types[1];
1861                         break;
1862                 case kVdwCutoffParType:
1863                         s_ScanAtomTypes(val, 2, types);
1864                         up->vdwcutoff.type1 = types[0];
1865                         up->vdwcutoff.type2 = types[1];
1866                         break;
1867                 default:
1868                         return Qnil;
1869         }
1870         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1871         return val;
1872 }
1873
1874 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1875         UnionPar *up;
1876         Int tp, i, n, oldsrc;
1877         VALUE *valp, oldval;
1878         up = s_UnionParFromValue(self, &tp, 1);
1879         oldval = s_ParameterRef_GetK(self);
1880         oldsrc = up->bond.src;
1881         switch (tp) {
1882                 case kBondParType:
1883                         val = rb_Float(val);
1884                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1885                         break;
1886                 case kAngleParType:
1887                         val = rb_Float(val);
1888                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1889                         break;
1890                 case kDihedralParType:
1891                 case kImproperParType:
1892                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1893                                 up->torsion.mult = 1;
1894                                 val = rb_Float(val);
1895                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1896                                 break;
1897                         }
1898                         n = up->torsion.mult;
1899                         if (n > 3)
1900                                 n = 3;
1901                         val = rb_ary_to_ary(val);
1902                         if (RARRAY_LEN(val) != n)
1903                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1904                         valp = RARRAY_PTR(val);
1905                         for (i = 0; i < n; i++) {
1906                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1907                         }
1908                         break;
1909                 default:
1910                         rb_raise(rb_eMolbyError, "invalid member k");
1911         }
1912         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1913         return val;
1914 }
1915
1916 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1917         UnionPar *up;
1918         Int tp, oldsrc;
1919         VALUE oldval;
1920         up = s_UnionParFromValue(self, &tp, 1);
1921         oldval = s_ParameterRef_GetR0(self);
1922         oldsrc = up->bond.src;
1923         if (tp == kBondParType) {
1924                 val = rb_Float(val);
1925                 up->bond.r0 = NUM2DBL(val);
1926         } else rb_raise(rb_eMolbyError, "invalid member r0");
1927         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1928         return val;
1929 }
1930
1931 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1932         UnionPar *up;
1933         Int tp, oldsrc;
1934         VALUE oldval;
1935         up = s_UnionParFromValue(self, &tp, 1);
1936         oldval = s_ParameterRef_GetA0(self);
1937         oldsrc = up->bond.src;
1938         if (tp == kAngleParType) {
1939                 val = rb_Float(val);
1940                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1941         } else rb_raise(rb_eMolbyError, "invalid member a0");
1942         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1943         return val;
1944 }
1945
1946 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1947         UnionPar *up;
1948         Int tp, oldsrc;
1949         VALUE oldval;
1950         up = s_UnionParFromValue(self, &tp, 1);
1951         oldval = s_ParameterRef_GetMult(self);
1952         oldsrc = up->bond.src;
1953         if (tp == kDihedralParType || tp == kImproperParType) {
1954                 int i;
1955                 val = rb_Integer(val);
1956                 i = NUM2INT(val);
1957                 if (i < 0 || i > 3)
1958                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1959                 up->torsion.mult = i;
1960         } else rb_raise(rb_eMolbyError, "invalid member mult");
1961         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1962         return val;
1963 }
1964
1965 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1966         UnionPar *up;
1967         Int tp, i, n, oldsrc;
1968         VALUE *valp, oldval;
1969         up = s_UnionParFromValue(self, &tp, 1);
1970         oldval = s_ParameterRef_GetPeriod(self);
1971         oldsrc = up->bond.src;
1972         if (tp == kDihedralParType || tp == kImproperParType) {
1973                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1974                         up->torsion.mult = 1;
1975                         val = rb_Integer(val);
1976                         up->torsion.period[0] = NUM2INT(val);
1977                 } else {
1978                         n = up->torsion.mult;
1979                         if (n > 3)
1980                                 n = 3;
1981                         val = rb_ary_to_ary(val);
1982                         if (RARRAY_LEN(val) != n)
1983                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1984                         valp = RARRAY_PTR(val);
1985                         for (i = 0; i < n; i++) {
1986                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1987                         }
1988                 }
1989         } else rb_raise(rb_eMolbyError, "invalid member period");
1990         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1991         return val;
1992 }
1993
1994 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1995         UnionPar *up;
1996         Int tp, i, n, oldsrc;
1997         VALUE *valp, oldval;
1998         up = s_UnionParFromValue(self, &tp, 1);
1999         oldval = s_ParameterRef_GetPhi0(self);
2000         oldsrc = up->bond.src;
2001         if (tp == kDihedralParType || tp == kImproperParType) {
2002                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2003                         up->torsion.mult = 1;
2004                         val = rb_Float(val);
2005                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2006                 } else {
2007                         n = up->torsion.mult;
2008                         if (n > 3)
2009                                 n = 3;
2010                         val = rb_ary_to_ary(val);
2011                         if (RARRAY_LEN(val) != n)
2012                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2013                         valp = RARRAY_PTR(val);
2014                         for (i = 0; i < n; i++)
2015                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2016                 }
2017         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2018         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2019         return val;
2020 }
2021
2022 /*
2023 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2024         UnionPar *up;
2025         Int tp, oldsrc;
2026         double d;
2027         VALUE oldval;
2028         up = s_UnionParFromValue(self, &tp, 1);
2029         oldval = s_ParameterRef_GetA(self);
2030         oldsrc = up->bond.src;
2031         val = rb_Float(val);
2032         d = NUM2DBL(val);
2033         if (tp == kVdwParType)
2034                 up->vdw.A = d;
2035         else if (tp == kVdwPairParType)
2036                 up->vdwp.A = d;
2037         else rb_raise(rb_eMolbyError, "invalid member A");
2038         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2039         return val;
2040 }
2041
2042 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2043         UnionPar *up;
2044         Int tp, oldsrc;
2045         double d;
2046         VALUE oldval;
2047         up = s_UnionParFromValue(self, &tp, 1);
2048         oldval = s_ParameterRef_GetB(self);
2049         oldsrc = up->bond.src;
2050         val = rb_Float(val);
2051         d = NUM2DBL(val);
2052         if (tp == kVdwParType)
2053                 up->vdw.B = d;
2054         else if (tp == kVdwPairParType)
2055                 up->vdwp.B = d;
2056         else rb_raise(rb_eMolbyError, "invalid member B");
2057         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2058         return val;
2059 }
2060 */
2061
2062 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2063         UnionPar *up;
2064         Int tp, oldsrc;
2065         Double r;
2066         VALUE oldval;
2067         up = s_UnionParFromValue(self, &tp, 1);
2068         oldval = s_ParameterRef_GetReq(self);
2069         oldsrc = up->bond.src;
2070         val = rb_Float(val);
2071         r = NUM2DBL(val);
2072         if (tp == kVdwParType) {
2073                 up->vdw.r_eq = r;
2074                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2075                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2076         } else if (tp == kVdwPairParType) {
2077                 up->vdwp.r_eq = r;
2078                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2079                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2080         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2081         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2082         return val;
2083 }
2084
2085 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2086         UnionPar *up;
2087         Int tp, oldsrc;
2088         Double e;
2089         VALUE oldval;
2090         up = s_UnionParFromValue(self, &tp, 1);
2091         oldval = s_ParameterRef_GetEps(self);
2092         oldsrc = up->bond.src;
2093         val = rb_Float(val);
2094         e = NUM2DBL(val) * KCAL2INTERNAL;
2095         if (tp == kVdwParType) {
2096                 up->vdw.eps = e;
2097                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2098                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2099         } else if (tp == kVdwPairParType) {
2100                 up->vdwp.eps = e;
2101                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2102                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2103         } else rb_raise(rb_eMolbyError, "invalid member eps");
2104         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2105         return val;
2106 }
2107
2108 /*
2109 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2110         UnionPar *up;
2111         Int tp, oldsrc;
2112         double d;
2113         VALUE oldval;
2114         up = s_UnionParFromValue(self, &tp, 1);
2115         oldval = s_ParameterRef_GetA14(self);
2116         oldsrc = up->bond.src;
2117         val = rb_Float(val);
2118         d = NUM2DBL(val);
2119         if (tp == kVdwParType)
2120                 up->vdw.A14 = d;
2121         else if (tp == kVdwPairParType)
2122                 up->vdwp.A14 = d;
2123         else rb_raise(rb_eMolbyError, "invalid member A14");
2124         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2125         return val;
2126 }
2127
2128 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2129         UnionPar *up;
2130         Int tp, oldsrc;
2131         double d;
2132         VALUE oldval;
2133         up = s_UnionParFromValue(self, &tp, 1);
2134         oldval = s_ParameterRef_GetB14(self);
2135         oldsrc = up->bond.src;
2136         val = rb_Float(val);
2137         d = NUM2DBL(val);
2138         if (tp == kVdwParType)
2139                 up->vdw.B14 = d;
2140         else if (tp == kVdwPairParType)
2141                 up->vdwp.B14 = d;
2142         else rb_raise(rb_eMolbyError, "invalid member B14");
2143         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2144         return val;
2145 }
2146 */
2147
2148 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2149         UnionPar *up;
2150         Int tp, oldsrc;
2151         Double r;
2152         VALUE oldval;
2153         up = s_UnionParFromValue(self, &tp, 1);
2154         oldval = s_ParameterRef_GetReq14(self);
2155         oldsrc = up->bond.src;
2156         val = rb_Float(val);
2157         r = NUM2DBL(val);
2158         if (tp == kVdwParType) {
2159                 up->vdw.r_eq14 = r;
2160                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2161                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2162         } else if (tp == kVdwPairParType) {
2163                 up->vdwp.r_eq14 = r;
2164                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2165                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2166         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2167         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2168         return val;
2169 }
2170
2171 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2172         UnionPar *up;
2173         Int tp, oldsrc;
2174         Double e;
2175         VALUE oldval;
2176         up = s_UnionParFromValue(self, &tp, 1);
2177         oldval = s_ParameterRef_GetEps14(self);
2178         oldsrc = up->bond.src;
2179         val = rb_Float(val);
2180         e = NUM2DBL(val) * KCAL2INTERNAL;
2181         if (tp == kVdwParType) {
2182                 up->vdw.eps14 = e;
2183                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2184                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2185         } else if (tp == kVdwPairParType) {
2186                 up->vdwp.eps14 = e;
2187                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2188                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2189         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2190         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2191         return val;
2192 }
2193
2194 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2195         UnionPar *up;
2196         Int tp, oldsrc;
2197         VALUE oldval;
2198         oldval = s_ParameterRef_GetCutoff(self);
2199         oldsrc = up->bond.src;
2200         up = s_UnionParFromValue(self, &tp, 1);
2201         val = rb_Float(val);
2202         if (tp == kVdwCutoffParType) {
2203                 up->vdwcutoff.cutoff = NUM2DBL(val);
2204         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2205         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2206         return val;
2207 }
2208
2209 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2210         UnionPar *up;
2211         Int tp, oldsrc;
2212         VALUE oldval;
2213         up = s_UnionParFromValue(self, &tp, 1);
2214         oldval = s_ParameterRef_GetRadius(self);
2215         oldsrc = up->bond.src;
2216         val = rb_Float(val);
2217         if (tp == kElementParType) {
2218                 up->atom.radius = NUM2DBL(val);
2219         } else rb_raise(rb_eMolbyError, "invalid member radius");
2220         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2221         return val;
2222 }
2223
2224 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2225         UnionPar *up;
2226         Int tp, oldsrc;
2227         VALUE oldval;
2228         up = s_UnionParFromValue(self, &tp, 1);
2229         oldval = s_ParameterRef_GetVdwRadius(self);
2230         oldsrc = up->bond.src;
2231         val = rb_Float(val);
2232         if (tp == kElementParType) {
2233                 up->atom.vdw_radius = NUM2DBL(val);
2234         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2235         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2236         return val;
2237 }
2238
2239 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2240         UnionPar *up;
2241         Int tp, oldsrc;
2242         VALUE *valp, oldval;
2243         up = s_UnionParFromValue(self, &tp, 1);
2244         oldval = s_ParameterRef_GetColor(self);
2245         oldsrc = up->bond.src;
2246         val = rb_ary_to_ary(val);
2247         if (RARRAY_LEN(val) != 3)
2248                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2249         valp = RARRAY_PTR(val);
2250         if (tp == kElementParType) {
2251                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2252                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2253                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2254         } else rb_raise(rb_eMolbyError, "invalid member color");
2255         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2256         return val;
2257 }
2258
2259 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2260         UnionPar *up;
2261         Int tp, oldsrc;
2262         VALUE oldval;
2263         up = s_UnionParFromValue(self, &tp, 1);
2264         oldval = s_ParameterRef_GetAtomicNumber(self);
2265         oldsrc = up->bond.src;
2266         val = rb_Integer(val);
2267         if (tp == kElementParType)
2268                 up->atom.number = NUM2INT(val);
2269         else if (tp == kVdwParType) {
2270                 up->vdw.atomicNumber = NUM2INT(val);
2271                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2272         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2273         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2274         return val;
2275 }
2276
2277 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2278         UnionPar *up;
2279         Int tp, oldsrc;
2280         VALUE oldval;
2281         up = s_UnionParFromValue(self, &tp, 1);
2282         oldval = s_ParameterRef_GetName(self);
2283         oldsrc = up->bond.src;
2284         if (tp == kElementParType) {
2285                 strncpy(up->atom.name, StringValuePtr(val), 4);
2286         } else rb_raise(rb_eMolbyError, "invalid member name");
2287         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2288         return val;
2289 }
2290
2291 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2292         UnionPar *up;
2293         Int tp, oldsrc;
2294         VALUE oldval;
2295         val = rb_Float(val);
2296         oldval = s_ParameterRef_GetWeight(self);
2297         up = s_UnionParFromValue(self, &tp, 1);
2298         oldsrc = up->bond.src;
2299         if (tp == kElementParType)
2300                 up->atom.weight = NUM2DBL(val);
2301         else if (tp == kVdwParType)
2302                 up->vdw.weight = NUM2DBL(val);
2303         else rb_raise(rb_eMolbyError, "invalid member weight");
2304         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2305         return val;
2306 }
2307
2308 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2309         UnionPar *up;
2310         Int tp, oldsrc;
2311         VALUE oldval;
2312         up = s_UnionParFromValue(self, &tp, 1);
2313         oldval = s_ParameterRef_GetFullName(self);
2314         oldsrc = up->bond.src;
2315         if (tp == kElementParType) {
2316                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2317                 up->atom.fullname[15] = 0;
2318         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2319         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2320         return val;
2321 }
2322
2323 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2324         UnionPar *up;
2325         Int tp, com, oldsrc;
2326         VALUE oldval;
2327         up = s_UnionParFromValue(self, &tp, 1);
2328         oldval = s_ParameterRef_GetComment(self);
2329         oldsrc = up->bond.src;
2330         if (val == Qnil)
2331                 up->bond.com = 0;
2332         else {
2333                 com = ParameterCommentIndex(StringValuePtr(val));
2334                 up->bond.com = com;
2335         }
2336         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2337         return val;     
2338 }
2339
2340 /*  Only false (undefined) and nil (local) can be set  */
2341 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2342         UnionPar *up;
2343         Int tp, oldsrc;
2344         VALUE oldval;
2345         up = s_UnionParFromValue(self, &tp, 1);
2346         if (val != Qfalse && val != Qnil)
2347                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2348         oldval = s_ParameterRef_GetSource(self);
2349         oldsrc = up->bond.src;
2350         if (oldsrc != 0 && oldsrc != -1)
2351                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2352         up->bond.src = (val == Qfalse ? -1 : 0);
2353         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2354         return val;     
2355 }
2356
2357 static struct s_ParameterAttrDef {
2358         char *name;
2359         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2360         ID id;                  /*  Will be set within InitMolby()  */
2361         VALUE (*getter)(VALUE);
2362         VALUE (*setter)(VALUE, VALUE);
2363 } s_ParameterAttrDefTable[] = {
2364         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2365         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2366         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2367         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2368         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2369         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2370         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2371         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2372         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2373         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2374 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2375         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2376         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2377         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2378 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2379         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2380         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2381         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2382         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2383         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2384         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2385         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2386         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2387         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2388         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2389         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2390         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2391         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2392         {NULL} /* Sentinel */
2393 };
2394
2395 static VALUE
2396 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2397 {
2398         int i;
2399         ID kid;
2400         if (TYPE(key) != T_SYMBOL) {
2401                 kid = rb_intern(StringValuePtr(key));
2402                 key = ID2SYM(kid);
2403         } else kid = SYM2ID(key);
2404         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2405                 if (s_ParameterAttrDefTable[i].id == kid) {
2406                         if (value == Qundef)
2407                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2408                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2409                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2410                         else
2411                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2412                 }
2413         }
2414         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2415         return Qnil; /* not reached */
2416 }
2417
2418 static VALUE
2419 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2420 {
2421         return s_ParameterRef_SetAttr(self, key, Qundef);
2422 }
2423
2424 /*
2425  *  call-seq:
2426  *     keys(idx)          -> array of valid parameter attributes
2427  *  
2428  *  Returns an array of valid parameter attributes (as Symbols).
2429  */
2430 static VALUE
2431 s_ParameterRef_Keys(VALUE self)
2432 {
2433         ParameterRef *pref;
2434         Data_Get_Struct(self, ParameterRef, pref);
2435         switch (pref->parType) {
2436                 case kBondParType:
2437                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2438                 case kAngleParType:
2439                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2440                 case kDihedralParType:
2441                 case kImproperParType:
2442                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2443                 case kVdwParType:
2444                         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);
2445                 case kVdwPairParType:
2446                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2447                 case kVdwCutoffParType:
2448                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2449                 case kElementParType:
2450                         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);
2451                 default:
2452                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2453         }
2454         return Qnil;  /*  Not reached  */
2455 }
2456
2457 /*
2458  *  call-seq:
2459  *     to_hash(idx)          -> Hash
2460  *  
2461  *  Returns a hash containing valid parameter names and values
2462  */
2463 static VALUE
2464 s_ParameterRef_ToHash(VALUE self)
2465 {
2466         VALUE keys = s_ParameterRef_Keys(self);
2467         VALUE retval;
2468         int i;
2469         if (keys == Qnil)
2470                 return Qnil;
2471         retval = rb_hash_new();
2472         for (i = 0; i < RARRAY_LEN(keys); i++) {
2473                 VALUE key = RARRAY_PTR(keys)[i];
2474                 VALUE val = s_ParameterRef_GetAttr(self, key);
2475                 rb_hash_aset(retval, key, val);
2476         }
2477         return retval;
2478 }
2479
2480 /*
2481  *  call-seq:
2482  *     parameter.to_s(idx)          -> String
2483  *  
2484  *  Returns a string representation of the given parameter
2485  */
2486 static VALUE
2487 s_ParameterRef_ToString(VALUE self)
2488 {
2489         Int tp, i, n;
2490         char buf[1024], types[4][8];
2491         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2492         switch (tp) {
2493                 case kBondParType:
2494                         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);
2495                         break;
2496                 case kAngleParType:
2497                         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);
2498                         break;
2499                 case kDihedralParType:
2500                 case kImproperParType:
2501                         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]));
2502                         n = strlen(buf);
2503                         for (i = 0; i < up->torsion.mult; i++) {
2504                                 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);
2505                                 n = strlen(buf);
2506                         }
2507                         break;
2508                 case kVdwParType:
2509                         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);
2510                         break;
2511                 case kVdwPairParType:
2512                         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);
2513                         break;
2514                 case kVdwCutoffParType:
2515                         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);
2516                         break;
2517                 case kElementParType:
2518                         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);
2519                         break;
2520         }
2521         return rb_str_new2(buf);
2522 }
2523
2524 /*
2525  *  call-seq:
2526  *     self == parameterRef -> boolean
2527  *  
2528  *  True if the parameters point to the same parameter record.
2529  */
2530 static VALUE
2531 s_ParameterRef_Equal(VALUE self, VALUE val)
2532 {
2533         Int tp1, tp2;
2534         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2535                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2536         } else return Qfalse;
2537 }
2538         
2539 #pragma mark ====== Parameter Class ======
2540
2541 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2542  *  is NULL, then the global parameters are looked for.  */
2543
2544 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2545 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2546 static void
2547 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2548 {
2549         Molecule *mol;
2550         Data_Get_Struct(val, Molecule, mol);
2551         if (mol == NULL)
2552                 rb_raise(rb_eMolbyError, "the molecule is empty");
2553         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2554                 /*  Do self.md_arena.prepare  */
2555                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2556                 if (val2 != Qnil)
2557                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2558         }
2559 }
2560
2561 static VALUE
2562 s_NewParameterValueFromValue(VALUE val)
2563 {
2564         Molecule *mol;
2565         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2566                 Data_Get_Struct(val, Molecule, mol);
2567                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2568                 MoleculeRetain(mol);
2569                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2570         } else {
2571                 mol = NULL;
2572                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2573         }
2574 }
2575
2576 static Molecule *
2577 s_MoleculeFromParameterValue(VALUE val)
2578 {
2579         Molecule *mol;
2580         Data_Get_Struct(val, Molecule, mol);
2581         return mol;
2582 }
2583
2584 static Parameter *
2585 s_ParameterFromParameterValue(VALUE val)
2586 {
2587         Molecule *mol;
2588         Data_Get_Struct(val, Molecule, mol);
2589         if (mol != NULL)
2590                 return mol->par;
2591         return gBuiltinParameters;
2592 }
2593
2594 /*  Forward declarations  */
2595 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2596 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2597
2598 static Molecule *
2599 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2600 {
2601         if (val == rb_cParameter) {
2602                 return NULL;  /*  Parameter class method: builtin parameters  */
2603         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2604                 return s_MoleculeFromParameterValue(val);
2605         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2606                 return s_MoleculeFromParEnumerableValue(val);
2607         } else return NULL;
2608 }
2609
2610 /*
2611  *  call-seq:
2612  *     builtin    -> Parameter
2613  *  
2614  *  Returns a parameter value that points to the global (builtin) parameters.
2615  *  Equivalent to Parameter::Builtin (constant).
2616  */
2617 static VALUE
2618 s_Parameter_Builtin(VALUE self)
2619 {
2620         static ID s_builtin_id = 0;
2621         if (s_builtin_id == 0)
2622                 s_builtin_id = rb_intern("Builtin");
2623         return rb_const_get(rb_cParameter, s_builtin_id);
2624 }
2625
2626 /*
2627  *  call-seq:
2628  *     bond(idx)          -> ParameterRef
2629  *  
2630  *  The index-th bond parameter record is returned.
2631  */
2632 static VALUE
2633 s_Parameter_Bond(VALUE self, VALUE ival)
2634 {
2635         Molecule *mol;
2636         int idx, n;
2637         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2638         idx = NUM2INT(rb_Integer(ival));
2639         if (mol == NULL)
2640                 n = gBuiltinParameters->nbondPars;
2641         else if (mol->par != NULL)
2642                 n = mol->par->nbondPars;
2643         else n = 0;
2644         if (idx < -n || idx >= n)
2645                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2646         if (idx < 0)
2647                 idx += n;
2648         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2649 }
2650
2651 /*
2652  *  call-seq:
2653  *     angle(idx)          -> ParameterRef
2654  *  
2655  *  The index-th angle parameter record is returned.
2656  */
2657 static VALUE
2658 s_Parameter_Angle(VALUE self, VALUE ival)
2659 {
2660         Molecule *mol;
2661         int idx, n;
2662         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2663         idx = NUM2INT(rb_Integer(ival));
2664         if (mol == NULL)
2665                 n = gBuiltinParameters->nanglePars;
2666         else if (mol->par != NULL)
2667                 n = mol->par->nanglePars;
2668         else n = 0;
2669         if (idx < -n || idx >= n)
2670                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2671         if (idx < 0)
2672                 idx += n;
2673         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2674 }
2675
2676 /*
2677  *  call-seq:
2678  *     dihedral(idx)          -> ParameterRef
2679  *  
2680  *  The index-th dihedral parameter record is returned.
2681  */
2682 static VALUE
2683 s_Parameter_Dihedral(VALUE self, VALUE ival)
2684 {
2685         Molecule *mol;
2686         int idx, n;
2687         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2688         idx = NUM2INT(rb_Integer(ival));
2689         if (mol == NULL)
2690                 n = gBuiltinParameters->ndihedralPars;
2691         else if (mol->par != NULL)
2692                 n = mol->par->ndihedralPars;
2693         else n = 0;
2694         if (idx < -n || idx >= n)
2695                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2696         if (idx < 0)
2697                 idx += n;
2698         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2699 }
2700
2701 /*
2702  *  call-seq:
2703  *     improper(idx)          -> ParameterRef
2704  *  
2705  *  The index-th improper parameter record is returned.
2706  */
2707 static VALUE
2708 s_Parameter_Improper(VALUE self, VALUE ival)
2709 {
2710         Molecule *mol;
2711         int idx, n;
2712         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2713         idx = NUM2INT(rb_Integer(ival));
2714         if (mol == NULL)
2715                 n = gBuiltinParameters->nimproperPars;
2716         else if (mol->par != NULL)
2717                 n = mol->par->nimproperPars;
2718         else n = 0;
2719         if (idx < -n || idx >= n)
2720                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2721         if (idx < 0)
2722                 idx += n;
2723         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2724 }
2725
2726 /*
2727  *  call-seq:
2728  *     vdw(idx)          -> ParameterRef
2729  *  
2730  *  The index-th vdw parameter record is returned.
2731  */
2732 static VALUE
2733 s_Parameter_Vdw(VALUE self, VALUE ival)
2734 {
2735         Molecule *mol;
2736         int idx, n;
2737         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2738         idx = NUM2INT(rb_Integer(ival));
2739         if (mol == NULL)
2740                 n = gBuiltinParameters->nvdwPars;
2741         else if (mol->par != NULL)
2742                 n = mol->par->nvdwPars;
2743         else n = 0;
2744         if (idx < -n || idx >= n)
2745                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2746         if (idx < 0)
2747                 idx += n;
2748         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2749 }
2750
2751 /*
2752  *  call-seq:
2753  *     vdw_pair(idx)          -> ParameterRef
2754  *  
2755  *  The index-th vdw pair parameter record is returned.
2756  */
2757 static VALUE
2758 s_Parameter_VdwPair(VALUE self, VALUE ival)
2759 {
2760         Molecule *mol;
2761         int idx, n;
2762         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2763         idx = NUM2INT(rb_Integer(ival));
2764         if (mol == NULL)
2765                 n = gBuiltinParameters->nvdwpPars;
2766         else if (mol->par != NULL)
2767                 n = mol->par->nvdwpPars;
2768         else n = 0;
2769         if (idx < -n || idx >= n)
2770                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2771         if (idx < 0)
2772                 idx += n;
2773         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2774 }
2775
2776 /*
2777  *  call-seq:
2778  *     vdw_cutoff(idx)          -> ParameterRef
2779  *  
2780  *  The index-th vdw cutoff parameter record is returned.
2781  */
2782 static VALUE
2783 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2784 {
2785         Molecule *mol;
2786         int idx, n;
2787         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2788         idx = NUM2INT(rb_Integer(ival));
2789         if (mol == NULL)
2790                 n = gBuiltinParameters->nvdwCutoffPars;
2791         else if (mol->par != NULL)
2792                 n = mol->par->nvdwCutoffPars;
2793         else n = 0;
2794         if (idx < -n || idx >= n)
2795                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2796         if (idx < 0)
2797                 idx += n;
2798         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2799 }
2800
2801 /*
2802  *  call-seq:
2803  *     element(idx)            -> ParameterRef
2804  *     element(t1)             -> ParameterRef
2805  *  
2806  *  In the first form, the index-th element parameter record is returned. In the second
2807  *  form, the element parameter for t1 is looked up (the last index first). t1
2808  *  is the element name string (up to 4 characters).
2809  *  Unlike other Parameter methods, this is used only for the global parameter.
2810  */
2811 static VALUE
2812 s_Parameter_Element(VALUE self, VALUE ival)
2813 {
2814         Int idx1;
2815         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2816                 int n = gCountElementParameters;
2817                 idx1 = NUM2INT(rb_Integer(ival));
2818                 if (idx1 < -n || idx1 >= n)
2819                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2820                 if (idx1 < 0)
2821                         idx1 += n;
2822                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2823         } else {
2824                 ElementPar *ep;
2825                 char name[6];
2826                 int i;
2827                 strncpy(name, StringValuePtr(ival), 4);
2828                 name[4] = 0;
2829                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2830                         if (strncmp(ep->name, name, 4) == 0)
2831                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2832                 }
2833                 return Qnil;
2834         }
2835 }
2836
2837 /*
2838  *  call-seq:
2839  *     nbonds          -> Integer
2840  *  
2841  *  Returns the number of bond parameters.
2842  */
2843 static VALUE
2844 s_Parameter_Nbonds(VALUE self)
2845 {
2846         Int n;
2847         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2848         if (mol == NULL)
2849                 n = gBuiltinParameters->nbondPars;
2850         else if (mol->par != NULL)
2851                 n = mol->par->nbondPars;
2852         else n = 0;
2853         return INT2NUM(n);
2854 }
2855
2856 /*
2857  *  call-seq:
2858  *     nangles          -> Integer
2859  *  
2860  *  Returns the number of angle parameters.
2861  */
2862 static VALUE
2863 s_Parameter_Nangles(VALUE self)
2864 {
2865         Int n;
2866         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2867         if (mol == NULL)
2868                 n = gBuiltinParameters->nanglePars;
2869         else if (mol->par != NULL)
2870                 n = mol->par->nanglePars;
2871         else n = 0;
2872         return INT2NUM(n);
2873 }
2874
2875 /*
2876  *  call-seq:
2877  *     ndihedrals          -> Integer
2878  *  
2879  *  Returns the number of dihedral parameters.
2880  */
2881 static VALUE
2882 s_Parameter_Ndihedrals(VALUE self)
2883 {
2884         Int n;
2885         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2886         if (mol == NULL)
2887                 n = gBuiltinParameters->ndihedralPars;
2888         else if (mol->par != NULL)
2889                 n = mol->par->ndihedralPars;
2890         else n = 0;
2891         return INT2NUM(n);
2892 }
2893
2894 /*
2895  *  call-seq:
2896  *     nimpropers          -> Integer
2897  *  
2898  *  Returns the number of improper parameters.
2899  */
2900 static VALUE
2901 s_Parameter_Nimpropers(VALUE self)
2902 {
2903         Int n;
2904         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2905         if (mol == NULL)
2906                 n = gBuiltinParameters->nimproperPars;
2907         else if (mol->par != NULL)
2908                 n = mol->par->nimproperPars;
2909         else n = 0;
2910         return INT2NUM(n);
2911 }
2912
2913 /*
2914  *  call-seq:
2915  *     nvdws          -> Integer
2916  *  
2917  *  Returns the number of vdw parameters.
2918  */
2919 static VALUE
2920 s_Parameter_Nvdws(VALUE self)
2921 {
2922         Int n;
2923         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2924         if (mol == NULL)
2925                 n = gBuiltinParameters->nvdwPars;
2926         else if (mol->par != NULL)
2927                 n = mol->par->nvdwPars;
2928         else n = 0;
2929         return INT2NUM(n);
2930 }
2931
2932 /*
2933  *  call-seq:
2934  *     nvdw_pairs          -> Integer
2935  *  
2936  *  Returns the number of vdw pair parameters.
2937  */
2938 static VALUE
2939 s_Parameter_NvdwPairs(VALUE self)
2940 {
2941         Int n;
2942         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2943         if (mol == NULL)
2944                 n = gBuiltinParameters->nvdwpPars;
2945         else if (mol->par != NULL)
2946                 n = mol->par->nvdwpPars;
2947         else n = 0;
2948         return INT2NUM(n);
2949 }
2950
2951 /*
2952  *  call-seq:
2953  *     nvdw_cutoffs          -> Integer
2954  *  
2955  *  Returns the number of vdw cutoff parameters.
2956  */
2957 static VALUE
2958 s_Parameter_NvdwCutoffs(VALUE self)
2959 {
2960         Int n;
2961         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2962         if (mol == NULL)
2963                 n = gBuiltinParameters->nvdwCutoffPars;
2964         else if (mol->par != NULL)
2965                 n = mol->par->nvdwCutoffPars;
2966         else n = 0;
2967         return INT2NUM(n);
2968 }
2969
2970 /*
2971  *  call-seq:
2972  *     nelements          -> Integer
2973  *  
2974  *  Returns the number of element parameters.
2975  */
2976 static VALUE
2977 s_Parameter_Nelements(VALUE self)
2978 {
2979         return INT2NUM(gCountElementParameters);
2980 }
2981
2982 /*
2983  *  call-seq:
2984  *     bonds          -> ParEnumerable
2985  *  
2986  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2987  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2988  *  useful when all accessible parameters should be examined by use of 'each' method.
2989  */
2990 static VALUE
2991 s_Parameter_Bonds(VALUE self)
2992 {
2993         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2994         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2995 }
2996
2997 /*
2998  *  call-seq:
2999  *     angles          -> ParEnumerable
3000  *  
3001  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3002  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3003  *  useful when all accessible parameters should be examined by use of 'each' method.
3004  */
3005 static VALUE
3006 s_Parameter_Angles(VALUE self)
3007 {
3008         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3009         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3010 }
3011
3012 /*
3013  *  call-seq:
3014  *     dihedrals          -> ParEnumerable
3015  *  
3016  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3017  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3018  *  useful when all accessible parameters should be examined by use of 'each' method.
3019  */
3020 static VALUE
3021 s_Parameter_Dihedrals(VALUE self)
3022 {
3023         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3024         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3025 }
3026
3027 /*
3028  *  call-seq:
3029  *     impropers          -> ParEnumerable
3030  *  
3031  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3032  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3033  *  useful when all accessible parameters should be examined by use of 'each' method.
3034  */
3035 static VALUE
3036 s_Parameter_Impropers(VALUE self)
3037 {
3038         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3039         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3040 }
3041
3042 /*
3043  *  call-seq:
3044  *     vdws          -> ParEnumerable
3045  *  
3046  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3047  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3048  *  useful when all accessible parameters should be examined by use of 'each' method.
3049  */
3050 static VALUE
3051 s_Parameter_Vdws(VALUE self)
3052 {
3053         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3054         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3055 }
3056
3057 /*
3058  *  call-seq:
3059  *     vdw_pairs          -> ParEnumerable
3060  *  
3061  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3062  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3063  *  useful when all accessible parameters should be examined by use of 'each' method.
3064  */
3065 static VALUE
3066 s_Parameter_VdwPairs(VALUE self)
3067 {
3068         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3069         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3070 }
3071
3072 /*
3073  *  call-seq:
3074  *     vdw_cutoffs          -> ParEnumerable
3075  *  
3076  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3077  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3078  *  useful when all accessible parameters should be examined by use of 'each' method.
3079  */
3080 static VALUE
3081 s_Parameter_VdwCutoffs(VALUE self)
3082 {
3083         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3084         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3085 }
3086
3087 /*
3088  *  call-seq:
3089  *     elements          -> ParEnumerable
3090  *  
3091  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3092  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3093  *  useful when all accessible parameters should be examined by use of 'each' method.
3094  */
3095 static VALUE
3096 s_Parameter_Elements(VALUE self)
3097 {
3098         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3099         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3100 }
3101
3102 static VALUE
3103 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3104 {
3105         VALUE atval, optval;
3106         UInt t[4];
3107         Int ii[4];
3108         int i, n, idx, flags, is_global;
3109
3110         rb_scan_args(argc, argv, "1*", &atval, &optval);
3111         
3112         /*  Get the atom types  */
3113         switch (parType) {
3114                 case kBondParType: n = 2; break;
3115                 case kAngleParType: n = 3; break;
3116                 case kDihedralParType: n = 4; break;
3117                 case kImproperParType: n = 4; break;
3118                 case kVdwParType: n = 1; break;
3119                 case kVdwPairParType: n = 2; break;
3120                 default: return Qnil;
3121         }
3122         s_ScanAtomTypes(atval, n, t);
3123         for (i = 0; i < n; i++) {
3124                 if (t[i] < kAtomTypeMinimum) {
3125                         /*  Explicit atom index  */
3126                         if (mol == NULL)
3127                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3128                         if (t[i] >= mol->natoms)
3129                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3130                         ii[i] = t[i];
3131                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3132                 } else ii[i] = -1;
3133         }
3134         
3135         /*  Analyze options  */
3136         flags = 0;
3137         n = RARRAY_LEN(optval);
3138         for (i = 0; i < n; i++) {
3139                 VALUE oval = RARRAY_PTR(optval)[i];
3140                 if (oval == ID2SYM(rb_intern("global")))
3141                         flags |= kParameterLookupGlobal;
3142                 else if (oval == ID2SYM(rb_intern("local")))
3143                         flags |= kParameterLookupLocal;
3144                 else if (oval == ID2SYM(rb_intern("missing")))
3145                         flags |= kParameterLookupMissing;
3146                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3147                         flags |= kParameterLookupNoWildcard;
3148                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3149                         flags |= kParameterLookupNoBaseAtomType;
3150                 else if (oval == ID2SYM(rb_intern("create")))
3151                         flags |= 256;
3152         }
3153         if (flags == 0)
3154                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3155         
3156         idx = -1;
3157         is_global = 0;
3158         switch (parType) {
3159                 case kBondParType: {
3160                         BondPar *bp;
3161                         if (mol != NULL) {
3162                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3163                                 if (bp != NULL) {
3164                                         idx = bp - mol->par->bondPars;
3165                                         break;
3166                                 }
3167                         }
3168                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3169                         if (bp != NULL) {
3170                                 idx = bp - gBuiltinParameters->bondPars;
3171                                 is_global = 1;
3172                         }
3173                         break;
3174                 }
3175                 case kAngleParType: {
3176                         AnglePar *ap;
3177                         if (mol != NULL) {
3178                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3179                                 if (ap != NULL) {
3180                                         idx = ap - mol->par->anglePars;
3181                                         break;
3182                                 }
3183                         }
3184                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3185                         if (ap != NULL) {
3186                                 idx = ap - gBuiltinParameters->anglePars;
3187                                 is_global = 1;
3188                         }
3189                         break;
3190                 }
3191                 case kDihedralParType: {
3192                         TorsionPar *tp;
3193                         if (mol != NULL) {
3194                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3195                                 if (tp != NULL) {
3196                                         idx = tp - mol->par->dihedralPars;
3197                                         break;
3198                                 }
3199                         }
3200                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3201                         if (tp != NULL) {
3202                                 idx = tp - gBuiltinParameters->dihedralPars;
3203                                 is_global = 1;
3204                         }
3205                         break;
3206                 }
3207                 case kImproperParType: {
3208                         TorsionPar *tp;
3209                         if (mol != NULL) {
3210                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3211                                 if (tp != NULL) {
3212                                         idx = tp - mol->par->improperPars;
3213                                         break;
3214                                 }
3215                         }
3216                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3217                         if (tp != NULL) {
3218                                 idx = tp - gBuiltinParameters->improperPars;
3219                                 is_global = 1;
3220                         }
3221                         break;
3222                 }       
3223                 case kVdwParType: {
3224                         VdwPar *vp;
3225                         if (mol != NULL) {
3226                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3227                                 if (vp != NULL) {
3228                                         idx = vp - mol->par->vdwPars;
3229                                         break;
3230                                 }
3231                         }
3232                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3233                         if (vp != NULL) {
3234                                 idx = vp - gBuiltinParameters->vdwPars;
3235                                 is_global = 1;
3236                         }
3237                         break;
3238                 }       
3239                 case kVdwPairParType: {
3240                         VdwPairPar *vp;
3241                         if (mol != NULL) {
3242                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3243                                 if (vp != NULL) {
3244                                         idx = vp - mol->par->vdwpPars;
3245                                         break;
3246                                 }
3247                         }
3248                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3249                         if (vp != NULL) {
3250                                 idx = vp - gBuiltinParameters->vdwpPars;
3251                                 is_global = 1;
3252                         }
3253                         break;
3254                 }
3255                 default:
3256                         return Qnil;
3257         }
3258         if (idx < 0) {
3259                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3260                         return Qnil;            
3261                 else {
3262                         /*  Insert a new parameter record  */
3263                         UnionPar *up;
3264                         Int count = ParameterGetCountForType(mol->par, parType);
3265                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3266                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3267                         IntGroupRelease(ig);
3268                         is_global = 0;
3269                         idx = count;
3270                         /*  Set atom types  */
3271                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3272                         if (up == NULL)
3273                                 return Qnil;
3274                         switch (parType) {
3275                                 case kBondParType:
3276                                         up->bond.type1 = t[0];
3277                                         up->bond.type2 = t[1];
3278                                         break;
3279                                 case kAngleParType:
3280                                         up->angle.type1 = t[0];
3281                                         up->angle.type2 = t[1];
3282                                         up->angle.type3 = t[2];
3283                                         break;
3284                                 case kDihedralParType:
3285                                 case kImproperParType:
3286                                         up->torsion.type1 = t[0];
3287                                         up->torsion.type2 = t[1];
3288                                         up->torsion.type3 = t[2];
3289                                         up->torsion.type4 = t[3];
3290                                         break;
3291                                 case kVdwParType:
3292                                         up->vdw.type1 = t[0];
3293                                         break;
3294                                 case kVdwPairParType:
3295                                         up->vdwp.type1 = t[0];
3296                                         up->vdwp.type2 = t[1];
3297                                         break;
3298                                 default:
3299                                         return Qnil;
3300                         }
3301                 }
3302         }
3303         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3304 }
3305
3306 /*
3307  *  call-seq:
3308  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3309  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3310  *
3311  *  Find the parameter record that matches the given atom types. The atom types are given
3312  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3313  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3314  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3315  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3316  */
3317 static VALUE
3318 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3319 {
3320         int parType;
3321         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3322         if (argc == 0)
3323                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3324         parType = s_ParTypeFromValue(argv[0]);
3325         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3326 }
3327
3328 /*
3329  *  call-seq:
3330  *     self == parameter -> boolean
3331  *  
3332  *  True if the parameters point to the same parameter table.
3333  */
3334 static VALUE
3335 s_Parameter_Equal(VALUE self, VALUE val)
3336 {
3337         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3338                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3339         } else return Qfalse;
3340 }
3341
3342 #pragma mark ====== ParEnumerable Class ======
3343
3344 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3345  and the parameter type. If the Molecule is NULL, then it refers to the
3346  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3347  the global parameters are always accessible. */
3348
3349 typedef struct ParEnumerable {
3350         Molecule *mol;
3351         Int parType;   /*  Same as parType in ParameterRef  */
3352 } ParEnumerable;
3353
3354 static ParEnumerable *
3355 s_ParEnumerableNew(Molecule *mol, Int parType)
3356 {
3357         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3358         if (pen != NULL) {
3359                 pen->mol = mol;
3360                 if (mol != NULL)
3361                         MoleculeRetain(mol);
3362                 pen->parType = parType;
3363         }
3364         return pen;
3365 }
3366
3367 static void
3368 s_ParEnumerableRelease(ParEnumerable *pen)
3369 {
3370         if (pen != NULL) {
3371                 if (pen->mol != NULL)
3372                         MoleculeRelease(pen->mol);
3373                 free(pen);
3374         }
3375 }
3376
3377 static Molecule *
3378 s_MoleculeFromParEnumerableValue(VALUE val)
3379 {
3380         ParEnumerable *pen;
3381     Data_Get_Struct(val, ParEnumerable, pen);
3382         return pen->mol;
3383 }
3384
3385 static VALUE
3386 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3387 {
3388         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3389         if (pen == NULL)
3390                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3391         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3392 }
3393
3394 /*
3395  *  call-seq:
3396  *     par_type -> String
3397  *
3398  *  Get the parameter type, like "bond", "angle", etc.
3399  */
3400 static VALUE
3401 s_ParEnumerable_ParType(VALUE self) {
3402         ParEnumerable *pen;
3403         Int tp;
3404     Data_Get_Struct(self, ParEnumerable, pen);
3405         tp = pen->parType;
3406         if (tp == kElementParType)
3407                 return rb_str_new2("element");
3408         tp -= kFirstParType;
3409         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3410                 return rb_str_new2(s_ParameterTypeNames[tp]);
3411         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3412 }
3413
3414 /*
3415  *  call-seq:
3416  *     self[idx]          -> ParameterRef
3417  *  
3418  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3419  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3420  *  parent Parameter object of self.
3421  *
3422  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3423  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3424  */
3425 static VALUE
3426 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3427 {
3428         ParEnumerable *pen;
3429     Data_Get_Struct(self, ParEnumerable, pen);
3430         switch (pen->parType) {
3431                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3432                 case kBondParType:      return s_Parameter_Bond(self, ival);
3433                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3434                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3435                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3436                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3437                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3438                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3439                 case kElementParType:   return s_Parameter_Element(self, ival);
3440                 default:
3441                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3442         }
3443         return Qnil;  /*  Not reached  */
3444 }
3445
3446 /*
3447  *  call-seq:
3448  *     length          -> Integer
3449  *  
3450  *  Returns the number of parameters included in this enumerable.
3451  */
3452 static VALUE
3453 s_ParEnumerable_Length(VALUE self)
3454 {
3455         ParEnumerable *pen;
3456     Data_Get_Struct(self, ParEnumerable, pen);
3457         switch (pen->parType) {
3458                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3459                 case kBondParType:      return s_Parameter_Nbonds(self);
3460                 case kAngleParType:     return s_Parameter_Nangles(self); 
3461                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3462                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3463                 case kVdwParType:       return s_Parameter_Nvdws(self);
3464                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3465                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3466                 case kElementParType:   return s_Parameter_Nelements(self);
3467                 default:
3468                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3469         }
3470         return Qnil;  /*  Not reached  */
3471 }
3472
3473 /*
3474  *  call-seq:
3475  *     each {|pref| ...}
3476  *  
3477  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3478  */
3479 VALUE
3480 s_ParEnumerable_Each(VALUE self)
3481 {
3482         VALUE aval;
3483         ParEnumerable *pen;
3484         ParameterRef *pref;
3485         int i, ofs, n;
3486     Data_Get_Struct(self, ParEnumerable, pen);
3487         if (pen->parType == kElementParType)
3488                 n = gCountElementParameters;
3489         else {
3490                 switch (pen->parType) {
3491                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3492                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3493                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3494                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3495                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3496                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3497                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3498                         default:
3499                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3500                 }
3501                 if (pen->mol == NULL)
3502                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3503                 else if (pen->mol->par != NULL)
3504                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3505                 else return self;
3506         }               
3507         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3508         Data_Get_Struct(aval, ParameterRef, pref);
3509         for (i = 0; i < n; i++) {
3510                 pref->idx = i;
3511                 rb_yield(aval);
3512         }
3513     return self;
3514 }
3515
3516 /*
3517  *  call-seq:
3518  *     reverse_each {|pref| ...}
3519  *  
3520  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3521  */
3522 VALUE
3523 s_ParEnumerable_ReverseEach(VALUE self)
3524 {
3525         VALUE aval;
3526         ParEnumerable *pen;
3527         ParameterRef *pref;
3528         int i, ofs, n;
3529     Data_Get_Struct(self, ParEnumerable, pen);
3530         if (pen->parType == kElementParType)
3531                 n = gCountElementParameters;
3532         else {
3533                 switch (pen->parType) {
3534                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3535                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3536                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3537                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3538                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3539                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3540                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3541                         default:
3542                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3543                 }
3544                 if (pen->mol == NULL)
3545                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3546                 else if (pen->mol->par != NULL)
3547                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3548                 else return self;
3549         }               
3550         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3551         Data_Get_Struct(aval, ParameterRef, pref);
3552         for (i = n - 1; i >= 0; i--) {
3553                 pref->idx = i;
3554                 rb_yield(aval);
3555         }
3556     return self;
3557 }
3558
3559 /*
3560  *  call-seq:
3561  *     insert(idx = nil, pref = nil)       -> ParameterRef
3562  *  
3563  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3564  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3565  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3566  *  parameter is left undefined.
3567  *  Throws an exception if ParEnumerable points to the global parameter.
3568  */
3569 static VALUE
3570 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3571 {
3572         VALUE ival, pval;
3573         ParEnumerable *pen;
3574         int i, n;
3575         IntGroup *ig;
3576         UnionPar u;
3577         MolAction *act;
3578     Data_Get_Struct(self, ParEnumerable, pen);
3579         if (pen->mol == NULL)
3580                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3581         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3582         rb_scan_args(argc, argv, "02", &ival, &pval);
3583         if (ival != Qnil) {
3584                 i = NUM2INT(rb_Integer(ival));
3585                 if (i < 0 || i > n)
3586                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3587                 n = i;
3588         }
3589         if (pval != Qnil) {
3590                 Int type;
3591                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3592                 if (up == NULL || type != pen->parType)
3593                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3594                 ParameterCopyOneWithType(&u, up, pen->parType);
3595                 u.bond.src = 0;
3596         } else {
3597                 memset(&u, 0, sizeof(u));
3598                 u.bond.src = 0;
3599         }
3600         ig = IntGroupNewWithPoints(n, 1, -1);
3601         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3602
3603         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3604         MolActionCallback_registerUndo(pen->mol, act);
3605         MolActionRelease(act);
3606         
3607         IntGroupRelease(ig);
3608         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3609 }
3610
3611 /*
3612  *  call-seq:
3613  *     delete(Integer)
3614  *     delete(IntGroup)
3615  *  
3616  *  Delete the parameter(s) specified by the argument.
3617  */
3618 static VALUE
3619 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3620 {
3621         ParEnumerable *pen;
3622         int i, n;
3623         IntGroup *ig;
3624     Data_Get_Struct(self, ParEnumerable, pen);
3625         if (pen->mol == NULL)
3626                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3627         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3628         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3629                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3630                 i = 1;
3631         } else {
3632                 ig = IntGroupFromValue(ival);
3633                 if ((i = IntGroupGetCount(ig)) == 0) {
3634                         IntGroupRelease(ig);
3635                         return Qnil;
3636                 }
3637         }
3638         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3639                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3640         n = i;
3641
3642         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3643         IntGroupRelease(ig);
3644         return ival;
3645 }
3646
3647 /*
3648  *  call-seq:
3649  *     lookup(atom_types, options, ...) -> ParameterRef
3650  *     lookup(atom_type_string, options, ...) -> ParameterRef
3651  *
3652  *  Find the parameter record that matches the given atom types. The arguments are
3653  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3654  *  specified.
3655  */
3656 static VALUE
3657 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3658 {
3659         ParEnumerable *pen;
3660     Data_Get_Struct(self, ParEnumerable, pen);
3661         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3662 }
3663
3664 /*
3665  *  call-seq:
3666  *     self == parEnumerable -> boolean
3667  *  
3668  *  True if the arguments point to the same parameter table and type.
3669  */
3670 static VALUE
3671 s_ParEnumerable_Equal(VALUE self, VALUE val)
3672 {
3673         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3674                 ParEnumerable *pen1, *pen2;
3675                 Data_Get_Struct(self, ParEnumerable, pen1);
3676                 Data_Get_Struct(val, ParEnumerable, pen2);
3677                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3678         } else return Qfalse;
3679 }
3680
3681 #pragma mark ====== AtomRef Class ======
3682
3683 /*  Forward declaration for register undo  */
3684 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3685
3686 /*  Ruby string "set_atom_attr"  */
3687 static VALUE s_SetAtomAttrString;
3688
3689 static int
3690 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3691 {
3692         AtomRef *aref;
3693         int idx;
3694         Data_Get_Struct(self, AtomRef, aref);
3695         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3696         if (idx < 0 || idx >= aref->mol->natoms)
3697                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3698         if (app != NULL)
3699                 *app = aref->mol->atoms + idx;
3700         if (mpp != NULL)
3701                 *mpp = aref->mol;
3702         return idx;
3703 }
3704
3705 static Atom *
3706 s_AtomFromValue(VALUE self)
3707 {
3708         Atom *ap;
3709         s_AtomIndexFromValue(self, &ap, NULL);
3710         return ap;
3711 }
3712
3713 static Atom *
3714 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3715 {
3716         Atom *ap;
3717         s_AtomIndexFromValue(self, &ap, mpp);
3718         return ap;
3719 }
3720
3721 static void
3722 s_NotifyModificationForAtomRef(VALUE self)
3723 {
3724         AtomRef *aref;
3725         Data_Get_Struct(self, AtomRef, aref);
3726         MoleculeIncrementModifyCount(aref->mol);
3727 }
3728
3729 static void
3730 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3731 {
3732         AtomRef *aref;
3733         Data_Get_Struct(self, AtomRef, aref);
3734         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3735                 /*  Register undo  */
3736                 MolAction *act;
3737                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3738                 MolActionCallback_registerUndo(aref->mol, act);
3739                 MoleculeCallback_notifyModification(aref->mol, 0);
3740                 /*  Request MD rebuilt if necessary  */
3741                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3742                         aref->mol->needsMDRebuild = 1;
3743         }
3744 }
3745
3746 VALUE
3747 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3748 {
3749         AtomRef *aref;
3750         aref = AtomRefNew(mol, idx);
3751         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3752 }
3753
3754 static VALUE
3755 s_AtomRef_GetMolecule(VALUE self)
3756 {
3757         Molecule *mpp;
3758         s_AtomIndexFromValue(self, NULL, &mpp);
3759         return ValueFromMolecule(mpp);
3760 }
3761
3762 static VALUE s_AtomRef_GetIndex(VALUE self) {
3763         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3764 }
3765
3766 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3767         return INT2NUM(s_AtomFromValue(self)->segSeq);
3768 }
3769
3770 static VALUE s_AtomRef_GetSegName(VALUE self) {
3771         char *p = s_AtomFromValue(self)->segName;
3772         return rb_str_new(p, strlen_limit(p, 4));
3773 }
3774
3775 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3776         return INT2NUM(s_AtomFromValue(self)->resSeq);
3777 }
3778
3779 static VALUE s_AtomRef_GetResName(VALUE self) {
3780         char *p = s_AtomFromValue(self)->resName;
3781         return rb_str_new(p, strlen_limit(p, 4));
3782 }
3783
3784 static VALUE s_AtomRef_GetName(VALUE self) {
3785         char *p = s_AtomFromValue(self)->aname;
3786         return rb_str_new(p, strlen_limit(p, 4));
3787 }
3788
3789 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3790         int type = s_AtomFromValue(self)->type;
3791         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3792         return rb_str_new(p, strlen_limit(p, 6));
3793 }
3794
3795 static VALUE s_AtomRef_GetCharge(VALUE self) {
3796         return rb_float_new(s_AtomFromValue(self)->charge);
3797 }
3798
3799 static VALUE s_AtomRef_GetWeight(VALUE self) {
3800         return rb_float_new(s_AtomFromValue(self)->weight);
3801 }
3802
3803 static VALUE s_AtomRef_GetElement(VALUE self) {
3804         char *p = s_AtomFromValue(self)->element;
3805         return rb_str_new(p, strlen_limit(p, 4));
3806 }
3807
3808 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3809         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3810 }
3811
3812 static VALUE s_AtomRef_GetConnects(VALUE self) {
3813         VALUE retval;
3814         Int i, *cp;
3815         Atom *ap = s_AtomFromValue(self);
3816         retval = rb_ary_new();
3817         cp = AtomConnectData(&ap->connect);
3818         for (i = 0; i < ap->connect.count; i++)
3819                 rb_ary_push(retval, INT2NUM(cp[i]));
3820         return retval;
3821 }
3822
3823 static VALUE s_AtomRef_GetR(VALUE self) {
3824         return ValueFromVector(&(s_AtomFromValue(self)->r));
3825 }
3826
3827 static VALUE s_AtomRef_GetX(VALUE self) {
3828         return rb_float_new(s_AtomFromValue(self)->r.x);
3829 }
3830
3831 static VALUE s_AtomRef_GetY(VALUE self) {
3832         return rb_float_new(s_AtomFromValue(self)->r.y);
3833 }
3834
3835 static VALUE s_AtomRef_GetZ(VALUE self) {
3836         return rb_float_new(s_AtomFromValue(self)->r.z);
3837 }
3838
3839 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3840         Atom *ap;
3841         Molecule *mp;
3842         Vector r1;
3843         s_AtomIndexFromValue(self, &ap, &mp);
3844         r1 = ap->r;
3845         if (mp->cell != NULL)
3846                 TransformVec(&r1, mp->cell->rtr, &r1);
3847         return r1;
3848 }
3849
3850 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3851         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3852         return ValueFromVector(&r1);
3853 }
3854
3855 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3856         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3857 }
3858
3859 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3860         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3861 }
3862
3863 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3864         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3865 }
3866
3867 static VALUE s_AtomRef_GetSigma(VALUE self) {
3868         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3869 }
3870
3871 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3872         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3873 }
3874
3875 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3876         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3877 }
3878
3879 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3880         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3881 }
3882
3883 static VALUE s_AtomRef_GetV(VALUE self) {
3884         return ValueFromVector(&(s_AtomFromValue(self)->v));
3885 }
3886
3887 static VALUE s_AtomRef_GetF(VALUE self) {
3888         Vector v = s_AtomFromValue(self)->f;
3889         VecScaleSelf(v, INTERNAL2KCAL);
3890         return ValueFromVector(&v);
3891 }
3892
3893 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3894         return rb_float_new(s_AtomFromValue(self)->occupancy);
3895 }
3896
3897 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3898         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3899 }
3900
3901 static VALUE s_AtomRef_GetAniso(VALUE self) {
3902         VALUE retval;
3903         int i;
3904         Atom *ap = s_AtomFromValue(self);
3905         if (ap->aniso == NULL)
3906                 return Qnil;
3907         retval = rb_ary_new();
3908         for (i = 0; i < 6; i++)
3909                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3910         if (ap->aniso->has_bsig) {
3911                 rb_ary_push(retval, INT2NUM(0));
3912                 for (i = 0; i < 6; i++)
3913                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3914         }
3915         return retval;
3916 }
3917
3918 static VALUE s_AtomRef_GetSymop(VALUE self) {
3919         VALUE retval;
3920         Atom *ap = s_AtomFromValue(self);
3921         if (!ap->symop.alive)
3922                 return Qnil;
3923         retval = rb_ary_new();
3924         rb_ary_push(retval, INT2NUM(ap->symop.sym));
3925         rb_ary_push(retval, INT2NUM(ap->symop.dx));
3926         rb_ary_push(retval, INT2NUM(ap->symop.dy));
3927         rb_ary_push(retval, INT2NUM(ap->symop.dz));
3928         rb_ary_push(retval, INT2NUM(ap->symbase));
3929         return retval;
3930 }
3931
3932 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3933         return INT2NUM(s_AtomFromValue(self)->intCharge);
3934 }
3935
3936 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3937         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3938 }
3939
3940 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3941         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3942 }
3943
3944 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3945         Molecule *mol;
3946         Atom *ap;
3947         int idx, i;
3948         MDExclusion *exinfo;
3949         Int *exlist;
3950         VALUE retval, aval;
3951         idx = s_AtomIndexFromValue(self, &ap, &mol);
3952         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3953                 VALUE mval = ValueFromMolecule(mol);
3954                 s_RebuildMDParameterIfNecessary(mval, Qnil);
3955         }
3956         if (mol->arena->exinfo == NULL)
3957                 return Qnil;
3958         exinfo = mol->arena->exinfo + idx;
3959         exlist = mol->arena->exlist;
3960         retval = rb_ary_new();
3961         aval = rb_ary_new();
3962         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
3963                 rb_ary_push(aval, INT2NUM(exlist[i]));
3964         rb_ary_push(retval, aval);
3965         aval = rb_ary_new();
3966         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
3967                 rb_ary_push(aval, INT2NUM(exlist[i]));
3968         rb_ary_push(retval, aval);
3969         aval = rb_ary_new();
3970         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
3971                 rb_ary_push(aval, INT2NUM(exlist[i]));
3972         rb_ary_push(retval, aval);
3973         return retval;
3974 }
3975
3976 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3977         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3978 }
3979
3980 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3981         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3982 }
3983
3984 static VALUE s_AtomRef_GetHidden(VALUE self) {
3985         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3986 }
3987
3988 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3989         VALUE retval;
3990         Int i, count, *cp;
3991         Atom *ap = s_AtomFromValue(self);
3992         if (ap->anchor == NULL)
3993                 return Qnil;
3994         count = ap->anchor->connect.count;
3995         retval = rb_ary_new2(count * 2);
3996         cp = AtomConnectData(&ap->anchor->connect);
3997         for (i = 0; i < count; i++) {
3998                 rb_ary_store(retval, i, INT2NUM(cp[i]));
3999                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4000         }
4001         return retval;
4002 }
4003
4004 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4005         char *p = s_AtomFromValue(self)->uff_type;
4006         return rb_str_new(p, strlen_limit(p, 5));
4007 }
4008
4009 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4010         rb_raise(rb_eMolbyError, "index cannot be directly set");
4011         return Qnil;
4012 }
4013
4014 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4015         VALUE oval = s_AtomRef_GetSegSeq(self);
4016         val = rb_Integer(val);
4017         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4018         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4019         return val;
4020 }
4021
4022 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4023         char *p = StringValuePtr(val);
4024         VALUE oval = s_AtomRef_GetSegName(self);
4025         strncpy(s_AtomFromValue(self)->segName, p, 4);
4026         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4027         return val;
4028 }
4029
4030 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4031         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4032         return val; /* Not reached */
4033 }
4034
4035 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4036         Atom *ap = s_AtomFromValue(self);
4037         char *p = StringValuePtr(val);
4038         VALUE oval = s_AtomRef_GetName(self);
4039         if (ap->anchor != NULL && p[0] == '_')
4040                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4041         strncpy(ap->aname, p, 4);
4042         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4043         return val;
4044 }
4045
4046 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4047         Molecule *mp;
4048         char *p = StringValuePtr(val);
4049         VALUE oval = s_AtomRef_GetAtomType(self);
4050         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4051         if (type != 0 && type < kAtomTypeMinimum)
4052                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4053         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4054         mp->needsMDRebuild = 1;
4055         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4056         return val;
4057 }
4058
4059 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4060         Molecule *mp;
4061         VALUE oval = s_AtomRef_GetCharge(self);
4062         val = rb_Float(val);
4063         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4064         mp->needsMDRebuild = 1;
4065         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4066         return val;
4067 }
4068
4069 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4070         Molecule *mp;
4071         VALUE oval = s_AtomRef_GetWeight(self);
4072         val = rb_Float(val);
4073         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4074         mp->needsMDRebuild = 1;
4075         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4076         return val;
4077 }
4078
4079 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4080         Double w;
4081         Molecule *mp;
4082         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4083         char *p = StringValuePtr(val);
4084         VALUE oval = s_AtomRef_GetElement(self);
4085         ap->atomicNumber = ElementToInt(p);
4086         ElementToString(ap->atomicNumber, ap->element);
4087         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4088                 ap->weight = w;
4089         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4090         mp->needsMDRebuild = 1;
4091         return val;
4092 }
4093
4094 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4095         Double w;
4096         Molecule *mp;
4097         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4098         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4099         val = rb_Integer(val);
4100         ap->atomicNumber = NUM2INT(val);
4101         ElementToString(ap->atomicNumber, ap->element);
4102         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4103                 ap->weight = w;
4104         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4105         mp->needsMDRebuild = 1;
4106         return val;
4107 }
4108
4109 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4110         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4111         return val; /* Not reached */
4112 }
4113
4114 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4115         Vector v;
4116         Molecule *mp;
4117         VALUE oval = s_AtomRef_GetR(self);
4118         VectorFromValue(val, &v);
4119         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4120         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4121         mp->needsMDCopyCoordinates = 1;
4122         return val;
4123 }
4124
4125 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4126         Double f;
4127         Molecule *mp;
4128         VALUE oval = s_AtomRef_GetX(self);
4129         val = rb_Float(val);
4130         f = NUM2DBL(val);
4131         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4132         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4133         mp->needsMDCopyCoordinates = 1;
4134         return val;
4135 }
4136
4137 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4138         Double f;
4139         Molecule *mp;
4140         VALUE oval = s_AtomRef_GetY(self);
4141         val = rb_Float(val);
4142         f = NUM2DBL(val);
4143         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4144         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4145         mp->needsMDCopyCoordinates = 1;
4146         return val;
4147 }
4148
4149 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4150         Double f;
4151         Molecule *mp;
4152         VALUE oval = s_AtomRef_GetZ(self);
4153         val = rb_Float(val);
4154         f = NUM2DBL(val);
4155         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4156         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4157         mp->needsMDCopyCoordinates = 1;
4158         return val;
4159 }
4160
4161 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4162         Vector v, ov;
4163         Atom *ap;
4164         Molecule *mp;
4165         s_AtomIndexFromValue(self, &ap, &mp);
4166         ov = ap->r;
4167         VectorFromValue(val, &v);
4168         if (mp->cell != NULL)
4169                 TransformVec(&v, mp->cell->tr, &v);
4170         ap->r = v;
4171         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4172         mp->needsMDCopyCoordinates = 1;
4173         return val;
4174 }
4175
4176 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4177         double f;
4178         Vector v, ov;
4179         Atom *ap;
4180         Molecule *mp;
4181         s_AtomIndexFromValue(self, &ap, &mp);
4182         ov = v = ap->r;
4183         val = rb_Float(val);
4184         f = NUM2DBL(val);
4185         if (mp->cell != NULL) {
4186                 TransformVec(&v, mp->cell->rtr, &v);
4187                 v.x = f;
4188                 TransformVec(&v, mp->cell->tr, &v);
4189         } else v.x = f;
4190         ap->r = v;
4191         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4192         mp->needsMDCopyCoordinates = 1;
4193         return val;
4194 }
4195
4196 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4197         double f;
4198         Vector v, ov;
4199         Atom *ap;
4200         Molecule *mp;
4201         s_AtomIndexFromValue(self, &ap, &mp);
4202         ov = v = ap->r;
4203         val = rb_Float(val);
4204         f = NUM2DBL(val);
4205         if (mp->cell != NULL) {
4206                 TransformVec(&v, mp->cell->rtr, &v);
4207                 v.y = f;
4208                 TransformVec(&v, mp->cell->tr, &v);
4209         } else v.y = f;
4210         ap->r = v;
4211         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4212         mp->needsMDCopyCoordinates = 1;
4213         return val;
4214 }
4215
4216 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4217         double f;
4218         Vector v, ov;
4219         Atom *ap;
4220         Molecule *mp;
4221         s_AtomIndexFromValue(self, &ap, &mp);
4222         ov = v = ap->r;
4223         val = rb_Float(val);
4224         f = NUM2DBL(val);
4225         if (mp->cell != NULL) {
4226                 TransformVec(&v, mp->cell->rtr, &v);
4227                 v.z = f;
4228                 TransformVec(&v, mp->cell->tr, &v);
4229         } else v.z = f;
4230         ap->r = v;
4231         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4232         mp->needsMDCopyCoordinates = 1;
4233         return val;
4234 }
4235
4236 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4237         Vector v;
4238         Molecule *mp;
4239         VALUE oval = s_AtomRef_GetSigma(self);
4240         VectorFromValue(val, &v);
4241         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4242         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4243         mp->needsMDCopyCoordinates = 1;
4244         return val;
4245 }
4246
4247 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4248         Double f;
4249         Molecule *mp;
4250         VALUE oval = s_AtomRef_GetSigmaX(self);
4251         val = rb_Float(val);
4252         f = NUM2DBL(val);
4253         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4254         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4255         mp->needsMDCopyCoordinates = 1;
4256         return val;
4257 }
4258
4259 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4260         Double f;
4261         Molecule *mp;
4262         VALUE oval = s_AtomRef_GetSigmaY(self);
4263         val = rb_Float(val);
4264         f = NUM2DBL(val);
4265         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4266         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4267         mp->needsMDCopyCoordinates = 1;
4268         return val;
4269 }
4270
4271 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4272         Double f;
4273         Molecule *mp;
4274         VALUE oval = s_AtomRef_GetSigmaZ(self);
4275         val = rb_Float(val);
4276         f = NUM2DBL(val);
4277         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4278         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4279         mp->needsMDCopyCoordinates = 1;
4280         return val;
4281 }
4282
4283 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4284         Vector v;
4285         Atom *ap;
4286         Molecule *mp;
4287         VALUE oval = s_AtomRef_GetV(self);
4288         VectorFromValue(val, &v);
4289         s_AtomIndexFromValue(self, &ap, &mp);
4290         ap->v = v;
4291         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4292         mp->needsMDCopyCoordinates = 1;
4293         return val;
4294 }
4295
4296 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4297         Vector v;
4298         Molecule *mp;
4299         VALUE oval = s_AtomRef_GetF(self);
4300         VectorFromValue(val, &v);
4301         VecScaleSelf(v, KCAL2INTERNAL);
4302         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4303         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4304         mp->needsMDCopyCoordinates = 1;
4305         return val;
4306 }
4307
4308 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4309         VALUE oval = s_AtomRef_GetOccupancy(self);
4310         Molecule *mp;
4311         val = rb_Float(val);
4312         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4313         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4314         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4315         return val;
4316 }
4317
4318 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4319         VALUE oval = s_AtomRef_GetTempFactor(self);
4320         val = rb_Float(val);
4321         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4322         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4323         return val;
4324 }
4325
4326 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4327         AtomRef *aref;
4328         int i, n, type;
4329         VALUE *valp;
4330         Double f[12];
4331         VALUE oval = s_AtomRef_GetAniso(self);
4332         Data_Get_Struct(self, AtomRef, aref);
4333         val = rb_funcall(val, rb_intern("to_a"), 0);
4334         n = RARRAY_LEN(val);
4335         valp = RARRAY_PTR(val);
4336         for (i = 0; i < 6; i++) {
4337                 if (i < n)
4338                         f[i] = NUM2DBL(rb_Float(valp[i]));
4339                 else f[i] = 0.0;
4340         }
4341         if (n >= 7)
4342                 type = NUM2INT(rb_Integer(valp[6]));
4343         else type = 0;
4344         if (n >= 13) {
4345                 for (i = 0; i < 6; i++)
4346                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4347         } else {
4348                 for (i = 0; i < 6; i++)
4349                         f[i + 6] = 0.0;
4350         }
4351         i = s_AtomIndexFromValue(self, NULL, NULL);
4352         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4353         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4354         return val;
4355 }
4356
4357 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4358         Molecule *mol;
4359         Atom *ap;
4360         int i, n;
4361         VALUE *valp;
4362         Int ival[5];
4363         VALUE oval = s_AtomRef_GetSymop(self);
4364         i = s_AtomIndexFromValue(self, &ap, &mol);
4365         if (val == Qnil) {
4366                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4367         } else {
4368                 val = rb_funcall(val, rb_intern("to_a"), 0);
4369                 n = RARRAY_LEN(val);
4370                 valp = RARRAY_PTR(val);
4371                 for (i = 0; i < 5; i++) {
4372                         if (i < n) {
4373                                 if (valp[i] == Qnil)
4374                                         ival[i] = -100000;
4375                                 else 
4376                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4377                         } else ival[i] = -100000;
4378                 }
4379         }
4380         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4381                 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));
4382         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4383                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4384         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4385                 return val;  /*  No need to change  */
4386         if (ival[0] != -100000)
4387                 ap->symop.sym = ival[0];
4388         if (ival[1] != -100000)
4389                 ap->symop.dx = ival[1];
4390         if (ival[2] != -100000)
4391                 ap->symop.dy = ival[2];
4392         if (ival[3] != -100000)
4393                 ap->symop.dz = ival[3];
4394         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4395         if (ival[4] != -100000)
4396                 ap->symbase = ival[4];
4397         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4398                 /*  The anisotropic parameters should be recalculated  */
4399                 VALUE oaval = s_AtomRef_GetAniso(self);
4400                 MoleculeSetAnisoBySymop(mol, i);
4401                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4402         }
4403         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4404         return val;
4405 }
4406
4407 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4408         VALUE oval = s_AtomRef_GetIntCharge(self);
4409         val = rb_Integer(val);
4410         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4411         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4412         return val;
4413 }
4414
4415 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4416         Molecule *mp;
4417         VALUE oval = s_AtomRef_GetFixForce(self);
4418         val = rb_Float(val);
4419         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4420         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4421         mp->needsMDRebuild = 1;
4422         return val;
4423 }
4424
4425 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4426         Vector v;
4427         Molecule *mp;
4428         VALUE oval = s_AtomRef_GetFixPos(self);
4429         VectorFromValue(val, &v);
4430         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4431         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4432         mp->needsMDRebuild = 1;
4433         return val;
4434 }
4435
4436 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4437         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4438         return val; /* Not reached */
4439 }
4440
4441 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4442         VALUE oval = s_AtomRef_GetIntCharge(self);
4443         val = rb_Integer(val);
4444         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4445         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4446         return val;
4447 }
4448
4449 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4450         VALUE oval = s_AtomRef_GetIntCharge(self);
4451         val = rb_Integer(val);
4452         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4453         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4454         return val;
4455 }
4456
4457 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4458         Atom *ap = s_AtomFromValue(self);
4459         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4460         if (RTEST(val)) {
4461                 ap->exflags |= kAtomHiddenFlag;
4462         } else {
4463                 ap->exflags &= ~kAtomHiddenFlag;
4464         }
4465         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4466         return val;
4467 }
4468
4469 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4470         Int idx, i, j, k, n, *ip;
4471         Double *dp;
4472         Atom *ap;
4473         Molecule *mol;
4474         VALUE oval, v;
4475         AtomConnect ac;
4476         Int nUndoActions;
4477         MolAction **undoActions;
4478         memset(&ac, 0, sizeof(ac));
4479         idx = s_AtomIndexFromValue(self, &ap, &mol);
4480         oval = s_AtomRef_GetAnchorList(self);
4481         if (val != Qnil) {
4482                 val = rb_ary_to_ary(val);
4483                 n = RARRAY_LEN(val);
4484         } else n = 0;
4485         if (n == 0) {
4486                 if (ap->anchor != NULL) {
4487                         AtomConnectResize(&ap->anchor->connect, 0);
4488                         free(ap->anchor->coeffs);
4489                         free(ap->anchor);
4490                         ap->anchor = NULL;
4491                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4492                 }
4493                 return val;
4494         }
4495         if (n < 2)
4496                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4497         if (ap->aname[0] == '_')
4498                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4499         ip = (Int *)malloc(sizeof(Int) * n);
4500         dp = NULL;
4501         for (i = 0; i < n; i++) {
4502                 v = RARRAY_PTR(val)[i];
4503                 if (rb_obj_is_kind_of(v, rb_cFloat))
4504                         break;
4505                 j = NUM2INT(rb_Integer(v));
4506                 if (j < 0 || j >= mol->natoms)
4507                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4508                 for (k = 0; k < i; k++) {
4509                         if (ip[k] == j)
4510                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4511                 }
4512                 ip[i] = j;
4513         }
4514         if (i < n) {
4515                 if (i < 2)
4516                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4517                 else if (i * 2 != n)
4518                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4519                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4520                 for (i = 0; i < n / 2; i++) {
4521                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4522                         if (dp[i] <= 0.0)
4523                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4524                 }
4525                 n /= 2;
4526         }
4527         nUndoActions = 0;
4528         undoActions = NULL;
4529         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4530         free(dp);
4531         free(ip);
4532         if (i != 0)
4533                 rb_raise(rb_eMolbyError, "invalid argument");
4534         if (nUndoActions > 0) {
4535                 for (i = 0; i < nUndoActions; i++) {
4536                         MolActionCallback_registerUndo(mol, undoActions[i]);
4537                         MolActionRelease(undoActions[i]);
4538                 }
4539                 free(undoActions);
4540         }
4541         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4542         return val;
4543 }
4544
4545 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4546         Atom *ap = s_AtomFromValue(self);
4547         char *p = StringValuePtr(val);
4548         VALUE oval = s_AtomRef_GetUFFType(self);
4549         strncpy(ap->uff_type, p, 5);
4550         ap->uff_type[5] = 0;
4551         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4552         return val;
4553 }
4554
4555 static struct s_AtomAttrDef {
4556         char *name;
4557         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4558         ID id;                  /*  Will be set within InitMolby()  */
4559         VALUE (*getter)(VALUE);
4560         VALUE (*setter)(VALUE, VALUE);
4561 } s_AtomAttrDefTable[] = {
4562         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4563         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4564         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4565         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4566         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4567         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4568         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4569         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4570         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4571         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4572         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4573         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4574         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4575         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4576         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4577     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4578         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4579         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4580         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4581         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4582         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4583         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4584         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4585         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4586         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4587         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4588         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4589         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4590         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4591         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4592         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4593         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4594         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4595         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4596         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4597         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4598         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4599         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4600         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4601         {NULL} /* Sentinel */
4602 };
4603
4604 static VALUE
4605 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4606 {
4607         int i;
4608         ID kid;
4609         if (TYPE(key) != T_SYMBOL) {
4610                 kid = rb_intern(StringValuePtr(key));
4611                 key = ID2SYM(kid);
4612         } else kid = SYM2ID(key);
4613         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4614                 if (s_AtomAttrDefTable[i].id == kid) {
4615                         if (value == Qundef)
4616                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4617                         else
4618                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4619                 }
4620         }
4621         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4622         return Qnil; /* not reached */
4623 }
4624
4625 static VALUE
4626 s_AtomRef_GetAttr(VALUE self, VALUE key)
4627 {
4628         return s_AtomRef_SetAttr(self, key, Qundef);
4629 }
4630
4631 /*
4632  *  call-seq:
4633  *     self == atomRef -> boolean
4634  *
4635  *  True if the two references point to the same atom.
4636  */
4637 static VALUE
4638 s_AtomRef_Equal(VALUE self, VALUE val)
4639 {
4640         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4641                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4642         } else return Qfalse;
4643 }
4644
4645 #pragma mark ====== MolEnumerable Class ======
4646
4647 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4648
4649 /*
4650  *  call-seq:
4651  *     self[idx] -> AtomRef or Array of Integers
4652  *  
4653  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4654  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4655  *  value is a String. Otherwise, the return value is an Array of Integers.
4656  */
4657 static VALUE
4658 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4659 {
4660         MolEnumerable *mseq;
4661         Molecule *mol;
4662         int idx1, idx2;
4663     Data_Get_Struct(self, MolEnumerable, mseq);
4664         mol = mseq->mol;
4665         if (mseq->kind == kAtomKind) {
4666                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4667         }
4668         idx1 = NUM2INT(arg1);
4669         switch (mseq->kind) {
4670                 case kBondKind: {
4671                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4672                         if (idx2 < 0 || idx2 >= mol->nbonds)
4673                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4674                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4675                 }
4676                 case kAngleKind: {
4677                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4678                         if (idx2 < 0 || idx2 >= mol->nangles)
4679                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4680                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4681                 }
4682                 case kDihedralKind: {
4683                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4684                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4685                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4686                         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]));
4687                 }
4688                 case kImproperKind: {
4689                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4690                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4691                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4692                         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]));
4693                 }
4694                 case kResidueKind: {
4695                         char *p;
4696                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4697                         if (idx2 < 0 || idx2 >= mol->nresidues)
4698                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4699                         p = mol->residues[idx2];
4700                         return rb_str_new(p, strlen_limit(p, 4));
4701                 }
4702         }
4703         return Qnil;
4704 }
4705
4706 /*
4707  *  call-seq:
4708  *     length          -> Integer
4709  *  
4710  *  Returns the number of objects included in this enumerable.
4711  */
4712 static VALUE
4713 s_MolEnumerable_Length(VALUE self)
4714 {
4715         MolEnumerable *mseq;
4716     Data_Get_Struct(self, MolEnumerable, mseq);
4717         switch (mseq->kind) {
4718                 case kAtomKind:
4719                         return INT2NUM(mseq->mol->natoms);
4720                 case kBondKind:
4721                         return INT2NUM(mseq->mol->nbonds);
4722                 case kAngleKind:
4723                         return INT2NUM(mseq->mol->nangles);
4724                 case kDihedralKind:
4725                         return INT2NUM(mseq->mol->ndihedrals);
4726                 case kImproperKind:
4727                         return INT2NUM(mseq->mol->nimpropers);
4728                 case kResidueKind:
4729                         return INT2NUM(mseq->mol->nresidues);
4730         }
4731         return INT2NUM(-1);
4732 }
4733
4734 /*
4735  *  call-seq:
4736  *     each {|obj| ...}
4737  *  
4738  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4739  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4740  *  For the atoms, a same AtomRef object is passed (with different internal information)
4741  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4742  *  for each iteration.
4743  */
4744 VALUE
4745 s_MolEnumerable_Each(VALUE self)
4746 {
4747         MolEnumerable *mseq;
4748         int i;
4749         int len = NUM2INT(s_MolEnumerable_Length(self));
4750     Data_Get_Struct(self, MolEnumerable, mseq);
4751         if (mseq->kind == kAtomKind) {
4752                 /*  The same AtomRef object will be used during the loop  */
4753                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4754                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4755                 for (i = 0; i < len; i++) {
4756                         aref->idx = i;
4757                         rb_yield(arval);
4758                 }
4759     } else {
4760                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4761                 for (i = 0; i < len; i++) {
4762                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4763                 }
4764         }
4765     return self;
4766 }
4767
4768 /*
4769  *  call-seq:
4770  *     self == molEnumerable -> boolean
4771  *
4772  *  True if the two arguments point to the same molecule and enumerable type.
4773  */
4774 static VALUE
4775 s_MolEnumerable_Equal(VALUE self, VALUE val)
4776 {
4777         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4778                 MolEnumerable *mseq1, *mseq2;
4779                 Data_Get_Struct(self, MolEnumerable, mseq1);
4780                 Data_Get_Struct(val, MolEnumerable, mseq2);
4781                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4782         } else return Qfalse;
4783 }
4784
4785
4786 #pragma mark ====== Molecule Class ======
4787
4788 #pragma mark ------ Allocate/Release/Accessor ------
4789
4790 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method.  */
4791 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4792 char *gLoadSaveErrorMessage = NULL;
4793
4794 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4795
4796 Molecule *
4797 MoleculeFromValue(VALUE val)
4798 {
4799         Molecule *mol;
4800         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4801                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4802     Data_Get_Struct(val, Molecule, mol);
4803         return mol;
4804 }
4805
4806 static VALUE sMoleculeRetainArray = Qnil;
4807
4808 /*  The function is called from MoleculeRelease()  */
4809 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4810 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4811 /*  object is always returned for the same Molecule.  */
4812 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4813 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4814 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4815 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4816 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4817
4818 /*  Register/unregister the exmolobj Ruby object  */
4819 void
4820 MoleculeReleaseExternalObj(Molecule *mol)
4821 {
4822         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4823                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4824                 mol->exmolobjProtected = 0;
4825         }
4826 }
4827
4828 void
4829 MoleculeRetainExternalObj(Molecule *mol)
4830 {
4831         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4832                 if (sMoleculeRetainArray == Qnil) {
4833                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4834                         sMoleculeRetainArray = rb_ary_new();
4835                 }
4836                 
4837                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4838                 mol->exmolobjProtected = 1;
4839         }
4840 }
4841
4842 /*  Release hook function for Ruby  */
4843 void
4844 MoleculeReleaseHook(Molecule *mol)
4845 {
4846         if (mol->exmolobj != NULL) {
4847                 /*  No need to remove from sMoleculeRetainArray  */
4848                 mol->exmolobj = NULL;
4849                 mol->exmolobjProtected = 0;
4850         }
4851         MoleculeRelease(mol);
4852 }
4853
4854 VALUE
4855 ValueFromMolecule(Molecule *mol)
4856 {
4857         if (mol == NULL)
4858                 return Qnil;
4859         if (mol->exmolobj != NULL)
4860                 return (VALUE)mol->exmolobj;
4861         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4862         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4863         return (VALUE)mol->exmolobj;
4864 }
4865
4866 /*  Allocator  */
4867 static VALUE
4868 s_Molecule_Alloc(VALUE klass)
4869 {
4870         VALUE val;
4871         Molecule *mol = MoleculeNew();
4872         val = ValueFromMolecule(mol);
4873         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4874         return val;
4875 }
4876
4877 static int
4878 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4879 {
4880         int n;
4881         char *p = "";
4882         if (FIXNUM_P(val)) {
4883                 n = FIX2INT(val);
4884                 if (n >= 0 && n < mol->natoms)
4885                         return n;
4886                 n = -1; /*  No such atom  */
4887                 val = rb_inspect(val);
4888         } else {
4889                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4890         }
4891         if (n >= 0 && n < mol->natoms)
4892                 return n;
4893         p = StringValuePtr(val);
4894         if (n == -1)
4895                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4896         else if (n == -2)
4897                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4898         else
4899                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4900         return 0; /* Not reached */
4901 }
4902
4903 static IntGroup *
4904 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4905 {
4906         IntGroup *ig;
4907         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4908         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4909                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4910         Data_Get_Struct(val, IntGroup, ig);
4911         IntGroupRetain(ig);
4912         return ig;
4913 }
4914
4915 /*
4916  *  call-seq:
4917  *     dup          -> Molecule
4918  *
4919  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
4920  *  created object does not affect the old object in any sense.
4921  */
4922 static VALUE
4923 s_Molecule_InitCopy(VALUE self, VALUE arg)
4924 {
4925         Molecule *mp1, *mp2;
4926         Data_Get_Struct(self, Molecule, mp1);
4927         mp2 = MoleculeFromValue(arg);
4928         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4929                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4930         return self;
4931 }
4932
4933 /*
4934  *  call-seq:
4935  *     atom_index(val)       -> Integer
4936  *
4937  *  Returns the atom index represented by val. val can be either a non-negative integer
4938  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4939  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
4940  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
4941  *  If val is a string and multiple atoms match the description, the atom with the lowest index
4942  *  is returned.
4943  */
4944 static VALUE
4945 s_Molecule_AtomIndex(VALUE self, VALUE val)
4946 {
4947     Molecule *mol;
4948     Data_Get_Struct(self, Molecule, mol);
4949         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4950 }
4951
4952 /*
4953  *  call-seq:
4954  *     self == Molecule -> boolean
4955  *
4956  *  True if the two arguments point to the same molecule.
4957  */
4958 static VALUE
4959 s_Molecule_Equal(VALUE self, VALUE val)
4960 {
4961         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4962                 Molecule *mol1, *mol2;
4963                 Data_Get_Struct(self, Molecule, mol1);
4964                 Data_Get_Struct(val, Molecule, mol2);
4965                 return (mol1 == mol2 ? Qtrue : Qfalse);
4966         } else return Qfalse;
4967 }
4968
4969 #pragma mark ------ Load/Save ------
4970
4971 static void
4972 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
4973 {
4974         if (gLoadSaveErrorMessage != NULL) {
4975                 MyAppCallback_setConsoleColor(1);
4976                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
4977                 MyAppCallback_setConsoleColor(0);
4978         }
4979         if (status != 0)
4980                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4981 }
4982
4983 /*
4984  *  call-seq:
4985  *     loadmbsf(file)       -> bool
4986  *
4987  *  Read a structure from a mbsf file.
4988  *  Return true if successful.
4989  */
4990 static VALUE
4991 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4992 {
4993         VALUE fname;
4994         char *fstr;
4995         Molecule *mol;
4996         int retval;
4997         MoleculeClearLoadSaveErrorMessage();
4998         Data_Get_Struct(self, Molecule, mol);
4999         rb_scan_args(argc, argv, "1", &fname);
5000         fstr = FileStringValuePtr(fname);
5001         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5002         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5003         return Qtrue;   
5004 }
5005
5006 /*
5007  *  call-seq:
5008  *     loadpsf(file, pdbfile = nil)       -> bool
5009  *
5010  *  Read a structure from a psf file. molecule must be empty. The psf may be
5011  *  an "extended" version, which also contains coordinates. If pdbfile 
5012  *  is given, then atomic coordinates are read from that file.
5013  *  Return true if successful.
5014  */
5015 static VALUE
5016 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5017 {
5018         VALUE fname, pdbname;
5019         char *fstr, *pdbstr;
5020         Molecule *mol;
5021         int retval;
5022         Data_Get_Struct(self, Molecule, mol);
5023         if (mol->natoms > 0)
5024                 return Qnil;  /*  Must be a new molecule  */
5025         MoleculeClearLoadSaveErrorMessage();
5026         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5027         fstr = FileStringValuePtr(fname);
5028         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5029         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5030         pdbstr = NULL;
5031         if (!NIL_P(pdbname)) {
5032                 pdbstr = strdup(FileStringValuePtr(pdbname));
5033                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5034                 free(pdbstr);
5035                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5036         }
5037         return Qtrue;
5038 }
5039
5040 /*
5041  *  call-seq:
5042  *     loadpdb(file)       -> bool
5043  *
5044  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5045  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5046  *  Return true if successful.
5047  */
5048 static VALUE
5049 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5050 {
5051         VALUE fname;
5052         char *fstr;
5053         Molecule *mol;
5054         int retval;
5055         Data_Get_Struct(self, Molecule, mol);
5056         rb_scan_args(argc, argv, "1", &fname);
5057         MoleculeClearLoadSaveErrorMessage();
5058         fstr = FileStringValuePtr(fname);
5059         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5060         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5061         return Qtrue;   
5062 }
5063
5064 /*
5065  *  call-seq:
5066  *     loaddcd(file)       -> bool
5067  *
5068  *  Read coordinates from a dcd file. The molecule should not empty.
5069  *  Return true if successful.
5070  */
5071 static VALUE
5072 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5073 {
5074         VALUE fname;
5075         char *fstr;
5076         Molecule *mol;
5077         int retval;
5078         Data_Get_Struct(self, Molecule, mol);
5079         rb_scan_args(argc, argv, "1", &fname);
5080         MoleculeClearLoadSaveErrorMessage();
5081         fstr = FileStringValuePtr(fname);
5082         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5083         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5084         return Qtrue;   
5085 }
5086
5087 /*
5088  *  call-seq:
5089  *     loadtep(file)       -> bool
5090  *
5091  *  Read coordinates from an ortep .tep file.
5092  *  Return true if successful.
5093  */
5094 static VALUE
5095 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5096 {
5097         VALUE fname;
5098         char *fstr;
5099         Molecule *mol;
5100         int retval;
5101         Data_Get_Struct(self, Molecule, mol);
5102         rb_scan_args(argc, argv, "1", &fname);
5103         MoleculeClearLoadSaveErrorMessage();
5104         fstr = FileStringValuePtr(fname);
5105         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5106         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5107         return Qtrue;   
5108 }
5109
5110 /*
5111  *  call-seq:
5112  *     loadres(file)       -> bool
5113  *
5114  *  Read coordinates from a shelx .res file.
5115  *  Return true if successful.
5116  */
5117 static VALUE
5118 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5119 {
5120         VALUE fname;
5121         char *fstr;
5122         Molecule *mol;
5123         int retval;
5124         Data_Get_Struct(self, Molecule, mol);
5125         rb_scan_args(argc, argv, "1", &fname);
5126         MoleculeClearLoadSaveErrorMessage();
5127         fstr = FileStringValuePtr(fname);
5128         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5129         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5130         return Qtrue;   
5131 }
5132
5133 /*
5134  *  call-seq:
5135  *     loadfchk(file)       -> bool
5136  *
5137  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5138  *  Return true if successful.
5139  */
5140 static VALUE
5141 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5142 {
5143         VALUE fname;
5144         char *fstr;
5145         Molecule *mol;
5146         int retval;
5147         Data_Get_Struct(self, Molecule, mol);
5148         rb_scan_args(argc, argv, "1", &fname);
5149         MoleculeClearLoadSaveErrorMessage();
5150         fstr = FileStringValuePtr(fname);
5151         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5152         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5153         return Qtrue;   
5154 }
5155
5156 /*
5157  *  call-seq:
5158  *     loaddat(file)       -> bool
5159  *
5160  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5161  *  Return true if successful.
5162  */
5163 static VALUE
5164 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5165 {
5166         VALUE fname;
5167         char *fstr;
5168         Molecule *mol;
5169         int retval;
5170         Data_Get_Struct(self, Molecule, mol);
5171         rb_scan_args(argc, argv, "1", &fname);
5172         MoleculeClearLoadSaveErrorMessage();
5173         fstr = FileStringValuePtr(fname);
5174         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5175         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5176         MyAppCallback_hideProgressPanel();
5177         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5178         return Qtrue;   
5179 }
5180
5181 /*
5182  *  call-seq:
5183  *     savembsf(file)       -> bool
5184  *
5185  *  Write structure as a mbsf file. Returns true if successful.
5186  */
5187 static VALUE
5188 s_Molecule_Savembsf(VALUE self, VALUE fname)
5189 {
5190         char *fstr;
5191     Molecule *mol;
5192         int retval;
5193     Data_Get_Struct(self, Molecule, mol);
5194         MoleculeClearLoadSaveErrorMessage();
5195         fstr = FileStringValuePtr(fname);
5196         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5197         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5198         return Qtrue;
5199 }
5200
5201 /*
5202  *  call-seq:
5203  *     savepsf(file)       -> bool
5204  *
5205  *  Write structure as a psf file. Returns true if successful.
5206  */
5207 static VALUE
5208 s_Molecule_Savepsf(VALUE self, VALUE fname)
5209 {
5210         char *fstr;
5211     Molecule *mol;
5212         int retval;
5213     Data_Get_Struct(self, Molecule, mol);
5214         MoleculeClearLoadSaveErrorMessage();
5215         fstr = FileStringValuePtr(fname);
5216         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5217         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5218         return Qtrue;
5219 }
5220
5221 /*
5222  *  call-seq:
5223  *     savepdb(file)       -> bool
5224  *
5225  *  Write coordinates as a pdb file. Returns true if successful.
5226  */
5227 static VALUE
5228 s_Molecule_Savepdb(VALUE self, VALUE fname)
5229 {
5230         char *fstr;
5231     Molecule *mol;
5232         int retval;
5233     Data_Get_Struct(self, Molecule, mol);
5234         MoleculeClearLoadSaveErrorMessage();
5235         fstr = FileStringValuePtr(fname);
5236         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5237         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5238         return Qtrue;
5239 }
5240
5241 /*
5242  *  call-seq:
5243  *     savedcd(file)       -> bool
5244  *
5245  *  Write coordinates as a dcd file. Returns true if successful.
5246  */
5247 static VALUE
5248 s_Molecule_Savedcd(VALUE self, VALUE fname)
5249 {
5250         char *fstr;
5251     Molecule *mol;
5252         int retval;
5253     Data_Get_Struct(self, Molecule, mol);
5254         MoleculeClearLoadSaveErrorMessage();
5255         fstr = FileStringValuePtr(fname);
5256         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5257         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5258         return Qtrue;
5259 }
5260
5261 /*  load([ftype, ] fname, ...)  */
5262 static VALUE
5263 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5264 {
5265         VALUE rval;
5266         char *argstr, *methname, *p, *type = "";
5267         ID mid = 0;
5268         int i;
5269         const char *ls = (loadFlag ? "load" : "save");
5270         int lslen = strlen(ls);
5271
5272         if (argc == 0)
5273                 return Qnil;
5274
5275         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5276                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5277         if (argstr[0] == ':') {
5278                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5279                 methname = ALLOC_N(char, lslen + strlen(argstr));
5280                 strcpy(methname, ls);
5281                 strcat(methname, argstr + 1);
5282                 type = argstr + 1;
5283                 for (i = lslen; methname[i] != 0; i++)
5284                         methname[i] = tolower(methname[i]);
5285                 mid = rb_intern(methname);
5286                 xfree(methname);
5287                 argc--;
5288                 argv++;
5289                 rval = rb_funcall2(self, mid, argc, argv);
5290                 if (rval == Qnil)
5291                         goto failure;
5292                 else
5293                         goto success;
5294         }
5295         /*  Guess file type from extension  */
5296         p = strrchr(argstr, '.');
5297         if (p != NULL) {
5298                 p++;
5299                 type = p;
5300                 for (methname = p; *methname != 0; methname++) {
5301                         if (!isalpha(*methname))
5302                                 break;
5303                 }
5304                 if (*methname == 0) {
5305                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5306                         if (methname == NULL)
5307                                 rb_raise(rb_eMolbyError, "Low memory");
5308                         strcpy(methname, ls);
5309                         strcat(methname, p);
5310                         for (i = lslen; methname[i] != 0; i++)
5311                                 methname[i] = tolower(methname[i]);
5312                         mid = rb_intern(methname);
5313                         xfree(methname);
5314                         if (loadFlag) {
5315                                 if (rb_respond_to(self, mid)) {
5316                                         /*  Load: try to call the load procedure only if it is available  */
5317                                         rval = rb_funcall2(self, mid, argc, argv);
5318                                         if (rval != Qnil)
5319                                                 goto success;
5320                                 }
5321                         } else {
5322                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5323                                 rval = rb_funcall2(self, mid, argc, argv);
5324                                 if (rval != Qnil)
5325                                         goto success;
5326                         }
5327                 }
5328         }
5329 failure:
5330         rval = rb_str_to_str(argv[0]);
5331         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5332         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5333         return Qnil;  /*  Does not reach here  */
5334
5335 success:
5336         {
5337                 /*  Register the path  */
5338                 Molecule *mol;
5339         /*      Atom *ap; */
5340                 Data_Get_Struct(self, Molecule, mol);
5341                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5342                 
5343                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5344         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5345                         if (ap->occupancy != 0.0)
5346                                 break;
5347                 }
5348                 if (i == mol->natoms) {
5349                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5350                                 ap->occupancy = 1.0;
5351                         }
5352                 } */
5353         }
5354         return rval;
5355 }
5356
5357 /*
5358  *  call-seq:
5359  *     molload(file, *args)       -> bool
5360  *
5361  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5362  *  file type given by the extension). If this method fails, then all defined (public)
5363  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5364  */
5365 static VALUE
5366 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5367 {
5368         return s_Molecule_LoadSave(argc, argv, self, 1);
5369 }
5370
5371 /*
5372  *  call-seq:
5373  *     molsave(file, *args)       -> bool
5374  *
5375  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5376  *  (XXX is the file type given by the extension).
5377  */
5378 static VALUE
5379 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5380 {
5381         return s_Molecule_LoadSave(argc, argv, self, 0);
5382 }
5383
5384 /*
5385  *  call-seq:
5386  *     open        -> Molecule
5387  *     open(file)  -> Molecule
5388  *
5389  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5390  */
5391 static VALUE
5392 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5393 {
5394         VALUE fname;
5395         const char *p;
5396         Molecule *mp;
5397         VALUE iflag;
5398         rb_scan_args(argc, argv, "01", &fname);
5399         if (NIL_P(fname))
5400                 p = NULL;
5401         else
5402                 p = FileStringValuePtr(fname);
5403         iflag = Ruby_SetInterruptFlag(Qfalse);
5404         mp = MoleculeCallback_openNewMolecule(p);
5405         Ruby_SetInterruptFlag(iflag);
5406         if (mp == NULL) {
5407                 if (p == NULL)
5408                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5409                 else
5410                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5411         }
5412         return ValueFromMolecule(mp);
5413 }
5414
5415 /*
5416  *  call-seq:
5417  *     new  -> Molecule
5418  *     new(file, *args)  -> Molecule
5419  *
5420  *  Create a new molecule and call "load" method with the same arguments.
5421  */
5422 static VALUE
5423 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5424 {
5425         if (argc > 0)
5426                 return s_Molecule_Load(argc, argv, self);
5427         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5428 }
5429
5430 /*
5431  *  call-seq:
5432  *     error_message       -> String
5433  *
5434  *  Get the error_message from the last load/save method. If no error, returns nil.
5435  */
5436 static VALUE
5437 s_Molecule_ErrorMessage(VALUE klass)
5438 {
5439         if (gLoadSaveErrorMessage == NULL)
5440                 return Qnil;
5441         else return rb_str_new2(gLoadSaveErrorMessage);
5442 }
5443
5444 /*
5445  *  call-seq:
5446  *     set_error_message(String)
5447  *     Molecule.error_message = String
5448  *
5449  *  Set the error_message for the present load/save method.
5450  */
5451 static VALUE
5452 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5453 {
5454         if (gLoadSaveErrorMessage != NULL) {
5455                 free(gLoadSaveErrorMessage);
5456                 gLoadSaveErrorMessage = NULL;
5457         }
5458         if (sval != Qnil) {
5459                 sval = rb_str_to_str(sval);
5460                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5461         }
5462         return sval;
5463 }
5464
5465 /*
5466  *  call-seq:
5467  *     set_molecule(Molecule)
5468  *
5469  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5470  *  This method is exclusively used for associating a new document with an existing molecule.
5471  */
5472 static VALUE
5473 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5474 {
5475         Molecule *mp1, *mp2;
5476         Data_Get_Struct(self, Molecule, mp1);
5477         mp2 = MoleculeFromValue(mval);
5478         MoleculeInitWithMolecule(mp1, mp2);
5479         MoleculeCallback_notifyModification(mp1, 1);
5480         return self;
5481 }
5482
5483 #pragma mark ------ Name attributes ------
5484
5485 /*
5486  *  call-seq:
5487  *     name       -> String
5488  *
5489  *  Returns the display name of the molecule. If the molecule has no associated
5490  *  document, then returns nil.
5491  */
5492 static VALUE
5493 s_Molecule_Name(VALUE self)
5494 {
5495     Molecule *mol;
5496         char buf[1024];
5497     Data_Get_Struct(self, Molecule, mol);
5498         MoleculeCallback_displayName(mol, buf, sizeof buf);
5499         if (buf[0] == 0)
5500                 return Qnil;
5501         else
5502                 return rb_str_new2(buf);
5503 }
5504
5505 /*
5506  *  call-seq:
5507  *     set_name(string) -> self
5508  *
5509  *  Set the name of an untitled molecule. If the molecule is not associated with window
5510  *  or it already has an associated file, then exception is thrown.
5511  */
5512 static VALUE
5513 s_Molecule_SetName(VALUE self, VALUE nval)
5514 {
5515     Molecule *mol;
5516     Data_Get_Struct(self, Molecule, mol);
5517         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5518                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5519         return self;
5520 }
5521
5522
5523 /*
5524  *  call-seq:
5525  *     path       -> String
5526  *
5527  *  Returns the full path name of the molecule, if it is associated with a file.
5528  *  If the molecule has no associated file, then returns nil.
5529  */
5530 static VALUE
5531 s_Molecule_Path(VALUE self)
5532 {
5533     Molecule *mol;
5534         char buf[1024];
5535     Data_Get_Struct(self, Molecule, mol);
5536         MoleculeCallback_pathName(mol, buf, sizeof buf);
5537         if (buf[0] == 0)
5538                 return Qnil;
5539         else
5540                 return Ruby_NewFileStringValue(buf);
5541 }
5542
5543 /*
5544  *  call-seq:
5545  *     dir       -> String
5546  *
5547  *  Returns the full path name of the directory in which the file associated with the
5548  *  molecule is located. If the molecule has no associated file, then returns nil.
5549  */
5550 static VALUE
5551 s_Molecule_Dir(VALUE self)
5552 {
5553     Molecule *mol;
5554         char buf[1024], *p;
5555     Data_Get_Struct(self, Molecule, mol);
5556         MoleculeCallback_pathName(mol, buf, sizeof buf);
5557 #if __WXMSW__
5558         translate_char(buf, '\\', '/');
5559 #endif
5560         if (buf[0] == 0)
5561                 return Qnil;
5562         else {
5563                 p = strrchr(buf, '/');
5564                 if (p != NULL)
5565                         *p = 0;
5566                 return rb_str_new2(buf);
5567         }
5568 }
5569
5570 /*
5571  *  call-seq:
5572  *     inspect       -> String
5573  *
5574  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5575  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5576  *  the Molecule structure) is returned.
5577  */
5578 static VALUE
5579 s_Molecule_Inspect(VALUE self)
5580 {
5581     Molecule *mol;
5582         char buf[256];
5583     Data_Get_Struct(self, Molecule, mol);
5584         MoleculeCallback_displayName(mol, buf, sizeof buf);
5585         if (buf[0] == 0) {
5586                 /*  No associated document  */
5587                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5588                 return rb_str_new2(buf);
5589         } else {
5590                 /*  Check whether the document name is duplicate  */
5591                 char buf2[256];
5592                 int idx, k, k2;
5593                 Molecule *mol2;
5594                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5595                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5596                         if (strcmp(buf, buf2) == 0) {
5597                                 k++;
5598                                 if (mol == mol2)
5599                                         k2 = k;
5600                         }
5601                 }
5602                 if (k > 1) {
5603                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5604                 } else {
5605                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5606                 }
5607                 return rb_str_new2(buf2);
5608         }
5609 }
5610
5611 #pragma mark ------ MolEnumerables ------
5612
5613 static VALUE
5614 s_Molecule_MolEnumerable(VALUE self, int kind)
5615 {
5616     Molecule *mol;
5617         MolEnumerable *mseq;
5618     Data_Get_Struct(self, Molecule, mol);
5619         mseq = MolEnumerableNew(mol, kind);
5620         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5621 }
5622
5623 /*
5624  *  call-seq:
5625  *     atoms       -> MolEnumerable
5626  *
5627  *  Returns a MolEnumerable object representing the array of atoms.
5628  */
5629 static VALUE
5630 s_Molecule_Atoms(VALUE self)
5631 {
5632         return s_Molecule_MolEnumerable(self, kAtomKind);
5633 }
5634
5635 /*
5636  *  call-seq:
5637  *     bonds       -> MolEnumerable
5638  *
5639  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5640  *  by an array of two atom indices.
5641  */
5642 static VALUE
5643 s_Molecule_Bonds(VALUE self)
5644 {
5645         return s_Molecule_MolEnumerable(self, kBondKind);
5646 }
5647
5648 /*
5649  *  call-seq:
5650  *     angles       -> MolEnumerable
5651  *
5652  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5653  *  by an array of three atom indices.
5654  */
5655 static VALUE
5656 s_Molecule_Angles(VALUE self)
5657 {
5658         return s_Molecule_MolEnumerable(self, kAngleKind);
5659 }
5660
5661 /*
5662  *  call-seq:
5663  *     dihedrals       -> MolEnumerable
5664  *
5665  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5666  *  by an array of four atom indices.
5667  */
5668 static VALUE
5669 s_Molecule_Dihedrals(VALUE self)
5670 {
5671         return s_Molecule_MolEnumerable(self, kDihedralKind);
5672 }
5673
5674 /*
5675  *  call-seq:
5676  *     impropers       -> MolEnumerable
5677  *
5678  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5679  *  by an array of four atom indices.
5680  */
5681 static VALUE
5682 s_Molecule_Impropers(VALUE self)
5683 {
5684         return s_Molecule_MolEnumerable(self, kImproperKind);
5685 }
5686
5687 /*
5688  *  call-seq:
5689  *     residues       -> MolEnumerable
5690  *
5691  *  Returns a MolEnumerable object representing the array of residue names.
5692  */
5693 static VALUE
5694 s_Molecule_Residues(VALUE self)
5695 {
5696         return s_Molecule_MolEnumerable(self, kResidueKind);
5697 }
5698
5699 /*
5700  *  call-seq:
5701  *     natoms       -> Integer
5702  *
5703  *  Returns the number of atoms.
5704  */
5705 static VALUE
5706 s_Molecule_Natoms(VALUE self)
5707 {
5708     Molecule *mol;
5709     Data_Get_Struct(self, Molecule, mol);
5710         return INT2NUM(mol->natoms);
5711 }
5712
5713 /*
5714  *  call-seq:
5715  *     nbonds       -> Integer
5716  *
5717  *  Returns the number of bonds.
5718  */
5719 static VALUE
5720 s_Molecule_Nbonds(VALUE self)
5721 {
5722     Molecule *mol;
5723     Data_Get_Struct(self, Molecule, mol);
5724         return INT2NUM(mol->nbonds);
5725 }
5726
5727 /*
5728  *  call-seq:
5729  *     nangles       -> Integer
5730  *
5731  *  Returns the number of angles.
5732  */
5733 static VALUE
5734 s_Molecule_Nangles(VALUE self)
5735 {
5736     Molecule *mol;
5737     Data_Get_Struct(self, Molecule, mol);
5738         return INT2NUM(mol->nangles);
5739 }
5740
5741 /*
5742  *  call-seq:
5743  *     ndihedrals       -> Integer
5744  *
5745  *  Returns the number of dihedrals.
5746  */
5747 static VALUE
5748 s_Molecule_Ndihedrals(VALUE self)
5749 {
5750     Molecule *mol;
5751     Data_Get_Struct(self, Molecule, mol);
5752         return INT2NUM(mol->ndihedrals);
5753 }
5754
5755 /*
5756  *  call-seq:
5757  *     nimpropers       -> Integer
5758  *
5759  *  Returns the number of impropers.
5760  */
5761 static VALUE
5762 s_Molecule_Nimpropers(VALUE self)
5763 {
5764     Molecule *mol;
5765     Data_Get_Struct(self, Molecule, mol);
5766         return INT2NUM(mol->nimpropers);
5767 }
5768
5769 /*
5770  *  call-seq:
5771  *     nresidues       -> Integer
5772  *
5773  *  Returns the number of residues.
5774  */
5775 static VALUE
5776 s_Molecule_Nresidues(VALUE self)
5777 {
5778     Molecule *mol;
5779     Data_Get_Struct(self, Molecule, mol);
5780         return INT2NUM(mol->nresidues);
5781 }
5782
5783 /*
5784  *  call-seq:
5785  *     nresidues = Integer
5786  *
5787  *  Change the number of residues.
5788  */
5789 static VALUE
5790 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5791 {
5792     Molecule *mol;
5793         int ival = NUM2INT(val);
5794     Data_Get_Struct(self, Molecule, mol);
5795         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5796         if (ival != mol->nresidues)
5797                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5798         return val;
5799 }
5800
5801 /*
5802  *  call-seq:
5803  *     max_residue_number(atom_group = nil)     -> Integer
5804  *
5805  *  Returns the maximum residue number actually used. If an atom group is given, only
5806  *  these atoms are examined. If no atom is present, nil is returned.
5807  */
5808 static VALUE
5809 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5810 {
5811     Molecule *mol;
5812         VALUE gval;
5813         int maxSeq;
5814         IntGroup *ig;
5815     Data_Get_Struct(self, Molecule, mol);
5816         rb_scan_args(argc, argv, "01", &gval);
5817         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5818         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5819         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5820 }
5821
5822 /*
5823  *  call-seq:
5824  *     min_residue_number(atom_group = nil)     -> Integer
5825  *
5826  *  Returns the minimum residue number actually used. If an atom group is given, only
5827  *  these atoms are examined. If no atom is present, nil is returned.
5828  */
5829 static VALUE
5830 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5831 {
5832     Molecule *mol;
5833         VALUE gval;
5834         int minSeq;
5835         IntGroup *ig;
5836     Data_Get_Struct(self, Molecule, mol);
5837         rb_scan_args(argc, argv, "01", &gval);
5838         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5839         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5840         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5841 }
5842
5843 /*
5844  *  call-seq:
5845  *     each_atom(atom_group = nil) {|aref| ...}
5846  *
5847  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5848  *  group is given, only these atoms are processed.
5849  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5850  *  is self (a Molecule object).
5851  */
5852 static VALUE
5853 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5854 {
5855         int i;
5856     Molecule *mol;
5857         AtomRef *aref;
5858         VALUE arval;
5859         VALUE gval;
5860         IntGroup *ig;
5861     Data_Get_Struct(self, Molecule, mol);
5862         rb_scan_args(argc, argv, "01", &gval);
5863         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5864         arval = ValueFromMoleculeAndIndex(mol, 0);
5865         Data_Get_Struct(arval, AtomRef, aref);
5866         for (i = 0; i < mol->natoms; i++) {
5867                 aref->idx = i;
5868                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5869                         rb_yield(arval);
5870         }
5871         if (ig != NULL)
5872                 IntGroupRelease(ig);
5873     return self;
5874 }
5875
5876 #pragma mark ------ Atom Group ------
5877
5878 static VALUE
5879 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5880 {
5881         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5882         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5883         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5884         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5885         return Qnil;
5886 }
5887
5888 /*
5889  *  call-seq:
5890  *     atom_group
5891  *     atom_group {|aref| ...}
5892  *     atom_group(arg1, arg2, ...)
5893  *     atom_group(arg1, arg2, ...) {|aref| ...}
5894  *
5895  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5896  *  If arguments are given, then the atoms reprensented by the arguments are added to the
5897  *  group. For a conversion of a string to an atom index, see the description
5898  *  of Molecule#atom_index.
5899  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
5900  *  representing each atom, and the atoms are removed from the result if the block returns false.
5901  *
5902  */
5903 static VALUE
5904 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5905 {
5906         IntGroup *ig1, *ig2;
5907     Molecule *mol;
5908         Int i, startPt, interval;
5909         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5910         Data_Get_Struct(retval, IntGroup, ig1);
5911     Data_Get_Struct(self, Molecule, mol);
5912         if (argc == 0) {
5913                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5914         } else {
5915                 while (argc > 0) {
5916                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5917                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5918                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5919                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5920                                 ig2 = IntGroupFromValue(*argv);
5921                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5922                                         interval = IntGroupGetInterval(ig2, i);
5923                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5924                                 }
5925                                 IntGroupRelease(ig2);
5926                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
5927                                 VALUE values[2];
5928                                 values[0] = (VALUE)mol;
5929                                 values[1] = (VALUE)ig1;
5930                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5931                         } else
5932                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5933                         argc--;
5934                         argv++;
5935                 }
5936         }
5937         if (rb_block_given_p()) {
5938                 /*  Evaluate the given block with an AtomRef as the argument, and delete
5939                         the index if the block returns false  */
5940                 AtomRef *aref = AtomRefNew(mol, 0);
5941                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5942                 ig2 = IntGroupNew();
5943                 IntGroupCopy(ig2, ig1);
5944                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5945                         VALUE resval;
5946                         if (startPt >= mol->natoms)
5947                                 break;
5948                         aref->idx = startPt;
5949                         resval = rb_yield(arval);
5950                         if (!RTEST(resval))
5951                                 IntGroupRemove(ig1, startPt, 1);
5952                 }
5953                 IntGroupRelease(ig2);
5954         }
5955         
5956         /*  Remove points that are out of bounds */
5957         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5958
5959         return retval;                  
5960 }
5961
5962 /*
5963  *  call-seq:
5964  *     selection       -> IntGroup
5965  *
5966  *  Returns the current selection.
5967  */
5968 static VALUE
5969 s_Molecule_Selection(VALUE self)
5970 {
5971     Molecule *mol;
5972         IntGroup *ig;
5973         VALUE val;
5974     Data_Get_Struct(self, Molecule, mol);
5975         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5976                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
5977                 val = ValueFromIntGroup(ig);
5978                 IntGroupRelease(ig);
5979         } else {
5980                 val = IntGroup_Alloc(rb_cIntGroup);
5981         }
5982         return val;
5983 }
5984
5985 static VALUE
5986 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5987 {
5988     Molecule *mol;
5989         IntGroup *ig;
5990     Data_Get_Struct(self, Molecule, mol);
5991         if (val == Qnil)
5992                 ig = NULL;
5993         else
5994                 ig = s_Molecule_AtomGroupFromValue(self, val);
5995         if (undoable)
5996                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
5997         else
5998                 MoleculeSetSelection(mol, ig);
5999         if (ig != NULL)
6000                 IntGroupRelease(ig);
6001         return val;
6002 }
6003
6004 /*
6005  *  call-seq:
6006  *     selection = IntGroup
6007  *
6008  *  Set the current selection. The right-hand operand may be nil.
6009  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6010  */
6011 static VALUE
6012 s_Molecule_SetSelection(VALUE self, VALUE val)
6013 {
6014         return s_Molecule_SetSelectionSub(self, val, 0);
6015 }
6016
6017 /*
6018  *  call-seq:
6019  *     set_undoable_selection(IntGroup)
6020  *
6021  *  Set the current selection with undo registration. The right-hand operand may be nil.
6022  *  This operation is undoable.
6023  */
6024 static VALUE
6025 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6026 {
6027         return s_Molecule_SetSelectionSub(self, val, 1);
6028 }
6029
6030 #pragma mark ------ Editing ------
6031
6032 /*
6033  *  call-seq:
6034  *     extract(group, dummy_flag = nil)       -> Molecule
6035  *
6036  *  Extract the atoms given by group and return as a new molecule object.
6037  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6038  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6039  *  names beginning with an underscore) and included in the new molecule object.
6040  */
6041 static VALUE
6042 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6043 {
6044     Molecule *mol1, *mol2;
6045         IntGroup *ig;
6046         VALUE group, dummy_flag, retval;
6047     Data_Get_Struct(self, Molecule, mol1);
6048         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6049         ig = s_Molecule_AtomGroupFromValue(self, group);
6050         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6051                 retval = Qnil;
6052         } else {
6053                 retval = ValueFromMolecule(mol2);
6054         }
6055         IntGroupRelease(ig);
6056         return retval;
6057 }
6058
6059 /*
6060  *  call-seq:
6061  *     add(molecule2)       -> self
6062  *
6063  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6064     conflicts.
6065     This operation is undoable.
6066  */
6067 static VALUE
6068 s_Molecule_Add(VALUE self, VALUE val)
6069 {
6070     Molecule *mol1, *mol2;
6071     Data_Get_Struct(self, Molecule, mol1);
6072         mol2 = MoleculeFromValue(val);
6073         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6074         return self; 
6075 }
6076
6077 /*
6078  *  call-seq:
6079  *     remove(group)       -> Molecule
6080  *
6081  *  The atoms designated by the given group are removed from the molecule.
6082  *  This operation is undoable.
6083  */
6084 static VALUE
6085 s_Molecule_Remove(VALUE self, VALUE group)
6086 {
6087     Molecule *mol1;
6088         IntGroup *ig, *bg;
6089         Int i;
6090         IntGroupIterator iter;
6091
6092     Data_Get_Struct(self, Molecule, mol1);
6093         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6094         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6095                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6096         Data_Get_Struct(group, IntGroup, ig);
6097
6098         /*  Remove the bonds between the two fragments  */
6099         /*  (This is necessary for undo to work correctly)  */
6100         IntGroupIteratorInit(ig, &iter);
6101         bg = NULL;
6102         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6103                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6104                 Int j, *cp;
6105                 cp = AtomConnectData(&ap->connect);
6106                 for (j = 0; j < ap->connect.count; j++) {
6107                         int n = cp[j];
6108                         if (!IntGroupLookup(ig, n, NULL)) {
6109                                 /*  bond i-n, i is in ig and n is not  */
6110                                 int k = MoleculeLookupBond(mol1, i, n);
6111                                 if (k >= 0) {
6112                                         if (bg == NULL)
6113                                                 bg = IntGroupNew();
6114                                         IntGroupAdd(bg, k, 1);
6115                                 }
6116                         }
6117                 }
6118         }
6119         IntGroupIteratorRelease(&iter);
6120         if (bg != NULL) {
6121                 /*  Remove bonds  */
6122                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6123                 IntGroupRelease(bg);
6124         }
6125         /*  Remove atoms  */
6126         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6127                 return Qnil;
6128         return self;
6129 }
6130
6131 /*
6132  *  call-seq:
6133  *     create_atom(name, pos = -1)  -> AtomRef
6134  *
6135  *  Create a new atom with the specified name (may contain residue 
6136  *  information) and position (if position is out of range, the atom is appended at
6137  *  the end). Returns the reference to the new atom.
6138  *  This operation is undoable.
6139  */
6140 static VALUE
6141 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6142 {
6143     Molecule *mol;
6144     Int i, pos;
6145         VALUE name, ival;
6146     Atom arec;
6147     AtomRef *aref;
6148         char *p, resName[6], atomName[6];
6149         int resSeq;
6150     Data_Get_Struct(self, Molecule, mol);
6151         rb_scan_args(argc, argv, "02", &name, &ival);
6152         if (ival != Qnil)
6153                 pos = NUM2INT(rb_Integer(ival));
6154         else pos = -1;
6155         if (name != Qnil) {
6156                 p = StringValuePtr(name);
6157                 if (p[0] != 0) {
6158                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6159                         if (atomName[0] == 0)
6160                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6161                 }
6162         } else p = NULL;
6163         if (p == NULL || p[0] == 0) {
6164                 memset(atomName, 0, 4);
6165                 resSeq = -1;
6166         }
6167     memset(&arec, 0, sizeof(arec));
6168     strncpy(arec.aname, atomName, 4);
6169     if (resSeq >= 0) {
6170       strncpy(arec.resName, resName, 4);
6171       arec.resSeq = resSeq;
6172     }
6173         arec.occupancy = 1.0;
6174 //    i = MoleculeCreateAnAtom(mol, &arec);
6175         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6176                 return Qnil;
6177     aref = AtomRefNew(mol, pos);
6178     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6179 }
6180
6181 /*
6182  *  call-seq:
6183  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6184  *
6185  *  Create a new atom with the same attributes (but no bonding information)
6186  *  with the specified atom. Returns the reference to the new atom.
6187  *  This operation is undoable.
6188  */
6189 static VALUE
6190 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6191 {
6192     Molecule *mol;
6193         const Atom *apsrc;
6194     Atom arec;
6195         AtomRef *aref;
6196         VALUE retval, aval, ival;
6197         Int pos;
6198     Data_Get_Struct(self, Molecule, mol);
6199         rb_scan_args(argc, argv, "11", &aval, &ival);
6200         if (FIXNUM_P(aval)) {
6201                 int idx = NUM2INT(aval);
6202                 if (idx < 0 || idx >= mol->natoms)
6203                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6204                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6205         } else {
6206                 apsrc = s_AtomFromValue(aval);
6207         }
6208         if (apsrc == NULL)
6209                 rb_raise(rb_eMolbyError, "bad atom specification");
6210         if (ival != Qnil)
6211                 pos = NUM2INT(rb_Integer(ival));
6212         else pos = -1;
6213         AtomDuplicate(&arec, apsrc);
6214         arec.connect.count = 0;
6215         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6216                 retval = Qnil;
6217         else {
6218                 aref = AtomRefNew(mol, pos);
6219                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6220         }
6221         AtomClean(&arec);
6222         return retval;
6223 }
6224
6225 /*
6226  *  call-seq:
6227  *     create_bond(n1, n2, ...)       -> Integer
6228  *
6229  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6230  *  do nothing for that pair. Returns the number of bonds actually created.
6231  *  This operation is undoable.
6232  */
6233 static VALUE
6234 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6235 {
6236     Molecule *mol;
6237         Int i, j, k, *ip, old_nbonds;
6238         if (argc == 0)
6239                 rb_raise(rb_eMolbyError, "missing arguments");
6240         if (argc % 2 != 0)
6241                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6242     Data_Get_Struct(self, Molecule, mol);
6243         ip = ALLOC_N(Int, argc + 1);
6244         for (i = j = 0; i < argc; i++, j++) {
6245                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6246                 if (i % 2 == 1) {
6247                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6248                                 j -= 2;  /*  This bond is already present: skip it  */
6249                         else {
6250                                 for (k = 0; k < j - 1; k += 2) {
6251                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6252                                                 j -= 2;   /*  The same entry is already in the argument  */
6253                                                 break;
6254                                         }
6255                                 }
6256                         }
6257                 }
6258         }
6259         old_nbonds = mol->nbonds;
6260         if (j > 0) {
6261                 ip[j] = kInvalidIndex;
6262                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6263         } else i = 0;
6264         xfree(ip);
6265         if (i == -1)
6266                 rb_raise(rb_eMolbyError, "atom index out of range");
6267         else if (i == -2)
6268                 rb_raise(rb_eMolbyError, "too many bonds");
6269         else if (i == -3)
6270                 rb_raise(rb_eMolbyError, "duplicate bonds");
6271         else if (i == -5)
6272                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6273         else if (i != 0)
6274                 rb_raise(rb_eMolbyError, "error in creating bonds");
6275         return INT2NUM(mol->nbonds - old_nbonds);
6276 }
6277
6278 /*
6279  *  call-seq:
6280  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6281  *
6282  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6283  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6284  *  This operation is undoable.
6285  */
6286 static VALUE
6287 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6288 {
6289     Molecule *mol;
6290         Int i, j, n[2];
6291         IntGroup *bg;
6292         if (argc == 0)
6293                 rb_raise(rb_eMolbyError, "missing arguments");
6294         if (argc % 2 != 0)
6295                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6296     Data_Get_Struct(self, Molecule, mol);
6297         bg = NULL;
6298         for (i = j = 0; i < argc; i++, j = 1 - j) {
6299                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6300                 if (j == 1) {
6301                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6302                         if (k >= 0) {
6303                                 if (bg == NULL)
6304                                         bg = IntGroupNew();
6305                                 IntGroupAdd(bg, k, 1);
6306                         }
6307                 }
6308         }
6309         if (bg != NULL) {
6310                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6311                 i = IntGroupGetCount(bg);
6312                 IntGroupRelease(bg);
6313         } else i = 0;
6314         return INT2NUM(i);
6315 }
6316
6317 /*
6318  *  call-seq:
6319  *     assign_bond_order(idx, d1)
6320  *     assign_bond_orders(group, [d1, d2, ...])
6321  *
6322  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6323  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6324  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6325  *  (This may change in the future)
6326  *  This operation is undoable.
6327  */
6328 static VALUE
6329 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6330 {
6331     Molecule *mol;
6332         IntGroup *ig;
6333     Data_Get_Struct(self, Molecule, mol);
6334         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6335                 /*  The first form  */
6336                 Int idx = NUM2INT(rb_Integer(idxval));
6337                 Double d1 = NUM2DBL(rb_Float(dval));
6338                 if (idx < 0 || idx >= mol->nbonds)
6339                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6340                 ig = IntGroupNewWithPoints(idx, 1, -1);
6341                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6342                 IntGroupRelease(ig);
6343         } else {
6344                 Int i, n;
6345                 Double *dp;
6346                 ig = IntGroupFromValue(idxval);
6347                 n = IntGroupGetCount(ig);
6348                 if (n == 0)
6349                         rb_raise(rb_eMolbyError, "the bond index is empty");
6350                 dval = rb_ary_to_ary(dval);
6351                 dp = (Double *)calloc(sizeof(Double), n);
6352                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6353                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6354                 }
6355                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6356                 free(dp);
6357                 IntGroupRelease(ig);
6358         }
6359         return self;
6360 }
6361
6362 /*
6363  *  call-seq:
6364  *     get_bond_order(idx) -> Float
6365  *     get_bond_orders(group) -> Array
6366  *
6367  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6368  *  In the second form, the bond orders at the indices in the group are returned as an array.
6369  *  If no bond order information have been assigned, returns nil (the first form)
6370  *  or an empty array (the second form).
6371  */
6372 static VALUE
6373 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6374 {
6375     Molecule *mol;
6376         IntGroup *ig;
6377         Double *dp;
6378         VALUE retval;
6379         Int i, n, numericArg;
6380     Data_Get_Struct(self, Molecule, mol);
6381         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6382                 /*  The first form  */
6383                 Int idx = NUM2INT(rb_Integer(idxval));
6384                 if (idx < 0 || idx >= mol->nbonds)
6385                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6386                 if (mol->bondOrders == NULL)
6387                         return Qnil;
6388                 ig = IntGroupNewWithPoints(idx, 1, -1);
6389                 n = 1;
6390                 numericArg = 1;
6391         } else {
6392                 if (mol->bondOrders == NULL)
6393                         return rb_ary_new();
6394                 ig = IntGroupFromValue(idxval);
6395                 n = IntGroupGetCount(ig);
6396                 if (n == 0)
6397                         rb_raise(rb_eMolbyError, "the bond index is empty");
6398                 numericArg = 0;
6399         }
6400         dp = (Double *)calloc(sizeof(Double), n);
6401         MoleculeGetBondOrders(mol, dp, ig);
6402         if (numericArg)
6403                 retval = rb_float_new(dp[0]);
6404         else {
6405                 retval = rb_ary_new();
6406                 for (i = 0; i < n; i++)
6407                         rb_ary_push(retval, rb_float_new(dp[i]));
6408         }
6409         free(dp);
6410         IntGroupRelease(ig);
6411         return retval;
6412 }
6413
6414 /*
6415  *  call-seq:
6416  *     bond_exist?(idx1, idx2) -> bool
6417  *
6418  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6419  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6420  */
6421 static VALUE
6422 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6423 {
6424         Molecule *mol;
6425         Int idx1, idx2, i;
6426         Atom *ap;
6427         Int *cp;
6428     Data_Get_Struct(self, Molecule, mol);
6429         idx1 = NUM2INT(rb_Integer(ival1));
6430         idx2 = NUM2INT(rb_Integer(ival2));
6431         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6432                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6433         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6434         cp = AtomConnectData(&ap->connect);
6435         for (i = 0; i < ap->connect.count; i++) {
6436                 if (cp[i] == idx2)
6437                         return Qtrue;
6438         }
6439         return Qfalse;
6440 }
6441
6442 /*
6443  *  call-seq:
6444  *     add_angle(n1, n2, n3)       -> Molecule
6445  *
6446  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6447  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6448  *  This operation is undoable.
6449  */
6450 static VALUE
6451 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6452 {
6453         Int n[4];
6454     Molecule *mol;
6455     Data_Get_Struct(self, Molecule, mol);
6456         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6457         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6458         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6459         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6460                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6461         n[3] = kInvalidIndex;
6462         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6463         return self;
6464 }
6465
6466 /*
6467  *  call-seq:
6468  *     remove_angle(n1, n2, n3)       -> Molecule
6469  *
6470  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6471  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6472  *  This operation is undoable.
6473  */
6474 static VALUE
6475 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6476 {
6477         Int n[4];
6478     Molecule *mol;
6479         IntGroup *ig;
6480     Data_Get_Struct(self, Molecule, mol);
6481         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6482         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6483         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6484         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6485                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6486         ig = IntGroupNewWithPoints(n[3], 1, -1);
6487         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6488         IntGroupRelease(ig);
6489         return self;
6490 }
6491
6492 /*
6493  *  call-seq:
6494  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6495  *
6496  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6497  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6498  *  This operation is undoable.
6499  */
6500 static VALUE
6501 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6502 {
6503         Int n[5];
6504     Molecule *mol;
6505     Data_Get_Struct(self, Molecule, mol);
6506         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6507         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6508         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6509         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6510         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6511                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6512         n[4] = kInvalidIndex;
6513         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6514         return self;
6515 }
6516
6517 /*
6518  *  call-seq:
6519  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6520  *
6521  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6522  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6523  *  This operation is undoable.
6524  */
6525 static VALUE
6526 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6527 {
6528         Int n[5];
6529     Molecule *mol;
6530         IntGroup *ig;
6531     Data_Get_Struct(self, Molecule, mol);
6532         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6533         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6534         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6535         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6536         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6537                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6538         ig = IntGroupNewWithPoints(n[4], 1, -1);
6539         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6540         IntGroupRelease(ig);
6541         return self;
6542 }
6543
6544 /*
6545  *  call-seq:
6546  *     add_improper(n1, n2, n3, n4)       -> Molecule
6547  *
6548  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6549  *  not automatically added when a new bond is created, so this method is more useful than
6550  *  the angle/dihedral counterpart.
6551  *  This operation is undoable.
6552  */
6553 static VALUE
6554 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6555 {
6556         Int n[5];
6557     Molecule *mol;
6558     Data_Get_Struct(self, Molecule, mol);
6559         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6560         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6561         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6562         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6563         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6564                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6565         n[4] = kInvalidIndex;
6566         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6567         return self;
6568 }
6569
6570 /*
6571  *  call-seq:
6572  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6573  *     remove_improper(intgroup)             -> Molecule
6574  *
6575  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6576  *  Returns self. Unlike angles and dihedrals, impropers are
6577  *  not automatically added when a new bond is created, so this method is more useful than
6578  *  the angle/dihedral counterpart.
6579  *  This operation is undoable.
6580  */
6581 static VALUE
6582 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6583 {
6584         Int n[5];
6585         VALUE v1, v2, v3, v4;
6586     Molecule *mol;
6587         IntGroup *ig;
6588     Data_Get_Struct(self, Molecule, mol);
6589         if (argc == 1) {
6590                 ig = IntGroupFromValue(argv[0]);
6591         } else {
6592                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6593                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6594                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6595                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6596                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6597                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6598                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6599                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6600         }
6601         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6602         IntGroupRelease(ig);
6603         return self;
6604 }
6605
6606 /*
6607  *  call-seq:
6608  *     assign_residue(group, res)       -> Molecule
6609  *
6610  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6611  *  or "resname.resno". When the residue number is not specified, the residue number of
6612  *  the first atom in the group is used.
6613  *  This operation is undoable.
6614  */
6615 static VALUE
6616 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6617 {
6618     Molecule *mol;
6619         IntGroup *ig;
6620         char *p, *pp, buf[16];
6621         Int resid, n;
6622         Atom *ap;
6623     Data_Get_Struct(self, Molecule, mol);
6624         
6625         /*  Parse the argument res  */
6626         if (FIXNUM_P(res)) {
6627                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6628                 resid = NUM2INT(res);
6629                 buf[0] = 0;
6630         } else {
6631                 p = StringValuePtr(res);
6632                 pp = strchr(p, '.');
6633                 if (pp != NULL) {
6634                         resid = atoi(pp + 1);
6635                         n = pp - p;
6636                 } else {
6637                         resid = -1;
6638                         n = strlen(p);
6639                 }
6640                 if (n > sizeof buf - 1)
6641                         n = sizeof buf - 1;
6642                 strncpy(buf, p, n);
6643                 buf[n] = 0;
6644         }
6645         ig = s_Molecule_AtomGroupFromValue(self, range);
6646         if (ig == NULL || IntGroupGetCount(ig) == 0)
6647                 return Qnil;
6648
6649         if (resid < 0) {
6650                 /*  Use the residue number of the first specified atom  */
6651                 n = IntGroupGetNthPoint(ig, 0);
6652                 if (n >= mol->natoms)
6653                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6654                 ap = ATOM_AT_INDEX(mol->atoms, n);
6655                 resid = ap->resSeq;
6656         }
6657         /*  Change the residue number  */
6658         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6659         /*  Change the residue name if necessary  */
6660         if (buf[0] != 0) {
6661         /*      Int seqs[2];
6662                 seqs[0] = resid;
6663                 seqs[1] = kInvalidIndex; */
6664                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6665         }
6666         IntGroupRelease(ig);
6667         return self;
6668 }
6669
6670 /*
6671  *  call-seq:
6672  *     offset_residue(group, offset)       -> Molecule
6673  *
6674  *  Offset the residue number of the specified atoms. If any of the residue number gets
6675  *  negative, then exception is thrown.
6676  *  This operation is undoable.
6677  */
6678 static VALUE
6679 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6680 {
6681     Molecule *mol;
6682         IntGroup *ig;
6683         int ofs, result;
6684     Data_Get_Struct(self, Molecule, mol);
6685         ig = s_Molecule_AtomGroupFromValue(self, range);
6686         ofs = NUM2INT(offset);
6687         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6688         if (result > 0)
6689                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6690         IntGroupRelease(ig);
6691         return self;
6692 }
6693
6694 /*
6695  *  call-seq:
6696  *     renumber_atoms(array)       -> IntGroup
6697  *
6698  *  Change the order of atoms so that the atoms specified in the array argument appear
6699  *  in this order from the top of the molecule. The atoms that are not included in array
6700  *  are placed after these atoms, and these atoms are returned as an intGroup.
6701  *  This operation is undoable.
6702  */
6703 static VALUE
6704 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6705 {
6706     Molecule *mol;
6707         Int *new2old;
6708         IntGroup *ig;
6709         int i, n;
6710         VALUE *valp, retval;
6711     Data_Get_Struct(self, Molecule, mol);
6712         if (TYPE(array) != T_ARRAY)
6713                 array = rb_funcall(array, rb_intern("to_a"), 0);
6714         n = RARRAY_LEN(array);
6715         valp = RARRAY_PTR(array);
6716         new2old = ALLOC_N(Int, n + 1);
6717         for (i = 0; i < n; i++)
6718                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6719         new2old[i] = kInvalidIndex;
6720         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6721         if (i == 1)
6722                 rb_raise(rb_eMolbyError, "Atom index out of range");
6723         else if (i == 2)
6724                 rb_raise(rb_eMolbyError, "Duplicate entry");
6725         else if (i == 3)
6726                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6727         retval = IntGroup_Alloc(rb_cIntGroup);
6728         Data_Get_Struct(retval, IntGroup, ig);
6729         if (mol->natoms > n)
6730                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6731         xfree(new2old);
6732         return retval;
6733 }
6734
6735 /*
6736  *  call-seq:
6737  *     set_atom_attr(index, key, value)
6738  *
6739  *  Set the atom attribute for the specified atom.
6740  *  This operation is undoable.
6741  */
6742 static VALUE
6743 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6744 {
6745         Molecule *mol;
6746         VALUE aref, oldval;
6747     Data_Get_Struct(self, Molecule, mol);
6748         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6749         oldval = s_AtomRef_GetAttr(aref, key);
6750         if (val == Qundef)
6751                 return oldval;
6752         s_AtomRef_SetAttr(aref, key, val);
6753         return val;
6754 }
6755
6756 /*
6757  *  call-seq:
6758  *     get_atom_attr(index, key)
6759  *
6760  *  Get the atom attribute for the specified atom.
6761  */
6762 static VALUE
6763 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6764 {
6765         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6766 }
6767
6768 #pragma mark ------ Undo Support ------
6769
6770 /*
6771  *  call-seq:
6772  *     register_undo(script, *args)
6773  *
6774  *  Register an undo operation with the current molecule.
6775  */
6776 static VALUE
6777 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6778 {
6779         Molecule *mol;
6780         VALUE script, args;
6781         MolAction *act;
6782     Data_Get_Struct(self, Molecule, mol);
6783         rb_scan_args(argc, argv, "1*", &script, &args);
6784         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6785         MolActionCallback_registerUndo(mol, act);
6786         return script;
6787 }
6788
6789 /*
6790  *  call-seq:
6791  *     undo_enabled? -> bool
6792  *
6793  *  Returns true if undo is enabled for this molecule; otherwise no.
6794  */
6795 static VALUE
6796 s_Molecule_UndoEnabled(VALUE self)
6797 {
6798     Molecule *mol;
6799     Data_Get_Struct(self, Molecule, mol);
6800         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6801                 return Qtrue;
6802         else return Qfalse;
6803 }
6804
6805 /*
6806  *  call-seq:
6807  *     undo_enabled = bool
6808  *
6809  *  Enable or disable undo.
6810  */
6811 static VALUE
6812 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6813 {
6814     Molecule *mol;
6815     Data_Get_Struct(self, Molecule, mol);
6816         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6817         return val;
6818 }
6819
6820 #pragma mark ------ Measure ------
6821
6822 static void
6823 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6824 {
6825         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6826                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6827                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6828                 case 0: break;
6829                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6830         }
6831 }
6832
6833 /*
6834  *  call-seq:
6835  *     center_of_mass(group = nil)       -> Vector3D
6836  *
6837  *  Calculate the center of mass for the given set of atoms. The argument
6838  *  group is null, then all atoms are considered.
6839  */
6840 static VALUE
6841 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6842 {
6843     Molecule *mol;
6844         VALUE group;
6845         IntGroup *ig;
6846         Vector v;
6847     Data_Get_Struct(self, Molecule, mol);
6848         rb_scan_args(argc, argv, "01", &group);
6849         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6850         s_Molecule_DoCenterOfMass(mol, &v, ig);
6851         if (ig != NULL)
6852                 IntGroupRelease(ig);
6853         return ValueFromVector(&v);
6854 }
6855
6856 /*
6857  *  call-seq:
6858  *     centralize(group = nil)       -> self
6859  *
6860  *  Translate the molecule so that the center of mass of the given group is located
6861  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6862  */
6863 static VALUE
6864 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6865 {
6866     Molecule *mol;
6867         VALUE group;
6868         IntGroup *ig;
6869         Vector v;
6870     Data_Get_Struct(self, Molecule, mol);
6871         rb_scan_args(argc, argv, "01", &group);
6872         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6873         s_Molecule_DoCenterOfMass(mol, &v, ig);
6874         if (ig != NULL)
6875                 IntGroupRelease(ig);
6876         v.x = -v.x;
6877         v.y = -v.y;
6878         v.z = -v.z;
6879         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6880         return self;
6881 }
6882
6883 /*
6884  *  call-seq:
6885  *     bounds(group = nil)       -> [min, max]
6886  *
6887  *  Calculate the boundary. The return value is an array of two Vector3D objects.
6888  */
6889 static VALUE
6890 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6891 {
6892     Molecule *mol;
6893         VALUE group;
6894         IntGroup *ig;
6895         Vector vmin, vmax;
6896         int n;
6897         Atom *ap;
6898     Data_Get_Struct(self, Molecule, mol);
6899         rb_scan_args(argc, argv, "01", &group);
6900         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6901         if (ig != NULL && IntGroupGetCount(ig) == 0)
6902                 rb_raise(rb_eMolbyError, "atom group is empty");
6903         vmin.x = vmin.y = vmin.z = 1e30;
6904         vmax.x = vmax.y = vmax.z = -1e30;
6905         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6906                 Vector r;
6907                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6908                         continue;
6909                 r = ap->r;
6910                 if (r.x < vmin.x)
6911                         vmin.x = r.x;
6912                 if (r.y < vmin.y)
6913                         vmin.y = r.y;
6914                 if (r.z < vmin.z)
6915                         vmin.z = r.z;
6916                 if (r.x > vmax.x)
6917                         vmax.x = r.x;
6918                 if (r.y > vmax.y)
6919                         vmax.y = r.y;
6920                 if (r.z > vmax.z)
6921                         vmax.z = r.z;
6922         }
6923         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6924 }
6925
6926 /*  Get atom position or a vector  */
6927 static void
6928 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6929 {
6930         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6931                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6932                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6933         } else {
6934                 VectorFromValue(val, vp);
6935         }
6936 }
6937
6938 /*
6939  *  call-seq:
6940  *     measure_bond(n1, n2)       -> Float
6941  *
6942  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
6943  *  or Vector3D values.
6944  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6945  */
6946 static VALUE
6947 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6948 {
6949     Molecule *mol;
6950         Vector v1, v2;
6951     Data_Get_Struct(self, Molecule, mol);
6952         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6953         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6954         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6955 }
6956
6957 /*
6958  *  call-seq:
6959  *     measure_angle(n1, n2, n3)       -> Float
6960  *
6961  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
6962  *  or Vector3D values. The return value is in degree.
6963  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6964  */
6965 static VALUE
6966 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6967 {
6968     Molecule *mol;
6969         Vector v1, v2, v3;
6970         Double d;
6971     Data_Get_Struct(self, Molecule, mol);
6972         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6973         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6974         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
6975         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6976         if (isnan(d))
6977                 return Qnil;  /*  Cannot define  */
6978         else return rb_float_new(d);
6979 }
6980
6981 /*
6982  *  call-seq:
6983  *     measure_dihedral(n1, n2, n3, n4)       -> Float
6984  *
6985  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
6986  *  or Vector3D values. The return value is in degree.
6987  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6988  */
6989 static VALUE
6990 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
6991 {
6992     Molecule *mol;
6993         Vector v1, v2, v3, v4;
6994         Double d;
6995     Data_Get_Struct(self, Molecule, mol);
6996         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6997         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6998         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
6999         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
7000         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7001         if (isnan(d))
7002                 return Qnil;  /*  Cannot define  */
7003         else return rb_float_new(d);
7004 }
7005
7006 /*
7007  *  call-seq:
7008  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7009  *
7010  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7011  *  first and second atom in the pair should belong to group1 and group2, respectively.
7012  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7013  */
7014 static VALUE
7015 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7016 {
7017     Molecule *mol;
7018         VALUE limval, gval1, gval2, rval, igval;
7019         IntGroup *ig1, *ig2;
7020         IntGroupIterator iter1, iter2;
7021         Int npairs, *pairs;
7022         Int n[2], i;
7023         Double lim;
7024         Vector r1;
7025         Atom *ap1, *ap2;
7026         MDExclusion *exinfo;
7027         Int *exlist;
7028         
7029     Data_Get_Struct(self, Molecule, mol);
7030         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7031         lim = NUM2DBL(rb_Float(limval));
7032         if (lim <= 0.0)
7033                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7034         if (gval1 != Qnil)
7035                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7036         else
7037                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7038         if (gval2 != Qnil)
7039                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7040         else
7041                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7042         
7043         if (!RTEST(igval)) {
7044                 /*  Use the exclusion table in MDArena  */
7045                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7046                         VALUE mval = ValueFromMolecule(mol);
7047                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7048                 }
7049                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7050                 exlist = mol->arena->exlist;    
7051         } else {
7052                 exinfo = NULL;
7053                 exlist = NULL;
7054         }
7055         IntGroupIteratorInit(ig1, &iter1);
7056         IntGroupIteratorInit(ig2, &iter2);
7057         npairs = 0;
7058         pairs = NULL;
7059         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7060                 Int exn1, exn2;
7061                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7062                 r1 = ap1->r;
7063                 if (exinfo != NULL) {
7064                         exn1 = exinfo[n[0]].index1;
7065                         exn2 = exinfo[n[0] + 1].index1;
7066                 } else exn1 = exn2 = -1;
7067                 IntGroupIteratorReset(&iter2);
7068                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7069                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7070                         if (n[0] == n[1])
7071                                 continue;  /*  Same atom  */
7072                         if (exinfo != NULL) {
7073                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7074                                 for (i = exn1; i < exn2; i++) {
7075                                         if (exlist[i] == n[1])
7076                                                 break;
7077                                 }
7078                                 if (i < exn2)
7079                                         continue;  /*  Should be excluded  */
7080                         }
7081                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7082                                 /*  Is this pair already registered?  */
7083                                 Int *ip;
7084                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7085                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7086                                                 break;
7087                                 }
7088                                 if (i >= npairs) {
7089                                         /*  Not registered yet  */
7090                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7091                                 }
7092                         }
7093                 }
7094         }
7095         IntGroupIteratorRelease(&iter2);
7096         IntGroupIteratorRelease(&iter1);
7097         IntGroupRelease(ig2);
7098         IntGroupRelease(ig1);
7099         rval = rb_ary_new2(npairs);
7100         if (pairs != NULL) {
7101                 for (i = 0; i < npairs; i++) {
7102                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7103                 }
7104                 free(pairs);
7105         }
7106         return rval;
7107 }
7108
7109 /*
7110  *  call-seq:
7111  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7112  *
7113  *  Find atoms that are within the threshold distance from the given atom.
7114  *  (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.)
7115  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7116  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7117  *  If limit is not given, a default value of 1.2 is used.
7118  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7119  */
7120 static VALUE
7121 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7122 {
7123     Molecule *mol;
7124         VALUE aval, limval, radval;
7125         double limit, radius;
7126         Int n1, nbonds, *bonds, an;
7127         Vector v;
7128     Data_Get_Struct(self, Molecule, mol);
7129         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7130         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)) {
7131                 VectorFromValue(aval, &v);
7132                 if (radval == Qnil)
7133                         radius = gElementParameters[6].radius;
7134                 else
7135                         radius = NUM2DBL(rb_Float(radval));
7136                 n1 = mol->natoms;
7137         } else {
7138                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7139                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7140                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7141                 if (an >= 0 && an < gCountElementParameters)
7142                         radius = gElementParameters[an].radius;
7143                 else radius = gElementParameters[6].radius;
7144         }
7145         if (limval == Qnil)
7146                 limit = 1.2;
7147         else
7148                 limit = NUM2DBL(rb_Float(limval));
7149         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7150         bonds = NULL;
7151         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7152         aval = rb_ary_new();
7153         if (nbonds > 0) {
7154                 for (n1 = 0; n1 < nbonds; n1++)
7155                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7156                 free(bonds);
7157         }
7158         return aval;
7159 }
7160
7161 /*
7162  *  call-seq:
7163  *     guess_bonds(limit = 1.2)       -> Integer
7164  *
7165  *  Create bonds between atoms that are within the threshold distance.
7166  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7167  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7168  *  If limit is not given, a default value of 1.2 is used.
7169  *  The number of the newly created bonds is returned.
7170  *  This operation is undoable.
7171  */
7172 static VALUE
7173 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7174 {
7175     Molecule *mol;
7176         VALUE limval;
7177         double limit;
7178         Int nbonds, *bonds;
7179     Data_Get_Struct(self, Molecule, mol);
7180         rb_scan_args(argc, argv, "01", &limval);
7181         if (limval == Qnil)
7182                 limit = 1.2;
7183         else
7184                 limit = NUM2DBL(rb_Float(limval));
7185         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7186         if (nbonds > 0) {
7187                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7188                 free(bonds);
7189         }
7190         return INT2NUM(nbonds);
7191 }
7192
7193 #pragma mark ------ Cell and Symmetry ------
7194
7195 /*
7196  *  call-seq:
7197  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7198  *
7199  *  Returns the unit cell parameters. If cell is not set, returns nil.
7200  */
7201 static VALUE
7202 s_Molecule_Cell(VALUE self)
7203 {
7204     Molecule *mol;
7205         int i;
7206         VALUE val;
7207     Data_Get_Struct(self, Molecule, mol);
7208         if (mol->cell == NULL)
7209                 return Qnil;
7210         val = rb_ary_new2(6);
7211         for (i = 0; i < 6; i++)
7212                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7213         if (mol->cell->has_sigma) {
7214                 for (i = 0; i < 6; i++) {
7215                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7216                 }
7217         }
7218         return val;
7219 }
7220
7221 /*
7222  *  call-seq:
7223  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7224  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7225  *
7226  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7227  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7228  This operation is undoable.
7229  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7230  */
7231 static VALUE
7232 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7233 {
7234     Molecule *mol;
7235         VALUE val, cval;
7236         int i, convert_coord, n;
7237         double d[12];
7238     Data_Get_Struct(self, Molecule, mol);
7239         rb_scan_args(argc, argv, "11", &val, &cval);
7240         if (val == Qnil) {
7241                 n = 0;
7242         } else {
7243                 int len;
7244                 val = rb_ary_to_ary(val);
7245                 len = RARRAY_LEN(val);
7246                 if (len >= 12) {
7247                         n = 12;
7248                 } else if (len >= 6) {
7249                         n = 6;
7250                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7251                 for (i = 0; i < n; i++)
7252                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7253         }
7254         convert_coord = (RTEST(cval) ? 1 : 0);
7255         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7256         return val;
7257 }
7258
7259 /*
7260  *  call-seq:
7261  *     box -> [avec, bvec, cvec, origin, flags]
7262  *
7263  *  Get the unit cell information in the form of a periodic bounding box.
7264  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7265  *  Integers which define whether the system is periodic along the axis.
7266  *  If no unit cell is defined, nil is returned.
7267  */
7268 static VALUE
7269 s_Molecule_Box(VALUE self)
7270 {
7271     Molecule *mol;
7272         VALUE v[5], val;
7273     Data_Get_Struct(self, Molecule, mol);
7274         if (mol == NULL || mol->cell == NULL)
7275                 return Qnil;
7276         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7277         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7278         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7279         v[3] = ValueFromVector(&(mol->cell->origin));
7280         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7281         val = rb_ary_new4(5, v);
7282         return val;
7283 }
7284
7285 /*
7286  *  call-seq:
7287  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7288  *     set_box(d, origin = [0, 0, 0])
7289  *     set_box
7290  *
7291  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7292  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7293  as the box vector.
7294  Flags, if present, is a 3-member array of Integers defining whether the system is
7295  periodic along the axis.
7296  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7297  In the second form, an isotropic box with cell-length d is set.
7298  In the third form, the existing box is cleared.
7299  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7300  */
7301 static VALUE
7302 s_Molecule_SetBox(VALUE self, VALUE aval)
7303 {
7304     Molecule *mol;
7305         VALUE v[6];
7306         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7307         Vector vv[3];
7308         Vector origin = {0, 0, 0};
7309         char flags[3];
7310         Double d;
7311         int i, convertCoordinates = 0;
7312     Data_Get_Struct(self, Molecule, mol);
7313         if (aval == Qnil) {
7314                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7315                 return self;
7316         }
7317         aval = rb_ary_to_ary(aval);
7318         for (i = 0; i < 6; i++) {
7319                 if (i < RARRAY_LEN(aval))
7320                         v[i] = (RARRAY_PTR(aval))[i];
7321                 else v[i] = Qnil;
7322         }
7323         if (v[0] == Qnil) {
7324                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7325                 return self;
7326         }
7327         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7328                 d = NUM2DBL(rb_Float(v[0]));
7329                 for (i = 0; i < 3; i++)
7330                         VecScale(vv[i], ax[i], d);
7331                 if (v[1] != Qnil)
7332                         VectorFromValue(v[1], &origin);
7333                 flags[0] = flags[1] = flags[2] = 1;
7334         } else {
7335                 for (i = 0; i < 3; i++) {
7336                         if (v[i] == Qnil) {
7337                                 VecZero(vv[i]);
7338                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7339                                 d = NUM2DBL(rb_Float(v[i]));
7340                                 VecScale(vv[i], ax[i], d);
7341                         } else {
7342                                 VectorFromValue(v[i], &vv[i]);
7343                         }
7344                         flags[i] = (VecLength2(vv[i]) > 0.0);
7345                 }
7346                 if (v[3] != Qnil)
7347                         VectorFromValue(v[3], &origin);
7348                 if (v[4] != Qnil) {
7349                         for (i = 0; i < 3; i++) {
7350                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7351                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7352                         }
7353                 }
7354                 if (RTEST(v[5]))
7355                         convertCoordinates = 1;
7356         }
7357         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7358         return self;
7359 }
7360
7361 /*
7362  *  call-seq:
7363  *     cell_periodicity -> [n1, n2, n3]
7364  *
7365  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7366  *  nil is returned.
7367  */
7368 static VALUE
7369 s_Molecule_CellPeriodicity(VALUE self)
7370 {
7371     Molecule *mol;
7372     Data_Get_Struct(self, Molecule, mol);
7373         if (mol->cell == NULL)
7374                 return Qnil;
7375         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7376 }
7377
7378 /*
7379  *  call-seq:
7380  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7381  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7382  *
7383  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7384  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7385  *  If cell is not defined, exception is raised.
7386  *  This operation is undoable.
7387  */
7388 static VALUE
7389 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7390 {
7391     Molecule *mol;
7392         Int flag;
7393     Data_Get_Struct(self, Molecule, mol);
7394         if (mol->cell == NULL)
7395                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7396         if (arg == Qnil)
7397                 flag = 0;
7398         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7399                 flag = NUM2INT(rb_Integer(arg));
7400         else {
7401                 Int i;
7402                 VALUE arg0;
7403                 arg = rb_ary_to_ary(arg);
7404                 flag = 0;
7405                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7406                         arg0 = RARRAY_PTR(arg)[i];
7407                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7408                                 flag |= (1 << (2 - i));
7409                 }
7410         }
7411         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7412         return arg;
7413 }
7414
7415 /*
7416  *  call-seq:
7417  *     cell_flexibility -> bool
7418  *
7419  *  Returns the unit cell is flexible or not
7420  */
7421 static VALUE
7422 s_Molecule_CellFlexibility(VALUE self)
7423 {
7424         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7425         return Qtrue;
7426         /*    Molecule *mol;
7427          Data_Get_Struct(self, Molecule, mol);
7428          if (mol->cell == NULL)
7429          return Qfalse;
7430          if (mol->useFlexibleCell)
7431          return Qtrue;
7432          else return Qfalse; */
7433 }
7434
7435 /*
7436  *  call-seq:
7437  *     self.cell_flexibility = bool
7438  *     set_cell_flexibility(bool)
7439  *
7440  *  Change the unit cell is flexible or not
7441  */
7442 static VALUE
7443 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7444 {
7445         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7446         return self;
7447         /*    Molecule *mol;
7448          Data_Get_Struct(self, Molecule, mol);
7449          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7450          return self; */
7451 }
7452
7453 /*
7454  *  call-seq:
7455  *     cell_transform -> Transform
7456  *
7457  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7458  *  If cell is not defined, nil is returned.
7459  */
7460 static VALUE
7461 s_Molecule_CellTransform(VALUE self)
7462 {
7463     Molecule *mol;
7464     Data_Get_Struct(self, Molecule, mol);
7465         if (mol == NULL || mol->cell == NULL)
7466                 return Qnil;
7467         return ValueFromTransform(&(mol->cell->tr));
7468 }
7469
7470 /*
7471  *  call-seq:
7472  *     symmetry -> Array of Transforms
7473  *     symmetries -> Array of Transforms
7474  *
7475  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7476  *  returns an empty array.
7477  */
7478 static VALUE
7479 s_Molecule_Symmetry(VALUE self)
7480 {
7481     Molecule *mol;
7482         VALUE val;
7483         int i;
7484     Data_Get_Struct(self, Molecule, mol);
7485         if (mol->nsyms <= 0)
7486                 return rb_ary_new();
7487         val = rb_ary_new2(mol->nsyms);
7488         for (i = 0; i < mol->nsyms; i++) {
7489                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7490         }
7491         return val;
7492 }
7493
7494 /*
7495  *  call-seq:
7496  *     nsymmetries -> Integer
7497  *
7498  *  Get the number of currently defined symmetry operations.
7499  */
7500 static VALUE
7501 s_Molecule_Nsymmetries(VALUE self)
7502 {
7503     Molecule *mol;
7504     Data_Get_Struct(self, Molecule, mol);
7505         return INT2NUM(mol->nsyms);
7506 }
7507
7508 /*
7509  *  call-seq:
7510  *     add_symmetry(Transform) -> Integer
7511  *
7512  *  Add a new symmetry operation. If no symmetry operation is defined and the
7513  *  given argument is not an identity transform, then also add an identity
7514  *  transform at the index 0.
7515  *  Returns the total number of symmetries after operation.
7516  */
7517 static VALUE
7518 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7519 {
7520     Molecule *mol;
7521         Transform tr;
7522     Data_Get_Struct(self, Molecule, mol);
7523         TransformFromValue(trans, &tr);
7524         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7525         return INT2NUM(mol->nsyms);
7526 }
7527
7528 /*
7529  *  call-seq:
7530  *     remove_symmetry(count = nil) -> Integer
7531  *     remove_symmetries(count = nil) -> Integer
7532  *
7533  *  Remove the specified number of symmetry operations. The last added ones are removed
7534  *  first. If count is nil, then all symmetry operations are removed. Returns the
7535  *  number of leftover symmetries.
7536  */
7537 static VALUE
7538 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7539 {
7540     Molecule *mol;
7541         VALUE cval;
7542         int i, n;
7543     Data_Get_Struct(self, Molecule, mol);
7544         rb_scan_args(argc, argv, "01", &cval);
7545         if (cval == Qnil)
7546                 n = mol->nsyms - 1;
7547         else {
7548                 n = NUM2INT(rb_Integer(cval));
7549                 if (n < 0 || n > mol->nsyms)
7550                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7551                 if (n == mol->nsyms)
7552                         n = mol->nsyms - 1;
7553         }
7554         for (i = 0; i < n; i++)
7555                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7556         return INT2NUM(mol->nsyms);
7557 }
7558
7559 /*
7560  *  call-seq:
7561  *     wrap_unit_cell(group) -> Vector3D
7562  *
7563  *  Move the specified group so that the center of mass of the group is within the
7564  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7565  *  exception is raised.
7566  */
7567 static VALUE
7568 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7569 {
7570     Molecule *mol;
7571         IntGroup *ig;
7572         Vector v, cv, dv;
7573     Data_Get_Struct(self, Molecule, mol);
7574         if (mol->cell == NULL)
7575                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7576         ig = s_Molecule_AtomGroupFromValue(self, gval);
7577         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7578         TransformVec(&v, mol->cell->rtr, &cv);
7579         if (mol->cell->flags[0])
7580                 v.x -= floor(v.x);
7581         if (mol->cell->flags[1])
7582                 v.y -= floor(v.y);
7583         if (mol->cell->flags[2])
7584                 v.z -= floor(v.z);
7585         TransformVec(&dv, mol->cell->tr, &v);
7586         VecDec(dv, cv);
7587         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7588         IntGroupRelease(ig);
7589         return ValueFromVector(&dv);
7590 }
7591
7592 /*
7593  *  call-seq:
7594  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7595  *
7596  *  Expand the specified part of the molecule by the given symmetry operation.
7597  *  Returns the array of atom indices corresponding to the expanded atoms.
7598  *  If allow_overlap is true, then new atoms are created even when the
7599  *  coordinates coincide with the some other atom (special position) of the
7600  *  same element; otherwise, such atom will not be created and the index of the
7601  *  existing atom is given in the returned array.
7602  */
7603 static VALUE
7604 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7605 {
7606     Molecule *mol;
7607         VALUE gval, sval, xval, yval, zval, rval, oval;
7608         IntGroup *ig;
7609         Int n[4], allow_overlap;
7610         Int natoms;
7611         Int nidx, *idx;
7612         
7613     Data_Get_Struct(self, Molecule, mol);
7614         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7615         n[0] = NUM2INT(rb_Integer(sval));
7616         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7617         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7618         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7619         allow_overlap = (RTEST(oval) ? 1 : 0);
7620         ig = s_Molecule_AtomGroupFromValue(self, gval);
7621         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7622                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7623         natoms = mol->natoms;
7624         
7625         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7626         
7627         rval = rb_ary_new2(nidx);
7628         while (--nidx >= 0) {
7629                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7630         }
7631         /*      if (natoms == mol->natoms)
7632          rval = Qnil;
7633          else {
7634          rval = IntGroup_Alloc(rb_cIntGroup);
7635          Data_Get_Struct(rval, IntGroup, ig);
7636          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7637          } */
7638         return rval;
7639 }
7640
7641 /*
7642  *  call-seq:
7643  *     amend_by_symmetry(group = nil) -> IntGroup
7644  *
7645  *  Expand the specified part of the molecule by the given symmetry operation.
7646  *  Returns an IntGroup containing the added atoms.
7647  */
7648 static VALUE
7649 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7650 {
7651     Molecule *mol;
7652         IntGroup *ig, *ig2;
7653         VALUE rval, gval;
7654     Data_Get_Struct(self, Molecule, mol);
7655         rb_scan_args(argc, argv, "01", &gval);
7656         if (gval != Qnil)
7657                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7658         else ig = NULL;
7659         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7660         rval = ValueFromIntGroup(ig2);
7661         IntGroupRelease(ig2);
7662         return rval;
7663 }
7664
7665 #pragma mark ------ Transforms ------
7666
7667 /*
7668  *  call-seq:
7669  *     translate(vec, group = nil)       -> Molecule
7670  *
7671  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7672  *  This operation is undoable.
7673  */
7674 static VALUE
7675 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7676 {
7677     Molecule *mol;
7678         VALUE vec, group;
7679         Vector v;
7680         IntGroup *ig;
7681     Data_Get_Struct(self, Molecule, mol);
7682         rb_scan_args(argc, argv, "11", &vec, &group);
7683         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7684         VectorFromValue(vec, &v);
7685         //      MoleculeTranslate(mol, &v, ig);
7686         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7687         if (ig != NULL)
7688                 IntGroupRelease(ig);
7689         return self;
7690 }
7691
7692 /*
7693  *  call-seq:
7694  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7695  *
7696  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7697  *  If group is given, only atoms in the group are moved.
7698  *  This operation is undoable.
7699  */
7700 static VALUE
7701 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7702 {
7703     Molecule *mol;
7704         volatile VALUE aval, anval, cval, gval;
7705         Double angle;
7706         Vector av, cv;
7707         Transform tr;
7708         IntGroup *ig;
7709     Data_Get_Struct(self, Molecule, mol);
7710         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7711         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7712         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7713         VectorFromValue(aval, &av);
7714         if (NIL_P(cval))
7715                 cv.x = cv.y = cv.z = 0.0;
7716         else
7717                 VectorFromValue(cval, &cv);
7718         if (TransformForRotation(tr, &av, angle, &cv))
7719                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7720         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7721         if (ig != NULL)
7722                 IntGroupRelease(ig);
7723         return self;
7724 }
7725
7726 /*
7727  *  call-seq:
7728  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7729  *
7730  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7731  *  axis must not be a zero vector.
7732  *  If group is given, only atoms in the group are moved.
7733  *  This operation is undoable.
7734  */
7735 static VALUE
7736 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7737 {
7738     Molecule *mol;
7739         volatile VALUE aval, cval, gval;
7740         Vector av, cv;
7741         Transform tr;
7742         IntGroup *ig;
7743     Data_Get_Struct(self, Molecule, mol);
7744         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7745         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7746         VectorFromValue(aval, &av);
7747         if (NIL_P(cval))
7748                 cv.x = cv.y = cv.z = 0.0;
7749         else
7750                 VectorFromValue(cval, &cv);
7751         if (TransformForReflection(tr, &av, &cv))
7752                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7753         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7754         if (ig != NULL)
7755                 IntGroupRelease(ig);
7756         return self;
7757 }
7758
7759 /*
7760  *  call-seq:
7761  *     invert(center = [0,0,0], group = nil)       -> Molecule
7762  *
7763  *  Invert the molecule with the given center.
7764  *  If group is given, only atoms in the group are moved.
7765  *  This operation is undoable.
7766  */
7767 static VALUE
7768 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7769 {
7770         Molecule *mol;
7771         volatile VALUE cval, gval;
7772         Vector cv;
7773         Transform tr;
7774         IntGroup *ig;
7775     Data_Get_Struct(self, Molecule, mol);
7776         rb_scan_args(argc, argv, "02", &cval, &gval);
7777         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7778         if (NIL_P(cval))
7779                 cv.x = cv.y = cv.z = 0.0;
7780         else
7781                 VectorFromValue(cval, &cv);
7782         TransformForInversion(tr, &cv);
7783         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7784         if (ig != NULL)
7785                 IntGroupRelease(ig);
7786         return self;
7787 }
7788
7789 /*
7790  *  call-seq:
7791  *     transform(transform, group = nil)       -> Molecule
7792  *
7793  *  Transform the molecule by the given Transform object.
7794  *  If group is given, only atoms in the group are moved.
7795  *  This operation is undoable.
7796  */
7797 static VALUE
7798 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7799 {
7800     Molecule *mol;
7801         VALUE trans, group;
7802         Transform tr;
7803         IntGroup *ig;
7804     Data_Get_Struct(self, Molecule, mol);
7805         rb_scan_args(argc, argv, "11", &trans, &group);
7806         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7807         TransformFromValue(trans, &tr);
7808         /*      MoleculeTransform(mol, tr, ig); */
7809         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7810         if (ig != NULL)
7811                 IntGroupRelease(ig);
7812         return self;
7813 }
7814
7815 /*
7816  *  call-seq:
7817  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7818  *
7819  *  Get the transform corresponding to the symmetry operation. The symop can either be
7820  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7821  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7822  *  Otherwise, the returned transform is for fractional coordinates.
7823  *  Raises exception when no cell or no transform are defined.
7824  */
7825 static VALUE
7826 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7827 {
7828     Molecule *mol;
7829         VALUE sval, fval;
7830         Symop symop;
7831         Transform tr;
7832     Data_Get_Struct(self, Molecule, mol);
7833         if (mol->cell == NULL)
7834                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7835         if (mol->nsyms == 0)
7836                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7837         rb_scan_args(argc, argv, "11", &sval, &fval);
7838         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7839                 symop.sym = NUM2INT(rb_Integer(sval));
7840                 symop.dx = symop.dy = symop.dz = 0;
7841         } else {
7842                 sval = rb_ary_to_ary(sval);
7843                 if (RARRAY_LEN(sval) < 4)
7844                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7845                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7846                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7847                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7848                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7849         }
7850         if (symop.sym >= mol->nsyms)
7851                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7852         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7853         return ValueFromTransform(&tr);
7854 }
7855
7856 /*
7857  *  call-seq:
7858  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7859  *
7860  *  Get the symmetry operation corresponding to the given transform.
7861  *  If is_cartesian is true, the given transform is for cartesian coordinates.
7862  *  Otherwise, the given transform is for fractional coordinates.
7863  *  Raises exception when no cell or no transform are defined.
7864  */
7865 static VALUE
7866 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7867 {
7868     Molecule *mol;
7869         VALUE tval, fval;
7870         Symop symop;
7871         Transform tr;
7872         int n;
7873     Data_Get_Struct(self, Molecule, mol);
7874         if (mol->cell == NULL)
7875                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7876         if (mol->nsyms == 0)
7877                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7878         rb_scan_args(argc, argv, "11", &tval, &fval);
7879         TransformFromValue(tval, &tr);
7880         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7881         if (n == 0) {
7882                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7883         } else {
7884                 return Qnil;  /*  Not found  */
7885         }
7886 }
7887
7888 #pragma mark ------ Frames ------
7889
7890 /*
7891  *  call-seq:
7892  *     select_frame(index)
7893  *     frame = index
7894  *
7895  *  Select the specified frame. If successful, returns true, otherwise returns false.
7896  */
7897 static VALUE
7898 s_Molecule_SelectFrame(VALUE self, VALUE val)
7899 {
7900     Molecule *mol;
7901         int ival = NUM2INT(val);
7902     Data_Get_Struct(self, Molecule, mol);
7903         ival = MoleculeSelectFrame(mol, ival, 1);
7904         if (ival >= 0)
7905                 return Qtrue;
7906         else return Qfalse;
7907 }
7908
7909 /*
7910  *  call-seq:
7911  *     frame -> Integer
7912  *
7913  *  Get the current frame.
7914  */
7915 static VALUE
7916 s_Molecule_Frame(VALUE self)
7917 {
7918     Molecule *mol;
7919     Data_Get_Struct(self, Molecule, mol);
7920         return INT2NUM(mol->cframe);
7921 }
7922
7923 /*
7924  *  call-seq:
7925  *     nframes -> Integer
7926  *
7927  *  Get the number of frames.
7928  */
7929 static VALUE
7930 s_Molecule_Nframes(VALUE self)
7931 {
7932     Molecule *mol;
7933     Data_Get_Struct(self, Molecule, mol);
7934         return INT2NUM(MoleculeGetNumberOfFrames(mol));
7935 }
7936
7937 /*
7938  *  call-seq:
7939  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7940  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7941  *
7942  *  Insert new frames at the indices specified by the intGroup. If the first argument is
7943  *  an integer, a single new frame is inserted at that index. If the first argument is 
7944  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7945  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
7946  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
7947  *  to the new frame.
7948  *  Returns an intGroup representing the inserted frames if successful, nil if not.
7949  */
7950 static VALUE
7951 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7952 {
7953         VALUE val, coords, cells;
7954     Molecule *mol;
7955         IntGroup *ig;
7956         int count, ival, i, j, len, len_c, len2, nframes;
7957         VALUE *ptr, *ptr2;
7958         Vector *vp, *vp2;
7959     Data_Get_Struct(self, Molecule, mol);
7960         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7961         if (coords != Qnil) {
7962                 if (TYPE(coords) != T_ARRAY)
7963                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7964                 len = RARRAY_LEN(coords);
7965         } else len = 0;
7966         if (cells != Qnil) {
7967                 if (mol->cell == NULL)
7968                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7969                 if (TYPE(cells) != T_ARRAY)
7970                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7971                 len_c = RARRAY_LEN(cells);
7972         } else len_c = 0;
7973         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
7974         nframes = MoleculeGetNumberOfFrames(mol);
7975         if (val == Qnil) {
7976                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7977                 val = ValueFromIntGroup(ig);
7978         } else {
7979                 ig = IntGroupFromValue(val);
7980         }
7981         count = IntGroupGetCount(ig);  /*  Count is updated here  */
7982         vp = ALLOC_N(Vector, mol->natoms * count);
7983         if (cells != Qnil)
7984                 vp2 = ALLOC_N(Vector, 4 * count);
7985         else vp2 = NULL;
7986         if (len > 0) {
7987                 if (len < count)
7988                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7989                 ptr = RARRAY_PTR(coords);
7990                 for (i = 0; i < count; i++) {
7991                         if (TYPE(ptr[i]) != T_ARRAY)
7992                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7993                         len2 = RARRAY_LEN(ptr[i]);
7994                         if (len2 < mol->natoms)
7995                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7996                         ptr2 = RARRAY_PTR(ptr[i]);
7997                         for (j = 0; j < mol->natoms; j++)
7998                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7999                 }
8000         } else {
8001                 Atom *ap;
8002                 for (i = 0; i < count; i++) {
8003                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8004                                 vp[i * mol->natoms + j] = ap->r;
8005                         }
8006                 }
8007         }
8008         if (len_c > 0) {
8009                 if (len_c < count)
8010                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8011                 ptr = RARRAY_PTR(cells);
8012                 for (i = 0; i < count; i++) {
8013                         if (TYPE(ptr[i]) != T_ARRAY)
8014                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8015                         len2 = RARRAY_LEN(ptr[i]);
8016                         if (len2 < 4)
8017                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8018                         ptr2 = RARRAY_PTR(ptr[i]);
8019                         for (j = 0; j < 4; j++)
8020                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8021                 }
8022         }
8023         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8024         IntGroupRelease(ig);
8025         xfree(vp);
8026         if (vp2 != NULL)
8027                 xfree(vp2);
8028         return (ival >= 0 ? val : Qnil);
8029 }
8030
8031 /*
8032  *  call-seq:
8033  *     create_frame(coordinates = nil) -> Integer
8034  *     create_frames(coordinates = nil) -> Integer
8035  *
8036  *  Same as molecule.insert_frames(nil, coordinates).
8037  */
8038 static VALUE
8039 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8040 {
8041         VALUE vals[3];
8042         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8043         vals[0] = Qnil;
8044         return s_Molecule_InsertFrames(3, vals, self);
8045 }
8046
8047 /*
8048  *  call-seq:
8049  *     remove_frames(IntGroup, wantCoordinates = false)
8050  *
8051  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8052  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8053  *  removed frames is returned if operation is successful.
8054  */
8055 static VALUE
8056 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8057 {
8058         VALUE val, flag;
8059         VALUE retval;
8060     Molecule *mol;
8061         IntGroup *ig;
8062         int count;
8063     Data_Get_Struct(self, Molecule, mol);
8064         rb_scan_args(argc, argv, "11", &val, &flag);
8065         ig = IntGroupFromValue(val);
8066         count = IntGroupGetCount(ig);
8067         if (RTEST(flag)) {
8068                 /*  Create return value before removing frames  */
8069                 VALUE coords;
8070                 int i, j, n;
8071                 Atom *ap;
8072                 Vector v;
8073                 retval = rb_ary_new2(count);
8074                 for (i = 0; i < count; i++) {
8075                         n = IntGroupGetNthPoint(ig, i);
8076                         coords = rb_ary_new2(mol->natoms);
8077                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8078                                 if (n < ap->nframes && n != mol->cframe)
8079                                         v = ap->frames[n];
8080                                 else v = ap->r;
8081                                 rb_ary_push(coords, ValueFromVector(&v));
8082                         }
8083                         rb_ary_push(retval, coords);
8084                 }
8085         } else retval = Qtrue;
8086         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8087                 return retval;
8088         else return Qnil;
8089 }
8090
8091 /*
8092  *  call-seq:
8093  *     each_frame {|n| ...}
8094  *
8095  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8096  *  the frame number. After completion, the original frame number is restored.
8097  */
8098 static VALUE
8099 s_Molecule_EachFrame(VALUE self)
8100 {
8101         int i, cframe, nframes;
8102     Molecule *mol;
8103     Data_Get_Struct(self, Molecule, mol);
8104         cframe = mol->cframe;
8105         nframes = MoleculeGetNumberOfFrames(mol);
8106         if (nframes > 0) {
8107                 for (i = 0; i < nframes; i++) {
8108                         MoleculeSelectFrame(mol, i, 1);
8109                         rb_yield(INT2NUM(i));
8110                 }
8111                 MoleculeSelectFrame(mol, cframe, 1);
8112         }
8113     return self;
8114 }
8115
8116 /*
8117  *  call-seq:
8118  *     get_coord_from_frame(index, group = nil)
8119  *
8120  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8121  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8122  *  copied; now they are always copied)
8123  */
8124 static VALUE
8125 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8126 {
8127         Molecule *mol;
8128         VALUE ival, gval, cval;
8129         Int index, i, j, n, nn;
8130         IntGroup *ig;
8131         IntGroupIterator iter;
8132         Atom *ap;
8133         Vector *vp;
8134     Data_Get_Struct(self, Molecule, mol);
8135         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8136         if (argc == 3)
8137                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8138         index = NUM2INT(rb_Integer(ival));
8139         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8140                 if (n == 0)
8141                         rb_raise(rb_eMolbyError, "No frame is present");
8142                 else
8143                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8144         }
8145         if (gval == Qnil) {
8146                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8147         } else {
8148                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8149         }
8150         n = IntGroupGetCount(ig);
8151         if (n > 0) {
8152                 vp = (Vector *)calloc(sizeof(Vector), n);
8153                 IntGroupIteratorInit(ig, &iter);
8154                 j = 0;
8155                 nn = 0;
8156                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8157                         ap = ATOM_AT_INDEX(mol->atoms, i);
8158                         if (index < ap->nframes) {
8159                                 vp[j] = ap->frames[index];
8160                                 nn++;
8161                         } else {
8162                                 vp[j] = ap->r;
8163                         }
8164                         j++;
8165                 }
8166                 if (nn > 0)
8167                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8168                 free(vp);
8169                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8170                         vp = mol->frame_cells + index * 4;
8171                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8172                 }
8173                 IntGroupIteratorRelease(&iter);
8174         }
8175         /*  Copy the extra properties  */
8176         IntGroupRelease(ig);
8177         for (i = 0; i < mol->nmolprops; i++) {
8178                 Double *dp = (Double *)malloc(sizeof(Double));
8179                 ig = IntGroupNew();
8180                 IntGroupAdd(ig, mol->cframe, 1);
8181                 *dp = mol->molprops[i].propvals[index];
8182                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8183                 free(dp);
8184                 IntGroupRelease(ig);
8185         }
8186         
8187         return self;
8188 }
8189
8190 /*
8191  *  call-seq:
8192  *     reorder_frames(old_indices)
8193  *
8194  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8195  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8196  *  same as the old frames 2/0/1, respectively.
8197  *  The argument must have the same number of integers as the number of frames.
8198  */
8199 static VALUE
8200 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8201 {
8202         Molecule *mol;
8203         Int *ip, *ip2, i, n, nframes;
8204     Data_Get_Struct(self, Molecule, mol);
8205         aval = rb_ary_to_ary(aval);
8206         nframes = MoleculeGetNumberOfFrames(mol);
8207         if (RARRAY_LEN(aval) != nframes)
8208                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8209         ip2 = (Int *)calloc(sizeof(Int), nframes);
8210         ip = (Int *)calloc(sizeof(Int), nframes);
8211         for (i = 0; i < nframes; i++) {
8212                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8213                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8214                         free(ip2);
8215                         free(ip);
8216                         if (n < 0 || n >= nframes)
8217                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8218                         else
8219                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8220                 }
8221                 ip2[n] = 1;
8222                 ip[i] = n;
8223         }
8224         free(ip2);
8225         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8226         free(ip);
8227         return self;
8228 }
8229
8230 #pragma mark ------ Fragments ------
8231
8232 /*
8233  *  call-seq:
8234  *     fragment(n1, *exatoms)  -> IntGroup
8235  *     fragment(group, *exatoms)  -> IntGroup
8236  *
8237  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8238  *  those atoms will not be counted during the search.
8239  */
8240 static VALUE
8241 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8242 {
8243     Molecule *mol;
8244         IntGroup *baseg, *ig, *exatoms;
8245         int n;
8246         volatile VALUE nval, exval;
8247     Data_Get_Struct(self, Molecule, mol);
8248         rb_scan_args(argc, argv, "1*", &nval, &exval);
8249         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8250                 baseg = NULL;
8251                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8252         } else {
8253                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8254         }
8255         if (RARRAY_LEN(exval) == 0) {
8256                 exatoms = NULL;
8257         } else {
8258                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8259                 Data_Get_Struct(exval, IntGroup, exatoms);
8260         }
8261         if (baseg == NULL) {
8262                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8263         } else {
8264                 IntGroupIterator iter;
8265                 IntGroupIteratorInit(baseg, &iter);
8266                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8267                         ig = IntGroupNew();
8268                 } else {
8269                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8270                         if (ig != NULL) {
8271                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8272                                         IntGroup *subg;
8273                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8274                                         if (subg != NULL) {
8275                                                 IntGroupAddIntGroup(ig, subg);
8276                                                 IntGroupRelease(subg);
8277                                         }
8278                                 }
8279                         }
8280                 }
8281                 IntGroupIteratorRelease(&iter);
8282                 IntGroupRelease(baseg);
8283         }
8284         if (ig == NULL)
8285                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8286         nval = ValueFromIntGroup(ig);
8287         IntGroupRelease(ig);
8288         return nval;
8289 }
8290
8291 /*
8292  *  call-seq:
8293  *     fragments(exclude = nil)
8294  *
8295  *  Returns the fragments as an array of IntGroups. 
8296  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8297  *  in defining the fragment.
8298  */
8299 static VALUE
8300 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8301 {
8302     Molecule *mol;
8303         IntGroup *ag, *fg, *eg;
8304         VALUE gval, exval, retval;
8305     Data_Get_Struct(self, Molecule, mol);
8306         if (mol == NULL)
8307                 return Qnil;
8308         if (mol->natoms == 0)
8309                 return rb_ary_new();
8310         rb_scan_args(argc, argv, "01", &exval);
8311         if (exval == Qnil)
8312                 eg = NULL;
8313         else
8314                 eg = IntGroupFromValue(exval);
8315         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8316         if (eg != NULL)
8317                 IntGroupRemoveIntGroup(ag, eg);
8318         retval = rb_ary_new();
8319         while (IntGroupGetCount(ag) > 0) {
8320                 int n = IntGroupGetNthPoint(ag, 0);
8321                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8322                 if (fg == NULL)
8323                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8324                 gval = ValueFromIntGroup(fg);
8325                 rb_ary_push(retval, gval);
8326                 IntGroupRemoveIntGroup(ag, fg);
8327                 IntGroupRelease(fg);
8328         }
8329         IntGroupRelease(ag);
8330         if (eg != NULL)
8331                 IntGroupRelease(eg);
8332         return retval;
8333 }
8334
8335 /*
8336  *  call-seq:
8337  *     each_fragment(exclude = nil) {|group| ...}
8338  *
8339  *  Execute the block, with the IntGroup object for each fragment as the argument.
8340  *  Atoms or bonds should not be added or removed during the execution of the block.
8341  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8342  *  in defining the fragment.
8343  */
8344 static VALUE
8345 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8346 {
8347     Molecule *mol;
8348         IntGroup *ag, *fg, *eg;
8349         VALUE gval, exval;
8350     Data_Get_Struct(self, Molecule, mol);
8351         if (mol == NULL || mol->natoms == 0)
8352                 return self;
8353         rb_scan_args(argc, argv, "01", &exval);
8354         if (exval == Qnil)
8355                 eg = NULL;
8356         else
8357                 eg = IntGroupFromValue(exval);
8358         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8359         if (eg != NULL)
8360                 IntGroupRemoveIntGroup(ag, eg);
8361         while (IntGroupGetCount(ag) > 0) {
8362                 int n = IntGroupGetNthPoint(ag, 0);
8363                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8364                 if (fg == NULL)
8365                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8366                 gval = ValueFromIntGroup(fg);
8367                 rb_yield(gval);
8368                 IntGroupRemoveIntGroup(ag, fg);
8369                 IntGroupRelease(fg);
8370         }
8371         IntGroupRelease(ag);
8372         if (eg != NULL)
8373                 IntGroupRelease(eg);
8374         return self;
8375 }
8376
8377 /*
8378  *  call-seq:
8379  *     detachable?(group)  -> [n1, n2]
8380  *
8381  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8382  *  of the molecule via only one bond. If it is, then the indices of the atoms
8383  *  belonging to the bond is returned, the first element being the atom included
8384  *  in the fragment. Otherwise, Qnil is returned.
8385  */
8386 static VALUE
8387 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8388 {
8389         Molecule *mol;
8390         IntGroup *ig;
8391         int n1, n2;
8392         VALUE retval;
8393     Data_Get_Struct(self, Molecule, mol);
8394         ig = s_Molecule_AtomGroupFromValue(self, gval);
8395         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8396                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8397         } else retval = Qnil;
8398         IntGroupRelease(ig);
8399         return retval;
8400 }
8401
8402 /*
8403  *  call-seq:
8404  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8405  *
8406  *  Returns an array of bonds that connect an atom in the group and an atom out
8407  *  of the group. The first atom in the bond always belongs to the group. If no
8408  *  such bonds are present, an empty array is returned.
8409  */
8410 static VALUE
8411 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8412 {
8413         Molecule *mol;
8414         IntGroup *ig, *bg;
8415         VALUE gval, retval;
8416     Data_Get_Struct(self, Molecule, mol);
8417         rb_scan_args(argc, argv, "01", &gval);
8418         if (gval == Qnil) {
8419                 ig = MoleculeGetSelection(mol);
8420                 if (ig != NULL)
8421                         IntGroupRetain(ig);
8422         } else {
8423                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8424         }
8425         retval = rb_ary_new();
8426         if (ig == NULL)
8427                 return retval;
8428         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8429         if (bg != NULL) {
8430                 IntGroupIterator iter;
8431                 Int i;
8432                 IntGroupIteratorInit(bg, &iter);
8433                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8434                         /*  The atoms at the border  */
8435                         Int n1, n2;
8436                         n1 = mol->bonds[i * 2];
8437                         n2 = mol->bonds[i * 2 + 1];
8438                         if (IntGroupLookupPoint(ig, n1) < 0) {
8439                                 int w = n1;
8440                                 n1 = n2;
8441                                 n2 = w;
8442                                 if (IntGroupLookupPoint(ig, n1) < 0)
8443                                         continue;  /*  Actually this is an internal error  */
8444                         }
8445                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8446                 }
8447                 IntGroupIteratorRelease(&iter);
8448         }
8449         IntGroupRelease(bg);
8450         IntGroupRelease(ig);
8451         return retval;
8452 }
8453
8454 /*  Calculate the transform that moves the current coordinates to the reference
8455  coordinates with least displacements.   */
8456 static Double
8457 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8458 {
8459         Atom *ap, *ap1;
8460         Int natoms, nn;
8461         Vector org1, org2;
8462         Int i, in, j, k;
8463         Double w, w1;
8464         Mat33 r, q, u;
8465         Double eigen_val[3];
8466         Vector eigen_vec[3];
8467         Vector s[3];
8468         IntGroupIterator iter;
8469
8470         natoms = mol->natoms;
8471         ap = mol->atoms;
8472         IntGroupIteratorInit(ig, &iter);
8473         
8474         /*  Calculate the weighted center  */
8475         VecZero(org1);
8476         VecZero(org2);
8477         w = 0.0;
8478         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8479                 ap1 = ATOM_AT_INDEX(ap, in);
8480                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8481                 VecScaleInc(org1, ap1->r, w1);
8482                 VecScaleInc(org2, ref[i], w1);
8483                 w += w1;
8484         }
8485         w = 1.0 / w;
8486         VecScaleSelf(org1, w);
8487         VecScaleSelf(org2, w);
8488
8489     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8490     /*  Matrix to diagonalize = R * tR    */
8491         memset(r, 0, sizeof(Mat33));
8492         memset(q, 0, sizeof(Mat33));
8493         memset(u, 0, sizeof(Mat33));
8494         nn = 0;
8495         IntGroupIteratorReset(&iter);
8496         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8497                 Vector v1, v2;
8498                 ap1 = ATOM_AT_INDEX(ap, in);
8499                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8500                 w1 *= w1;
8501                 VecSub(v1, ap1->r, org1);
8502                 VecSub(v2, ref[i], org2);
8503                 r[0] += w1 * v1.x * v2.x;
8504                 r[1] += w1 * v1.y * v2.x;
8505                 r[2] += w1 * v1.z * v2.x;
8506                 r[3] += w1 * v1.x * v2.y;
8507                 r[4] += w1 * v1.y * v2.y;
8508                 r[5] += w1 * v1.z * v2.y;
8509                 r[6] += w1 * v1.x * v2.z;
8510                 r[7] += w1 * v1.y * v2.z;
8511                 r[8] += w1 * v1.z * v2.z;
8512                 nn++;
8513         }
8514         for (i = 0; i < 9; i++)
8515                 r[i] /= (nn * nn);
8516         for (i = 0; i < 3; i++) {
8517                 for (j = 0; j < 3; j++) {
8518                         for (k = 0; k < 3; k++) {
8519                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8520                         }
8521                 }
8522         }
8523         
8524         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8525                 IntGroupIteratorRelease(&iter);
8526                 return -1.0;  /*  Cannot determine the eigenvector  */
8527         }
8528
8529     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8530     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8531         MatrixTranspose(r, r);
8532         for (i = 0; i < 3; i++) {
8533                 MatrixVec(&s[i], r, &eigen_vec[i]);
8534                 w1 = 1.0 / sqrt(eigen_val[i]);
8535                 VecScaleSelf(s[i], w1);
8536         }
8537         for (k = 0; k < 3; k++) {
8538                 u[0] += s[k].x * eigen_vec[k].x;
8539                 u[1] += s[k].y * eigen_vec[k].x;
8540                 u[2] += s[k].z * eigen_vec[k].x;
8541                 u[3] += s[k].x * eigen_vec[k].y;
8542                 u[4] += s[k].y * eigen_vec[k].y;
8543                 u[5] += s[k].z * eigen_vec[k].y;
8544                 u[6] += s[k].x * eigen_vec[k].z;
8545                 u[7] += s[k].y * eigen_vec[k].z;
8546                 u[8] += s[k].z * eigen_vec[k].z;
8547         }
8548         
8549         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8550         MatrixVec(&org1, u, &org1);
8551         VecDec(org2, org1);
8552         for (i = 0; i < 9; i++)
8553                 trans[i] = u[i];
8554         trans[9] = org2.x;
8555         trans[10] = org2.y;
8556         trans[11] = org2.z;
8557         
8558         /*  Calculate rmsd  */
8559         IntGroupIteratorReset(&iter);
8560         w = 0.0;
8561         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8562                 Vector tv;
8563                 ap1 = ATOM_AT_INDEX(ap, in);
8564                 TransformVec(&tv, trans, &ap1->r);
8565                 VecDec(tv, ref[i]);
8566                 w += VecLength2(tv);
8567         }
8568         w = sqrt(w / nn);
8569         IntGroupIteratorRelease(&iter);
8570         return w;
8571 }
8572
8573 /*
8574  *  call-seq:
8575  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8576  *
8577  *  Calculate the transform to fit the given group to the set of reference coordinates.
8578  *  The reference coordinates ref is given as either a frame number, an array of
8579  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8580  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8581  *  Return values are the transform (that converts the present coordinates to the
8582  *  target coordinates) and root mean square deviation (without weight).
8583  */
8584 static VALUE
8585 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8586 {
8587         Molecule *mol;
8588         Atom *ap;
8589         VALUE gval, rval, wval;
8590         IntGroup *ig;
8591         IntGroupIterator iter;
8592         int nn, errno, i, j, in, status;
8593         Vector *ref;
8594         Double *weights, dval[3];
8595         Transform tr;
8596
8597         Data_Get_Struct(self, Molecule, mol);
8598         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8599         if (gval == Qnil)
8600                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8601         else
8602                 ig = IntGroupFromValue(gval);
8603         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8604                 IntGroupRelease(ig);
8605                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8606         }
8607         ref = (Vector *)calloc(sizeof(Vector), nn);
8608         weights = (Double *)calloc(sizeof(Double), nn);
8609         IntGroupIteratorInit(ig, &iter);
8610         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8611                 int fn = NUM2INT(rb_Integer(rval));
8612                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8613                         errno = 1;
8614                         status = fn;
8615                         goto err;
8616                 }
8617                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8618                         ap = ATOM_AT_INDEX(mol->atoms, in);
8619                         if (fn < ap->nframes)
8620                                 ref[i] = ap->frames[fn];
8621                         else ref[i] = ap->r;
8622                 }
8623         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8624                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8625                 if (m->row * m->column < nn * 3) {
8626                         errno = 2;
8627                         goto err;
8628                 }
8629                 for (i = 0; i < nn; i++) {
8630                         ref[i].x = m->data[i * 3];
8631                         ref[i].y = m->data[i * 3 + 1];
8632                         ref[i].z = m->data[i * 3 + 2];
8633                 }
8634         } else {
8635                 VALUE aval;
8636                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8637                 if (status != 0) {
8638                         errno = 3;
8639                         goto err;
8640                 }
8641                 if (RARRAY_LEN(rval) < nn) {
8642                         errno = 2;
8643                         goto err;
8644                 }
8645                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8646                         /*  Array of 3*nn numbers  */
8647                         if (RARRAY_LEN(rval) < nn * 3) {
8648                                 errno = 2;
8649                                 goto err;
8650                         }
8651                         for (i = 0; i < nn; i++) {
8652                                 for (j = 0; j < 3; j++) {
8653                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8654                                         if (status != 0) {
8655                                                 errno = 3;
8656                                                 goto err;
8657                                         }
8658                                         dval[j] = NUM2DBL(aval);
8659                                 }
8660                                 ref[i].x = dval[0];
8661                                 ref[i].y = dval[1];
8662                                 ref[i].z = dval[2];
8663                         }
8664                 } else {
8665                         /*  Array of nn Vector3Ds or Arrays  */
8666                         for (i = 0; i < nn; i++) {
8667                                 aval = (RARRAY_PTR(rval))[i];
8668                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8669                                         VectorFromValue(aval, &ref[i]);
8670                                 } else {
8671                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8672                                         if (status != 0) {
8673                                                 errno = 3;
8674                                                 goto err;
8675                                         }
8676                                         if (RARRAY_LEN(aval) < 3) {
8677                                                 errno = 4;
8678                                                 status = i;
8679                                                 goto err;
8680                                         }
8681                                         for (j = 0; j < 3; j++) {
8682                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8683                                                 if (status != 0) {
8684                                                         errno = 3;
8685                                                         goto err;
8686                                                 }
8687                                                 dval[j] = NUM2DBL(aaval);
8688                                         }
8689                                         ref[i].x = dval[0];
8690                                         ref[i].y = dval[1];
8691                                         ref[i].z = dval[2];
8692                                 }
8693                         }
8694                 }
8695         }
8696         if (wval == Qnil) {
8697                 /*  Use atomic weights  */
8698                 IntGroupIteratorReset(&iter);
8699                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8700                         ap = ATOM_AT_INDEX(mol->atoms, in);
8701                         weights[i] = ap->weight;
8702                 }
8703         } else {
8704                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8705                 if (status != 0) {
8706                         errno = 3;
8707                         goto err;
8708                 }
8709                 if (RARRAY_LEN(wval) < nn) {
8710                         errno = 5;
8711                         goto err;
8712                 }
8713                 for (i = 0; i < nn; i++) {
8714                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8715                         if (status != 0) {
8716                                 errno = 3;
8717                                 goto err;
8718                         }
8719                         weights[i] = NUM2DBL(wwval);
8720                 }
8721         }
8722         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8723         if (dval[0] < 0) {
8724                 errno = 6;
8725                 goto err;
8726         }
8727         errno = 0;
8728 err:
8729         IntGroupIteratorRelease(&iter);
8730         free(ref);
8731         free(weights);
8732         if (errno == 0) {
8733                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8734         } else if (errno == 1) {
8735                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8736         } else if (errno == 2) {
8737                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8738         } else if (errno == 3) {
8739                 rb_jump_tag(status);
8740         } else if (errno == 4) {
8741                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8742         } else if (errno == 5) {
8743                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8744         } else if (errno == 6) {
8745                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8746         }
8747         return Qnil;  /*  Not reached  */
8748 }
8749
8750 #pragma mark ------ Screen Display ------
8751
8752 /*
8753  *  call-seq:
8754  *     display
8755  *
8756  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8757  */
8758 static VALUE
8759 s_Molecule_Display(VALUE self)
8760 {
8761     Molecule *mol;
8762     Data_Get_Struct(self, Molecule, mol);
8763         if (mol->mview != NULL)
8764                 MainViewCallback_display(mol->mview);
8765         return Qnil;
8766 }
8767
8768 /*
8769  *  call-seq:
8770  *     make_front
8771  *
8772  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8773  */
8774 static VALUE
8775 s_Molecule_MakeFront(VALUE self)
8776 {
8777     Molecule *mol;
8778     Data_Get_Struct(self, Molecule, mol);
8779         if (mol->mview != NULL)
8780                 MainViewCallback_makeFront(mol->mview);
8781         return Qnil;
8782 }
8783
8784 /*
8785  *  call-seq:
8786  *     update_enabled? -> bool
8787  *
8788  *  Returns true if screen update is enabled; otherwise no.
8789  */
8790 static VALUE
8791 s_Molecule_UpdateEnabled(VALUE self)
8792 {
8793     Molecule *mol;
8794     Data_Get_Struct(self, Molecule, mol);
8795         if (mol->mview != NULL && !mol->mview->freezeScreen)
8796                 return Qtrue;
8797         else return Qfalse;
8798 }
8799
8800 /*
8801  *  call-seq:
8802  *     update_enabled = bool
8803  *
8804  *  Enable or disable screen update. This is effective for automatic update on modification.
8805  *  Explicit call to molecule.display() always updates the screen.
8806  */
8807 static VALUE
8808 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8809 {
8810     Molecule *mol;
8811     Data_Get_Struct(self, Molecule, mol);
8812         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8813         if (mol->mview != NULL)
8814                 mol->mview->freezeScreen = (val == Qfalse);
8815         else val = Qfalse;
8816         return val;
8817 }
8818
8819 /*
8820  *  call-seq:
8821  *     show_unitcell
8822  *     show_unitcell(bool)
8823  *     show_unitcell = bool
8824  *
8825  *  Set the flag whether to show the unit cell. If no argument is given, the
8826  *  current flag is returned.
8827  */
8828 static VALUE
8829 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8830 {
8831     Molecule *mol;
8832     Data_Get_Struct(self, Molecule, mol);
8833         if (mol->mview == NULL)
8834                 return Qnil;
8835         if (argc > 0) {
8836                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8837                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8838         }
8839         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8840 }
8841
8842 /*
8843  *  call-seq:
8844  *     show_hydrogens
8845  *     show_hydrogens(bool)
8846  *     show_hydrogens = bool
8847  *
8848  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8849  *  current flag is returned.
8850  */
8851 static VALUE
8852 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8853 {
8854     Molecule *mol;
8855     Data_Get_Struct(self, Molecule, mol);
8856         if (mol->mview == NULL)
8857                 return Qnil;
8858         if (argc > 0) {
8859                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8860                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8861         }
8862         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8863 }
8864
8865 /*
8866  *  call-seq:
8867  *     show_dummy_atoms
8868  *     show_dummy_atoms(bool)
8869  *     show_dummy_atoms = bool
8870  *
8871  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8872  *  current flag is returned.
8873  */
8874 static VALUE
8875 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8876 {
8877     Molecule *mol;
8878     Data_Get_Struct(self, Molecule, mol);
8879         if (mol->mview == NULL)
8880                 return Qnil;
8881         if (argc > 0) {
8882                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8883                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8884         }
8885         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8886 }
8887
8888 /*
8889  *  call-seq:
8890  *     show_expanded
8891  *     show_expanded(bool)
8892  *     show_expanded = bool
8893  *
8894  *  Set the flag whether to show the expanded atoms. If no argument is given, the
8895  *  current flag is returned.
8896  */
8897 static VALUE
8898 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8899 {
8900     Molecule *mol;
8901     Data_Get_Struct(self, Molecule, mol);
8902         if (mol->mview == NULL)
8903                 return Qnil;
8904         if (argc > 0) {
8905                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8906                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8907         }
8908         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8909 }
8910
8911 /*
8912  *  call-seq:
8913  *     show_ellipsoids
8914  *     show_ellipsoids(bool)
8915  *     show_ellipsoids = bool
8916  *
8917  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8918  *  current flag is returned.
8919  */
8920 static VALUE
8921 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8922 {
8923     Molecule *mol;
8924     Data_Get_Struct(self, Molecule, mol);
8925         if (mol->mview == NULL)
8926                 return Qnil;
8927         if (argc > 0) {
8928                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8929                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8930         }
8931         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8932 }
8933
8934 /*
8935  *  call-seq:
8936  *     is_atom_visible(index)  -> Boolean
8937  *
8938  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8939  *  as well as the molecule attributes (showHydrogens, etc.)
8940  */
8941 static VALUE
8942 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8943 {
8944         Molecule *mol;
8945         Int idx;
8946         Atom *ap;
8947     Data_Get_Struct(self, Molecule, mol);
8948         idx = s_Molecule_AtomIndexFromValue(mol, ival);
8949         if (idx < 0 || idx >= mol->natoms)
8950                 return Qnil;
8951         ap = ATOM_AT_INDEX(mol->atoms, idx);
8952         if (mol->mview != NULL) {
8953                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8954                         return Qfalse;
8955                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8956                         return Qfalse;
8957                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8958                         return Qfalse;
8959         }
8960         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8961 }
8962
8963 /*
8964  *  call-seq:
8965  *     hidden_atoms       -> IntGroup
8966  *
8967  *  Returns the currently hidden atoms.
8968  */
8969 static VALUE
8970 s_Molecule_HiddenAtoms(VALUE self)
8971 {
8972         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8973         return Qnil;  /*  Not reached  */
8974 }
8975
8976 /*
8977  *  call-seq:
8978  *     set_hidden_atoms(IntGroup)
8979  *     self.hidden_atoms = IntGroup
8980  *
8981  *  Hide the specified atoms. This operation is _not_ undoable.
8982  */
8983 static VALUE
8984 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8985 {
8986         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8987         return Qnil;  /*  Not reached  */
8988 }
8989
8990 /*
8991  *  call-seq:
8992  *     show_graphite -> Integer
8993  *     show_graphite = Integer
8994  *     show_graphite = boolean
8995  *
8996  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
8997  *  number of rings to display for each direction.
8998  *  If the argument is boolean, only the show/hide flag is set.
8999  */
9000 static VALUE
9001 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9002 {
9003     Molecule *mol;
9004     Data_Get_Struct(self, Molecule, mol);
9005         if (mol->mview == NULL)
9006                 return Qnil;
9007         if (argc > 0) {
9008                 if (argv[0] == Qnil || argv[0] == Qfalse)
9009                         mol->mview->showGraphiteFlag = 0;
9010                 else if (argv[0] == Qtrue)
9011                         mol->mview->showGraphiteFlag = 1;
9012                 else {
9013                         int n = NUM2INT(rb_Integer(argv[0]));
9014                         if (n < 0)
9015                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9016                         mol->mview->showGraphite = n;
9017                 }
9018                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9019         }
9020         return INT2NUM(mol->mview->showGraphite);
9021 }
9022
9023 /*
9024  *  call-seq:
9025  *     show_graphite? -> boolean
9026  *
9027  *  Return whether the graphite is set visible or not.
9028 */
9029 static VALUE
9030 s_Molecule_ShowGraphiteFlag(VALUE self)
9031 {
9032     Molecule *mol;
9033     Data_Get_Struct(self, Molecule, mol);
9034         if (mol->mview == NULL)
9035                 return Qnil;
9036         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9037 }
9038         
9039 /*
9040  *  call-seq:
9041  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9042  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9043  *     show_periodic_image = boolean
9044  *
9045  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9046  *  set but no visual effects are observed.
9047  *  If the argument is boolean, only the show/hide flag is modified.
9048  */
9049 static VALUE
9050 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9051 {
9052     Molecule *mol;
9053         VALUE val;
9054         int ival[6];
9055         int i;
9056     Data_Get_Struct(self, Molecule, mol);
9057         if (mol->mview == NULL)
9058                 return Qnil;
9059         rb_scan_args(argc, argv, "01", &val);
9060         if (argc > 0) {
9061                 /*  Change current settings  */
9062                 if (val == Qnil || val == Qfalse)
9063                         mol->mview->showPeriodicImageFlag = 0;
9064                 else if (val == Qtrue)
9065                         mol->mview->showPeriodicImageFlag = 1;
9066                 else {
9067                         val = rb_ary_to_ary(val);
9068                         for (i = 0; i < 6; i++) {
9069                                 if (i < RARRAY_LEN(val))
9070                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9071                         }
9072                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9073                                 rb_raise(rb_eMolbyError, "bad arguments");
9074                         for (i = 0; i < 6; i++)
9075                                 mol->mview->showPeriodicImage[i] = ival[i];
9076                 }
9077                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9078         }
9079         val = rb_ary_new();
9080         for (i = 0; i < 6; i++)
9081                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9082         return val;
9083 }
9084
9085 /*
9086  *  call-seq:
9087  *     show_periodic_image? -> boolean
9088  *
9089  *  Return whether the periodic images are set to visible or not. This flag is
9090  *  independent from the show_periodic_image settings.
9091  */
9092 static VALUE
9093 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9094 {
9095     Molecule *mol;
9096     Data_Get_Struct(self, Molecule, mol);
9097         if (mol->mview == NULL)
9098                 return Qnil;
9099         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9100 }
9101
9102 /*
9103  *  call-seq:
9104  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9105  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9106  *     show_rotation_center = boolean
9107  *
9108  *  Set to show the rotation center of the screen.
9109  *  If the argument is boolean, only the show/hide flag is modified.
9110  */
9111 static VALUE
9112 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9113 {
9114     Molecule *mol;
9115     Data_Get_Struct(self, Molecule, mol);
9116         if (mol->mview == NULL)
9117                 return Qnil;
9118         if (argc > 0) {
9119                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9120                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9121         }
9122         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9123 }
9124
9125 /*
9126  *  call-seq:
9127  *     line_mode
9128  *     line_mode(bool)
9129  *     line_mode = bool
9130  *
9131  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9132  *  current flag is returned.
9133  */
9134 static VALUE
9135 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9136 {
9137     Molecule *mol;
9138     Data_Get_Struct(self, Molecule, mol);
9139         if (mol->mview == NULL)
9140                 return Qnil;
9141         if (argc > 0) {
9142                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9143                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9144         }
9145         return (mol->mview->lineMode ? Qtrue : Qfalse);
9146 }
9147
9148 /*
9149  *  call-seq:
9150  *     atom_radius = float
9151  *     atom_radius
9152  *
9153  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9154  *  (Default = 0.4)
9155  *  If no argument is given, the current value is returned.
9156  */
9157 static VALUE
9158 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9159 {
9160     Molecule *mol;
9161     Data_Get_Struct(self, Molecule, mol);
9162         if (mol->mview == NULL)
9163                 return Qnil;
9164         if (argc > 0) {
9165                 double rad = NUM2DBL(rb_Float(argv[0]));
9166                 if (rad > 0.0) {
9167                         mol->mview->atomRadius = rad;
9168                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9169                 }
9170                 return argv[0];
9171         }
9172         return rb_float_new(mol->mview->atomRadius);
9173 }
9174
9175 /*
9176  *  call-seq:
9177  *     bond_radius = float
9178  *     bond_radius
9179  *
9180  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9181  *  If no argument is given, the current value is returned.
9182  */
9183 static VALUE
9184 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9185 {
9186     Molecule *mol;
9187     Data_Get_Struct(self, Molecule, mol);
9188         if (mol->mview == NULL)
9189                 return Qnil;
9190         if (argc > 0) {
9191                 double rad = NUM2DBL(rb_Float(argv[0]));
9192                 if (rad > 0.0) {
9193                         mol->mview->bondRadius = rad;
9194                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9195                 }
9196                 return argv[0];
9197         }
9198         return rb_float_new(mol->mview->bondRadius);
9199 }
9200
9201 /*
9202  *  call-seq:
9203  *     atom_resolution = integer
9204  *     atom_resolution
9205  *
9206  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9207  *  (Default = 12; minimum = 6)
9208  *  If no argument is given, the current value is returned.
9209  */
9210 static VALUE
9211 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9212 {
9213     Molecule *mol;
9214     Data_Get_Struct(self, Molecule, mol);
9215         if (mol->mview == NULL)
9216                 return Qnil;
9217         if (argc > 0) {
9218                 int res = NUM2INT(rb_Integer(argv[0]));
9219                 if (res < 6)
9220                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9221                 mol->mview->atomResolution = res;
9222                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9223                 return INT2NUM(res);
9224         }
9225         return INT2NUM(mol->mview->atomResolution);
9226 }
9227
9228 /*
9229  *  call-seq:
9230  *     bond_resolution = integer
9231  *     bond_resolution
9232  *
9233  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9234  *  (Default = 8; minimum = 4)
9235  *  If no argument is given, the current value is returned.
9236  */
9237 static VALUE
9238 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9239 {
9240     Molecule *mol;
9241     Data_Get_Struct(self, Molecule, mol);
9242         if (mol->mview == NULL)
9243                 return Qnil;
9244         if (argc > 0) {
9245                 int res = NUM2INT(rb_Integer(argv[0]));
9246                 if (res < 4)
9247                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9248                 mol->mview->bondResolution = res;
9249                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9250                 return INT2NUM(res);
9251         }
9252         return INT2NUM(mol->mview->bondResolution);
9253 }
9254
9255 /*
9256  *  call-seq:
9257  *     resize_to_fit
9258  *
9259  *  Resize the model drawing to fit in the window.
9260  */
9261 static VALUE
9262 s_Molecule_ResizeToFit(VALUE self)
9263 {
9264     Molecule *mol;
9265     Data_Get_Struct(self, Molecule, mol);
9266         if (mol->mview != NULL)
9267                 MainView_resizeToFit(mol->mview);
9268         return self;    
9269 }
9270
9271 /*
9272  *  call-seq:
9273  *     get_view_rotation -> [[ax, ay, az], angle]
9274  *
9275  *  Get the current rotation for the view. Angle is in degree, not radian.
9276  */
9277 static VALUE
9278 s_Molecule_GetViewRotation(VALUE self)
9279 {
9280     Molecule *mol;
9281         double f[4];
9282         Vector v;
9283     Data_Get_Struct(self, Molecule, mol);
9284         if (mol->mview == NULL)
9285                 return Qnil;
9286         TrackballGetRotate(mol->mview->track, f);
9287         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9288         v.x = f[1];
9289         v.y = f[2];
9290         v.z = f[3];
9291         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9292 }
9293
9294 /*
9295  *  call-seq:
9296  *     get_view_scale -> float
9297  *
9298  *  Get the current scale for the view.
9299  */
9300 static VALUE
9301 s_Molecule_GetViewScale(VALUE self)
9302 {
9303     Molecule *mol;
9304     Data_Get_Struct(self, Molecule, mol);
9305         if (mol->mview == NULL)
9306                 return Qnil;
9307         return rb_float_new(TrackballGetScale(mol->mview->track));
9308 }
9309
9310 /*
9311  *  call-seq:
9312  *     get_view_center -> Vector
9313  *
9314  *  Get the current center point of the view.
9315  */
9316 static VALUE
9317 s_Molecule_GetViewCenter(VALUE self)
9318 {
9319     Molecule *mol;
9320         double f[4];
9321         Vector v;
9322     Data_Get_Struct(self, Molecule, mol);
9323         if (mol->mview == NULL)
9324                 return Qnil;
9325         TrackballGetTranslate(mol->mview->track, f);
9326         v.x = -f[0] * mol->mview->dimension;
9327         v.y = -f[1] * mol->mview->dimension;
9328         v.z = -f[2] * mol->mview->dimension;
9329         return ValueFromVector(&v);
9330 }
9331
9332 /*
9333  *  call-seq:
9334  *     set_view_rotation([ax, ay, az], angle) -> self
9335  *
9336  *  Set the current rotation for the view. Angle is in degree, not radian.
9337  */
9338 static VALUE
9339 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9340 {
9341     Molecule *mol;
9342         double f[4];
9343         Vector v;
9344     Data_Get_Struct(self, Molecule, mol);
9345         if (mol->mview == NULL)
9346                 return Qnil;
9347         VectorFromValue(aval, &v);
9348         if (NormalizeVec(&v, &v))
9349                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9350         f[1] = v.x;
9351         f[2] = v.y;
9352         f[3] = v.z;
9353         f[0] = -NUM2DBL(rb_Float(angval));
9354         TrackballSetRotate(mol->mview->track, f);
9355         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9356         return self;
9357 }
9358
9359 /*
9360  *  call-seq:
9361  *     set_view_scale(scale) -> self
9362  *
9363  *  Set the current scale for the view.
9364  */
9365 static VALUE
9366 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9367 {
9368     Molecule *mol;
9369     Data_Get_Struct(self, Molecule, mol);
9370         if (mol->mview == NULL)
9371                 return Qnil;
9372         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9373         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9374         return self;
9375 }
9376
9377 /*
9378  *  call-seq:
9379  *     set_view_center(vec) -> self
9380  *
9381  *  Set the current center point of the view.
9382  */
9383 static VALUE
9384 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9385 {
9386     Molecule *mol;
9387         Vector v;
9388         double f[4];
9389     Data_Get_Struct(self, Molecule, mol);
9390         if (mol->mview == NULL)
9391                 return Qnil;
9392         VectorFromValue(aval, &v);
9393         f[0] = -v.x / mol->mview->dimension;
9394         f[1] = -v.y / mol->mview->dimension;
9395         f[2] = -v.z / mol->mview->dimension;
9396         TrackballSetTranslate(mol->mview->track, f);
9397         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9398         return self;
9399 }
9400
9401 /*
9402  *  call-seq:
9403  *     set_background_color(red, green, blue)
9404  *
9405  *  Set the background color of the model window.
9406  */
9407 static VALUE
9408 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9409 {
9410     Molecule *mol;
9411     Data_Get_Struct(self, Molecule, mol);
9412         if (mol->mview != NULL) {
9413                 VALUE rval, gval, bval;
9414                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9415                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9416         }
9417         return self;    
9418 }
9419
9420 /*
9421  *  call-seq:
9422  *     export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9423  *
9424  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9425  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9426  *  If either width or height is not specified, then the screen width/height is used instead.
9427  */
9428 static VALUE
9429 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9430 {
9431         Molecule *mol;
9432         VALUE fval, sval, bval, wval, hval;
9433         char *fname;
9434         float scale;
9435         int bg_color, width, height;
9436     Data_Get_Struct(self, Molecule, mol);
9437         if (mol->mview == NULL)
9438                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9439         rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9440         fname = FileStringValuePtr(fval);
9441         if (sval == Qnil)
9442                 scale = 1.0;
9443         else scale = NUM2DBL(rb_Float(sval));
9444         if (bval == Qnil)
9445                 bg_color = -1;
9446         else bg_color = NUM2INT(rb_Integer(bval));
9447         if (wval == Qnil)
9448                 width = 0;
9449         else width = NUM2INT(rb_Integer(wval));
9450         if (hval == Qnil)
9451                 height = 0;
9452         else height = NUM2INT(rb_Integer(hval));
9453         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9454                 return fval;
9455         else return Qnil;
9456 }
9457
9458 #pragma mark ------ Graphics ------
9459
9460 static void
9461 s_CalculateGraphicNormals(MainViewGraphic *gp)
9462 {
9463         int i;
9464         Vector v1, v2, v3;
9465         if (gp == NULL || gp->npoints < 3)
9466                 return;
9467         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9468         v1.x = gp->points[3] - gp->points[0];
9469         v1.y = gp->points[4] - gp->points[1];
9470         v1.z = gp->points[5] - gp->points[2];
9471         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9472         for (i = 2; i < gp->npoints; i++) {
9473                 v2.x = gp->points[i * 3] - gp->points[0];
9474                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9475                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9476                 VecCross(v3, v1, v2);
9477                 NormalizeVec(&v3, &v3);
9478                 gp->normals[i * 3] = v3.x;
9479                 gp->normals[i * 3 + 1] = v3.y;
9480                 gp->normals[i * 3 + 2] = v3.z;
9481                 v1 = v2;
9482         }
9483         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9484         VecZero(v1);
9485         for (i = 2; i < gp->npoints; i++) {
9486                 v1.x += gp->normals[i * 3];
9487                 v1.y += gp->normals[i * 3 + 1];
9488                 v1.z += gp->normals[i * 3 + 2];
9489         }
9490         NormalizeVec(&v1, &v1);
9491         gp->normals[0] = v1.x;
9492         gp->normals[1] = v1.y;
9493         gp->normals[2] = v1.z;
9494         /*  normals[1] = nv[2].normalize  */
9495         v2.x = gp->normals[6];
9496         v2.y = gp->normals[7];
9497         v2.z = gp->normals[8];
9498         NormalizeVec(&v1, &v2);
9499         gp->normals[3] = v1.x;
9500         gp->normals[4] = v1.y;
9501         gp->normals[5] = v1.z;
9502         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9503         for (i = 2; i < gp->npoints; i++) {
9504                 if (i == gp->npoints - 1)
9505                         VecZero(v3);
9506                 else {
9507                         v3.x = gp->normals[i * 3 + 3];
9508                         v3.y = gp->normals[i * 3 + 4];
9509                         v3.z = gp->normals[i * 3 + 5];
9510                 }
9511                 VecInc(v2, v3);
9512                 NormalizeVec(&v1, &v2);
9513                 gp->normals[i * 3] = v1.x;
9514                 gp->normals[i * 3 + 1] = v1.y;
9515                 gp->normals[i * 3 + 2] = v1.z;
9516                 v2 = v3;
9517         }
9518 }
9519
9520 /*
9521  *  call-seq:
9522  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9523  *
9524  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9525  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9526  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9527  *   points: an array of Vectors
9528  *   
9529  */
9530 static VALUE
9531 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9532 {
9533     Molecule *mol;
9534         MainViewGraphic g;
9535         int i, n, ni, idx;
9536         const char *p;
9537         VALUE kval, cval, pval, fval, ival;
9538     Data_Get_Struct(self, Molecule, mol);
9539         if (mol->mview == NULL)
9540                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9541         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9542         idx = NUM2INT(rb_Integer(ival));
9543         if (idx == -1)
9544                 idx = mol->mview->ngraphics;
9545         else if (idx < 0 || idx > mol->mview->ngraphics)
9546                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9547         memset(&g, 0, sizeof(g));
9548         g.visible = 1;
9549         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9550                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9551         } else {
9552                 kval = rb_obj_as_string(kval);
9553                 p = StringValuePtr(kval);
9554                 if (strcmp(p, "line") == 0)
9555                         g.kind = kMainViewGraphicLine;
9556                 else if (strcmp(p, "poly") == 0)
9557                         g.kind = kMainViewGraphicPoly;
9558                 else if (strcmp(p, "cylinder") == 0)
9559                         g.kind = kMainViewGraphicCylinder;
9560                 else if (strcmp(p, "cone") == 0)
9561                         g.kind = kMainViewGraphicCone;
9562                 else if (strcmp(p, "ellipsoid") == 0)
9563                         g.kind = kMainViewGraphicEllipsoid;
9564                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9565         }
9566         g.closed = (RTEST(fval) ? 1 : 0);
9567         cval = rb_ary_to_ary(cval);
9568         n = RARRAY_LEN(cval);
9569         if (n < 3 || n >= 5)
9570                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9571         if (n == 3)
9572                 g.rgba[3] = 1.0;
9573         for (i = 0; i < n; i++)
9574                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9575         pval = rb_ary_to_ary(pval);
9576         n = RARRAY_LEN(pval);
9577         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9578         if (n <= 0)
9579                 rb_raise(rb_eArgError, "no control points are given");
9580         switch (g.kind) {
9581                 case kMainViewGraphicLine:
9582                         if (n < 2)
9583                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9584                         break;
9585                 case kMainViewGraphicPoly:
9586                         if (n < 3)
9587                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9588                         break;
9589                 case kMainViewGraphicCylinder:
9590                 case kMainViewGraphicCone:
9591                         if (n != 3)
9592                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9593                         ni = 2;
9594                         break;
9595                 case kMainViewGraphicEllipsoid:
9596                         if (n == 2) {
9597                                 ni = 1;
9598                         } else if (n != 4)
9599                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9600                         break;
9601         }
9602         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9603         for (i = 0; i < n; i++) {
9604                 Vector v;
9605                 VALUE rval = RARRAY_PTR(pval)[i];
9606                 if (i == ni) {
9607                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9608                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9609                                 VectorFromValue(rval, &v);
9610                         } else {
9611                                 v.x = NUM2DBL(rb_Float(rval));
9612                         }
9613                         v.y = v.z = 0;
9614                 } else {
9615                         VectorFromValue(rval, &v);
9616                 }
9617                 g.points[i * 3] = v.x;
9618                 g.points[i * 3 + 1] = v.y;
9619                 g.points[i * 3 + 2] = v.z;
9620         }
9621         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9622                 /*  Sphere  */
9623                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9624                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9625                 g.points[7] = g.points[11] = g.points[3];
9626         }
9627         if (g.kind == kMainViewGraphicPoly) {
9628                 /*  Calculate normals  */
9629                 s_CalculateGraphicNormals(&g);
9630         }
9631         MainView_insertGraphic(mol->mview, idx, &g);
9632         
9633         {
9634                 /*  Register undo  */
9635                 MolAction *act;
9636                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9637                 MolActionCallback_registerUndo(mol, act);
9638                 MolActionRelease(act);
9639         }
9640
9641         return INT2NUM(idx);    
9642 }
9643
9644 /*
9645  *  call-seq:
9646  *     create_graphic(kind, color, points, fill = nil) -> integer
9647  *
9648  *  Create a new graphic object. The arguments are similar as insert_graphic.
9649  */
9650 static VALUE
9651 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9652 {
9653         VALUE args[5];
9654         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9655         args[0] = INT2NUM(-1);
9656         return s_Molecule_InsertGraphic(argc + 1, args, self);
9657 }
9658
9659 /*
9660  *  call-seq:
9661  *     remove_graphic(index) -> integer
9662  *
9663  *  Remove a graphic object.
9664  */
9665 static VALUE
9666 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9667 {
9668     Molecule *mol;
9669         int i;
9670     Data_Get_Struct(self, Molecule, mol);
9671         if (mol->mview == NULL)
9672                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9673         i = NUM2INT(rb_Integer(ival));
9674         if (i < 0 || i >= mol->mview->ngraphics)
9675                 rb_raise(rb_eArgError, "graphic index is out of range");
9676         {
9677                 /*  Prepare data for undo  */
9678                 MainViewGraphic *gp;
9679                 Vector *vp;
9680                 MolAction *act;
9681                 double col[4];
9682                 int n;
9683                 gp = mol->mview->graphics + i;
9684                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9685                 for (n = 0; n < gp->npoints; n++) {
9686                         vp[n].x = gp->points[n * 3];
9687                         vp[n].y = gp->points[n * 3 + 1];
9688                         vp[n].z = gp->points[n * 3 + 2];
9689                 }
9690                 col[0] = gp->rgba[0];
9691                 col[1] = gp->rgba[1];
9692                 col[2] = gp->rgba[2];
9693                 col[3] = gp->rgba[3];
9694                 if (gp->visible == 0) {
9695                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9696                         MolActionCallback_registerUndo(mol, act);
9697                         MolActionRelease(act);
9698                 }
9699                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9700                 MolActionCallback_registerUndo(mol, act);
9701                 free(vp);
9702                 MolActionRelease(act);
9703         }
9704         MainView_removeGraphic(mol->mview, i);
9705         return ival;
9706 }
9707
9708 /*
9709  *  call-seq:
9710  *     ngraphics -> integer
9711  *
9712  *  Get the number of graphic objects.
9713  */
9714 static VALUE
9715 s_Molecule_NGraphics(VALUE self)
9716 {
9717     Molecule *mol;
9718     Data_Get_Struct(self, Molecule, mol);
9719         if (mol->mview == NULL)
9720                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9721         return INT2NUM(mol->mview->ngraphics);
9722 }
9723
9724 /*
9725  *  call-seq:
9726  *     get_graphic_point(graphic_index, point_index) -> value
9727  *     get_graphic_points(graphic_index) -> values
9728  *
9729  *  Get the point_index-th control point of graphic_index-th graphic object.
9730  *  Get an array of all control points with the given values.
9731  *   
9732  */
9733 static VALUE
9734 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9735 {
9736         MainViewGraphic *gp;
9737     Molecule *mol;
9738         int index, pindex;
9739         Vector v;
9740         VALUE gval, pval;
9741     Data_Get_Struct(self, Molecule, mol);
9742         if (mol->mview == NULL)
9743                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9744         rb_scan_args(argc, argv, "11", &gval, &pval);
9745         index = NUM2INT(rb_Integer(gval));
9746         if (index < 0 || index >= mol->mview->ngraphics)
9747                 rb_raise(rb_eArgError, "the graphic index is out of range");
9748         gp = mol->mview->graphics + index;
9749         if (pval != Qnil) {
9750                 pindex = NUM2INT(rb_Integer(pval));
9751                 if (pindex < 0 || pindex >= gp->npoints)
9752                         rb_raise(rb_eArgError, "the point index is out of range");
9753                 v.x = gp->points[pindex * 3];
9754                 v.y = gp->points[pindex * 3 + 1];
9755                 v.z = gp->points[pindex * 3 + 2];
9756                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9757                         return rb_float_new(v.x);
9758                 } else {
9759                         return ValueFromVector(&v);
9760                 }
9761         } else {
9762                 pval = rb_ary_new();
9763                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9764                         v.x = gp->points[pindex * 3];
9765                         v.y = gp->points[pindex * 3 + 1];
9766                         v.z = gp->points[pindex * 3 + 2];
9767                         rb_ary_push(pval, ValueFromVector(&v));
9768                 }
9769                 return pval;
9770         }
9771 }
9772
9773 /*
9774  *  call-seq:
9775  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9776  *     set_graphic_points(graphic_index, new_values) -> new_values
9777  *
9778  *  Change the point_index-th control point of graphic_index-th graphic object.
9779  *  Replace the control points with the given values.
9780  *   
9781  */
9782 static VALUE
9783 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9784 {
9785         MainViewGraphic *gp;
9786     Molecule *mol;
9787         int index, pindex;
9788         Vector v, v0;
9789         VALUE gval, pval, nval;
9790         MolAction *act;
9791     Data_Get_Struct(self, Molecule, mol);
9792         if (mol->mview == NULL)
9793                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9794         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9795         index = NUM2INT(rb_Integer(gval));
9796         if (index < 0 || index >= mol->mview->ngraphics)
9797                 rb_raise(rb_eArgError, "the graphic index is out of range");
9798         gp = mol->mview->graphics + index;
9799         if (nval != Qnil) {
9800                 pindex = NUM2INT(rb_Integer(pval));
9801                 if (pindex < 0 || pindex >= gp->npoints)
9802                         rb_raise(rb_eArgError, "the point index is out of range");
9803                 v0.x = gp->points[pindex * 3];
9804                 v0.y = gp->points[pindex * 3 + 1];
9805                 v0.z = gp->points[pindex * 3 + 2];
9806                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9807                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9808                                 v.x = NUM2DBL(rb_Float(nval));
9809                                 v.y = v.z = 0;
9810                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9811                                 v.x = NUM2DBL(rb_Float(nval));
9812                                 v.y = v.z = 0;
9813                                 gp->points[7] = gp->points[11] = v.x;
9814                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9815                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9816                 } else {
9817                         if (nval == Qnil) {
9818                                 v.x = kInvalidFloat;
9819                                 v.y = v.z = 0.0;
9820                         } else VectorFromValue(nval, &v);
9821                 }
9822                 gp->points[pindex * 3] = v.x;
9823                 gp->points[pindex * 3 + 1] = v.y;
9824                 gp->points[pindex * 3 + 2] = v.z;
9825                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9826         } else {
9827                 VALUE aval;
9828                 int len;
9829                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9830                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9831                         vp[pindex].x = gp->points[pindex * 3];
9832                         vp[pindex].y = gp->points[pindex * 3 + 1];
9833                         vp[pindex].z = gp->points[pindex * 3 + 2];
9834                 }
9835                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9836                 free(vp);
9837                 pval = rb_ary_to_ary(pval);
9838                 len = RARRAY_LEN(pval);
9839                 if (gp->npoints < len) {
9840                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9841                         gp->npoints = len;
9842                 } else if (gp->npoints > len) {
9843                         int len2 = 3;
9844                         switch (gp->kind) {
9845                                 case kMainViewGraphicLine: len2 = 2; break;
9846                                 case kMainViewGraphicPoly: len2 = 3; break;
9847                                 case kMainViewGraphicCylinder: len2 = 3; break;
9848                                 case kMainViewGraphicCone: len2 = 3; break;
9849                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9850                         }
9851                         if (len2 < len)
9852                                 len2 = len;
9853                         gp->npoints = len2;
9854                 }
9855                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9856                         aval = RARRAY_PTR(pval)[pindex];
9857                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9858                                 v.x = NUM2DBL(rb_Float(aval));
9859                                 v.y = v.z = 0;
9860                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9861                                 v.x = NUM2DBL(rb_Float(aval));
9862                                 v.y = v.z = 0;
9863                                 gp->points[7] = gp->points[11] = v.x;
9864                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9865                                 break;
9866                         } else VectorFromValue(aval, &v);
9867                         gp->points[pindex * 3] = v.x;
9868                         gp->points[pindex * 3 + 1] = v.y;
9869                         gp->points[pindex * 3 + 2] = v.z;
9870                 }
9871         }
9872         if (gp->kind == kMainViewGraphicPoly) {
9873                 /*  Calculate normals  */
9874                 s_CalculateGraphicNormals(gp);
9875         }
9876         MolActionCallback_registerUndo(mol, act);
9877         MolActionRelease(act);          
9878         MoleculeCallback_notifyModification(mol, 0);
9879         return nval;
9880 }
9881
9882 /*
9883  *  call-seq:
9884  *     get_graphic_color(graphic_index) -> value
9885  *
9886  *  Get the color of graphic_index-th graphic object
9887  */
9888 static VALUE
9889 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9890 {
9891         MainViewGraphic *gp;
9892     Molecule *mol;
9893         int index;
9894     Data_Get_Struct(self, Molecule, mol);
9895         if (mol->mview == NULL)
9896                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9897         index = NUM2INT(rb_Integer(gval));
9898         if (index < 0 || index >= mol->mview->ngraphics)
9899                 rb_raise(rb_eArgError, "the graphic index is out of range");
9900         gp = mol->mview->graphics + index;
9901         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]));
9902 }
9903
9904 /*
9905  *  call-seq:
9906  *     set_graphic_color(graphic_index, new_value) -> new_value
9907  *
9908  *  Change the color of graphic_index-th graphic object
9909  *   
9910  */
9911 static VALUE
9912 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9913 {
9914         MainViewGraphic *gp;
9915     Molecule *mol;
9916         MolAction *act;
9917         double c[4];
9918         int index, i, n;
9919     Data_Get_Struct(self, Molecule, mol);
9920         if (mol->mview == NULL)
9921                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9922         index = NUM2INT(rb_Integer(gval));
9923         if (index < 0 || index >= mol->mview->ngraphics)
9924                 rb_raise(rb_eArgError, "the graphic index is out of range");
9925         gp = mol->mview->graphics + index;
9926         for (i = 0; i < 4; i++)
9927                 c[i] = gp->rgba[i];
9928         cval = rb_ary_to_ary(cval);
9929         n = RARRAY_LEN(cval);
9930         if (n != 3 && n != 4)
9931                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9932
9933         for (i = 0; i < n; i++) {
9934                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9935         }
9936         if (n == 3)
9937                 gp->rgba[3] = 1.0;
9938         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9939         MolActionCallback_registerUndo(mol, act);
9940         MolActionRelease(act);          
9941         MoleculeCallback_notifyModification(mol, 0);
9942         return cval;
9943 }
9944
9945 /*
9946  *  call-seq:
9947  *     show_graphic(graphic_index) -> self
9948  *
9949  *  Enable the visible flag of the graphic_index-th graphic object
9950  *   
9951  */
9952 static VALUE
9953 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9954 {
9955         MainViewGraphic *gp;
9956     Molecule *mol;
9957         int index;
9958     Data_Get_Struct(self, Molecule, mol);
9959         if (mol->mview == NULL)
9960                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9961         index = NUM2INT(rb_Integer(gval));
9962         if (index < 0 || index >= mol->mview->ngraphics)
9963                 rb_raise(rb_eArgError, "the graphic index is out of range");
9964         gp = mol->mview->graphics + index;
9965         gp->visible = 1;
9966         MoleculeCallback_notifyModification(mol, 0);
9967         return self;
9968 }
9969
9970 /*
9971  *  call-seq:
9972  *     hide_graphic(graphic_index) -> self
9973  *
9974  *  Disable the visible flag of the graphic_index-th graphic object
9975  *   
9976  */
9977 static VALUE
9978 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9979 {
9980         MainViewGraphic *gp;
9981     Molecule *mol;
9982         int index;
9983     Data_Get_Struct(self, Molecule, mol);
9984         if (mol->mview == NULL)
9985                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9986         index = NUM2INT(rb_Integer(gval));
9987         if (index < 0 || index >= mol->mview->ngraphics)
9988                 rb_raise(rb_eArgError, "the graphic index is out of range");
9989         gp = mol->mview->graphics + index;
9990         gp->visible = 0;
9991         MoleculeCallback_notifyModification(mol, 0);
9992         return self;
9993 }
9994
9995 /*
9996  *  call-seq:
9997  *     show_text(string)
9998  *
9999  *  Show the string in the info text box.
10000  */
10001 static VALUE
10002 s_Molecule_ShowText(VALUE self, VALUE arg)
10003 {
10004     Molecule *mol;
10005     Data_Get_Struct(self, Molecule, mol);
10006         if (mol->mview != NULL)
10007                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10008         return Qnil;
10009 }
10010
10011 #pragma mark ------ MD Support ------
10012
10013 /*
10014  *  call-seq:
10015  *     md_arena -> MDArena
10016  *
10017  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10018  *  this molecule, a new arena is created.
10019  */
10020 static VALUE
10021 s_Molecule_MDArena(VALUE self)
10022 {
10023     Molecule *mol;
10024         VALUE retval;
10025     Data_Get_Struct(self, Molecule, mol);
10026         if (mol->arena == NULL)
10027                 md_arena_new(mol);
10028         retval = ValueFromMDArena(mol->arena);
10029         return retval;
10030 }
10031
10032 /*
10033  *  call-seq:
10034  *     set_parameter_attr(type, index, key, value, src) -> value
10035  *
10036  *  This method is used only internally.
10037  */
10038 static VALUE
10039 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10040 {
10041         /*  This method is called from MolAction to change a MM parameter attribute.  */
10042     Molecule *mol;
10043         VALUE pval;
10044         ParameterRef *pref;
10045         UnionPar *up;
10046     Data_Get_Struct(self, Molecule, mol);
10047         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10048         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10049         
10050         /*  This is the special part of this method; it allows modification of the src field. */
10051         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10052         Data_Get_Struct(pval, ParameterRef, pref);
10053         up = ParameterRefGetPar(pref);
10054         up->bond.src = FIX2INT(sval);
10055         
10056         return vval;
10057 }
10058
10059 /*
10060  *  call-seq:
10061  *     parameter -> Parameter
10062  *
10063  *  Get the local parameter of this molecule. If not defined, returns nil.
10064  */
10065 static VALUE
10066 s_Molecule_Parameter(VALUE self)
10067 {
10068     Molecule *mol;
10069     Data_Get_Struct(self, Molecule, mol);
10070 /*      if (mol->par == NULL)
10071                 return Qnil; */
10072         return s_NewParameterValueFromValue(self);
10073 }
10074
10075 /*
10076  *  call-seq:
10077  *     start_step       -> Integer
10078  *
10079  *  Returns the start step (defined by dcd format).
10080  */
10081 static VALUE
10082 s_Molecule_StartStep(VALUE self)
10083 {
10084     Molecule *mol;
10085     Data_Get_Struct(self, Molecule, mol);
10086         return INT2NUM(mol->startStep);
10087 }
10088
10089 /*
10090  *  call-seq:
10091  *     start_step = Integer
10092  *
10093  *  Set the start step (defined by dcd format).
10094  */
10095 static VALUE
10096 s_Molecule_SetStartStep(VALUE self, VALUE val)
10097 {
10098     Molecule *mol;
10099     Data_Get_Struct(self, Molecule, mol);
10100         mol->startStep = NUM2INT(rb_Integer(val));
10101         return val;
10102 }
10103
10104 /*
10105  *  call-seq:
10106  *     steps_per_frame       -> Integer
10107  *
10108  *  Returns the number of steps between frames (defined by dcd format).
10109  */
10110 static VALUE
10111 s_Molecule_StepsPerFrame(VALUE self)
10112 {
10113     Molecule *mol;
10114     Data_Get_Struct(self, Molecule, mol);
10115         return INT2NUM(mol->stepsPerFrame);
10116 }
10117
10118 /*
10119  *  call-seq:
10120  *     steps_per_frame = Integer
10121  *
10122  *  Set the number of steps between frames (defined by dcd format).
10123  */
10124 static VALUE
10125 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10126 {
10127     Molecule *mol;
10128     Data_Get_Struct(self, Molecule, mol);
10129         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10130         return val;
10131 }
10132
10133 /*
10134  *  call-seq:
10135  *     ps_per_step       -> Float
10136  *
10137  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10138  */
10139 static VALUE
10140 s_Molecule_PsPerStep(VALUE self)
10141 {
10142     Molecule *mol;
10143     Data_Get_Struct(self, Molecule, mol);
10144         return rb_float_new(mol->psPerStep);
10145 }
10146
10147 /*
10148  *  call-seq:
10149  *     ps_per_step = Float
10150  *
10151  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10152  */
10153 static VALUE
10154 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10155 {
10156     Molecule *mol;
10157     Data_Get_Struct(self, Molecule, mol);
10158         mol->psPerStep = NUM2DBL(rb_Float(val));
10159         return val;
10160 }
10161
10162 static VALUE
10163 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10164 {
10165         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.");
10166 }
10167
10168 #pragma mark ------ MO Handling ------
10169
10170 /*
10171  *  call-seq:
10172  *     selectedMO -> IntGroup
10173  *
10174  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10175  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10176  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10177  */
10178 static VALUE
10179 s_Molecule_SelectedMO(VALUE self)
10180 {
10181     Molecule *mol;
10182         IntGroup *ig;
10183         VALUE val;
10184     Data_Get_Struct(self, Molecule, mol);
10185         if (mol->mview == NULL)
10186                 return Qnil;
10187         ig = MainView_selectedMO(mol->mview);
10188         if (ig == NULL)
10189                 return Qnil;
10190         IntGroupOffset(ig, 1);
10191         val = ValueFromIntGroup(ig);
10192         IntGroupRelease(ig);
10193         return val;
10194 }
10195
10196 /*
10197  *  call-seq:
10198  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10199  *
10200  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10201  *  If the molecule does not contain a basis set information, then returns nil.
10202  */
10203 static VALUE
10204 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10205 {
10206     Molecule *mol;
10207         Vector o, dx, dy, dz;
10208         Int nx, ny, nz;
10209         VALUE nval;
10210         Int npoints = 80 * 80 * 80;
10211     Data_Get_Struct(self, Molecule, mol);
10212         if (mol->bset == NULL)
10213                 return Qnil;
10214         rb_scan_args(argc, argv, "01", &nval);
10215         if (nval != Qnil)
10216                 npoints = NUM2INT(rb_Integer(nval));
10217         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10218                 return Qnil;
10219         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));
10220 }
10221
10222 static int
10223 s_Cubegen_callback(double progress, void *ref)
10224 {
10225         MyAppCallback_setProgressValue(progress);
10226         if (MyAppCallback_checkInterrupt())
10227                 return 1;
10228         else return 0;
10229 }
10230
10231 /*
10232  *  call-seq:
10233  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10234  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10235  *
10236  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10237  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10238  *  second form, the cube dimension is explicitly given.
10239  *  Returns fname when successful, nil otherwise.
10240  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10241  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10242  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10243  */
10244 static VALUE
10245 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10246 {
10247         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10248     Molecule *mol;
10249         Int mono, nx, ny, nz, npoints;
10250         Vector o, dx, dy, dz;
10251         int index, n;
10252         char buf[1024];
10253     Data_Get_Struct(self, Molecule, mol);
10254         if (mol->bset == NULL)
10255                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10256         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10257         
10258         /*  Set up parameters  */
10259         mono = NUM2INT(rb_Integer(mval));
10260         if (mono < 0 || mono > mol->bset->ncomps)
10261                 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);
10262         if (RTEST(bval)) {
10263                 if (mol->bset->rflag != 0)
10264                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10265                 mono += mol->bset->ncomps;
10266         }
10267                 
10268         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10269                 /*  Automatic grid formation  */
10270                 if (oval != Qnil)
10271                         npoints = NUM2INT(rb_Integer(oval));
10272                 else npoints = 0;
10273                 if (npoints == 0)
10274                         npoints = 1000000;
10275                 else if (npoints < 8)
10276                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10277                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10278                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10279                 ival = dxval;
10280                 bval = dyval;
10281         } else {
10282                 VectorFromValue(oval, &o);
10283                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10284                         VectorFromValue(dxval, &dx);
10285                 else {
10286                         dx.x = NUM2DBL(rb_Float(dxval));
10287                         dx.y = dx.z = 0.0;
10288                 }
10289                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10290                         VectorFromValue(dyval, &dy);
10291                 else {
10292                         dy.y = NUM2DBL(rb_Float(dyval));
10293                         dy.x = dy.z = 0.0;
10294                 }
10295                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10296                         VectorFromValue(dzval, &dz);
10297                 else {
10298                         dz.z = NUM2DBL(rb_Float(dzval));
10299                         dz.x = dz.y = 0.0;
10300                 }
10301                 nx = NUM2INT(rb_Integer(nxval));
10302                 ny = NUM2INT(rb_Integer(nyval));
10303                 nz = NUM2INT(rb_Integer(nzval));
10304                 if (nx <= 0 || ny <= 0 || nz <= 0)
10305                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10306                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10307                         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);
10308         }
10309         
10310         /*  Calc MO  */
10311         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10312         if (index == -2)
10313                 rb_interrupt();
10314         else if (index < 0)
10315                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10316         
10317         /*  Output to file  */
10318         MoleculeCallback_displayName(mol, buf, sizeof buf);
10319         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10320         if (n != 0)
10321                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10322         
10323         /*  Discard the cube  */
10324         MoleculeClearCubeAtIndex(mol, index);
10325         return fval;
10326 }
10327
10328 /*
10329  *  call-seq:
10330  *     clear_surface
10331  *
10332  *  Clear the MO surface if present.
10333  */
10334 static VALUE
10335 s_Molecule_ClearSurface(VALUE self)
10336 {
10337     Molecule *mol;
10338     Data_Get_Struct(self, Molecule, mol);
10339         if (mol->mcube != NULL)
10340                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10341         return self;
10342 }
10343
10344 /*
10345  *  call-seq:
10346  *     hide_surface
10347  *
10348  *  Hide the MO surface if present.
10349  */
10350 static VALUE
10351 s_Molecule_HideSurface(VALUE self)
10352 {
10353     Molecule *mol;
10354     Data_Get_Struct(self, Molecule, mol);
10355         if (mol->mcube != NULL) {
10356                 mol->mcube->hidden = 1;
10357                 MoleculeCallback_notifyModification(mol, 0);
10358         }
10359         return self;
10360 }
10361
10362 /*
10363  *  call-seq:
10364  *     show_surface
10365  *
10366  *  Show the MO surface if present.
10367  */
10368 static VALUE
10369 s_Molecule_ShowSurface(VALUE self)
10370 {
10371     Molecule *mol;
10372     Data_Get_Struct(self, Molecule, mol);
10373         if (mol->mcube != NULL) {
10374                 mol->mcube->hidden = 0;
10375                 MoleculeCallback_notifyModification(mol, 0);
10376         }
10377         return self;
10378 }
10379
10380 /*
10381  *  call-seq:
10382  *     create_surface(mo, attr = nil)
10383  *
10384  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10385  *  then it denotes the beta orbital.
10386  *  If mo is nil, then the attributes of the current surface are modified.
10387  *  Attributes:
10388  *    :npoints : the approximate number of grid points
10389  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10390  *    :thres   : the threshold for the isovalue surface
10391  *  If the molecule does not contain MO information, raises exception.
10392  */
10393 static VALUE
10394 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10395 {
10396     Molecule *mol;
10397         Vector o, dx, dy, dz;
10398         Int nmo, nx, ny, nz, i;
10399         Int need_recalc = 0;
10400         VALUE nval, hval, aval;
10401         Int npoints;
10402         Double expand;
10403         Double thres;
10404         Double d[4];
10405     Data_Get_Struct(self, Molecule, mol);
10406         rb_scan_args(argc, argv, "11", &nval, &hval);
10407         if (mol->bset == NULL)
10408                 rb_raise(rb_eMolbyError, "No MO information is given");
10409         if (nval == Qnil) {
10410                 nmo = -1;
10411         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10412                 nmo = mol->bset->nmos + 1;
10413         } else {
10414                 nmo = NUM2INT(rb_Integer(nval));
10415                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10416                         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);
10417                 if (nmo < 0)
10418                         nmo = -nmo + mol->bset->ncomps;
10419         }
10420         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10421                 npoints = NUM2INT(rb_Integer(aval));
10422                 need_recalc = 1;
10423         } else if (mol->mcube != NULL) {
10424                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10425         } else npoints = 80 * 80 * 80;
10426         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10427                 expand = NUM2DBL(rb_Float(aval));
10428         } else if (mol->mcube != NULL) {
10429                 expand = mol->mcube->expand;
10430         } else expand = 1.0;
10431         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10432                 thres = NUM2DBL(rb_Float(aval));
10433         } else if (mol->mcube != NULL) {
10434                 thres = mol->mcube->thres;
10435         } else thres = 0.05;
10436         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10437                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10438                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10439                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10440                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10441         }
10442         for (nx = 0; nx < 2; nx++) {
10443                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10444                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10445                         aval = rb_ary_to_ary(aval);
10446                         if (RARRAY_LEN(aval) < 3) {
10447                         raise:
10448                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10449                         }
10450                         for (i = 0; i < 4; i++)
10451                                 d[i] = mol->mcube->c[nx].rgba[i];
10452                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10453                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10454                                 if (d[i] < 0.0 && d[i] > 1.0)
10455                                         goto raise;
10456                         }
10457                         for (i = 0; i < 4; i++)
10458                                 mol->mcube->c[nx].rgba[i] = d[i];
10459                 }
10460         }
10461         if (mol->mcube->expand != expand)
10462                 need_recalc = 1;
10463         mol->mcube->thres = thres;
10464         mol->mcube->expand = expand;
10465         if (nmo < 0) {
10466                 if (mol->mcube->idn < 0)
10467                         return self;  /*  Only set attributes for now  */
10468                 if (need_recalc)
10469                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10470         }
10471         if (MoleculeUpdateMCube(mol, nmo) != 0)
10472                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10473         return self;
10474 }
10475
10476 /*
10477  *  call-seq:
10478  *     set_surface_attr(attr = nil)
10479  *
10480  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10481  */
10482 static VALUE
10483 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10484 {
10485         VALUE args[2];
10486         args[0] = Qnil;
10487         args[1] = hval;
10488         return s_Molecule_CreateSurface(2, args, self);
10489 }
10490
10491 /*
10492  *  call-seq:
10493  *     nelpots
10494  *
10495  *  Get the number of electrostatic potential info.
10496  */
10497 static VALUE
10498 s_Molecule_NElpots(VALUE self)
10499 {
10500         Molecule *mol;
10501     Data_Get_Struct(self, Molecule, mol);
10502         return INT2NUM(mol->nelpots);
10503 }
10504
10505 /*
10506  *  call-seq:
10507  *     elpot(idx)
10508  *
10509  *  Get the electrostatic potential info at the given index. If present, then the
10510  *  return value is [Vector, Float] (position and potential). If not present, then
10511  *  returns nil.
10512  */
10513 static VALUE
10514 s_Molecule_Elpot(VALUE self, VALUE ival)
10515 {
10516         Molecule *mol;
10517         int idx;
10518     Data_Get_Struct(self, Molecule, mol);
10519         idx = NUM2INT(rb_Integer(ival));
10520         if (idx < 0 || idx >= mol->nelpots)
10521                 return Qnil;
10522         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10523 }
10524
10525 /*
10526  *  call-seq:
10527  *     clear_basis_set
10528  *
10529  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10530  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10531  */
10532 static VALUE
10533 s_Molecule_ClearBasisSet(VALUE self)
10534 {
10535         Molecule *mol;
10536     Data_Get_Struct(self, Molecule, mol);
10537         if (mol != NULL) {
10538                 if (mol->bset != NULL) {
10539                         BasisSetRelease(mol->bset);
10540                         mol->bset = NULL;
10541                 }
10542                 if (mol->mcube != NULL) {
10543                         MoleculeDeallocateMCube(mol->mcube);
10544                         mol->mcube = NULL;
10545                 }
10546         }
10547         return self;
10548 }
10549
10550 /*
10551  *  call-seq:
10552  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10553  *
10554  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10555  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10556  *  -2, D5-type.
10557  */
10558 static VALUE
10559 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10560 {
10561         Molecule *mol;
10562         int sym, nprims, a_idx, n;
10563     Data_Get_Struct(self, Molecule, mol);
10564         a_idx = NUM2INT(rb_Integer(aval));
10565         sym = NUM2INT(rb_Integer(symval));
10566         nprims = NUM2INT(rb_Integer(npval));
10567         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10568         if (n == -1)
10569                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10570         else if (n == -2)
10571                 rb_raise(rb_eMolbyError, "Low memory");
10572         else if (n == -3)
10573                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10574         else if (n != 0)
10575                 rb_raise(rb_eMolbyError, "Unknown error");
10576         return self;
10577 }
10578
10579 /*
10580  *  call-seq:
10581  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10582  *
10583  *  To be used internally. Add a gaussian primitive coefficients.
10584  */
10585 static VALUE
10586 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10587 {
10588         Molecule *mol;
10589         Int n;
10590         Double exponent, contraction, contraction_sp;
10591     Data_Get_Struct(self, Molecule, mol);
10592         exponent = NUM2DBL(rb_Float(expval));
10593         contraction = NUM2DBL(rb_Float(cval));
10594         contraction_sp = NUM2DBL(rb_Float(cspval));
10595         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10596         if (n == -1)
10597                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10598         else if (n == -2)
10599                 rb_raise(rb_eMolbyError, "Low memory");
10600         else if (n != 0)
10601                 rb_raise(rb_eMolbyError, "Unknown error");
10602         return self;
10603 }
10604
10605 /*
10606  *  call-seq:
10607  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10608  *
10609  *  Get the Gaussian shell information for the given MO coefficient index.
10610  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10611  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10612  *  is the number of MO component belonging to this shell.
10613  */
10614 static VALUE
10615 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10616 {
10617         Molecule *mol;
10618         ShellInfo *sp;
10619         int s_idx, sym;
10620     Data_Get_Struct(self, Molecule, mol);
10621         if (mol->bset == NULL)
10622                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10623         s_idx = NUM2INT(rb_Integer(sval));
10624         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10625                 return Qnil;
10626         sp = mol->bset->shells + s_idx;
10627         sym = sp->sym;
10628         switch (sym) {
10629                 case kGTOType_S:  sym = 0;  break;
10630                 case kGTOType_SP: sym = -1; break;
10631                 case kGTOType_P:  sym = 1;  break;
10632                 case kGTOType_D:  sym = 2;  break;
10633                 case kGTOType_D5: sym = -2; break;
10634                 case kGTOType_F:  sym = 3;  break;
10635                 case kGTOType_F7: sym = -3; break;
10636                 case kGTOType_G:  sym = 4;  break;
10637                 case kGTOType_G9: sym = -4; break;
10638                 default:
10639                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10640         }
10641         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10642 }
10643
10644 /*
10645  *  call-seq:
10646  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10647  *
10648  *  Get the Gaussian primitive coefficients for the given MO component.
10649  */
10650 static VALUE
10651 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10652 {
10653         Molecule *mol;
10654         ShellInfo *sp;
10655         PrimInfo *pp;
10656         int s_idx, i;
10657         VALUE retval, aval;
10658     Data_Get_Struct(self, Molecule, mol);
10659         if (mol->bset == NULL)
10660                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10661         s_idx = NUM2INT(rb_Integer(sval));
10662         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10663                 return Qnil;
10664         sp = mol->bset->shells + s_idx;
10665         pp = mol->bset->priminfos + sp->p_idx;
10666         retval = rb_ary_new2(sp->nprim);
10667         for (i = 0; i < sp->nprim; i++) {
10668                 if (sp->sym == kGTOType_SP) {
10669                         /*  With P contraction coefficient  */
10670                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10671                 } else {
10672                         /*  Without P contraction coefficient  */
10673                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10674                 }
10675                 rb_ary_store(retval, i, aval);
10676         }
10677         return retval;
10678 }
10679
10680 /*
10681  *  call-seq:
10682  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10683  *
10684  *  Get the Gaussian shell information for the given MO coefficient index.
10685  */
10686 static VALUE
10687 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10688 {
10689         Molecule *mol;
10690         Int n, c, atom_idx, shell_idx;
10691         char label[32];
10692     Data_Get_Struct(self, Molecule, mol);
10693         if (mol->bset == NULL)
10694                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10695         c = NUM2INT(rb_Integer(cval));
10696         if (c < 0 || c >= mol->bset->ncomps)
10697                 return Qnil;
10698         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10699         if (n != 0)
10700                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10701         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10702 }
10703
10704 /*
10705  *  call-seq:
10706  *     clear_mo_coefficients
10707  *
10708  *  Clear the existing MO coefficients.
10709  */
10710 static VALUE
10711 s_Molecule_ClearMOCoefficients(VALUE self)
10712 {
10713         Molecule *mol;
10714         Data_Get_Struct(self, Molecule, mol);
10715         if (mol->bset != NULL) {
10716                 if (mol->bset->moenergies != NULL) {
10717                         free(mol->bset->moenergies);
10718                         mol->bset->moenergies = NULL;
10719                 }
10720                 if (mol->bset->mo != NULL) {
10721                         free(mol->bset->mo);
10722                         mol->bset->mo = NULL;
10723                 }
10724                 mol->bset->nmos = 0;
10725         }
10726         return self;
10727 }
10728
10729 /*
10730  *  call-seq:
10731  *     set_mo_coefficients(idx, energy, coefficients)
10732  *
10733  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10734  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10735  *  Energy is the MO energy, and coefficients is an array
10736  *  of MO coefficients.
10737  */
10738 static VALUE
10739 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10740 {
10741         Molecule *mol;
10742         Int idx, ncomps, i;
10743         Double energy;
10744         Double *coeffs;
10745     Data_Get_Struct(self, Molecule, mol);
10746         idx = NUM2INT(rb_Integer(ival));
10747         energy = NUM2DBL(rb_Float(eval));
10748         aval = rb_ary_to_ary(aval);
10749         ncomps = RARRAY_LEN(aval);
10750         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10751         if (coeffs == NULL) {
10752                 i = -2;
10753                 goto end;
10754         }
10755         for (i = 0; i < ncomps; i++)
10756                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10757         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10758 end:
10759         if (i == -1)
10760                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10761         else if (i == -2)
10762                 rb_raise(rb_eMolbyError, "Low memory");
10763         else if (i == -3)
10764                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10765         else if (i == -4)
10766                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10767         else if (i == -5)
10768                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10769         else if (i != 0)
10770                 rb_raise(rb_eMolbyError, "Unknown error");
10771         return self;
10772 }
10773
10774 /*
10775  *  call-seq:
10776  *     get_mo_coefficients(idx)
10777  *
10778  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10779  */
10780 static VALUE
10781 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10782 {
10783         Molecule *mol;
10784         Int idx, ncomps, n;
10785         Double energy;
10786         Double *coeffs;
10787         VALUE retval;
10788     Data_Get_Struct(self, Molecule, mol);
10789         idx = NUM2INT(rb_Integer(ival));
10790         ncomps = 0;
10791         coeffs = NULL;
10792         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10793         if (n == -1)
10794                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10795         else if (n == -2)
10796                 rb_raise(rb_eMolbyError, "No basis set information is present");
10797         else if (n == -3)
10798                 return Qnil;  /*  Silently returns nil  */
10799         retval = rb_ary_new2(ncomps);
10800         for (n = 0; n < ncomps; n++)
10801                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10802         free(coeffs);
10803         return retval;
10804 }
10805
10806 /*
10807  *  call-seq:
10808  *     get_mo_energy(idx)
10809  *
10810  *  To be used internally. Get the MO energy for the given MO index (1-based).
10811  */
10812 static VALUE
10813 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10814 {
10815         Molecule *mol;
10816         Int idx, n;
10817         Double energy;
10818     Data_Get_Struct(self, Molecule, mol);
10819         idx = NUM2INT(rb_Integer(ival));
10820         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10821         if (n == -1)
10822                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10823         else if (n == -2)
10824                 rb_raise(rb_eMolbyError, "No basis set information is present");
10825         else if (n == -3)
10826                 return Qnil;
10827         return rb_float_new(energy);
10828 }
10829
10830 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10831
10832 static inline void
10833 s_InitMOInfoKeys(void)
10834 {
10835         if (sTypeSym == 0) {
10836                 sTypeSym = ID2SYM(rb_intern("type"));
10837                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10838                 sBetaSym = ID2SYM(rb_intern("beta"));
10839                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10840                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10841         }
10842 }
10843
10844 /*
10845  *  call-seq:
10846  *     set_mo_info(hash)
10847  *
10848  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10849  *  :alpha=>integer, :beta=>integer
10850  */
10851 static VALUE
10852 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10853 {
10854         Molecule *mol;
10855         VALUE aval;
10856         Int rflag, na, nb, n;
10857         char *s;
10858     Data_Get_Struct(self, Molecule, mol);
10859         if (mol->bset != NULL) {
10860                 rflag = mol->bset->rflag;
10861                 na = mol->bset->ne_alpha;
10862                 nb = mol->bset->ne_beta;
10863         } else {
10864                 rflag = 1;
10865                 na = 0;
10866                 nb = 0;
10867         }
10868         if (hval != Qnil) {
10869                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10870                         s = StringValuePtr(aval);
10871                         if (strcasecmp(s, "RHF") == 0)
10872                                 rflag = 1;
10873                         else if (strcasecmp(s, "UHF") == 0)
10874                                 rflag = 0;
10875                         else if (strcasecmp(s, "ROHF") == 0)
10876                                 rflag = 2;
10877                 }
10878                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10879                         n = NUM2INT(rb_Integer(aval));
10880                         if (n >= 0)
10881                                 na = n;
10882                 }
10883                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10884                         n = NUM2INT(rb_Integer(aval));
10885                         if (n >= 0)
10886                                 nb = n;
10887                 }
10888                 MoleculeSetMOInfo(mol, rflag, na, nb);
10889         }
10890         return self;
10891 }
10892
10893 /*
10894  *  call-seq:
10895  *     get_mo_info(key)
10896  *
10897  *  Get the MO info. The key is as described in set_mo_info.
10898  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10899  */
10900 static VALUE
10901 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10902 {
10903         Molecule *mol;
10904     Data_Get_Struct(self, Molecule, mol);
10905         if (mol->bset == NULL)
10906                 return Qnil;
10907         if (kval == sTypeSym) {
10908                 switch (mol->bset->rflag) {
10909                         case 0: return rb_str_new2("UHF");
10910                         case 1: return rb_str_new2("RHF");
10911                         case 2: return rb_str_new2("ROHF");
10912                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10913                 }
10914         } else if (kval == sAlphaSym) {
10915                 return INT2NUM(mol->bset->ne_alpha);
10916         } else if (kval == sBetaSym) {
10917                 return INT2NUM(mol->bset->ne_beta);
10918         } else if (kval == sNcompsSym) {
10919                 return INT2NUM(mol->bset->ncomps);
10920         } else if (kval == sNshellsSym) {
10921                 return INT2NUM(mol->bset->nshells);
10922         } else {
10923                 kval = rb_inspect(kval);
10924                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10925                 return Qnil;  /*  Does not reach here  */
10926         }
10927 }
10928
10929 /*
10930  *  call-seq:
10931  *     mo_type
10932  *
10933  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10934  */
10935 static VALUE
10936 s_Molecule_MOType(VALUE self)
10937 {
10938         return s_Molecule_GetMOInfo(self, sTypeSym);
10939 }
10940
10941 #pragma mark ------ Molecular Topology ------
10942
10943 /*
10944  *  call-seq:
10945  *     search_equivalent_atoms(ig = nil)
10946  *
10947  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
10948  */
10949 static VALUE
10950 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10951 {
10952         Molecule *mol;
10953         Int *result, i;
10954         VALUE val;
10955         IntGroup *ig;
10956     Data_Get_Struct(self, Molecule, mol);
10957         if (mol->natoms == 0)
10958                 return Qnil;
10959         rb_scan_args(argc, argv, "01", &val);
10960         if (val != Qnil)
10961                 ig = IntGroupFromValue(val);
10962         else ig = NULL;
10963         result = MoleculeSearchEquivalentAtoms(mol, ig);
10964         if (result == NULL)
10965                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10966         if (ig != NULL)
10967                 IntGroupRelease(ig);
10968         val = rb_ary_new2(mol->natoms);
10969         for (i = 0; i < mol->natoms; i++)
10970                 rb_ary_push(val, INT2NUM(result[i]));
10971         free(result);
10972         return val;
10973 }
10974
10975 /*
10976  *  call-seq:
10977  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10978  *
10979  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10980  *  Name is the name of the new pi anchor, and group is the atoms that define
10981  *  the pi system. Type (a String) is an atom type for MM implementation.
10982  *  Weights represent the relative significance of the component atoms; if omitted, then
10983  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
10984  *  The weight values will be normalized so that the sum of the weights is 1.0.
10985  *  The weight values must be positive.
10986  *  Index is the atom index where the created pi-anchor is inserted in the 
10987  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
10988  *  having the largest index.
10989  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
10990  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10991  */
10992 static VALUE
10993 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10994 {
10995         Molecule *mol;
10996         VALUE nval, gval;
10997         IntGroup *ig;
10998         Int i, n, idx, last_component;
10999         Atom a, *ap;
11000         PiAnchor an;
11001         AtomRef *aref;
11002         if (argc < 2 || argc >= 6)
11003                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11004         nval = *argv++;
11005         gval = *argv++;
11006         argc -= 2;
11007     Data_Get_Struct(self, Molecule, mol);
11008         ig = IntGroupFromValue(gval);
11009         memset(&a, 0, sizeof(a));
11010         memset(&an, 0, sizeof(an));
11011         strncpy(a.aname, StringValuePtr(nval), 4);
11012         if (a.aname[0] == '_')
11013                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11014         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11015         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11016                 if (n >= mol->natoms) {
11017                         AtomConnectResize(&an.connect, 0);
11018                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11019                 }
11020                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11021                 last_component = n;
11022         }
11023         if (an.connect.count == 0)
11024                 rb_raise(rb_eMolbyError, "no atoms are specified");
11025         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11026         for (i = 0; i < an.connect.count; i++) {
11027                 an.coeffs[i] = 1.0 / an.connect.count;
11028         }
11029         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11030                 /*  Atom type  */
11031                 if (argv[0] != Qnil)
11032                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11033                 argc--;
11034                 argv++;
11035         }
11036         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11037                 if (argv[0] != Qnil) {
11038                         VALUE aval = rb_ary_to_ary(argv[0]);
11039                         Double d, sum;
11040                         if (RARRAY_LEN(aval) != an.connect.count)
11041                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11042                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11043                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11044                                 if (d <= 0.0)
11045                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11046                                 sum += d;
11047                                 an.coeffs[i] = d;
11048                         }
11049                         for (i = 0; i < an.connect.count; i++)
11050                                 an.coeffs[i] /= sum;
11051                 }
11052                 argc--;
11053                 argv++;
11054         }
11055         if (argc > 0 && argv[0] != Qnil) {
11056                 /*  Index  */
11057                 idx = NUM2INT(rb_Integer(argv[0]));
11058         } else idx = -1;
11059         if (idx < 0 || idx > mol->natoms) {
11060                 /*  Immediately after the last specified atom  */
11061                 idx = last_component + 1;
11062         }
11063         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11064         memmove(a.anchor, &an, sizeof(PiAnchor));
11065         /*  Use residue information of the last specified atom  */
11066         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11067         a.resSeq = ap->resSeq;
11068         strncpy(a.resName, ap->resName, 4);
11069         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11070                 return Qnil;
11071         MoleculeCalculatePiAnchorPosition(mol, idx);
11072     aref = AtomRefNew(mol, idx);
11073     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11074 }
11075
11076 #pragma mark ------ Molecular Properties ------
11077
11078 /*
11079  *  call-seq:
11080  *     set_property(name, value[, index]) -> value
11081  *     set_property(name, values, group) -> values
11082  *
11083  *  Set molecular property. A property is a floating-point number with a specified name,
11084  *  and can be set for each frame separately. The name of the property is given as a String.
11085  *  The value can be a single floating point number, which is set to the current frame.
11086  *  
11087  */
11088 static VALUE
11089 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11090 {
11091         Molecule *mol;
11092         VALUE nval, vval, ival;
11093         char *name;
11094         IntGroup *ig;
11095         Int i, n, idx, fidx;
11096         Double *dp;
11097         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11098     Data_Get_Struct(self, Molecule, mol);
11099         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11100                 idx = NUM2INT(rb_Integer(nval));
11101                 if (idx < 0 || idx >= mol->nmolprops)
11102                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11103         } else {
11104                 name = StringValuePtr(nval);
11105                 idx = MoleculeLookUpProperty(mol, name);
11106                 if (idx < 0) {
11107                         idx = MoleculeCreateProperty(mol, name);
11108                         if (idx < 0)
11109                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11110                 }
11111         }
11112         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11113                 if (ival == Qnil)
11114                         fidx = mol->cframe;
11115                 else {
11116                         fidx = NUM2INT(rb_Integer(ival));
11117                         n = MoleculeGetNumberOfFrames(mol);
11118                         if (fidx < 0 || fidx >= n)
11119                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11120                 }
11121                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11122                 dp = (Double *)malloc(sizeof(Double));
11123                 *dp = NUM2DBL(rb_Float(vval));
11124                 n = 1;
11125         } else {
11126                 vval = rb_ary_to_ary(vval);
11127                 ig = IntGroupFromValue(ival);
11128                 n = IntGroupGetCount(ig);
11129                 if (n == 0)
11130                         rb_raise(rb_eMolbyError, "No frames are specified");
11131                 if (RARRAY_LEN(vval) < n)
11132                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11133                 dp = (Double *)calloc(sizeof(Double), n);
11134                 for (i = 0; i < n; i++)
11135                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11136         }
11137         
11138         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11139         free(dp);
11140         IntGroupRelease(ig);
11141         return self;
11142 }
11143
11144 /*
11145  *  call-seq:
11146  *     get_property(name[, index]) -> value
11147  *     get_property(name, group) -> values
11148  *
11149  *  Get molecular property. In the first form, a property value for a single frame is returned.
11150  *  (If index is omitted, then the value for the current frame is given)
11151  *  In the second form, an array of property values for the given frames is returned.
11152  *  If name is not one of known properties or a valid index integer, exception is raised.
11153  */
11154 static VALUE
11155 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11156 {
11157         Molecule *mol;
11158         VALUE nval, ival;
11159         char *name;
11160         IntGroup *ig;
11161         Int i, n, idx, fidx;
11162         Double *dp;
11163         rb_scan_args(argc, argv, "11", &nval, &ival);
11164     Data_Get_Struct(self, Molecule, mol);
11165         if (mol->nmolprops == 0)
11166                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11167         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11168                 idx = NUM2INT(rb_Integer(nval));
11169                 if (idx < 0 || idx >= mol->nmolprops)
11170                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11171         } else {
11172                 name = StringValuePtr(nval);
11173                 idx = MoleculeLookUpProperty(mol, name);
11174                 if (idx < 0)
11175                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11176         }
11177         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11178                 if (ival == Qnil)
11179                         fidx = mol->cframe;
11180                 else {
11181                         fidx = NUM2INT(rb_Integer(ival));
11182                         n = MoleculeGetNumberOfFrames(mol);
11183                         if (fidx < 0 || fidx >= n)
11184                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11185                 }
11186                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11187                 ival = INT2FIX(fidx);
11188                 n = 1;
11189         } else {
11190                 ig = IntGroupFromValue(ival);
11191                 n = IntGroupGetCount(ig);
11192                 if (n == 0)
11193                         return rb_ary_new();
11194         }
11195         dp = (Double *)calloc(sizeof(Double), n);
11196         MoleculeGetProperty(mol, idx, ig, dp);  
11197         if (FIXNUM_P(ival))
11198                 ival = rb_float_new(dp[0]);
11199         else {
11200                 ival = rb_ary_new();
11201                 for (i = n - 1; i >= 0; i--) {
11202                         nval = rb_float_new(dp[i]);
11203                         rb_ary_store(ival, i, nval);
11204                 }
11205         }
11206         free(dp);
11207         IntGroupRelease(ig);
11208         return ival;
11209 }
11210
11211 /*
11212  *  call-seq:
11213  *     property_names -> Array
11214  *
11215  *  Get an array of property names.
11216  */
11217 static VALUE
11218 s_Molecule_PropertyNames(VALUE self)
11219 {
11220         Molecule *mol;
11221         VALUE rval, nval;
11222         int i;
11223     Data_Get_Struct(self, Molecule, mol);
11224         rval = rb_ary_new();
11225         for (i = mol->nmolprops - 1; i >= 0; i--) {
11226                 nval = rb_str_new2(mol->molprops[i].propname);
11227                 rb_ary_store(rval, i, nval);
11228         }
11229         return rval;
11230 }
11231
11232 #pragma mark ------ Class methods ------
11233
11234 /*
11235  *  call-seq:
11236  *     current       -> Molecule
11237  *
11238  *  Get the currently "active" molecule.
11239  */
11240 static VALUE
11241 s_Molecule_Current(VALUE klass)
11242 {
11243         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11244 }
11245
11246 /*
11247  *  call-seq:
11248  *     Molecule[]          -> Molecule
11249  *     Molecule[n]         -> Molecule
11250  *     Molecule[name]      -> Molecule
11251  *     Molecule[name, k]   -> Molecule
11252  *     Molecule[regex]     -> Molecule
11253  *     Molecule[regex, k]  -> Molecule
11254  *
11255  *  Molecule[] is equivalent to Molecule.current.
11256  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11257  *  Molecule[name] gives the first document (in the order of creation time) that has
11258  *  the given name. If a second argument (k) is given, the k-th document that has the
11259  *  given name is returned.
11260  *  Molecule[regex] gives the first document (in the order of creation time) that
11261  *  has a name matching the regular expression. If a second argument (k) is given, 
11262  *  the k-th document that has a name matching the re is returned.
11263  */
11264 static VALUE
11265 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11266 {
11267         VALUE val, kval;
11268         int idx, k;
11269         Molecule *mol;
11270         char buf[1024];
11271         rb_scan_args(argc, argv, "02", &val, &kval);
11272         if (val == Qnil)
11273                 return s_Molecule_Current(klass);
11274         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11275                 idx = NUM2INT(val);
11276                 mol = MoleculeCallback_moleculeAtIndex(idx);
11277         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11278                 char *p = StringValuePtr(val);
11279                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11280                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11281                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11282                         if (strcmp(buf, p) == 0 && --k == 0)
11283                                 break;
11284                 }
11285         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11286                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11287                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11288                         VALUE name;
11289                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11290                         name = rb_str_new2(buf);
11291                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11292                                 break;
11293                 }       
11294         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11295         
11296         if (mol == NULL)
11297                 return Qnil;
11298         else return ValueFromMolecule(mol);
11299 }
11300
11301 /*
11302  *  call-seq:
11303  *     list         -> array of Molecules
11304  *
11305  *  Get the list of molecules associated to the documents, in the order of creation
11306  *  time of the document. If no document is open, returns an empry array.
11307  */
11308 static VALUE
11309 s_Molecule_List(VALUE klass)
11310 {
11311         Molecule *mol;
11312         int i;
11313         VALUE ary;
11314         i = 0;
11315         ary = rb_ary_new();
11316         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11317                 rb_ary_push(ary, ValueFromMolecule(mol));
11318                 i++;
11319         }
11320         return ary;
11321 }
11322
11323 /*
11324  *  call-seq:
11325  *     ordered_list         -> array of Molecules
11326  *
11327  *  Get the list of molecules associated to the documents, in the order of front-to-back
11328  *  ordering of the associated window. If no document is open, returns an empry array.
11329  */
11330 static VALUE
11331 s_Molecule_OrderedList(VALUE klass)
11332 {
11333         Molecule *mol;
11334         int i;
11335         VALUE ary;
11336         i = 0;
11337         ary = rb_ary_new();
11338         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11339                 rb_ary_push(ary, ValueFromMolecule(mol));
11340                 i++;
11341         }
11342         return ary;
11343 }
11344
11345 #pragma mark ------ Call Subprocess ------
11346
11347 /*  The callback functions for call_subprocess_async  */
11348 static int
11349 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11350 {
11351         int ruby_status;
11352         VALUE procval, retval, args[2];
11353         args[0] = ValueFromMolecule(mol);
11354         args[1] = INT2NUM(status);
11355         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11356         if (procval != Qnil) {
11357                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11358                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11359                         return 1;
11360         }
11361         return 0;
11362 }
11363
11364 static int
11365 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11366 {
11367         int ruby_status;
11368         VALUE procval, retval, args[2];
11369         args[0] = ValueFromMolecule(mol);
11370         args[1] = INT2NUM(tcount);
11371         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11372         if (procval != Qnil) {
11373                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11374                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11375                         return 1;
11376         }
11377         return 0;
11378 }
11379
11380 /*
11381  *  call-seq:
11382  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11383  *
11384  *  Call subprocess asynchronically.
11385  *  If end_callback is given, it will be called (with two arguments self and termination status)
11386  *  when the subprocess terminated.
11387  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11388  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11389  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11390  *  filename begins with ">>", then the message will be appended to the file.
11391  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11392  *  If the argument is nil, then the message will be sent to the Ruby console.
11393  *  Returns the process ID as an integer.
11394  */
11395 static VALUE
11396 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11397 {
11398         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11399         Molecule *mol;
11400         char *sout, *serr;
11401         int n;
11402         FILE *fpout, *fperr;
11403         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11404         Data_Get_Struct(self, Molecule, mol);
11405
11406         if (stdout_val == Qnil) {
11407                 fpout = (FILE *)1;
11408         } else {
11409                 sout = StringValuePtr(stdout_val);
11410                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11411                         fpout = NULL;
11412                 else {
11413                         if (strncmp(sout, ">>", 2) == 0) {
11414                                 sout += 2;
11415                                 fpout = fopen(sout, "a");
11416                         } else {
11417                                 if (*sout == '>')
11418                                         sout++;
11419                                 fpout = fopen(sout, "w");
11420                         }
11421                         if (fpout == NULL)
11422                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11423                 }
11424         }
11425         if (stderr_val == Qnil) {
11426                 fperr = (FILE *)1;
11427         } else {
11428                 serr = StringValuePtr(stderr_val);
11429                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11430                         fperr = NULL;
11431                 else {
11432                         if (strncmp(serr, ">>", 2) == 0) {
11433                                 serr += 2;
11434                                 fpout = fopen(serr, "a");
11435                         } else {
11436                                 if (*serr == '>')
11437                                         serr++;
11438                                 fperr = fopen(serr, "w");
11439                         }
11440                         if (fperr == NULL)
11441                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11442                 }
11443         }
11444         
11445         /*  Register procs as instance variables  */
11446         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11447         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11448         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11449         if (fpout != NULL && fpout != (FILE *)1)
11450                 fclose(fpout);
11451         if (fperr != NULL && fperr != (FILE *)1)
11452                 fclose(fperr);
11453         return INT2NUM(n);
11454 }
11455
11456 #pragma mark ====== Define Molby Classes ======
11457
11458 void
11459 Init_Molby(void)
11460 {
11461         int i;
11462         
11463         /*  Define module Molby  */
11464         rb_mMolby = rb_define_module("Molby");
11465         
11466         /*  Define Vector3D, Transform, IntGroup  */
11467         Init_MolbyTypes();
11468         
11469         /*  Define MDArena  */
11470         Init_MolbyMDTypes();
11471
11472         /*  class Molecule  */
11473         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11474
11475         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11476     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11477     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11478         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11479         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11480
11481     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11482     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11483     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11484     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11485     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11486     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11487     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11488     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11489     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11490     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11491         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11492     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11493     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11494     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11495     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11496     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11497     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11498     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11499         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11500         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11501         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11502         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11503         
11504     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11505         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11506     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11507     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11508     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11509
11510     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11511     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11512     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11513     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11514     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11515     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11516         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11517         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11518         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11519         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11520         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11521         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11522         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11523         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11524         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11525         
11526         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11527         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11528         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11529         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11530         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11531         
11532         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11533     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11534         rb_define_alias(rb_cMolecule, "+", "add");
11535     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11536         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11537         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11538         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11539         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11540         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11541         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11542         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11543         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11544         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11545         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11546         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11547         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11548         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11549         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11550         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11551         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11552         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11553         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11554         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11555         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11556
11557         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11558         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11559         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11560         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11561         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11562
11563         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11564         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11565         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11566         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11567         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11568         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11569         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11570         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11571         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11572
11573         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11574         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11575         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11576         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11577         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11578         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11579         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11580         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11581         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11582         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11583         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11584         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11585         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11586         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11587         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11588         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11589         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11590         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11591         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11592         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11593         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11594         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11595
11596         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11597         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11598         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11599         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11600         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11601         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11602         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11603
11604         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11605         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11606         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11607         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11608         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11609         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11610         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11611         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11612         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11613         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11614         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11615         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11616         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11617
11618         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11619         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11620         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11621         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11622         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11623         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11624
11625         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11626         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11627         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11628         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11629         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11630         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11631         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11632         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11633         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11634         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11635         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11636         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11637         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11638         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11639         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11640         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11641         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11642         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11643         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11644         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11645         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11646         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11647         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11648         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11649         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11650         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11651         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11652         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11653         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11654         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11655         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11656         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11657         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11658         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11659         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11660         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11661         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11662         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11663         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11664         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11665         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11666         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11667         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11668         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11669         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11670         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11671         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11672         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11673         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11674         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11675         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11676         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11677         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11678         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11679         
11680         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11681         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11682         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11683         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11684         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11685         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11686         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11687         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11688         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11689         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11690         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11691         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11692         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11693
11694         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11695         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11696         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11697         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11698         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11699         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11700         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11701         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11702         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11703         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11704         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11705         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11706         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11707         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11708                 
11709         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11710         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11711         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11712         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11713         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11714         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11715         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11716         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11717         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11718         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11719         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11720         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11721         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11722         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11723         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11724         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11725         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11726         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11727         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11728         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11729         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11730         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11731         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11732
11733         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11734         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11735         
11736         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11737         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11738         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11739                 
11740         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11741         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11742         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11743         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11744         
11745         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11746         
11747         /*  class MolEnumerable  */
11748         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11749     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11750         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11751         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11752     rb_define_alias(rb_cMolEnumerable, "size", "length");
11753         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11754         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11755
11756         /*  class AtomRef  */
11757         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11758         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11759                 char buf[64];
11760                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11761                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11762                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11763                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11764                 strcat(buf, "=");
11765                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11766         }
11767         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11768         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11769         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11770         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11771         s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11772         rb_global_variable(&s_SetAtomAttrString);
11773         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11774         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11775
11776         /*  class Parameter  */
11777         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11778         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11779         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11780         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11781         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11782         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11783         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11784         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11785         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11786         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11787         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11788         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11789         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11790         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11791         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11792         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11793         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11794         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11795         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11796         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11797         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11798         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11799         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11800         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11801         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11802         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11803         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11804         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11805         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11806         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11807         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11808         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11809         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11810         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11811         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11812         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11813         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11814         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11815         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11816         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11817         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11818         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11819         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11820         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11821         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11822         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11823         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11824         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11825         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11826         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11827         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11828         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11829         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11830         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11831
11832         /*  class ParEnumerable  */
11833         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11834     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11835         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11836         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11837         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11838         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11839         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11840         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11841         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11842         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11843         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11844         
11845         /*  class ParameterRef  */
11846         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11847         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11848                 char buf[64];
11849                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11850                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11851                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11852                 if (s_ParameterAttrDefTable[i].symref != NULL)
11853                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11854                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11855                         strcat(buf, "=");
11856                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11857                 }
11858         }
11859         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11860         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11861         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11862         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11863         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11864         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11865         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11866         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11867
11868         /*  class MolbyError  */
11869         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11870
11871         /*  module Kernel  */
11872         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11873         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11874         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11875         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11876         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11877         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11878         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11879         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11880         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11881         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11882         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11883         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11884         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11885         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11886         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11887         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11888         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11889         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11890         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11891         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11892         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11893         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11894         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11895         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11896
11897         /*  class IO  */
11898         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11899         
11900         s_ID_equal = rb_intern("==");
11901         g_RubyID_call = rb_intern("call");
11902         
11903         s_InitMOInfoKeys();
11904         
11905         /*  Symbols for graphics  */
11906         s_LineSym = ID2SYM(rb_intern("line"));
11907         s_PolySym = ID2SYM(rb_intern("poly"));
11908         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11909         s_ConeSym = ID2SYM(rb_intern("cone"));
11910         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11911 }
11912
11913 #pragma mark ====== Interface with RubyDialog class ======
11914
11915 RubyValue
11916 RubyDialogCallback_parentModule(void)
11917 {
11918         return (RubyValue)rb_mMolby;
11919 }
11920
11921 #pragma mark ====== External functions ======
11922
11923 static VALUE s_ruby_top_self = Qfalse;
11924 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11925 static VALUE s_ruby_export_local_variables = Qfalse;
11926
11927 static VALUE
11928 s_evalRubyScriptOnMoleculeSub(VALUE val)
11929 {
11930         void **ptr = (void **)val;
11931         Molecule *mol = (Molecule *)ptr[1];
11932         VALUE sval, fnval, lnval, retval;
11933         VALUE binding;
11934
11935         /*  Clear the error information (store in the history array if necessary)  */
11936         sval = rb_errinfo();
11937         if (sval != Qnil) {
11938                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11939                 rb_set_errinfo(Qnil);
11940         }
11941
11942         if (s_ruby_top_self == Qfalse) {
11943                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11944         }
11945         if (s_ruby_get_binding_for_molecule == Qfalse) {
11946                 const char *s1 =
11947                  "lambda { |_mol_, _bind_| \n"
11948                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11949                  "  _proc_.call(_mol_) } ";
11950                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11951                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11952         }
11953         if (s_ruby_export_local_variables == Qfalse) {
11954                 const char *s2 =
11955                 "lambda { |_bind_| \n"
11956                 "   # find local variables newly defined in _bind_ \n"
11957                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11958                 " _a_.each { |_vsym_| \n"
11959                 "   _vname_ = _vsym_.to_s \n"
11960                 "   _vval_ = _bind_.eval(_vname_) \n"
11961                 "   #  Define local variable \n"
11962                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11963                 "   #  Then set value  \n"
11964                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11965                 " } \n"
11966                 "}";
11967                 s_ruby_export_local_variables = rb_eval_string(s2);
11968                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11969         }
11970         if (ptr[2] == NULL) {
11971                 char *scr;
11972                 /*  String literal: we need to specify string encoding  */
11973 #if defined(__WXMSW__)
11974                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11975 #else
11976                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11977 #endif
11978                 sval = rb_str_new2(scr);
11979                 free(scr);
11980                 fnval = rb_str_new2("(eval)");
11981                 lnval = INT2FIX(0);
11982         } else {
11983                 sval = rb_str_new2((char *)ptr[0]);
11984                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11985                 lnval = INT2FIX(1);
11986         }
11987         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11988         if (mol != NULL) {
11989                 VALUE mval = ValueFromMolecule(mol);
11990                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11991         }
11992         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11993         if (mol != NULL) {
11994                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11995         }
11996         return retval;
11997 }
11998
11999 RubyValue
12000 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12001 {
12002         RubyValue retval;
12003         void *args[3];
12004         VALUE save_interrupt_flag;
12005 /*      char *save_ruby_sourcefile;
12006         int save_ruby_sourceline; */
12007         if (gMolbyIsCheckingInterrupt) {
12008                 MolActionAlertRubyIsRunning();
12009                 *status = -1;
12010                 return (RubyValue)Qnil;
12011         }
12012         gMolbyRunLevel++;
12013         args[0] = (void *)script;
12014         args[1] = (void *)mol;
12015         args[2] = (void *)fname;
12016         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12017 /*      save_ruby_sourcefile = ruby_sourcefile;
12018         save_ruby_sourceline = ruby_sourceline; */
12019         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12020         if (*status != 0) {
12021                 /*  Is this 'exit' exception?  */
12022                 VALUE last_exception = rb_gv_get("$!");
12023                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12024                         /*  Capture exit and return the status value  */
12025                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12026                         *status = 0;
12027                         rb_set_errinfo(Qnil);
12028                 }
12029         }
12030         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12031 /*      ruby_sourcefile = save_ruby_sourcefile;
12032         ruby_sourceline = save_ruby_sourceline; */
12033         gMolbyRunLevel--;
12034         return retval;
12035 }
12036
12037 int
12038 Ruby_showValue(RubyValue value, char **outValueString)
12039 {
12040         VALUE val = (VALUE)value;
12041         if (gMolbyIsCheckingInterrupt) {
12042                 MolActionAlertRubyIsRunning();
12043                 return 0;
12044         }
12045         if (val != Qnil) {
12046                 int status;
12047                 char *str;
12048                 gMolbyRunLevel++;
12049                 val = rb_protect(rb_inspect, val, &status);
12050                 gMolbyRunLevel--;
12051                 if (status != 0)
12052                         return status;
12053                 str = StringValuePtr(val);
12054                 if (outValueString != NULL)
12055                         *outValueString = strdup(str);
12056                 MyAppCallback_showScriptMessage("%s", str);
12057         } else {
12058                 if (outValueString != NULL)
12059                         *outValueString = NULL;
12060         }
12061         return 0;
12062 }
12063
12064 void
12065 Ruby_showError(int status)
12066 {
12067         static const int tag_raise = 6;
12068         char *msg = NULL, *msg2;
12069         VALUE val, backtrace;
12070         int interrupted = 0;
12071         if (status == tag_raise) {
12072                 VALUE errinfo = rb_errinfo();
12073                 VALUE eclass = CLASS_OF(errinfo);
12074                 if (eclass == rb_eInterrupt) {
12075                         msg = "Interrupt";
12076                         interrupted = 1;
12077                 }
12078         }
12079         gMolbyRunLevel++;
12080         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12081         if (msg == NULL) {
12082                 val = rb_eval_string_protect("$!.to_s", &status);
12083                 if (status == 0)
12084                         msg = RSTRING_PTR(val);
12085                 else msg = "(message not available)";
12086         }
12087         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12088         MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
12089         free(msg2);
12090         gMolbyRunLevel--;
12091 }
12092
12093 char *
12094 Molby_getDescription(void)
12095 {
12096         extern const char *gVersionString, *gCopyrightString;
12097         extern int gRevisionNumber;
12098         extern char *gLastBuildString;
12099         char *s;
12100         char *revisionString;
12101         if (gRevisionNumber > 0) {
12102                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12103         } else revisionString = "";
12104         asprintf(&s, 
12105                          "Molby %s%s\n%s\nLast compile: %s\n"
12106 #if !defined(__CMDMAC__)
12107                          "\nIncluding:\n"
12108                          "%s"
12109 #else
12110                          "Including "
12111 #endif
12112                          "ruby %s, http://www.ruby-lang.org/\n"
12113                          "%s\n"
12114                          "FFTW 3.3.2, http://www.fftw.org/\n"
12115                          "  Copyright (C) 2003, 2007-11 Matteo Frigo\n"
12116                          "  and Massachusetts Institute of Technology",
12117                          gVersionString, revisionString, gCopyrightString, gLastBuildString,
12118 #if !defined(__CMDMAC__)
12119                          MyAppCallback_getGUIDescriptionString(),
12120 #endif
12121                          gRubyVersion, gRubyCopyright);
12122         if (revisionString[0] != 0)
12123                 free(revisionString);
12124         return s;
12125 }
12126
12127 void
12128 Molby_startup(const char *script, const char *dir)
12129 {
12130         VALUE val;
12131         int status;
12132         char *libpath;
12133         char *respath, *p, *wbuf;
12134
12135         /*  Get version/copyright string from Ruby interpreter  */
12136         {
12137                 gRubyVersion = strdup(ruby_version);
12138                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12139 #if defined(__CMDMAC__)
12140                                  "",
12141 #else
12142                                  "  ",  /*  Indent for displaying in About dialog  */
12143 #endif
12144                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12145         }
12146         
12147         /*  Read build and revision information for Molby  */
12148 /*      {
12149                 char buf[200];
12150                 extern int gRevisionNumber;
12151                 extern char *gLastBuildString;
12152                 FILE *fp = fopen("../buildInfo.txt", "r");
12153                 gLastBuildString = "";
12154                 if (fp != NULL) {
12155                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12156                                 char *p1 = strchr(buf, '\"');
12157                                 char *p2 = strrchr(buf, '\"');
12158                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12159                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12160                                         buf[p2 - p1 - 1] = 0;
12161                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12162                                 }
12163                         }
12164                         fclose(fp);
12165                 }
12166                 fp = fopen("../revisionInfo.txt", "r");
12167                 gRevisionNumber = 0;
12168                 if (fp != NULL) {
12169                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12170                                 gRevisionNumber = strtol(buf, NULL, 0);
12171                         }
12172                         fclose(fp);
12173                 }
12174     } */
12175
12176 #if defined(__CMDMAC__)
12177         wbuf = Molby_getDescription();
12178         printf("%s\n", wbuf);
12179         free(wbuf);
12180 #endif
12181         
12182         /*  Read atom display parameters  */
12183         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12184 #if defined(__CMDMAC__)
12185                 fprintf(stderr, "%s\n", wbuf);
12186 #else
12187                 MyAppCallback_setConsoleColor(1);
12188                 MyAppCallback_showScriptMessage("%s", wbuf);
12189                 MyAppCallback_setConsoleColor(0);
12190 #endif
12191                 free(wbuf);
12192         }
12193         
12194         /*  Read default parameters  */
12195         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12196         if (wbuf != NULL) {
12197 #if defined(__CMDMAC__)
12198                 fprintf(stderr, "%s\n", wbuf);
12199 #else
12200                 MyAppCallback_setConsoleColor(1);
12201                 MyAppCallback_showScriptMessage("%s", wbuf);
12202                 MyAppCallback_setConsoleColor(0);
12203 #endif
12204                 free(wbuf);
12205         }
12206                 
12207         /*  Initialize Ruby interpreter  */
12208 #if __WXMSW__
12209         {
12210                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12211                     it causes rb_bug() (= fatal error) during ruby_init().
12212                     As a workaround, these standard streams are reopend as
12213                     NUL stream.  */
12214                 freopen("NUL", "r", stdin);
12215                 freopen("NUL", "w", stdout);
12216                 freopen("NUL", "w", stderr);
12217         }
12218 #endif
12219         ruby_init();
12220
12221         {
12222                 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12223                 Init_shift_jis();
12224                 Init_trans_japanese_sjis();
12225         }
12226         
12227         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12228         ruby_incpush(".");
12229         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12230         ruby_incpush(libpath);
12231         free(libpath);
12232         ruby_incpush(dir);
12233
12234         ruby_script("Molby");
12235         
12236         /*  Find the resource path (the parent directory of the given directory)  */
12237         respath = strdup(dir);
12238         p = strrchr(respath, '/');
12239         if (p == NULL && PATH_SEPARATOR != '/')
12240                 p = strrchr(respath, PATH_SEPARATOR);
12241         if (p != NULL)
12242                 *p = 0;
12243         val = Ruby_NewFileStringValue(respath);
12244         rb_define_global_const("MolbyResourcePath", val);
12245         free(respath);
12246
12247         /*  Define Molby classes  */
12248         Init_Molby();
12249         RubyDialogInitClass();
12250
12251         rb_define_const(rb_mMolby, "ResourcePath", val);
12252         val = Ruby_NewFileStringValue(dir);
12253         rb_define_const(rb_mMolby, "ScriptPath", val);
12254         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12255         val = Ruby_NewFileStringValue(p);
12256         rb_define_const(rb_mMolby, "MbsfPath", val);    
12257         free(p);
12258         
12259         p = MyAppCallback_getHomeDir();
12260         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12261         rb_define_const(rb_mMolby, "HomeDirectory", val);
12262         free(p);
12263         p = MyAppCallback_getDocumentHomeDir();
12264         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12265         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12266         free(p);
12267         
12268 #if defined(__CMDMAC__)
12269         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12270 #else
12271         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12272 #endif
12273
12274 #if !__CMDMAC__
12275         
12276         /*  Create objects for stdout and stderr  */
12277         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12278         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12279         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12280         rb_gv_set("$stdout", val);
12281         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12282         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12283         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12284         rb_gv_set("$stderr", val);
12285
12286         /*  Create objects for stdin  */
12287         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12288         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12289         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12290         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12291         rb_gv_set("$stdin", val);
12292         
12293 #endif
12294         
12295         /*  Global variable to hold error information  */
12296         rb_define_variable("$backtrace", &gMolbyBacktrace);
12297         rb_define_variable("$error_history", &gMolbyErrorHistory);
12298         gMolbyErrorHistory = rb_ary_new();
12299         
12300         /*  Global variables for script menus  */
12301         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12302         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12303         gScriptMenuCommands = rb_ary_new();
12304         gScriptMenuEnablers = rb_ary_new();
12305         
12306 #if !__CMDMAC__
12307         /*  Register interrupt check code  */
12308         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12309 #endif
12310         
12311 #if !__CMDMAC__
12312         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12313         s_SetIntervalTimer(0, 50);
12314 #endif
12315         
12316         /*  Read the startup script  */
12317         if (script != NULL && script[0] != 0) {
12318                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12319                 gMolbyRunLevel++;
12320                 rb_load_protect(rb_str_new2(script), 0, &status);
12321                 gMolbyRunLevel--;
12322                 if (status != 0)
12323                         Ruby_showError(status);
12324                 else
12325                         MyAppCallback_showScriptMessage("Done.\n");
12326         }
12327 }
12328
12329 void
12330 Molby_buildARGV(int argc, const char **argv)
12331 {
12332         int i;
12333     rb_ary_clear(rb_argv);
12334     for (i = 0; i < argc; i++) {
12335                 VALUE arg = rb_tainted_str_new2(argv[i]);
12336                 OBJ_FREEZE(arg);
12337                 rb_ary_push(rb_argv, arg);
12338     }
12339 }