OSDN Git Service

A CIF file with multiple structures now can be processed (multiple documents are...
[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;
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);
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;
996         VALUE val;
997         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
998         if (n != 0)
999                 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
1000         if (buf != NULL) {
1001                 val = Ruby_NewEncodedStringValue(buf, 0);
1002                 free(buf);
1003         } else {
1004                 val = Ruby_NewEncodedStringValue("", 0);
1005         }
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 /*
1045  *  call-seq:
1046  *     gets_any_eol
1047  *
1048  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1049  */
1050 static VALUE
1051 s_IO_gets_any_eol(VALUE self)
1052 {
1053         VALUE val, cval;
1054         char buf[1024];
1055         int i, c;
1056         static ID id_getbyte = 0, id_ungetbyte;
1057         if (id_getbyte == 0) {
1058                 id_getbyte = rb_intern("getbyte");
1059                 id_ungetbyte = rb_intern("ungetbyte");
1060         }
1061         i = 0;
1062         val = Qnil;
1063         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1064                 c = NUM2INT(rb_Integer(cval));
1065                 if (c == 0x0d) {
1066                         cval = rb_funcall(self, id_getbyte, 0);
1067                         if (cval != Qnil) {
1068                                 c = NUM2INT(rb_Integer(cval));
1069                                 if (c != 0x0a)
1070                                         rb_funcall(self, id_ungetbyte, 1, cval);
1071                         }
1072                         break;
1073                 } else if (c != 0x0a) {
1074                         buf[i++] = c;
1075                         if (i >= 1020) {
1076                                 buf[i] = 0;
1077                                 if (val == Qnil)
1078                                         val = rb_str_new(buf, i);
1079                                 else
1080                                         rb_str_append(val, rb_str_new(buf, i));
1081                                 i = 0;
1082                         }
1083                 } else break;
1084         }
1085         if (cval == Qnil && i == 0 && val == Qnil)
1086                 return Qnil;  /*  End of file  */
1087         buf[i] = 0;
1088         if (val == Qnil)
1089                 val = rb_str_new(buf, i);
1090         else if (i > 0)
1091                 rb_str_append(val, rb_str_new(buf, i));
1092         val = rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
1093         if (cval != Qnil) {
1094                 /*  Needs a end-of-line mark  */
1095                 cval = rb_gv_get("$/");
1096                 rb_str_append(val, cval);
1097         }
1098         rb_gv_set("$_", val);
1099         return val;
1100 }
1101
1102 #pragma mark ====== Utility functions (protected funcall) ======
1103
1104 struct Ruby_funcall2_record {
1105         VALUE recv;
1106         ID mid;
1107         int argc;
1108         VALUE *argv;
1109 };
1110
1111 static VALUE
1112 s_Ruby_funcall2_sub(VALUE data)
1113 {
1114         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1115         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1116 }
1117
1118 VALUE
1119 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1120 {
1121         struct Ruby_funcall2_record rec;
1122         rec.recv = recv;
1123         rec.mid = mid;
1124         rec.argc = argc;
1125         rec.argv = argv;
1126         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1127 }
1128
1129 RubyValue
1130 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1131 {
1132         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1133 }
1134
1135 #pragma mark ====== ParameterRef Class ======
1136
1137 static UnionPar *
1138 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1139 {
1140         UnionPar *up;
1141         ParameterRef *pref;
1142         Data_Get_Struct(self, ParameterRef, pref);
1143         if (typep != NULL)
1144                 *typep = pref->parType;
1145         if (pref->parType == kElementParType) {
1146                 up = (UnionPar *)&gElementParameters[pref->idx];
1147         } else {
1148                 up = ParameterRefGetPar(pref);
1149                 if (checkEditable) {
1150                         if (pref->idx < 0)
1151                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1152                         if (up->bond.src != 0 && up->bond.src != -1)
1153                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1154                 }
1155         }
1156         return up;
1157 }
1158
1159 static void
1160 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1161 {
1162         UnionPar *up;
1163         ParameterRef *pref;
1164         Data_Get_Struct(self, ParameterRef, pref);
1165         if (pref->mol == NULL)
1166                 return;
1167         up = ParameterRefGetPar(pref);
1168         if (key != s_SourceSym)
1169                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1170         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1171                 /*  Register undo  */
1172                 MolAction *act;
1173                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1174                 MolActionCallback_registerUndo(pref->mol, act);
1175                 MoleculeCallback_notifyModification(pref->mol, 0);
1176                 pref->mol->needsMDRebuild = 1;
1177         }
1178 }
1179
1180 VALUE
1181 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1182 {
1183         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1184         if (pref != NULL)
1185                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1186         else
1187                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1188 }
1189
1190 static int
1191 s_AtomTypeIndexFromValue(VALUE val)
1192 {
1193         if (rb_obj_is_kind_of(val, rb_cNumeric))
1194                 return NUM2INT(val);
1195         else
1196                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1197 }
1198
1199 static const char *s_ParameterTypeNames[] = {
1200         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1201 };
1202 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1203
1204 static int
1205 s_ParTypeFromValue(VALUE val)
1206 {
1207         int i, n;
1208         ID valid;
1209         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1210         if (s_ParameterTypeIDs[0] == 0) {
1211                 for (i = 0; i < n; i++)
1212                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1213         }
1214         valid = rb_to_id(val);
1215         for (i = 0; i < n; i++) {
1216                 if (valid == s_ParameterTypeIDs[i]) {
1217                         if (i == 7)
1218                                 return kElementParType;
1219                         else return kFirstParType + i;
1220                 }
1221         }
1222         return kInvalidParType;
1223 }
1224
1225 /*
1226  *  call-seq:
1227  *     index -> Integer
1228  *
1229  *  Get the index in the parameter list.
1230  */
1231 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1232         ParameterRef *pref;
1233         Data_Get_Struct(self, ParameterRef, pref);
1234         return INT2NUM(pref->idx);
1235 }
1236
1237 /*
1238  *  call-seq:
1239  *     par_type -> String
1240  *
1241  *  Get the parameter type, like "bond", "angle", etc.
1242  */
1243 static VALUE s_ParameterRef_GetParType(VALUE self) {
1244         Int tp;
1245         s_UnionParFromValue(self, &tp, 0);
1246         if (tp == kElementParType)
1247                 return rb_str_new2("element");
1248         tp -= kFirstParType;
1249         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1250                 return rb_str_new2(s_ParameterTypeNames[tp]);
1251         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1252 }
1253
1254 /*
1255  *  call-seq:
1256  *     atom_type -> String or Array of String
1257  *     atom_types -> String or Array of String
1258  *
1259  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1260  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1261  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1262  *  The atom type may be "X", which is a wildcard that matches any atom type.
1263  */
1264 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1265         UnionPar *up;
1266         Int tp, i, n;
1267         UInt types[4];
1268         VALUE vals[4];
1269         up = s_UnionParFromValue(self, &tp, 0);
1270         n = ParameterGetAtomTypes(tp, up, types);
1271         if (n == 0)
1272                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1273         for (i = 0; i < n; i++) {
1274                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1275                         vals[i] = INT2NUM(types[i]);
1276                 else
1277                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1278         }
1279         if (n == 1)
1280                 return vals[0];
1281         else
1282                 return rb_ary_new4(n, vals);
1283 }
1284
1285 /*
1286  *  call-seq:
1287  *     k -> Float
1288  *
1289  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1290  */
1291 static VALUE s_ParameterRef_GetK(VALUE self) {
1292         UnionPar *up;
1293         Int tp, i, n;
1294         VALUE vals[3];
1295         up = s_UnionParFromValue(self, &tp, 0);
1296         switch (tp) {
1297                 case kBondParType:
1298                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1299                 case kAngleParType:
1300                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1301                 case kDihedralParType:
1302                 case kImproperParType:
1303                         if (up->torsion.mult == 1)
1304                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1305                         n = up->torsion.mult;
1306                         if (n > 3)
1307                                 n = 3;
1308                         for (i = 0; i < n; i++)
1309                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1310                         return rb_ary_new4(n, vals);
1311                 default:
1312                         rb_raise(rb_eMolbyError, "invalid member k");
1313         }
1314 }
1315
1316 /*
1317  *  call-seq:
1318  *     r0 -> Float
1319  *
1320  *  Get the equilibrium bond length. Only available for bond parameters.
1321  */
1322 static VALUE s_ParameterRef_GetR0(VALUE self) {
1323         UnionPar *up;
1324         Int tp;
1325         up = s_UnionParFromValue(self, &tp, 0);
1326         if (tp == kBondParType)
1327                 return rb_float_new(up->bond.r0);
1328         else rb_raise(rb_eMolbyError, "invalid member r0");
1329 }
1330
1331 /*
1332  *  call-seq:
1333  *     a0 -> Float
1334  *
1335  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1336  */
1337 static VALUE s_ParameterRef_GetA0(VALUE self) {
1338         UnionPar *up;
1339         Int tp;
1340         up = s_UnionParFromValue(self, &tp, 0);
1341         if (tp == kAngleParType)
1342                 return rb_float_new(up->angle.a0 * kRad2Deg);
1343         else rb_raise(rb_eMolbyError, "invalid member a0");
1344 }
1345
1346 /*
1347  *  call-seq:
1348  *     mult -> Float
1349  *
1350  *  Get the multiplicity. Only available for dihedral and improper parameters.
1351  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1352  */
1353 static VALUE s_ParameterRef_GetMult(VALUE self) {
1354         UnionPar *up;
1355         Int tp;
1356         up = s_UnionParFromValue(self, &tp, 0);
1357         if (tp == kDihedralParType || tp == kImproperParType)
1358                 return rb_float_new(up->torsion.mult);
1359         else rb_raise(rb_eMolbyError, "invalid member mult");
1360 }
1361
1362 /*
1363  *  call-seq:
1364  *     period -> Integer or Array of Integers
1365  *
1366  *  Get the periodicity. Only available for dihedral and improper parameters.
1367  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1368  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1369  */
1370 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1371         UnionPar *up;
1372         Int tp, i, n;
1373         VALUE vals[3];
1374         up = s_UnionParFromValue(self, &tp, 0);
1375         if (tp == kDihedralParType || tp == kImproperParType) {
1376                 if (up->torsion.mult == 1)
1377                         return INT2NUM(up->torsion.period[0]);
1378                 n = up->torsion.mult;
1379                 if (n > 3)
1380                         n = 3;
1381                 for (i = 0; i < n; i++)
1382                         vals[i] = INT2NUM(up->torsion.period[i]);
1383                 return rb_ary_new4(n, vals);
1384         } else rb_raise(rb_eMolbyError, "invalid member period");
1385 }
1386
1387 /*
1388  *  call-seq:
1389  *     phi0 -> Float or Array of Floats
1390  *
1391  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1392  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1393  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1394  */
1395 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1396         UnionPar *up;
1397         Int tp, i, n;
1398         VALUE vals[3];
1399         up = s_UnionParFromValue(self, &tp, 0);
1400         if (tp == kDihedralParType || tp == kImproperParType) {
1401                 if (up->torsion.mult == 1)
1402                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1403                 n = up->torsion.mult;
1404                 if (n > 3)
1405                         n = 3;
1406                 for (i = 0; i < n; i++)
1407                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1408                 return rb_ary_new4(n, vals);
1409         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1410 }
1411
1412 /*
1413  *  call-seq:
1414  *     A -> Float
1415  *
1416  *  Get the "A" value for the van der Waals parameter.
1417  */
1418 /*
1419  static VALUE s_ParameterRef_GetA(VALUE self) {
1420         UnionPar *up;
1421         Int tp;
1422         up = s_UnionParFromValue(self, &tp, 0);
1423         if (tp == kVdwParType)
1424                 return rb_float_new(up->vdw.A);
1425         else if (tp == kVdwPairParType)
1426                 return rb_float_new(up->vdwp.A);
1427         else rb_raise(rb_eMolbyError, "invalid member A");
1428 }
1429 */
1430
1431 /*
1432  *  call-seq:
1433  *     B -> Float
1434  *
1435  *  Get the "B" value for the van der Waals parameter.
1436  */
1437 /*
1438 static VALUE s_ParameterRef_GetB(VALUE self) {
1439         UnionPar *up;
1440         Int tp;
1441         up = s_UnionParFromValue(self, &tp, 0);
1442         if (tp == kVdwParType)
1443                 return rb_float_new(up->vdw.B);
1444         else if (tp == kVdwPairParType)
1445                 return rb_float_new(up->vdwp.B);
1446         else rb_raise(rb_eMolbyError, "invalid member B");
1447 }
1448 */
1449
1450 /*
1451  *  call-seq:
1452  *     r_eq -> Float
1453  *
1454  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1455  */
1456 static VALUE s_ParameterRef_GetReq(VALUE self) {
1457         UnionPar *up;
1458         Int tp;
1459 /*      Double a, b, r; */
1460         Double r;
1461         up = s_UnionParFromValue(self, &tp, 0);
1462         if (tp == kVdwParType) {
1463         /*      a = up->vdw.A;
1464                 b = up->vdw.B;  */
1465                 r = up->vdw.r_eq;
1466         } else if (tp == kVdwPairParType) {
1467         /*      a = up->vdwp.A;
1468                 b = up->vdwp.B;  */
1469                 r = up->vdwp.r_eq;
1470         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1471 /*      if (a == 0.0 || b == 0.0) */
1472         return rb_float_new(r);
1473 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1474 }
1475
1476 /*
1477  *  call-seq:
1478  *     eps -> Float
1479  *
1480  *  Get the minimum energy for the van der Waals parameter.
1481  */
1482 static VALUE s_ParameterRef_GetEps(VALUE self) {
1483         UnionPar *up;
1484         Int tp;
1485 /*      Double a, b; */
1486         Double eps;
1487         up = s_UnionParFromValue(self, &tp, 0);
1488         if (tp == kVdwParType) {
1489         /*      a = up->vdw.A;
1490                 b = up->vdw.B;  */
1491                 eps = up->vdw.eps;
1492         } else if (tp == kVdwPairParType) {
1493         /*      a = up->vdwp.A;
1494                 b = up->vdwp.B; */
1495                 eps = up->vdwp.eps;
1496         } else rb_raise(rb_eMolbyError, "invalid member eps");
1497 /*      if (a == 0.0 || b == 0.0)  */
1498                 return rb_float_new(eps * INTERNAL2KCAL);
1499 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1500 }
1501
1502 /*
1503  *  call-seq:
1504  *     A14 -> Float
1505  *
1506  *  Get the "A" value for the 1-4 van der Waals parameter.
1507  */
1508 /*
1509 static VALUE s_ParameterRef_GetA14(VALUE self) {
1510         UnionPar *up;
1511         Int tp;
1512         up = s_UnionParFromValue(self, &tp, 0);
1513         if (tp == kVdwParType)
1514                 return rb_float_new(up->vdw.A14);
1515         else if (tp == kVdwPairParType)
1516                 return rb_float_new(up->vdwp.A14);
1517         else rb_raise(rb_eMolbyError, "invalid member A14");
1518 }
1519 */
1520
1521 /*
1522  *  call-seq:
1523  *     B14 -> Float
1524  *
1525  *  Get the "B" value for the 1-4 van der Waals parameter.
1526  */
1527 /*
1528 static VALUE s_ParameterRef_GetB14(VALUE self) {
1529         UnionPar *up;
1530         Int tp;
1531         up = s_UnionParFromValue(self, &tp, 0);
1532         if (tp == kVdwParType)
1533                 return rb_float_new(up->vdw.B14);
1534         else if (tp == kVdwPairParType)
1535                 return rb_float_new(up->vdwp.B14);
1536         else rb_raise(rb_eMolbyError, "invalid member B14");
1537 }
1538 */
1539
1540 /*
1541  *  call-seq:
1542  *     r_eq14 -> Float
1543  *
1544  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1545  */
1546 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1547         UnionPar *up;
1548         Int tp;
1549 /*      Double a, b, r; */
1550         Double r;
1551         up = s_UnionParFromValue(self, &tp, 0);
1552         if (tp == kVdwParType) {
1553         /*      a = up->vdw.A14;
1554                 b = up->vdw.B14; */
1555                 r = up->vdw.r_eq14;
1556         } else if (tp == kVdwPairParType) {
1557         /*      a = up->vdwp.A14;
1558                 b = up->vdwp.B14;  */
1559                 r = up->vdwp.r_eq14;
1560         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1561 /*      if (a == 0.0 || b == 0.0)  */
1562         return rb_float_new(r);
1563 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1564 }
1565
1566 /*
1567  *  call-seq:
1568  *     eps14 -> Float
1569  *
1570  *  Get the minimum energy for the 1-4 van der Waals parameter.
1571  */
1572 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1573         UnionPar *up;
1574         Int tp;
1575 /*      Double a, b;  */
1576         Double eps;
1577         up = s_UnionParFromValue(self, &tp, 0);
1578         if (tp == kVdwParType) {
1579         /*      a = up->vdw.A14;
1580                 b = up->vdw.B14;  */
1581                 eps = up->vdw.eps14;
1582         } else if (tp == kVdwPairParType) {
1583         /*      a = up->vdwp.A14;
1584                 b = up->vdwp.B14; */
1585                 eps = up->vdwp.eps14;
1586         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1587 /*      if (a == 0.0 || b == 0.0) */
1588         return rb_float_new(eps * INTERNAL2KCAL);
1589 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1590 }
1591
1592 /*
1593  *  call-seq:
1594  *     cutoff -> Float
1595  *
1596  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1597  */
1598 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1599         UnionPar *up;
1600         Int tp;
1601         up = s_UnionParFromValue(self, &tp, 0);
1602         if (tp == kVdwCutoffParType)
1603                 return rb_float_new(up->vdwcutoff.cutoff);
1604         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1605 }
1606
1607 /*
1608  *  call-seq:
1609  *     radius -> Float
1610  *
1611  *  Get the atomic (covalent) radius for the element parameter.
1612  */
1613 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1614         UnionPar *up;
1615         Int tp;
1616         up = s_UnionParFromValue(self, &tp, 0);
1617         if (tp == kElementParType)
1618                 return rb_float_new(up->atom.radius);
1619         else rb_raise(rb_eMolbyError, "invalid member radius");
1620 }
1621
1622 /*
1623  *  call-seq:
1624  *     vdw_radius -> Float
1625  *
1626  *  Get the van der Waals radius for the element parameter. (0 if not given)
1627  */
1628 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1629         UnionPar *up;
1630         Int tp;
1631         up = s_UnionParFromValue(self, &tp, 0);
1632         if (tp == kElementParType)
1633                 return rb_float_new(up->atom.vdw_radius);
1634         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1635 }
1636
1637 /*
1638  *  call-seq:
1639  *     color -> [Float, Float, Float]
1640  *
1641  *  Get the rgb color for the element parameter.
1642  */
1643 static VALUE s_ParameterRef_GetColor(VALUE self) {
1644         UnionPar *up;
1645         Int tp;
1646         up = s_UnionParFromValue(self, &tp, 0);
1647         if (tp == kElementParType)
1648                 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));
1649         else rb_raise(rb_eMolbyError, "invalid member color");
1650 }
1651
1652 /*
1653  *  call-seq:
1654  *     atomic_number -> Integer
1655  *
1656  *  Get the atomic number for the vdw or element parameter.
1657  */
1658 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1659         UnionPar *up;
1660         Int tp;
1661         up = s_UnionParFromValue(self, &tp, 0);
1662         if (tp == kElementParType)
1663                 return INT2NUM(up->atom.number);
1664         else if (tp == kVdwParType)
1665                 return INT2NUM(up->vdw.atomicNumber);
1666         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1667 }
1668
1669 /*
1670  *  call-seq:
1671  *     name -> String
1672  *
1673  *  Get the name for the element parameter.
1674  */
1675 static VALUE s_ParameterRef_GetName(VALUE self) {
1676         UnionPar *up;
1677         Int tp;
1678         up = s_UnionParFromValue(self, &tp, 0);
1679         if (tp == kElementParType) {
1680                 char name[5];
1681                 strncpy(name, up->atom.name, 4);
1682                 name[4] = 0;
1683                 return rb_str_new2(name);
1684         } else rb_raise(rb_eMolbyError, "invalid member name");
1685 }
1686
1687 /*
1688  *  call-seq:
1689  *     weight -> Float
1690  *
1691  *  Get the atomic weight for the element parameter.
1692  */
1693 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1694         UnionPar *up;
1695         Int tp;
1696         up = s_UnionParFromValue(self, &tp, 0);
1697         if (tp == kElementParType)
1698                 return rb_float_new(up->atom.weight);
1699         else if (tp == kVdwParType)
1700                 return rb_float_new(up->vdw.weight);
1701         else rb_raise(rb_eMolbyError, "invalid member weight");
1702 }
1703
1704 /*
1705  *  call-seq:
1706  *     fullname -> String
1707  *
1708  *  Get the full name for the element parameter.
1709  */
1710 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1711         UnionPar *up;
1712         Int tp;
1713         up = s_UnionParFromValue(self, &tp, 0);
1714         if (tp == kElementParType) {
1715                 char fullname[16];
1716                 strncpy(fullname, up->atom.fullname, 15);
1717                 fullname[15] = 0;
1718                 return rb_str_new2(fullname);
1719         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1720 }
1721
1722 /*
1723  *  call-seq:
1724  *     comment -> String
1725  *
1726  *  Get the comment for the parameter.
1727  */
1728 static VALUE s_ParameterRef_GetComment(VALUE self) {
1729         UnionPar *up;
1730         Int tp, com;
1731         up = s_UnionParFromValue(self, &tp, 0);
1732         com = up->bond.com;
1733         if (com == 0)
1734                 return Qnil;
1735         else return rb_str_new2(ParameterGetComment(com));
1736 }
1737
1738 /*
1739  *  call-seq:
1740  *     source -> String
1741  *
1742  *  Get the source string for the parameter. Returns false for undefined parameter,
1743  *  and nil for "local" parameter that is specific for the molecule.
1744  */
1745 static VALUE s_ParameterRef_GetSource(VALUE self) {
1746         UnionPar *up;
1747         Int tp, src;
1748         up = s_UnionParFromValue(self, &tp, 0);
1749         src = up->bond.src;
1750         if (src < 0)
1751                 return Qfalse;  /* undefined */
1752         else if (src == 0)
1753                 return Qnil;  /*  local  */
1754         else return rb_str_new2(ParameterGetComment(src));
1755 }
1756
1757 static void
1758 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1759 {
1760         VALUE *valp;
1761         int i;
1762         if (n == 1)
1763                 valp = &val;
1764         else {
1765                 if (rb_obj_is_kind_of(val, rb_cString)) {
1766                         char *s = StringValuePtr(val);
1767                         char *p;
1768                         for (i = 0; i < n; i++) {
1769                                 char buf[40];
1770                                 int len;
1771                                 /*  Skip leading separaters  */
1772                                 while (*s == '-' || *s == ' ' || *s == '\t')
1773                                         s++;
1774                                 for (p = s; *p != 0; p++) {
1775                                         if (*p == '-' || *p == ' ' || *p == '\t')
1776                                                 break;
1777                                 }
1778                                 len = p - s;
1779                                 if (len >= sizeof(buf))
1780                                         len = sizeof(buf) - 1;
1781                                 strncpy(buf, s, len);
1782                                 buf[len] = 0;
1783                                 /*  Skip trailing blanks  */
1784                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1785                                         buf[len] = 0;
1786                                 if (buf[0] == 0)
1787                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1788                                 if (buf[0] >= '0' && buf[0] <= '9')
1789                                         types[i] = atoi(buf);
1790                                 else
1791                                         types[i] = AtomTypeEncodeToUInt(buf);
1792                                 if (p == NULL || *p == 0) {
1793                                         i++;
1794                                         break;
1795                                 } else s = p + 1;
1796                         }
1797                         if (i < n)
1798                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1799                         return;
1800                 }
1801                 val = rb_ary_to_ary(val);
1802                 if (RARRAY_LEN(val) != n)
1803                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1804                 valp = RARRAY_PTR(val);
1805         }
1806         for (i = 0; i < n; i++) {
1807                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1808                         types[i] = NUM2INT(rb_Integer(valp[i]));
1809                 else {
1810                         VALUE sval = valp[i];
1811                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1812                 }
1813         }
1814 }
1815
1816 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1817         UnionPar *up;
1818         VALUE oldval;
1819         Int oldsrc, tp;
1820         UInt types[4];
1821         up = s_UnionParFromValue(self, &tp, 1);
1822         oldval = s_ParameterRef_GetAtomTypes(self);
1823         oldsrc = up->bond.src;
1824         switch (tp) {
1825                 case kBondParType:
1826                         s_ScanAtomTypes(val, 2, types);
1827                         up->bond.type1 = types[0];
1828                         up->bond.type2 = types[1];
1829                         break;
1830                 case kAngleParType:
1831                         s_ScanAtomTypes(val, 3, types);
1832                         up->angle.type1 = types[0];
1833                         up->angle.type2 = types[1];
1834                         up->angle.type3 = types[2];
1835                         break;
1836                 case kDihedralParType:
1837                 case kImproperParType:
1838                         s_ScanAtomTypes(val, 4, types);
1839                         up->torsion.type1 = types[0];
1840                         up->torsion.type2 = types[1];
1841                         up->torsion.type3 = types[2];
1842                         up->torsion.type4 = types[3];
1843                         break;
1844                 case kVdwParType:
1845                         s_ScanAtomTypes(val, 1, types);
1846                         up->vdw.type1 = types[0];
1847                         break;
1848                 case kVdwPairParType:
1849                         s_ScanAtomTypes(val, 2, types);
1850                         up->vdwp.type1 = types[0];
1851                         up->vdwp.type2 = types[1];
1852                         break;
1853                 case kVdwCutoffParType:
1854                         s_ScanAtomTypes(val, 2, types);
1855                         up->vdwcutoff.type1 = types[0];
1856                         up->vdwcutoff.type2 = types[1];
1857                         break;
1858                 default:
1859                         return Qnil;
1860         }
1861         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1862         return val;
1863 }
1864
1865 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1866         UnionPar *up;
1867         Int tp, i, n, oldsrc;
1868         VALUE *valp, oldval;
1869         up = s_UnionParFromValue(self, &tp, 1);
1870         oldval = s_ParameterRef_GetK(self);
1871         oldsrc = up->bond.src;
1872         switch (tp) {
1873                 case kBondParType:
1874                         val = rb_Float(val);
1875                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1876                         break;
1877                 case kAngleParType:
1878                         val = rb_Float(val);
1879                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1880                         break;
1881                 case kDihedralParType:
1882                 case kImproperParType:
1883                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1884                                 up->torsion.mult = 1;
1885                                 val = rb_Float(val);
1886                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1887                                 break;
1888                         }
1889                         n = up->torsion.mult;
1890                         if (n > 3)
1891                                 n = 3;
1892                         val = rb_ary_to_ary(val);
1893                         if (RARRAY_LEN(val) != n)
1894                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1895                         valp = RARRAY_PTR(val);
1896                         for (i = 0; i < n; i++) {
1897                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1898                         }
1899                         break;
1900                 default:
1901                         rb_raise(rb_eMolbyError, "invalid member k");
1902         }
1903         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1904         return val;
1905 }
1906
1907 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1908         UnionPar *up;
1909         Int tp, oldsrc;
1910         VALUE oldval;
1911         up = s_UnionParFromValue(self, &tp, 1);
1912         oldval = s_ParameterRef_GetR0(self);
1913         oldsrc = up->bond.src;
1914         if (tp == kBondParType) {
1915                 val = rb_Float(val);
1916                 up->bond.r0 = NUM2DBL(val);
1917         } else rb_raise(rb_eMolbyError, "invalid member r0");
1918         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1919         return val;
1920 }
1921
1922 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1923         UnionPar *up;
1924         Int tp, oldsrc;
1925         VALUE oldval;
1926         up = s_UnionParFromValue(self, &tp, 1);
1927         oldval = s_ParameterRef_GetA0(self);
1928         oldsrc = up->bond.src;
1929         if (tp == kAngleParType) {
1930                 val = rb_Float(val);
1931                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1932         } else rb_raise(rb_eMolbyError, "invalid member a0");
1933         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1934         return val;
1935 }
1936
1937 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1938         UnionPar *up;
1939         Int tp, oldsrc;
1940         VALUE oldval;
1941         up = s_UnionParFromValue(self, &tp, 1);
1942         oldval = s_ParameterRef_GetMult(self);
1943         oldsrc = up->bond.src;
1944         if (tp == kDihedralParType || tp == kImproperParType) {
1945                 int i;
1946                 val = rb_Integer(val);
1947                 i = NUM2INT(val);
1948                 if (i < 0 || i > 3)
1949                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1950                 up->torsion.mult = i;
1951         } else rb_raise(rb_eMolbyError, "invalid member mult");
1952         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1953         return val;
1954 }
1955
1956 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1957         UnionPar *up;
1958         Int tp, i, n, oldsrc;
1959         VALUE *valp, oldval;
1960         up = s_UnionParFromValue(self, &tp, 1);
1961         oldval = s_ParameterRef_GetPeriod(self);
1962         oldsrc = up->bond.src;
1963         if (tp == kDihedralParType || tp == kImproperParType) {
1964                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1965                         up->torsion.mult = 1;
1966                         val = rb_Integer(val);
1967                         up->torsion.period[0] = NUM2INT(val);
1968                 } else {
1969                         n = up->torsion.mult;
1970                         if (n > 3)
1971                                 n = 3;
1972                         val = rb_ary_to_ary(val);
1973                         if (RARRAY_LEN(val) != n)
1974                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1975                         valp = RARRAY_PTR(val);
1976                         for (i = 0; i < n; i++) {
1977                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1978                         }
1979                 }
1980         } else rb_raise(rb_eMolbyError, "invalid member period");
1981         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1982         return val;
1983 }
1984
1985 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1986         UnionPar *up;
1987         Int tp, i, n, oldsrc;
1988         VALUE *valp, oldval;
1989         up = s_UnionParFromValue(self, &tp, 1);
1990         oldval = s_ParameterRef_GetPhi0(self);
1991         oldsrc = up->bond.src;
1992         if (tp == kDihedralParType || tp == kImproperParType) {
1993                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1994                         up->torsion.mult = 1;
1995                         val = rb_Float(val);
1996                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1997                 } else {
1998                         n = up->torsion.mult;
1999                         if (n > 3)
2000                                 n = 3;
2001                         val = rb_ary_to_ary(val);
2002                         if (RARRAY_LEN(val) != n)
2003                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2004                         valp = RARRAY_PTR(val);
2005                         for (i = 0; i < n; i++)
2006                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2007                 }
2008         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2009         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2010         return val;
2011 }
2012
2013 /*
2014 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2015         UnionPar *up;
2016         Int tp, oldsrc;
2017         double d;
2018         VALUE oldval;
2019         up = s_UnionParFromValue(self, &tp, 1);
2020         oldval = s_ParameterRef_GetA(self);
2021         oldsrc = up->bond.src;
2022         val = rb_Float(val);
2023         d = NUM2DBL(val);
2024         if (tp == kVdwParType)
2025                 up->vdw.A = d;
2026         else if (tp == kVdwPairParType)
2027                 up->vdwp.A = d;
2028         else rb_raise(rb_eMolbyError, "invalid member A");
2029         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2030         return val;
2031 }
2032
2033 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2034         UnionPar *up;
2035         Int tp, oldsrc;
2036         double d;
2037         VALUE oldval;
2038         up = s_UnionParFromValue(self, &tp, 1);
2039         oldval = s_ParameterRef_GetB(self);
2040         oldsrc = up->bond.src;
2041         val = rb_Float(val);
2042         d = NUM2DBL(val);
2043         if (tp == kVdwParType)
2044                 up->vdw.B = d;
2045         else if (tp == kVdwPairParType)
2046                 up->vdwp.B = d;
2047         else rb_raise(rb_eMolbyError, "invalid member B");
2048         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2049         return val;
2050 }
2051 */
2052
2053 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2054         UnionPar *up;
2055         Int tp, oldsrc;
2056         Double r;
2057         VALUE oldval;
2058         up = s_UnionParFromValue(self, &tp, 1);
2059         oldval = s_ParameterRef_GetReq(self);
2060         oldsrc = up->bond.src;
2061         val = rb_Float(val);
2062         r = NUM2DBL(val);
2063         if (tp == kVdwParType) {
2064                 up->vdw.r_eq = r;
2065                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2066                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2067         } else if (tp == kVdwPairParType) {
2068                 up->vdwp.r_eq = r;
2069                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2070                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2071         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2072         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2073         return val;
2074 }
2075
2076 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2077         UnionPar *up;
2078         Int tp, oldsrc;
2079         Double e;
2080         VALUE oldval;
2081         up = s_UnionParFromValue(self, &tp, 1);
2082         oldval = s_ParameterRef_GetEps(self);
2083         oldsrc = up->bond.src;
2084         val = rb_Float(val);
2085         e = NUM2DBL(val) * KCAL2INTERNAL;
2086         if (tp == kVdwParType) {
2087                 up->vdw.eps = e;
2088                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2089                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2090         } else if (tp == kVdwPairParType) {
2091                 up->vdwp.eps = e;
2092                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2093                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2094         } else rb_raise(rb_eMolbyError, "invalid member eps");
2095         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2096         return val;
2097 }
2098
2099 /*
2100 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2101         UnionPar *up;
2102         Int tp, oldsrc;
2103         double d;
2104         VALUE oldval;
2105         up = s_UnionParFromValue(self, &tp, 1);
2106         oldval = s_ParameterRef_GetA14(self);
2107         oldsrc = up->bond.src;
2108         val = rb_Float(val);
2109         d = NUM2DBL(val);
2110         if (tp == kVdwParType)
2111                 up->vdw.A14 = d;
2112         else if (tp == kVdwPairParType)
2113                 up->vdwp.A14 = d;
2114         else rb_raise(rb_eMolbyError, "invalid member A14");
2115         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2116         return val;
2117 }
2118
2119 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2120         UnionPar *up;
2121         Int tp, oldsrc;
2122         double d;
2123         VALUE oldval;
2124         up = s_UnionParFromValue(self, &tp, 1);
2125         oldval = s_ParameterRef_GetB14(self);
2126         oldsrc = up->bond.src;
2127         val = rb_Float(val);
2128         d = NUM2DBL(val);
2129         if (tp == kVdwParType)
2130                 up->vdw.B14 = d;
2131         else if (tp == kVdwPairParType)
2132                 up->vdwp.B14 = d;
2133         else rb_raise(rb_eMolbyError, "invalid member B14");
2134         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2135         return val;
2136 }
2137 */
2138
2139 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2140         UnionPar *up;
2141         Int tp, oldsrc;
2142         Double r;
2143         VALUE oldval;
2144         up = s_UnionParFromValue(self, &tp, 1);
2145         oldval = s_ParameterRef_GetReq14(self);
2146         oldsrc = up->bond.src;
2147         val = rb_Float(val);
2148         r = NUM2DBL(val);
2149         if (tp == kVdwParType) {
2150                 up->vdw.r_eq14 = r;
2151                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2152                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2153         } else if (tp == kVdwPairParType) {
2154                 up->vdwp.r_eq14 = r;
2155                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2156                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2157         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2158         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2159         return val;
2160 }
2161
2162 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2163         UnionPar *up;
2164         Int tp, oldsrc;
2165         Double e;
2166         VALUE oldval;
2167         up = s_UnionParFromValue(self, &tp, 1);
2168         oldval = s_ParameterRef_GetEps14(self);
2169         oldsrc = up->bond.src;
2170         val = rb_Float(val);
2171         e = NUM2DBL(val) * KCAL2INTERNAL;
2172         if (tp == kVdwParType) {
2173                 up->vdw.eps14 = e;
2174                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2175                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2176         } else if (tp == kVdwPairParType) {
2177                 up->vdwp.eps14 = e;
2178                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2179                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2180         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2181         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2182         return val;
2183 }
2184
2185 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2186         UnionPar *up;
2187         Int tp, oldsrc;
2188         VALUE oldval;
2189         oldval = s_ParameterRef_GetCutoff(self);
2190         oldsrc = up->bond.src;
2191         up = s_UnionParFromValue(self, &tp, 1);
2192         val = rb_Float(val);
2193         if (tp == kVdwCutoffParType) {
2194                 up->vdwcutoff.cutoff = NUM2DBL(val);
2195         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2196         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2197         return val;
2198 }
2199
2200 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2201         UnionPar *up;
2202         Int tp, oldsrc;
2203         VALUE oldval;
2204         up = s_UnionParFromValue(self, &tp, 1);
2205         oldval = s_ParameterRef_GetRadius(self);
2206         oldsrc = up->bond.src;
2207         val = rb_Float(val);
2208         if (tp == kElementParType) {
2209                 up->atom.radius = NUM2DBL(val);
2210         } else rb_raise(rb_eMolbyError, "invalid member radius");
2211         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2212         return val;
2213 }
2214
2215 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2216         UnionPar *up;
2217         Int tp, oldsrc;
2218         VALUE oldval;
2219         up = s_UnionParFromValue(self, &tp, 1);
2220         oldval = s_ParameterRef_GetVdwRadius(self);
2221         oldsrc = up->bond.src;
2222         val = rb_Float(val);
2223         if (tp == kElementParType) {
2224                 up->atom.vdw_radius = NUM2DBL(val);
2225         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2226         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2227         return val;
2228 }
2229
2230 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2231         UnionPar *up;
2232         Int tp, oldsrc;
2233         VALUE *valp, oldval;
2234         up = s_UnionParFromValue(self, &tp, 1);
2235         oldval = s_ParameterRef_GetColor(self);
2236         oldsrc = up->bond.src;
2237         val = rb_ary_to_ary(val);
2238         if (RARRAY_LEN(val) != 3)
2239                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2240         valp = RARRAY_PTR(val);
2241         if (tp == kElementParType) {
2242                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2243                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2244                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2245         } else rb_raise(rb_eMolbyError, "invalid member color");
2246         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2247         return val;
2248 }
2249
2250 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2251         UnionPar *up;
2252         Int tp, oldsrc;
2253         VALUE oldval;
2254         up = s_UnionParFromValue(self, &tp, 1);
2255         oldval = s_ParameterRef_GetAtomicNumber(self);
2256         oldsrc = up->bond.src;
2257         val = rb_Integer(val);
2258         if (tp == kElementParType)
2259                 up->atom.number = NUM2INT(val);
2260         else if (tp == kVdwParType) {
2261                 up->vdw.atomicNumber = NUM2INT(val);
2262                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2263         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2264         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2265         return val;
2266 }
2267
2268 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2269         UnionPar *up;
2270         Int tp, oldsrc;
2271         VALUE oldval;
2272         up = s_UnionParFromValue(self, &tp, 1);
2273         oldval = s_ParameterRef_GetName(self);
2274         oldsrc = up->bond.src;
2275         if (tp == kElementParType) {
2276                 strncpy(up->atom.name, StringValuePtr(val), 4);
2277         } else rb_raise(rb_eMolbyError, "invalid member name");
2278         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2279         return val;
2280 }
2281
2282 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2283         UnionPar *up;
2284         Int tp, oldsrc;
2285         VALUE oldval;
2286         val = rb_Float(val);
2287         oldval = s_ParameterRef_GetWeight(self);
2288         up = s_UnionParFromValue(self, &tp, 1);
2289         oldsrc = up->bond.src;
2290         if (tp == kElementParType)
2291                 up->atom.weight = NUM2DBL(val);
2292         else if (tp == kVdwParType)
2293                 up->vdw.weight = NUM2DBL(val);
2294         else rb_raise(rb_eMolbyError, "invalid member weight");
2295         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2296         return val;
2297 }
2298
2299 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2300         UnionPar *up;
2301         Int tp, oldsrc;
2302         VALUE oldval;
2303         up = s_UnionParFromValue(self, &tp, 1);
2304         oldval = s_ParameterRef_GetFullName(self);
2305         oldsrc = up->bond.src;
2306         if (tp == kElementParType) {
2307                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2308                 up->atom.fullname[15] = 0;
2309         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2310         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2311         return val;
2312 }
2313
2314 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2315         UnionPar *up;
2316         Int tp, com, oldsrc;
2317         VALUE oldval;
2318         up = s_UnionParFromValue(self, &tp, 1);
2319         oldval = s_ParameterRef_GetComment(self);
2320         oldsrc = up->bond.src;
2321         if (val == Qnil)
2322                 up->bond.com = 0;
2323         else {
2324                 com = ParameterCommentIndex(StringValuePtr(val));
2325                 up->bond.com = com;
2326         }
2327         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2328         return val;     
2329 }
2330
2331 /*  Only false (undefined) and nil (local) can be set  */
2332 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2333         UnionPar *up;
2334         Int tp, oldsrc;
2335         VALUE oldval;
2336         up = s_UnionParFromValue(self, &tp, 1);
2337         if (val != Qfalse && val != Qnil)
2338                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2339         oldval = s_ParameterRef_GetSource(self);
2340         oldsrc = up->bond.src;
2341         if (oldsrc != 0 && oldsrc != -1)
2342                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2343         up->bond.src = (val == Qfalse ? -1 : 0);
2344         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2345         return val;     
2346 }
2347
2348 static struct s_ParameterAttrDef {
2349         char *name;
2350         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2351         ID id;                  /*  Will be set within InitMolby()  */
2352         VALUE (*getter)(VALUE);
2353         VALUE (*setter)(VALUE, VALUE);
2354 } s_ParameterAttrDefTable[] = {
2355         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2356         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2357         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2358         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2359         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2360         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2361         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2362         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2363         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2364         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2365 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2366         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2367         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2368         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2369 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2370         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2371         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2372         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2373         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2374         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2375         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2376         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2377         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2378         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2379         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2380         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2381         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2382         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2383         {NULL} /* Sentinel */
2384 };
2385
2386 static VALUE
2387 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2388 {
2389         int i;
2390         ID kid;
2391         if (TYPE(key) != T_SYMBOL) {
2392                 kid = rb_intern(StringValuePtr(key));
2393                 key = ID2SYM(kid);
2394         } else kid = SYM2ID(key);
2395         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2396                 if (s_ParameterAttrDefTable[i].id == kid) {
2397                         if (value == Qundef)
2398                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2399                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2400                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2401                         else
2402                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2403                 }
2404         }
2405         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2406         return Qnil; /* not reached */
2407 }
2408
2409 static VALUE
2410 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2411 {
2412         return s_ParameterRef_SetAttr(self, key, Qundef);
2413 }
2414
2415 /*
2416  *  call-seq:
2417  *     keys(idx)          -> array of valid parameter attributes
2418  *  
2419  *  Returns an array of valid parameter attributes (as Symbols).
2420  */
2421 static VALUE
2422 s_ParameterRef_Keys(VALUE self)
2423 {
2424         ParameterRef *pref;
2425         Data_Get_Struct(self, ParameterRef, pref);
2426         switch (pref->parType) {
2427                 case kBondParType:
2428                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2429                 case kAngleParType:
2430                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2431                 case kDihedralParType:
2432                 case kImproperParType:
2433                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2434                 case kVdwParType:
2435                         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);
2436                 case kVdwPairParType:
2437                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2438                 case kVdwCutoffParType:
2439                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2440                 case kElementParType:
2441                         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);
2442                 default:
2443                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2444         }
2445         return Qnil;  /*  Not reached  */
2446 }
2447
2448 /*
2449  *  call-seq:
2450  *     to_hash(idx)          -> Hash
2451  *  
2452  *  Returns a hash containing valid parameter names and values
2453  */
2454 static VALUE
2455 s_ParameterRef_ToHash(VALUE self)
2456 {
2457         VALUE keys = s_ParameterRef_Keys(self);
2458         VALUE retval;
2459         int i;
2460         if (keys == Qnil)
2461                 return Qnil;
2462         retval = rb_hash_new();
2463         for (i = 0; i < RARRAY_LEN(keys); i++) {
2464                 VALUE key = RARRAY_PTR(keys)[i];
2465                 VALUE val = s_ParameterRef_GetAttr(self, key);
2466                 rb_hash_aset(retval, key, val);
2467         }
2468         return retval;
2469 }
2470
2471 /*
2472  *  call-seq:
2473  *     parameter.to_s(idx)          -> String
2474  *  
2475  *  Returns a string representation of the given parameter
2476  */
2477 static VALUE
2478 s_ParameterRef_ToString(VALUE self)
2479 {
2480         Int tp, i, n;
2481         char buf[1024], types[4][8];
2482         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2483         switch (tp) {
2484                 case kBondParType:
2485                         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);
2486                         break;
2487                 case kAngleParType:
2488                         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);
2489                         break;
2490                 case kDihedralParType:
2491                 case kImproperParType:
2492                         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]));
2493                         n = strlen(buf);
2494                         for (i = 0; i < up->torsion.mult; i++) {
2495                                 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);
2496                                 n = strlen(buf);
2497                         }
2498                         break;
2499                 case kVdwParType:
2500                         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);
2501                         break;
2502                 case kVdwPairParType:
2503                         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);
2504                         break;
2505                 case kVdwCutoffParType:
2506                         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);
2507                         break;
2508                 case kElementParType:
2509                         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);
2510                         break;
2511         }
2512         return rb_str_new2(buf);
2513 }
2514
2515 /*
2516  *  call-seq:
2517  *     self == parameterRef -> boolean
2518  *  
2519  *  True if the parameters point to the same parameter record.
2520  */
2521 static VALUE
2522 s_ParameterRef_Equal(VALUE self, VALUE val)
2523 {
2524         Int tp1, tp2;
2525         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2526                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2527         } else return Qfalse;
2528 }
2529         
2530 #pragma mark ====== Parameter Class ======
2531
2532 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2533  *  is NULL, then the global parameters are looked for.  */
2534
2535 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2536 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2537 static void
2538 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2539 {
2540         Molecule *mol;
2541         Data_Get_Struct(val, Molecule, mol);
2542         if (mol == NULL)
2543                 rb_raise(rb_eMolbyError, "the molecule is empty");
2544         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2545                 /*  Do self.md_arena.prepare  */
2546                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2547                 if (val2 != Qnil)
2548                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2549         }
2550 }
2551
2552 static VALUE
2553 s_NewParameterValueFromValue(VALUE val)
2554 {
2555         Molecule *mol;
2556         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2557                 Data_Get_Struct(val, Molecule, mol);
2558                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2559                 MoleculeRetain(mol);
2560                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2561         } else {
2562                 mol = NULL;
2563                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2564         }
2565 }
2566
2567 static Molecule *
2568 s_MoleculeFromParameterValue(VALUE val)
2569 {
2570         Molecule *mol;
2571         Data_Get_Struct(val, Molecule, mol);
2572         return mol;
2573 }
2574
2575 static Parameter *
2576 s_ParameterFromParameterValue(VALUE val)
2577 {
2578         Molecule *mol;
2579         Data_Get_Struct(val, Molecule, mol);
2580         if (mol != NULL)
2581                 return mol->par;
2582         return gBuiltinParameters;
2583 }
2584
2585 /*  Forward declarations  */
2586 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2587 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2588
2589 static Molecule *
2590 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2591 {
2592         if (val == rb_cParameter) {
2593                 return NULL;  /*  Parameter class method: builtin parameters  */
2594         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2595                 return s_MoleculeFromParameterValue(val);
2596         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2597                 return s_MoleculeFromParEnumerableValue(val);
2598         } else return NULL;
2599 }
2600
2601 /*
2602  *  call-seq:
2603  *     builtin    -> Parameter
2604  *  
2605  *  Returns a parameter value that points to the global (builtin) parameters.
2606  *  Equivalent to Parameter::Builtin (constant).
2607  */
2608 static VALUE
2609 s_Parameter_Builtin(VALUE self)
2610 {
2611         static ID s_builtin_id = 0;
2612         if (s_builtin_id == 0)
2613                 s_builtin_id = rb_intern("Builtin");
2614         return rb_const_get(rb_cParameter, s_builtin_id);
2615 }
2616
2617 /*
2618  *  call-seq:
2619  *     bond(idx)          -> ParameterRef
2620  *  
2621  *  The index-th bond parameter record is returned.
2622  */
2623 static VALUE
2624 s_Parameter_Bond(VALUE self, VALUE ival)
2625 {
2626         Molecule *mol;
2627         int idx, n;
2628         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2629         idx = NUM2INT(rb_Integer(ival));
2630         if (mol == NULL)
2631                 n = gBuiltinParameters->nbondPars;
2632         else if (mol->par != NULL)
2633                 n = mol->par->nbondPars;
2634         else n = 0;
2635         if (idx < -n || idx >= n)
2636                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2637         if (idx < 0)
2638                 idx += n;
2639         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2640 }
2641
2642 /*
2643  *  call-seq:
2644  *     angle(idx)          -> ParameterRef
2645  *  
2646  *  The index-th angle parameter record is returned.
2647  */
2648 static VALUE
2649 s_Parameter_Angle(VALUE self, VALUE ival)
2650 {
2651         Molecule *mol;
2652         int idx, n;
2653         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2654         idx = NUM2INT(rb_Integer(ival));
2655         if (mol == NULL)
2656                 n = gBuiltinParameters->nanglePars;
2657         else if (mol->par != NULL)
2658                 n = mol->par->nanglePars;
2659         else n = 0;
2660         if (idx < -n || idx >= n)
2661                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2662         if (idx < 0)
2663                 idx += n;
2664         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2665 }
2666
2667 /*
2668  *  call-seq:
2669  *     dihedral(idx)          -> ParameterRef
2670  *  
2671  *  The index-th dihedral parameter record is returned.
2672  */
2673 static VALUE
2674 s_Parameter_Dihedral(VALUE self, VALUE ival)
2675 {
2676         Molecule *mol;
2677         int idx, n;
2678         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2679         idx = NUM2INT(rb_Integer(ival));
2680         if (mol == NULL)
2681                 n = gBuiltinParameters->ndihedralPars;
2682         else if (mol->par != NULL)
2683                 n = mol->par->ndihedralPars;
2684         else n = 0;
2685         if (idx < -n || idx >= n)
2686                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2687         if (idx < 0)
2688                 idx += n;
2689         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2690 }
2691
2692 /*
2693  *  call-seq:
2694  *     improper(idx)          -> ParameterRef
2695  *  
2696  *  The index-th improper parameter record is returned.
2697  */
2698 static VALUE
2699 s_Parameter_Improper(VALUE self, VALUE ival)
2700 {
2701         Molecule *mol;
2702         int idx, n;
2703         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2704         idx = NUM2INT(rb_Integer(ival));
2705         if (mol == NULL)
2706                 n = gBuiltinParameters->nimproperPars;
2707         else if (mol->par != NULL)
2708                 n = mol->par->nimproperPars;
2709         else n = 0;
2710         if (idx < -n || idx >= n)
2711                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2712         if (idx < 0)
2713                 idx += n;
2714         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2715 }
2716
2717 /*
2718  *  call-seq:
2719  *     vdw(idx)          -> ParameterRef
2720  *  
2721  *  The index-th vdw parameter record is returned.
2722  */
2723 static VALUE
2724 s_Parameter_Vdw(VALUE self, VALUE ival)
2725 {
2726         Molecule *mol;
2727         int idx, n;
2728         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2729         idx = NUM2INT(rb_Integer(ival));
2730         if (mol == NULL)
2731                 n = gBuiltinParameters->nvdwPars;
2732         else if (mol->par != NULL)
2733                 n = mol->par->nvdwPars;
2734         else n = 0;
2735         if (idx < -n || idx >= n)
2736                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2737         if (idx < 0)
2738                 idx += n;
2739         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2740 }
2741
2742 /*
2743  *  call-seq:
2744  *     vdw_pair(idx)          -> ParameterRef
2745  *  
2746  *  The index-th vdw pair parameter record is returned.
2747  */
2748 static VALUE
2749 s_Parameter_VdwPair(VALUE self, VALUE ival)
2750 {
2751         Molecule *mol;
2752         int idx, n;
2753         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2754         idx = NUM2INT(rb_Integer(ival));
2755         if (mol == NULL)
2756                 n = gBuiltinParameters->nvdwpPars;
2757         else if (mol->par != NULL)
2758                 n = mol->par->nvdwpPars;
2759         else n = 0;
2760         if (idx < -n || idx >= n)
2761                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2762         if (idx < 0)
2763                 idx += n;
2764         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2765 }
2766
2767 /*
2768  *  call-seq:
2769  *     vdw_cutoff(idx)          -> ParameterRef
2770  *  
2771  *  The index-th vdw cutoff parameter record is returned.
2772  */
2773 static VALUE
2774 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2775 {
2776         Molecule *mol;
2777         int idx, n;
2778         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2779         idx = NUM2INT(rb_Integer(ival));
2780         if (mol == NULL)
2781                 n = gBuiltinParameters->nvdwCutoffPars;
2782         else if (mol->par != NULL)
2783                 n = mol->par->nvdwCutoffPars;
2784         else n = 0;
2785         if (idx < -n || idx >= n)
2786                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2787         if (idx < 0)
2788                 idx += n;
2789         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2790 }
2791
2792 /*
2793  *  call-seq:
2794  *     element(idx)            -> ParameterRef
2795  *     element(t1)             -> ParameterRef
2796  *  
2797  *  In the first form, the index-th element parameter record is returned. In the second
2798  *  form, the element parameter for t1 is looked up (the last index first). t1
2799  *  is the element name string (up to 4 characters).
2800  *  Unlike other Parameter methods, this is used only for the global parameter.
2801  */
2802 static VALUE
2803 s_Parameter_Element(VALUE self, VALUE ival)
2804 {
2805         Int idx1;
2806         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2807                 int n = gCountElementParameters;
2808                 idx1 = NUM2INT(rb_Integer(ival));
2809                 if (idx1 < -n || idx1 >= n)
2810                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2811                 if (idx1 < 0)
2812                         idx1 += n;
2813                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2814         } else {
2815                 ElementPar *ep;
2816                 char name[6];
2817                 int i;
2818                 strncpy(name, StringValuePtr(ival), 4);
2819                 name[4] = 0;
2820                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2821                         if (strncmp(ep->name, name, 4) == 0)
2822                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2823                 }
2824                 return Qnil;
2825         }
2826 }
2827
2828 /*
2829  *  call-seq:
2830  *     nbonds          -> Integer
2831  *  
2832  *  Returns the number of bond parameters.
2833  */
2834 static VALUE
2835 s_Parameter_Nbonds(VALUE self)
2836 {
2837         Int n;
2838         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2839         if (mol == NULL)
2840                 n = gBuiltinParameters->nbondPars;
2841         else if (mol->par != NULL)
2842                 n = mol->par->nbondPars;
2843         else n = 0;
2844         return INT2NUM(n);
2845 }
2846
2847 /*
2848  *  call-seq:
2849  *     nangles          -> Integer
2850  *  
2851  *  Returns the number of angle parameters.
2852  */
2853 static VALUE
2854 s_Parameter_Nangles(VALUE self)
2855 {
2856         Int n;
2857         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2858         if (mol == NULL)
2859                 n = gBuiltinParameters->nanglePars;
2860         else if (mol->par != NULL)
2861                 n = mol->par->nanglePars;
2862         else n = 0;
2863         return INT2NUM(n);
2864 }
2865
2866 /*
2867  *  call-seq:
2868  *     ndihedrals          -> Integer
2869  *  
2870  *  Returns the number of dihedral parameters.
2871  */
2872 static VALUE
2873 s_Parameter_Ndihedrals(VALUE self)
2874 {
2875         Int n;
2876         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2877         if (mol == NULL)
2878                 n = gBuiltinParameters->ndihedralPars;
2879         else if (mol->par != NULL)
2880                 n = mol->par->ndihedralPars;
2881         else n = 0;
2882         return INT2NUM(n);
2883 }
2884
2885 /*
2886  *  call-seq:
2887  *     nimpropers          -> Integer
2888  *  
2889  *  Returns the number of improper parameters.
2890  */
2891 static VALUE
2892 s_Parameter_Nimpropers(VALUE self)
2893 {
2894         Int n;
2895         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2896         if (mol == NULL)
2897                 n = gBuiltinParameters->nimproperPars;
2898         else if (mol->par != NULL)
2899                 n = mol->par->nimproperPars;
2900         else n = 0;
2901         return INT2NUM(n);
2902 }
2903
2904 /*
2905  *  call-seq:
2906  *     nvdws          -> Integer
2907  *  
2908  *  Returns the number of vdw parameters.
2909  */
2910 static VALUE
2911 s_Parameter_Nvdws(VALUE self)
2912 {
2913         Int n;
2914         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2915         if (mol == NULL)
2916                 n = gBuiltinParameters->nvdwPars;
2917         else if (mol->par != NULL)
2918                 n = mol->par->nvdwPars;
2919         else n = 0;
2920         return INT2NUM(n);
2921 }
2922
2923 /*
2924  *  call-seq:
2925  *     nvdw_pairs          -> Integer
2926  *  
2927  *  Returns the number of vdw pair parameters.
2928  */
2929 static VALUE
2930 s_Parameter_NvdwPairs(VALUE self)
2931 {
2932         Int n;
2933         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2934         if (mol == NULL)
2935                 n = gBuiltinParameters->nvdwpPars;
2936         else if (mol->par != NULL)
2937                 n = mol->par->nvdwpPars;
2938         else n = 0;
2939         return INT2NUM(n);
2940 }
2941
2942 /*
2943  *  call-seq:
2944  *     nvdw_cutoffs          -> Integer
2945  *  
2946  *  Returns the number of vdw cutoff parameters.
2947  */
2948 static VALUE
2949 s_Parameter_NvdwCutoffs(VALUE self)
2950 {
2951         Int n;
2952         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2953         if (mol == NULL)
2954                 n = gBuiltinParameters->nvdwCutoffPars;
2955         else if (mol->par != NULL)
2956                 n = mol->par->nvdwCutoffPars;
2957         else n = 0;
2958         return INT2NUM(n);
2959 }
2960
2961 /*
2962  *  call-seq:
2963  *     nelements          -> Integer
2964  *  
2965  *  Returns the number of element parameters.
2966  */
2967 static VALUE
2968 s_Parameter_Nelements(VALUE self)
2969 {
2970         return INT2NUM(gCountElementParameters);
2971 }
2972
2973 /*
2974  *  call-seq:
2975  *     bonds          -> ParEnumerable
2976  *  
2977  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2978  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2979  *  useful when all accessible parameters should be examined by use of 'each' method.
2980  */
2981 static VALUE
2982 s_Parameter_Bonds(VALUE self)
2983 {
2984         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2985         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2986 }
2987
2988 /*
2989  *  call-seq:
2990  *     angles          -> ParEnumerable
2991  *  
2992  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2993  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2994  *  useful when all accessible parameters should be examined by use of 'each' method.
2995  */
2996 static VALUE
2997 s_Parameter_Angles(VALUE self)
2998 {
2999         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3000         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3001 }
3002
3003 /*
3004  *  call-seq:
3005  *     dihedrals          -> ParEnumerable
3006  *  
3007  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3008  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3009  *  useful when all accessible parameters should be examined by use of 'each' method.
3010  */
3011 static VALUE
3012 s_Parameter_Dihedrals(VALUE self)
3013 {
3014         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3015         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3016 }
3017
3018 /*
3019  *  call-seq:
3020  *     impropers          -> ParEnumerable
3021  *  
3022  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3023  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3024  *  useful when all accessible parameters should be examined by use of 'each' method.
3025  */
3026 static VALUE
3027 s_Parameter_Impropers(VALUE self)
3028 {
3029         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3030         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3031 }
3032
3033 /*
3034  *  call-seq:
3035  *     vdws          -> ParEnumerable
3036  *  
3037  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3038  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3039  *  useful when all accessible parameters should be examined by use of 'each' method.
3040  */
3041 static VALUE
3042 s_Parameter_Vdws(VALUE self)
3043 {
3044         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3045         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3046 }
3047
3048 /*
3049  *  call-seq:
3050  *     vdw_pairs          -> ParEnumerable
3051  *  
3052  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3053  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3054  *  useful when all accessible parameters should be examined by use of 'each' method.
3055  */
3056 static VALUE
3057 s_Parameter_VdwPairs(VALUE self)
3058 {
3059         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3060         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3061 }
3062
3063 /*
3064  *  call-seq:
3065  *     vdw_cutoffs          -> ParEnumerable
3066  *  
3067  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3068  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3069  *  useful when all accessible parameters should be examined by use of 'each' method.
3070  */
3071 static VALUE
3072 s_Parameter_VdwCutoffs(VALUE self)
3073 {
3074         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3075         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3076 }
3077
3078 /*
3079  *  call-seq:
3080  *     elements          -> ParEnumerable
3081  *  
3082  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3083  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3084  *  useful when all accessible parameters should be examined by use of 'each' method.
3085  */
3086 static VALUE
3087 s_Parameter_Elements(VALUE self)
3088 {
3089         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3090         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3091 }
3092
3093 static VALUE
3094 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3095 {
3096         VALUE atval, optval;
3097         UInt t[4];
3098         Int ii[4];
3099         int i, n, idx, flags, is_global;
3100
3101         rb_scan_args(argc, argv, "1*", &atval, &optval);
3102         
3103         /*  Get the atom types  */
3104         switch (parType) {
3105                 case kBondParType: n = 2; break;
3106                 case kAngleParType: n = 3; break;
3107                 case kDihedralParType: n = 4; break;
3108                 case kImproperParType: n = 4; break;
3109                 case kVdwParType: n = 1; break;
3110                 case kVdwPairParType: n = 2; break;
3111                 default: return Qnil;
3112         }
3113         s_ScanAtomTypes(atval, n, t);
3114         for (i = 0; i < n; i++) {
3115                 if (t[i] < kAtomTypeMinimum) {
3116                         /*  Explicit atom index  */
3117                         if (mol == NULL)
3118                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3119                         if (t[i] >= mol->natoms)
3120                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3121                         ii[i] = t[i];
3122                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3123                 } else ii[i] = -1;
3124         }
3125         
3126         /*  Analyze options  */
3127         flags = 0;
3128         n = RARRAY_LEN(optval);
3129         for (i = 0; i < n; i++) {
3130                 VALUE oval = RARRAY_PTR(optval)[i];
3131                 if (oval == ID2SYM(rb_intern("global")))
3132                         flags |= kParameterLookupGlobal;
3133                 else if (oval == ID2SYM(rb_intern("local")))
3134                         flags |= kParameterLookupLocal;
3135                 else if (oval == ID2SYM(rb_intern("missing")))
3136                         flags |= kParameterLookupMissing;
3137                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3138                         flags |= kParameterLookupNoWildcard;
3139                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3140                         flags |= kParameterLookupNoBaseAtomType;
3141                 else if (oval == ID2SYM(rb_intern("create")))
3142                         flags |= 256;
3143         }
3144         if (flags == 0)
3145                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3146         
3147         idx = -1;
3148         is_global = 0;
3149         switch (parType) {
3150                 case kBondParType: {
3151                         BondPar *bp;
3152                         if (mol != NULL) {
3153                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3154                                 if (bp != NULL) {
3155                                         idx = bp - mol->par->bondPars;
3156                                         break;
3157                                 }
3158                         }
3159                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3160                         if (bp != NULL) {
3161                                 idx = bp - gBuiltinParameters->bondPars;
3162                                 is_global = 1;
3163                         }
3164                         break;
3165                 }
3166                 case kAngleParType: {
3167                         AnglePar *ap;
3168                         if (mol != NULL) {
3169                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3170                                 if (ap != NULL) {
3171                                         idx = ap - mol->par->anglePars;
3172                                         break;
3173                                 }
3174                         }
3175                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3176                         if (ap != NULL) {
3177                                 idx = ap - gBuiltinParameters->anglePars;
3178                                 is_global = 1;
3179                         }
3180                         break;
3181                 }
3182                 case kDihedralParType: {
3183                         TorsionPar *tp;
3184                         if (mol != NULL) {
3185                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3186                                 if (tp != NULL) {
3187                                         idx = tp - mol->par->dihedralPars;
3188                                         break;
3189                                 }
3190                         }
3191                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3192                         if (tp != NULL) {
3193                                 idx = tp - gBuiltinParameters->dihedralPars;
3194                                 is_global = 1;
3195                         }
3196                         break;
3197                 }
3198                 case kImproperParType: {
3199                         TorsionPar *tp;
3200                         if (mol != NULL) {
3201                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3202                                 if (tp != NULL) {
3203                                         idx = tp - mol->par->improperPars;
3204                                         break;
3205                                 }
3206                         }
3207                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3208                         if (tp != NULL) {
3209                                 idx = tp - gBuiltinParameters->improperPars;
3210                                 is_global = 1;
3211                         }
3212                         break;
3213                 }       
3214                 case kVdwParType: {
3215                         VdwPar *vp;
3216                         if (mol != NULL) {
3217                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3218                                 if (vp != NULL) {
3219                                         idx = vp - mol->par->vdwPars;
3220                                         break;
3221                                 }
3222                         }
3223                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3224                         if (vp != NULL) {
3225                                 idx = vp - gBuiltinParameters->vdwPars;
3226                                 is_global = 1;
3227                         }
3228                         break;
3229                 }       
3230                 case kVdwPairParType: {
3231                         VdwPairPar *vp;
3232                         if (mol != NULL) {
3233                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3234                                 if (vp != NULL) {
3235                                         idx = vp - mol->par->vdwpPars;
3236                                         break;
3237                                 }
3238                         }
3239                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3240                         if (vp != NULL) {
3241                                 idx = vp - gBuiltinParameters->vdwpPars;
3242                                 is_global = 1;
3243                         }
3244                         break;
3245                 }
3246                 default:
3247                         return Qnil;
3248         }
3249         if (idx < 0) {
3250                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3251                         return Qnil;            
3252                 else {
3253                         /*  Insert a new parameter record  */
3254                         UnionPar *up;
3255                         Int count = ParameterGetCountForType(mol->par, parType);
3256                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3257                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3258                         IntGroupRelease(ig);
3259                         is_global = 0;
3260                         idx = count;
3261                         /*  Set atom types  */
3262                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3263                         if (up == NULL)
3264                                 return Qnil;
3265                         switch (parType) {
3266                                 case kBondParType:
3267                                         up->bond.type1 = t[0];
3268                                         up->bond.type2 = t[1];
3269                                         break;
3270                                 case kAngleParType:
3271                                         up->angle.type1 = t[0];
3272                                         up->angle.type2 = t[1];
3273                                         up->angle.type3 = t[2];
3274                                         break;
3275                                 case kDihedralParType:
3276                                 case kImproperParType:
3277                                         up->torsion.type1 = t[0];
3278                                         up->torsion.type2 = t[1];
3279                                         up->torsion.type3 = t[2];
3280                                         up->torsion.type4 = t[3];
3281                                         break;
3282                                 case kVdwParType:
3283                                         up->vdw.type1 = t[0];
3284                                         break;
3285                                 case kVdwPairParType:
3286                                         up->vdwp.type1 = t[0];
3287                                         up->vdwp.type2 = t[1];
3288                                         break;
3289                                 default:
3290                                         return Qnil;
3291                         }
3292                 }
3293         }
3294         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3295 }
3296
3297 /*
3298  *  call-seq:
3299  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3300  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3301  *
3302  *  Find the parameter record that matches the given atom types. The atom types are given
3303  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3304  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3305  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3306  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3307  */
3308 static VALUE
3309 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3310 {
3311         int parType;
3312         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3313         if (argc == 0)
3314                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3315         parType = s_ParTypeFromValue(argv[0]);
3316         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3317 }
3318
3319 /*
3320  *  call-seq:
3321  *     self == parameter -> boolean
3322  *  
3323  *  True if the parameters point to the same parameter table.
3324  */
3325 static VALUE
3326 s_Parameter_Equal(VALUE self, VALUE val)
3327 {
3328         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3329                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3330         } else return Qfalse;
3331 }
3332
3333 #pragma mark ====== ParEnumerable Class ======
3334
3335 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3336  and the parameter type. If the Molecule is NULL, then it refers to the
3337  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3338  the global parameters are always accessible. */
3339
3340 typedef struct ParEnumerable {
3341         Molecule *mol;
3342         Int parType;   /*  Same as parType in ParameterRef  */
3343 } ParEnumerable;
3344
3345 static ParEnumerable *
3346 s_ParEnumerableNew(Molecule *mol, Int parType)
3347 {
3348         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3349         if (pen != NULL) {
3350                 pen->mol = mol;
3351                 if (mol != NULL)
3352                         MoleculeRetain(mol);
3353                 pen->parType = parType;
3354         }
3355         return pen;
3356 }
3357
3358 static void
3359 s_ParEnumerableRelease(ParEnumerable *pen)
3360 {
3361         if (pen != NULL) {
3362                 if (pen->mol != NULL)
3363                         MoleculeRelease(pen->mol);
3364                 free(pen);
3365         }
3366 }
3367
3368 static Molecule *
3369 s_MoleculeFromParEnumerableValue(VALUE val)
3370 {
3371         ParEnumerable *pen;
3372     Data_Get_Struct(val, ParEnumerable, pen);
3373         return pen->mol;
3374 }
3375
3376 static VALUE
3377 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3378 {
3379         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3380         if (pen == NULL)
3381                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3382         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3383 }
3384
3385 /*
3386  *  call-seq:
3387  *     par_type -> String
3388  *
3389  *  Get the parameter type, like "bond", "angle", etc.
3390  */
3391 static VALUE
3392 s_ParEnumerable_ParType(VALUE self) {
3393         ParEnumerable *pen;
3394         Int tp;
3395     Data_Get_Struct(self, ParEnumerable, pen);
3396         tp = pen->parType;
3397         if (tp == kElementParType)
3398                 return rb_str_new2("element");
3399         tp -= kFirstParType;
3400         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3401                 return rb_str_new2(s_ParameterTypeNames[tp]);
3402         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3403 }
3404
3405 /*
3406  *  call-seq:
3407  *     self[idx]          -> ParameterRef
3408  *  
3409  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3410  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3411  *  parent Parameter object of self.
3412  *
3413  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3414  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3415  */
3416 static VALUE
3417 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3418 {
3419         ParEnumerable *pen;
3420     Data_Get_Struct(self, ParEnumerable, pen);
3421         switch (pen->parType) {
3422                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3423                 case kBondParType:      return s_Parameter_Bond(self, ival);
3424                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3425                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3426                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3427                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3428                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3429                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3430                 case kElementParType:   return s_Parameter_Element(self, ival);
3431                 default:
3432                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3433         }
3434         return Qnil;  /*  Not reached  */
3435 }
3436
3437 /*
3438  *  call-seq:
3439  *     length          -> Integer
3440  *  
3441  *  Returns the number of parameters included in this enumerable.
3442  */
3443 static VALUE
3444 s_ParEnumerable_Length(VALUE self)
3445 {
3446         ParEnumerable *pen;
3447     Data_Get_Struct(self, ParEnumerable, pen);
3448         switch (pen->parType) {
3449                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3450                 case kBondParType:      return s_Parameter_Nbonds(self);
3451                 case kAngleParType:     return s_Parameter_Nangles(self); 
3452                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3453                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3454                 case kVdwParType:       return s_Parameter_Nvdws(self);
3455                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3456                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3457                 case kElementParType:   return s_Parameter_Nelements(self);
3458                 default:
3459                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3460         }
3461         return Qnil;  /*  Not reached  */
3462 }
3463
3464 /*
3465  *  call-seq:
3466  *     each {|pref| ...}
3467  *  
3468  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3469  */
3470 VALUE
3471 s_ParEnumerable_Each(VALUE self)
3472 {
3473         VALUE aval;
3474         ParEnumerable *pen;
3475         ParameterRef *pref;
3476         int i, ofs, n;
3477     Data_Get_Struct(self, ParEnumerable, pen);
3478         if (pen->parType == kElementParType)
3479                 n = gCountElementParameters;
3480         else {
3481                 switch (pen->parType) {
3482                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3483                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3484                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3485                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3486                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3487                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3488                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3489                         default:
3490                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3491                 }
3492                 if (pen->mol == NULL)
3493                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3494                 else if (pen->mol->par != NULL)
3495                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3496                 else return self;
3497         }               
3498         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3499         Data_Get_Struct(aval, ParameterRef, pref);
3500         for (i = 0; i < n; i++) {
3501                 pref->idx = i;
3502                 rb_yield(aval);
3503         }
3504     return self;
3505 }
3506
3507 /*
3508  *  call-seq:
3509  *     reverse_each {|pref| ...}
3510  *  
3511  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3512  */
3513 VALUE
3514 s_ParEnumerable_ReverseEach(VALUE self)
3515 {
3516         VALUE aval;
3517         ParEnumerable *pen;
3518         ParameterRef *pref;
3519         int i, ofs, n;
3520     Data_Get_Struct(self, ParEnumerable, pen);
3521         if (pen->parType == kElementParType)
3522                 n = gCountElementParameters;
3523         else {
3524                 switch (pen->parType) {
3525                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3526                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3527                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3528                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3529                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3530                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3531                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3532                         default:
3533                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3534                 }
3535                 if (pen->mol == NULL)
3536                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3537                 else if (pen->mol->par != NULL)
3538                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3539                 else return self;
3540         }               
3541         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3542         Data_Get_Struct(aval, ParameterRef, pref);
3543         for (i = n - 1; i >= 0; i--) {
3544                 pref->idx = i;
3545                 rb_yield(aval);
3546         }
3547     return self;
3548 }
3549
3550 /*
3551  *  call-seq:
3552  *     insert(idx = nil, pref = nil)       -> ParameterRef
3553  *  
3554  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3555  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3556  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3557  *  parameter is left undefined.
3558  *  Throws an exception if ParEnumerable points to the global parameter.
3559  */
3560 static VALUE
3561 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3562 {
3563         VALUE ival, pval;
3564         ParEnumerable *pen;
3565         int i, n;
3566         IntGroup *ig;
3567         UnionPar u;
3568         MolAction *act;
3569     Data_Get_Struct(self, ParEnumerable, pen);
3570         if (pen->mol == NULL)
3571                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3572         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3573         rb_scan_args(argc, argv, "02", &ival, &pval);
3574         if (ival != Qnil) {
3575                 i = NUM2INT(rb_Integer(ival));
3576                 if (i < 0 || i > n)
3577                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3578                 n = i;
3579         }
3580         if (pval != Qnil) {
3581                 Int type;
3582                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3583                 if (up == NULL || type != pen->parType)
3584                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3585                 ParameterCopyOneWithType(&u, up, pen->parType);
3586                 u.bond.src = 0;
3587         } else {
3588                 memset(&u, 0, sizeof(u));
3589                 u.bond.src = 0;
3590         }
3591         ig = IntGroupNewWithPoints(n, 1, -1);
3592         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3593
3594         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3595         MolActionCallback_registerUndo(pen->mol, act);
3596         MolActionRelease(act);
3597         
3598         IntGroupRelease(ig);
3599         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3600 }
3601
3602 /*
3603  *  call-seq:
3604  *     delete(Integer)
3605  *     delete(IntGroup)
3606  *  
3607  *  Delete the parameter(s) specified by the argument.
3608  */
3609 static VALUE
3610 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3611 {
3612         ParEnumerable *pen;
3613         int i, n;
3614         IntGroup *ig;
3615     Data_Get_Struct(self, ParEnumerable, pen);
3616         if (pen->mol == NULL)
3617                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3618         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3619         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3620                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3621                 i = 1;
3622         } else {
3623                 ig = IntGroupFromValue(ival);
3624                 if ((i = IntGroupGetCount(ig)) == 0) {
3625                         IntGroupRelease(ig);
3626                         return Qnil;
3627                 }
3628         }
3629         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3630                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3631         n = i;
3632
3633         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3634         IntGroupRelease(ig);
3635         return ival;
3636 }
3637
3638 /*
3639  *  call-seq:
3640  *     lookup(atom_types, options, ...) -> ParameterRef
3641  *     lookup(atom_type_string, options, ...) -> ParameterRef
3642  *
3643  *  Find the parameter record that matches the given atom types. The arguments are
3644  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3645  *  specified.
3646  */
3647 static VALUE
3648 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3649 {
3650         ParEnumerable *pen;
3651     Data_Get_Struct(self, ParEnumerable, pen);
3652         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3653 }
3654
3655 /*
3656  *  call-seq:
3657  *     self == parEnumerable -> boolean
3658  *  
3659  *  True if the arguments point to the same parameter table and type.
3660  */
3661 static VALUE
3662 s_ParEnumerable_Equal(VALUE self, VALUE val)
3663 {
3664         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3665                 ParEnumerable *pen1, *pen2;
3666                 Data_Get_Struct(self, ParEnumerable, pen1);
3667                 Data_Get_Struct(val, ParEnumerable, pen2);
3668                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3669         } else return Qfalse;
3670 }
3671
3672 #pragma mark ====== AtomRef Class ======
3673
3674 /*  Forward declaration for register undo  */
3675 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3676
3677 /*  Ruby string "set_atom_attr"  */
3678 static VALUE s_SetAtomAttrString;
3679
3680 static int
3681 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3682 {
3683         AtomRef *aref;
3684         int idx;
3685         Data_Get_Struct(self, AtomRef, aref);
3686         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3687         if (idx < 0 || idx >= aref->mol->natoms)
3688                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3689         if (app != NULL)
3690                 *app = aref->mol->atoms + idx;
3691         if (mpp != NULL)
3692                 *mpp = aref->mol;
3693         return idx;
3694 }
3695
3696 static Atom *
3697 s_AtomFromValue(VALUE self)
3698 {
3699         Atom *ap;
3700         s_AtomIndexFromValue(self, &ap, NULL);
3701         return ap;
3702 }
3703
3704 static Atom *
3705 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3706 {
3707         Atom *ap;
3708         s_AtomIndexFromValue(self, &ap, mpp);
3709         return ap;
3710 }
3711
3712 static void
3713 s_NotifyModificationForAtomRef(VALUE self)
3714 {
3715         AtomRef *aref;
3716         Data_Get_Struct(self, AtomRef, aref);
3717         MoleculeIncrementModifyCount(aref->mol);
3718 }
3719
3720 static void
3721 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3722 {
3723         AtomRef *aref;
3724         Data_Get_Struct(self, AtomRef, aref);
3725         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3726                 /*  Register undo  */
3727                 MolAction *act;
3728                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3729                 MolActionCallback_registerUndo(aref->mol, act);
3730                 MoleculeCallback_notifyModification(aref->mol, 0);
3731                 /*  Request MD rebuilt if necessary  */
3732                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3733                         aref->mol->needsMDRebuild = 1;
3734         }
3735 }
3736
3737 VALUE
3738 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3739 {
3740         AtomRef *aref;
3741         aref = AtomRefNew(mol, idx);
3742         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3743 }
3744
3745 static VALUE
3746 s_AtomRef_GetMolecule(VALUE self)
3747 {
3748         Molecule *mpp;
3749         s_AtomIndexFromValue(self, NULL, &mpp);
3750         return ValueFromMolecule(mpp);
3751 }
3752
3753 static VALUE s_AtomRef_GetIndex(VALUE self) {
3754         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3755 }
3756
3757 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3758         return INT2NUM(s_AtomFromValue(self)->segSeq);
3759 }
3760
3761 static VALUE s_AtomRef_GetSegName(VALUE self) {
3762         char *p = s_AtomFromValue(self)->segName;
3763         return rb_str_new(p, strlen_limit(p, 4));
3764 }
3765
3766 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3767         return INT2NUM(s_AtomFromValue(self)->resSeq);
3768 }
3769
3770 static VALUE s_AtomRef_GetResName(VALUE self) {
3771         char *p = s_AtomFromValue(self)->resName;
3772         return rb_str_new(p, strlen_limit(p, 4));
3773 }
3774
3775 static VALUE s_AtomRef_GetName(VALUE self) {
3776         char *p = s_AtomFromValue(self)->aname;
3777         return rb_str_new(p, strlen_limit(p, 4));
3778 }
3779
3780 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3781         int type = s_AtomFromValue(self)->type;
3782         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3783         return rb_str_new(p, strlen_limit(p, 6));
3784 }
3785
3786 static VALUE s_AtomRef_GetCharge(VALUE self) {
3787         return rb_float_new(s_AtomFromValue(self)->charge);
3788 }
3789
3790 static VALUE s_AtomRef_GetWeight(VALUE self) {
3791         return rb_float_new(s_AtomFromValue(self)->weight);
3792 }
3793
3794 static VALUE s_AtomRef_GetElement(VALUE self) {
3795         char *p = s_AtomFromValue(self)->element;
3796         return rb_str_new(p, strlen_limit(p, 4));
3797 }
3798
3799 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3800         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3801 }
3802
3803 static VALUE s_AtomRef_GetConnects(VALUE self) {
3804         VALUE retval;
3805         Int i, *cp;
3806         Atom *ap = s_AtomFromValue(self);
3807         retval = rb_ary_new();
3808         cp = AtomConnectData(&ap->connect);
3809         for (i = 0; i < ap->connect.count; i++)
3810                 rb_ary_push(retval, INT2NUM(cp[i]));
3811         return retval;
3812 }
3813
3814 static VALUE s_AtomRef_GetR(VALUE self) {
3815         return ValueFromVector(&(s_AtomFromValue(self)->r));
3816 }
3817
3818 static VALUE s_AtomRef_GetX(VALUE self) {
3819         return rb_float_new(s_AtomFromValue(self)->r.x);
3820 }
3821
3822 static VALUE s_AtomRef_GetY(VALUE self) {
3823         return rb_float_new(s_AtomFromValue(self)->r.y);
3824 }
3825
3826 static VALUE s_AtomRef_GetZ(VALUE self) {
3827         return rb_float_new(s_AtomFromValue(self)->r.z);
3828 }
3829
3830 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3831         Atom *ap;
3832         Molecule *mp;
3833         Vector r1;
3834         s_AtomIndexFromValue(self, &ap, &mp);
3835         r1 = ap->r;
3836         if (mp->cell != NULL)
3837                 TransformVec(&r1, mp->cell->rtr, &r1);
3838         return r1;
3839 }
3840
3841 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3842         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3843         return ValueFromVector(&r1);
3844 }
3845
3846 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3847         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3848 }
3849
3850 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3851         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3852 }
3853
3854 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3855         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3856 }
3857
3858 static VALUE s_AtomRef_GetSigma(VALUE self) {
3859         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3860 }
3861
3862 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3863         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3864 }
3865
3866 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3867         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3868 }
3869
3870 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3871         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3872 }
3873
3874 static VALUE s_AtomRef_GetV(VALUE self) {
3875         return ValueFromVector(&(s_AtomFromValue(self)->v));
3876 }
3877
3878 static VALUE s_AtomRef_GetF(VALUE self) {
3879         Vector v = s_AtomFromValue(self)->f;
3880         VecScaleSelf(v, INTERNAL2KCAL);
3881         return ValueFromVector(&v);
3882 }
3883
3884 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3885         return rb_float_new(s_AtomFromValue(self)->occupancy);
3886 }
3887
3888 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3889         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3890 }
3891
3892 static VALUE s_AtomRef_GetAniso(VALUE self) {
3893         VALUE retval;
3894         int i;
3895         Atom *ap = s_AtomFromValue(self);
3896         if (ap->aniso == NULL)
3897                 return Qnil;
3898         retval = rb_ary_new();
3899         for (i = 0; i < 6; i++)
3900                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3901         if (ap->aniso->has_bsig) {
3902                 rb_ary_push(retval, INT2NUM(0));
3903                 for (i = 0; i < 6; i++)
3904                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3905         }
3906         return retval;
3907 }
3908
3909 static VALUE s_AtomRef_GetSymop(VALUE self) {
3910         VALUE retval;
3911         Atom *ap = s_AtomFromValue(self);
3912         if (!ap->symop.alive)
3913                 return Qnil;
3914         retval = rb_ary_new();
3915         rb_ary_push(retval, INT2NUM(ap->symop.sym));
3916         rb_ary_push(retval, INT2NUM(ap->symop.dx));
3917         rb_ary_push(retval, INT2NUM(ap->symop.dy));
3918         rb_ary_push(retval, INT2NUM(ap->symop.dz));
3919         rb_ary_push(retval, INT2NUM(ap->symbase));
3920         return retval;
3921 }
3922
3923 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3924         return INT2NUM(s_AtomFromValue(self)->intCharge);
3925 }
3926
3927 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3928         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3929 }
3930
3931 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3932         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3933 }
3934
3935 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3936         Molecule *mol;
3937         Atom *ap;
3938         int idx, i;
3939         MDExclusion *exinfo;
3940         Int *exlist;
3941         VALUE retval, aval;
3942         idx = s_AtomIndexFromValue(self, &ap, &mol);
3943         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3944                 VALUE mval = ValueFromMolecule(mol);
3945                 s_RebuildMDParameterIfNecessary(mval, Qnil);
3946         }
3947         if (mol->arena->exinfo == NULL)
3948                 return Qnil;
3949         exinfo = mol->arena->exinfo + idx;
3950         exlist = mol->arena->exlist;
3951         retval = rb_ary_new();
3952         aval = rb_ary_new();
3953         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
3954                 rb_ary_push(aval, INT2NUM(exlist[i]));
3955         rb_ary_push(retval, aval);
3956         aval = rb_ary_new();
3957         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
3958                 rb_ary_push(aval, INT2NUM(exlist[i]));
3959         rb_ary_push(retval, aval);
3960         aval = rb_ary_new();
3961         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
3962                 rb_ary_push(aval, INT2NUM(exlist[i]));
3963         rb_ary_push(retval, aval);
3964         return retval;
3965 }
3966
3967 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3968         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3969 }
3970
3971 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3972         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3973 }
3974
3975 static VALUE s_AtomRef_GetHidden(VALUE self) {
3976         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3977 }
3978
3979 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3980         VALUE retval;
3981         Int i, count, *cp;
3982         Atom *ap = s_AtomFromValue(self);
3983         if (ap->anchor == NULL)
3984                 return Qnil;
3985         count = ap->anchor->connect.count;
3986         retval = rb_ary_new2(count * 2);
3987         cp = AtomConnectData(&ap->anchor->connect);
3988         for (i = 0; i < count; i++) {
3989                 rb_ary_store(retval, i, INT2NUM(cp[i]));
3990                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3991         }
3992         return retval;
3993 }
3994
3995 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3996         char *p = s_AtomFromValue(self)->uff_type;
3997         return rb_str_new(p, strlen_limit(p, 5));
3998 }
3999
4000 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4001         rb_raise(rb_eMolbyError, "index cannot be directly set");
4002         return Qnil;
4003 }
4004
4005 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4006         VALUE oval = s_AtomRef_GetSegSeq(self);
4007         val = rb_Integer(val);
4008         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4009         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4010         return val;
4011 }
4012
4013 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4014         char *p = StringValuePtr(val);
4015         VALUE oval = s_AtomRef_GetSegName(self);
4016         strncpy(s_AtomFromValue(self)->segName, p, 4);
4017         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4018         return val;
4019 }
4020
4021 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4022         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4023         return val; /* Not reached */
4024 }
4025
4026 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4027         Atom *ap = s_AtomFromValue(self);
4028         char *p = StringValuePtr(val);
4029         VALUE oval = s_AtomRef_GetName(self);
4030         if (ap->anchor != NULL && p[0] == '_')
4031                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4032         strncpy(ap->aname, p, 4);
4033         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4034         return val;
4035 }
4036
4037 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4038         Molecule *mp;
4039         char *p = StringValuePtr(val);
4040         VALUE oval = s_AtomRef_GetAtomType(self);
4041         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4042         if (type != 0 && type < kAtomTypeMinimum)
4043                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4044         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4045         mp->needsMDRebuild = 1;
4046         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4047         return val;
4048 }
4049
4050 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4051         Molecule *mp;
4052         VALUE oval = s_AtomRef_GetCharge(self);
4053         val = rb_Float(val);
4054         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4055         mp->needsMDRebuild = 1;
4056         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4057         return val;
4058 }
4059
4060 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4061         Molecule *mp;
4062         VALUE oval = s_AtomRef_GetWeight(self);
4063         val = rb_Float(val);
4064         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4065         mp->needsMDRebuild = 1;
4066         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4067         return val;
4068 }
4069
4070 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4071         Double w;
4072         Molecule *mp;
4073         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4074         char *p = StringValuePtr(val);
4075         VALUE oval = s_AtomRef_GetElement(self);
4076         ap->atomicNumber = ElementToInt(p);
4077         ElementToString(ap->atomicNumber, ap->element);
4078         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4079                 ap->weight = w;
4080         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4081         mp->needsMDRebuild = 1;
4082         return val;
4083 }
4084
4085 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4086         Double w;
4087         Molecule *mp;
4088         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4089         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4090         val = rb_Integer(val);
4091         ap->atomicNumber = NUM2INT(val);
4092         ElementToString(ap->atomicNumber, ap->element);
4093         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4094                 ap->weight = w;
4095         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4096         mp->needsMDRebuild = 1;
4097         return val;
4098 }
4099
4100 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4101         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4102         return val; /* Not reached */
4103 }
4104
4105 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4106         Vector v;
4107         Molecule *mp;
4108         VALUE oval = s_AtomRef_GetR(self);
4109         VectorFromValue(val, &v);
4110         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4111         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4112         mp->needsMDCopyCoordinates = 1;
4113         return val;
4114 }
4115
4116 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4117         Double f;
4118         Molecule *mp;
4119         VALUE oval = s_AtomRef_GetX(self);
4120         val = rb_Float(val);
4121         f = NUM2DBL(val);
4122         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4123         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4124         mp->needsMDCopyCoordinates = 1;
4125         return val;
4126 }
4127
4128 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4129         Double f;
4130         Molecule *mp;
4131         VALUE oval = s_AtomRef_GetY(self);
4132         val = rb_Float(val);
4133         f = NUM2DBL(val);
4134         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4135         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4136         mp->needsMDCopyCoordinates = 1;
4137         return val;
4138 }
4139
4140 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4141         Double f;
4142         Molecule *mp;
4143         VALUE oval = s_AtomRef_GetZ(self);
4144         val = rb_Float(val);
4145         f = NUM2DBL(val);
4146         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4147         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4148         mp->needsMDCopyCoordinates = 1;
4149         return val;
4150 }
4151
4152 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4153         Vector v, ov;
4154         Atom *ap;
4155         Molecule *mp;
4156         s_AtomIndexFromValue(self, &ap, &mp);
4157         ov = ap->r;
4158         VectorFromValue(val, &v);
4159         if (mp->cell != NULL)
4160                 TransformVec(&v, mp->cell->tr, &v);
4161         ap->r = v;
4162         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4163         mp->needsMDCopyCoordinates = 1;
4164         return val;
4165 }
4166
4167 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4168         double f;
4169         Vector v, ov;
4170         Atom *ap;
4171         Molecule *mp;
4172         s_AtomIndexFromValue(self, &ap, &mp);
4173         ov = v = ap->r;
4174         val = rb_Float(val);
4175         f = NUM2DBL(val);
4176         if (mp->cell != NULL) {
4177                 TransformVec(&v, mp->cell->rtr, &v);
4178                 v.x = f;
4179                 TransformVec(&v, mp->cell->tr, &v);
4180         } else v.x = f;
4181         ap->r = v;
4182         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4183         mp->needsMDCopyCoordinates = 1;
4184         return val;
4185 }
4186
4187 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4188         double f;
4189         Vector v, ov;
4190         Atom *ap;
4191         Molecule *mp;
4192         s_AtomIndexFromValue(self, &ap, &mp);
4193         ov = v = ap->r;
4194         val = rb_Float(val);
4195         f = NUM2DBL(val);
4196         if (mp->cell != NULL) {
4197                 TransformVec(&v, mp->cell->rtr, &v);
4198                 v.y = f;
4199                 TransformVec(&v, mp->cell->tr, &v);
4200         } else v.y = f;
4201         ap->r = v;
4202         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4203         mp->needsMDCopyCoordinates = 1;
4204         return val;
4205 }
4206
4207 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4208         double f;
4209         Vector v, ov;
4210         Atom *ap;
4211         Molecule *mp;
4212         s_AtomIndexFromValue(self, &ap, &mp);
4213         ov = v = ap->r;
4214         val = rb_Float(val);
4215         f = NUM2DBL(val);
4216         if (mp->cell != NULL) {
4217                 TransformVec(&v, mp->cell->rtr, &v);
4218                 v.z = f;
4219                 TransformVec(&v, mp->cell->tr, &v);
4220         } else v.z = f;
4221         ap->r = v;
4222         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4223         mp->needsMDCopyCoordinates = 1;
4224         return val;
4225 }
4226
4227 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4228         Vector v;
4229         Molecule *mp;
4230         VALUE oval = s_AtomRef_GetSigma(self);
4231         VectorFromValue(val, &v);
4232         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4233         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4234         mp->needsMDCopyCoordinates = 1;
4235         return val;
4236 }
4237
4238 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4239         Double f;
4240         Molecule *mp;
4241         VALUE oval = s_AtomRef_GetSigmaX(self);
4242         val = rb_Float(val);
4243         f = NUM2DBL(val);
4244         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4245         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4246         mp->needsMDCopyCoordinates = 1;
4247         return val;
4248 }
4249
4250 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4251         Double f;
4252         Molecule *mp;
4253         VALUE oval = s_AtomRef_GetSigmaY(self);
4254         val = rb_Float(val);
4255         f = NUM2DBL(val);
4256         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4257         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4258         mp->needsMDCopyCoordinates = 1;
4259         return val;
4260 }
4261
4262 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4263         Double f;
4264         Molecule *mp;
4265         VALUE oval = s_AtomRef_GetSigmaZ(self);
4266         val = rb_Float(val);
4267         f = NUM2DBL(val);
4268         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4269         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4270         mp->needsMDCopyCoordinates = 1;
4271         return val;
4272 }
4273
4274 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4275         Vector v;
4276         Atom *ap;
4277         Molecule *mp;
4278         VALUE oval = s_AtomRef_GetV(self);
4279         VectorFromValue(val, &v);
4280         s_AtomIndexFromValue(self, &ap, &mp);
4281         ap->v = v;
4282         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4283         mp->needsMDCopyCoordinates = 1;
4284         return val;
4285 }
4286
4287 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4288         Vector v;
4289         Molecule *mp;
4290         VALUE oval = s_AtomRef_GetF(self);
4291         VectorFromValue(val, &v);
4292         VecScaleSelf(v, KCAL2INTERNAL);
4293         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4294         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4295         mp->needsMDCopyCoordinates = 1;
4296         return val;
4297 }
4298
4299 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4300         VALUE oval = s_AtomRef_GetOccupancy(self);
4301         Molecule *mp;
4302         val = rb_Float(val);
4303         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4304         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4305         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4306         return val;
4307 }
4308
4309 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4310         VALUE oval = s_AtomRef_GetTempFactor(self);
4311         val = rb_Float(val);
4312         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4313         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4314         return val;
4315 }
4316
4317 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4318         AtomRef *aref;
4319         int i, n, type;
4320         VALUE *valp;
4321         Double f[12];
4322         VALUE oval = s_AtomRef_GetAniso(self);
4323         Data_Get_Struct(self, AtomRef, aref);
4324         val = rb_funcall(val, rb_intern("to_a"), 0);
4325         n = RARRAY_LEN(val);
4326         valp = RARRAY_PTR(val);
4327         for (i = 0; i < 6; i++) {
4328                 if (i < n)
4329                         f[i] = NUM2DBL(rb_Float(valp[i]));
4330                 else f[i] = 0.0;
4331         }
4332         if (n >= 7)
4333                 type = NUM2INT(rb_Integer(valp[6]));
4334         else type = 0;
4335         if (n >= 13) {
4336                 for (i = 0; i < 6; i++)
4337                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4338         } else {
4339                 for (i = 0; i < 6; i++)
4340                         f[i + 6] = 0.0;
4341         }
4342         i = s_AtomIndexFromValue(self, NULL, NULL);
4343         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4344         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4345         return val;
4346 }
4347
4348 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4349         Molecule *mol;
4350         Atom *ap;
4351         int i, n;
4352         VALUE *valp;
4353         Int ival[5];
4354         VALUE oval = s_AtomRef_GetSymop(self);
4355         i = s_AtomIndexFromValue(self, &ap, &mol);
4356         if (val == Qnil) {
4357                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4358         } else {
4359                 val = rb_funcall(val, rb_intern("to_a"), 0);
4360                 n = RARRAY_LEN(val);
4361                 valp = RARRAY_PTR(val);
4362                 for (i = 0; i < 5; i++) {
4363                         if (i < n) {
4364                                 if (valp[i] == Qnil)
4365                                         ival[i] = -100000;
4366                                 else 
4367                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4368                         } else ival[i] = -100000;
4369                 }
4370         }
4371         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4372                 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));
4373         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4374                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4375         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4376                 return val;  /*  No need to change  */
4377         if (ival[0] != -100000)
4378                 ap->symop.sym = ival[0];
4379         if (ival[1] != -100000)
4380                 ap->symop.dx = ival[1];
4381         if (ival[2] != -100000)
4382                 ap->symop.dy = ival[2];
4383         if (ival[3] != -100000)
4384                 ap->symop.dz = ival[3];
4385         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4386         if (ival[4] != -100000)
4387                 ap->symbase = ival[4];
4388         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4389                 /*  The anisotropic parameters should be recalculated  */
4390                 VALUE oaval = s_AtomRef_GetAniso(self);
4391                 MoleculeSetAnisoBySymop(mol, i);
4392                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4393         }
4394         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4395         return val;
4396 }
4397
4398 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4399         VALUE oval = s_AtomRef_GetIntCharge(self);
4400         val = rb_Integer(val);
4401         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4402         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4403         return val;
4404 }
4405
4406 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4407         Molecule *mp;
4408         VALUE oval = s_AtomRef_GetFixForce(self);
4409         val = rb_Float(val);
4410         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4411         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4412         mp->needsMDRebuild = 1;
4413         return val;
4414 }
4415
4416 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4417         Vector v;
4418         Molecule *mp;
4419         VALUE oval = s_AtomRef_GetFixPos(self);
4420         VectorFromValue(val, &v);
4421         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4422         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4423         mp->needsMDRebuild = 1;
4424         return val;
4425 }
4426
4427 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4428         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4429         return val; /* Not reached */
4430 }
4431
4432 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4433         VALUE oval = s_AtomRef_GetIntCharge(self);
4434         val = rb_Integer(val);
4435         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4436         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4437         return val;
4438 }
4439
4440 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4441         VALUE oval = s_AtomRef_GetIntCharge(self);
4442         val = rb_Integer(val);
4443         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4444         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4445         return val;
4446 }
4447
4448 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4449         Atom *ap = s_AtomFromValue(self);
4450         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4451         if (RTEST(val)) {
4452                 ap->exflags |= kAtomHiddenFlag;
4453         } else {
4454                 ap->exflags &= ~kAtomHiddenFlag;
4455         }
4456         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4457         return val;
4458 }
4459
4460 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4461         Int idx, i, j, k, n, *ip;
4462         Double *dp;
4463         Atom *ap;
4464         Molecule *mol;
4465         VALUE oval, v;
4466         AtomConnect ac;
4467         Int nUndoActions;
4468         MolAction **undoActions;
4469         memset(&ac, 0, sizeof(ac));
4470         idx = s_AtomIndexFromValue(self, &ap, &mol);
4471         oval = s_AtomRef_GetAnchorList(self);
4472         if (val != Qnil) {
4473                 val = rb_ary_to_ary(val);
4474                 n = RARRAY_LEN(val);
4475         } else n = 0;
4476         if (n == 0) {
4477                 if (ap->anchor != NULL) {
4478                         AtomConnectResize(&ap->anchor->connect, 0);
4479                         free(ap->anchor->coeffs);
4480                         free(ap->anchor);
4481                         ap->anchor = NULL;
4482                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4483                 }
4484                 return val;
4485         }
4486         if (n < 2)
4487                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4488         if (ap->aname[0] == '_')
4489                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4490         ip = (Int *)malloc(sizeof(Int) * n);
4491         dp = NULL;
4492         for (i = 0; i < n; i++) {
4493                 v = RARRAY_PTR(val)[i];
4494                 if (rb_obj_is_kind_of(v, rb_cFloat))
4495                         break;
4496                 j = NUM2INT(rb_Integer(v));
4497                 if (j < 0 || j >= mol->natoms)
4498                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4499                 for (k = 0; k < i; k++) {
4500                         if (ip[k] == j)
4501                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4502                 }
4503                 ip[i] = j;
4504         }
4505         if (i < n) {
4506                 if (i < 2)
4507                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4508                 else if (i * 2 != n)
4509                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4510                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4511                 for (i = 0; i < n / 2; i++) {
4512                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4513                         if (dp[i] <= 0.0)
4514                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4515                 }
4516                 n /= 2;
4517         }
4518         nUndoActions = 0;
4519         undoActions = NULL;
4520         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4521         free(dp);
4522         free(ip);
4523         if (i != 0)
4524                 rb_raise(rb_eMolbyError, "invalid argument");
4525         if (nUndoActions > 0) {
4526                 for (i = 0; i < nUndoActions; i++) {
4527                         MolActionCallback_registerUndo(mol, undoActions[i]);
4528                         MolActionRelease(undoActions[i]);
4529                 }
4530                 free(undoActions);
4531         }
4532         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4533         return val;
4534 }
4535
4536 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4537         Atom *ap = s_AtomFromValue(self);
4538         char *p = StringValuePtr(val);
4539         VALUE oval = s_AtomRef_GetUFFType(self);
4540         strncpy(ap->uff_type, p, 5);
4541         ap->uff_type[5] = 0;
4542         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4543         return val;
4544 }
4545
4546 static struct s_AtomAttrDef {
4547         char *name;
4548         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4549         ID id;                  /*  Will be set within InitMolby()  */
4550         VALUE (*getter)(VALUE);
4551         VALUE (*setter)(VALUE, VALUE);
4552 } s_AtomAttrDefTable[] = {
4553         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4554         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4555         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4556         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4557         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4558         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4559         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4560         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4561         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4562         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4563         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4564         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4565         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4566         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4567         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4568     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4569         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4570         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4571         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4572         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4573         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4574         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4575         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4576         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4577         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4578         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4579         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4580         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4581         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4582         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4583         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4584         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4585         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4586         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4587         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4588         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4589         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4590         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4591         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4592         {NULL} /* Sentinel */
4593 };
4594
4595 static VALUE
4596 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4597 {
4598         int i;
4599         ID kid;
4600         if (TYPE(key) != T_SYMBOL) {
4601                 kid = rb_intern(StringValuePtr(key));
4602                 key = ID2SYM(kid);
4603         } else kid = SYM2ID(key);
4604         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4605                 if (s_AtomAttrDefTable[i].id == kid) {
4606                         if (value == Qundef)
4607                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4608                         else
4609                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4610                 }
4611         }
4612         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4613         return Qnil; /* not reached */
4614 }
4615
4616 static VALUE
4617 s_AtomRef_GetAttr(VALUE self, VALUE key)
4618 {
4619         return s_AtomRef_SetAttr(self, key, Qundef);
4620 }
4621
4622 /*
4623  *  call-seq:
4624  *     self == atomRef -> boolean
4625  *
4626  *  True if the two references point to the same atom.
4627  */
4628 static VALUE
4629 s_AtomRef_Equal(VALUE self, VALUE val)
4630 {
4631         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4632                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4633         } else return Qfalse;
4634 }
4635
4636 #pragma mark ====== MolEnumerable Class ======
4637
4638 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4639
4640 /*
4641  *  call-seq:
4642  *     self[idx] -> AtomRef or Array of Integers
4643  *  
4644  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4645  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4646  *  value is a String. Otherwise, the return value is an Array of Integers.
4647  */
4648 static VALUE
4649 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4650 {
4651         MolEnumerable *mseq;
4652         Molecule *mol;
4653         int idx1, idx2;
4654     Data_Get_Struct(self, MolEnumerable, mseq);
4655         mol = mseq->mol;
4656         if (mseq->kind == kAtomKind) {
4657                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4658         }
4659         idx1 = NUM2INT(arg1);
4660         switch (mseq->kind) {
4661                 case kBondKind: {
4662                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4663                         if (idx2 < 0 || idx2 >= mol->nbonds)
4664                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4665                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4666                 }
4667                 case kAngleKind: {
4668                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4669                         if (idx2 < 0 || idx2 >= mol->nangles)
4670                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4671                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4672                 }
4673                 case kDihedralKind: {
4674                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4675                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4676                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4677                         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]));
4678                 }
4679                 case kImproperKind: {
4680                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4681                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4682                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4683                         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]));
4684                 }
4685                 case kResidueKind: {
4686                         char *p;
4687                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4688                         if (idx2 < 0 || idx2 >= mol->nresidues)
4689                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4690                         p = mol->residues[idx2];
4691                         return rb_str_new(p, strlen_limit(p, 4));
4692                 }
4693         }
4694         return Qnil;
4695 }
4696
4697 /*
4698  *  call-seq:
4699  *     length          -> Integer
4700  *  
4701  *  Returns the number of objects included in this enumerable.
4702  */
4703 static VALUE
4704 s_MolEnumerable_Length(VALUE self)
4705 {
4706         MolEnumerable *mseq;
4707     Data_Get_Struct(self, MolEnumerable, mseq);
4708         switch (mseq->kind) {
4709                 case kAtomKind:
4710                         return INT2NUM(mseq->mol->natoms);
4711                 case kBondKind:
4712                         return INT2NUM(mseq->mol->nbonds);
4713                 case kAngleKind:
4714                         return INT2NUM(mseq->mol->nangles);
4715                 case kDihedralKind:
4716                         return INT2NUM(mseq->mol->ndihedrals);
4717                 case kImproperKind:
4718                         return INT2NUM(mseq->mol->nimpropers);
4719                 case kResidueKind:
4720                         return INT2NUM(mseq->mol->nresidues);
4721         }
4722         return INT2NUM(-1);
4723 }
4724
4725 /*
4726  *  call-seq:
4727  *     each {|obj| ...}
4728  *  
4729  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4730  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4731  *  For the atoms, a same AtomRef object is passed (with different internal information)
4732  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4733  *  for each iteration.
4734  */
4735 VALUE
4736 s_MolEnumerable_Each(VALUE self)
4737 {
4738         MolEnumerable *mseq;
4739         int i;
4740         int len = NUM2INT(s_MolEnumerable_Length(self));
4741     Data_Get_Struct(self, MolEnumerable, mseq);
4742         if (mseq->kind == kAtomKind) {
4743                 /*  The same AtomRef object will be used during the loop  */
4744                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4745                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4746                 for (i = 0; i < len; i++) {
4747                         aref->idx = i;
4748                         rb_yield(arval);
4749                 }
4750     } else {
4751                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4752                 for (i = 0; i < len; i++) {
4753                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4754                 }
4755         }
4756     return self;
4757 }
4758
4759 /*
4760  *  call-seq:
4761  *     self == molEnumerable -> boolean
4762  *
4763  *  True if the two arguments point to the same molecule and enumerable type.
4764  */
4765 static VALUE
4766 s_MolEnumerable_Equal(VALUE self, VALUE val)
4767 {
4768         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4769                 MolEnumerable *mseq1, *mseq2;
4770                 Data_Get_Struct(self, MolEnumerable, mseq1);
4771                 Data_Get_Struct(val, MolEnumerable, mseq2);
4772                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4773         } else return Qfalse;
4774 }
4775
4776
4777 #pragma mark ====== Molecule Class ======
4778
4779 #pragma mark ------ Allocate/Release/Accessor ------
4780
4781 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method.  */
4782 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4783 char *gLoadSaveErrorMessage = NULL;
4784
4785 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4786
4787 Molecule *
4788 MoleculeFromValue(VALUE val)
4789 {
4790         Molecule *mol;
4791         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4792                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4793     Data_Get_Struct(val, Molecule, mol);
4794         return mol;
4795 }
4796
4797 static VALUE sMoleculeRetainArray = Qnil;
4798
4799 /*  The function is called from MoleculeRelease()  */
4800 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4801 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4802 /*  object is always returned for the same Molecule.  */
4803 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4804 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4805 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4806 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4807 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4808
4809 /*  Register/unregister the exmolobj Ruby object  */
4810 void
4811 MoleculeReleaseExternalObj(Molecule *mol)
4812 {
4813         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4814                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4815                 mol->exmolobjProtected = 0;
4816         }
4817 }
4818
4819 void
4820 MoleculeRetainExternalObj(Molecule *mol)
4821 {
4822         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4823                 if (sMoleculeRetainArray == Qnil) {
4824                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4825                         sMoleculeRetainArray = rb_ary_new();
4826                 }
4827                 
4828                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4829                 mol->exmolobjProtected = 1;
4830         }
4831 }
4832
4833 /*  Release hook function for Ruby  */
4834 void
4835 MoleculeReleaseHook(Molecule *mol)
4836 {
4837         if (mol->exmolobj != NULL) {
4838                 /*  No need to remove from sMoleculeRetainArray  */
4839                 mol->exmolobj = NULL;
4840                 mol->exmolobjProtected = 0;
4841         }
4842         MoleculeRelease(mol);
4843 }
4844
4845 VALUE
4846 ValueFromMolecule(Molecule *mol)
4847 {
4848         if (mol == NULL)
4849                 return Qnil;
4850         if (mol->exmolobj != NULL)
4851                 return (VALUE)mol->exmolobj;
4852         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4853         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4854         return (VALUE)mol->exmolobj;
4855 }
4856
4857 /*  Allocator  */
4858 static VALUE
4859 s_Molecule_Alloc(VALUE klass)
4860 {
4861         VALUE val;
4862         Molecule *mol = MoleculeNew();
4863         val = ValueFromMolecule(mol);
4864         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4865         return val;
4866 }
4867
4868 static int
4869 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4870 {
4871         int n;
4872         char *p = "";
4873         if (FIXNUM_P(val)) {
4874                 n = FIX2INT(val);
4875                 if (n >= 0 && n < mol->natoms)
4876                         return n;
4877                 n = -1; /*  No such atom  */
4878                 val = rb_inspect(val);
4879         } else {
4880                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4881         }
4882         if (n >= 0 && n < mol->natoms)
4883                 return n;
4884         p = StringValuePtr(val);
4885         if (n == -1)
4886                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4887         else if (n == -2)
4888                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4889         else
4890                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4891         return 0; /* Not reached */
4892 }
4893
4894 static IntGroup *
4895 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4896 {
4897         IntGroup *ig;
4898         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4899         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4900                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4901         Data_Get_Struct(val, IntGroup, ig);
4902         IntGroupRetain(ig);
4903         return ig;
4904 }
4905
4906 /*
4907  *  call-seq:
4908  *     dup          -> Molecule
4909  *
4910  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
4911  *  created object does not affect the old object in any sense.
4912  */
4913 static VALUE
4914 s_Molecule_InitCopy(VALUE self, VALUE arg)
4915 {
4916         Molecule *mp1, *mp2;
4917         Data_Get_Struct(self, Molecule, mp1);
4918         mp2 = MoleculeFromValue(arg);
4919         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4920                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4921         return self;
4922 }
4923
4924 /*
4925  *  call-seq:
4926  *     atom_index(val)       -> Integer
4927  *
4928  *  Returns the atom index represented by val. val can be either a non-negative integer
4929  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
4930  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
4931  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
4932  *  If val is a string and multiple atoms match the description, the atom with the lowest index
4933  *  is returned.
4934  */
4935 static VALUE
4936 s_Molecule_AtomIndex(VALUE self, VALUE val)
4937 {
4938     Molecule *mol;
4939     Data_Get_Struct(self, Molecule, mol);
4940         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
4941 }
4942
4943 /*
4944  *  call-seq:
4945  *     self == Molecule -> boolean
4946  *
4947  *  True if the two arguments point to the same molecule.
4948  */
4949 static VALUE
4950 s_Molecule_Equal(VALUE self, VALUE val)
4951 {
4952         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
4953                 Molecule *mol1, *mol2;
4954                 Data_Get_Struct(self, Molecule, mol1);
4955                 Data_Get_Struct(val, Molecule, mol2);
4956                 return (mol1 == mol2 ? Qtrue : Qfalse);
4957         } else return Qfalse;
4958 }
4959
4960 #pragma mark ------ Load/Save ------
4961
4962 static void
4963 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
4964 {
4965         if (gLoadSaveErrorMessage != NULL) {
4966                 MyAppCallback_setConsoleColor(1);
4967                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
4968                 MyAppCallback_setConsoleColor(0);
4969         }
4970         if (status != 0)
4971                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4972 }
4973
4974 /*
4975  *  call-seq:
4976  *     loadmbsf(file)       -> bool
4977  *
4978  *  Read a structure from a mbsf file.
4979  *  Return true if successful.
4980  */
4981 static VALUE
4982 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4983 {
4984         VALUE fname;
4985         char *fstr;
4986         Molecule *mol;
4987         int retval;
4988         MoleculeClearLoadSaveErrorMessage();
4989         Data_Get_Struct(self, Molecule, mol);
4990         rb_scan_args(argc, argv, "1", &fname);
4991         fstr = FileStringValuePtr(fname);
4992         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4993         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
4994         return Qtrue;   
4995 }
4996
4997 /*
4998  *  call-seq:
4999  *     loadpsf(file, pdbfile = nil)       -> bool
5000  *
5001  *  Read a structure from a psf file. molecule must be empty. The psf may be
5002  *  an "extended" version, which also contains coordinates. If pdbfile 
5003  *  is given, then atomic coordinates are read from that file.
5004  *  Return true if successful.
5005  */
5006 static VALUE
5007 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5008 {
5009         VALUE fname, pdbname;
5010         char *fstr, *pdbstr;
5011         Molecule *mol;
5012         int retval;
5013         Data_Get_Struct(self, Molecule, mol);
5014         if (mol->natoms > 0)
5015                 return Qnil;  /*  Must be a new molecule  */
5016         MoleculeClearLoadSaveErrorMessage();
5017         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5018         fstr = FileStringValuePtr(fname);
5019         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5020         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5021         pdbstr = NULL;
5022         if (!NIL_P(pdbname)) {
5023                 pdbstr = strdup(FileStringValuePtr(pdbname));
5024                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5025                 free(pdbstr);
5026                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5027         }
5028         return Qtrue;
5029 }
5030
5031 /*
5032  *  call-seq:
5033  *     loadpdb(file)       -> bool
5034  *
5035  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5036  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5037  *  Return true if successful.
5038  */
5039 static VALUE
5040 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5041 {
5042         VALUE fname;
5043         char *fstr;
5044         Molecule *mol;
5045         int retval;
5046         Data_Get_Struct(self, Molecule, mol);
5047         rb_scan_args(argc, argv, "1", &fname);
5048         MoleculeClearLoadSaveErrorMessage();
5049         fstr = FileStringValuePtr(fname);
5050         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5051         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5052         return Qtrue;   
5053 }
5054
5055 /*
5056  *  call-seq:
5057  *     loaddcd(file)       -> bool
5058  *
5059  *  Read coordinates from a dcd file. The molecule should not empty.
5060  *  Return true if successful.
5061  */
5062 static VALUE
5063 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5064 {
5065         VALUE fname;
5066         char *fstr;
5067         Molecule *mol;
5068         int retval;
5069         Data_Get_Struct(self, Molecule, mol);
5070         rb_scan_args(argc, argv, "1", &fname);
5071         MoleculeClearLoadSaveErrorMessage();
5072         fstr = FileStringValuePtr(fname);
5073         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5074         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5075         return Qtrue;   
5076 }
5077
5078 /*
5079  *  call-seq:
5080  *     loadtep(file)       -> bool
5081  *
5082  *  Read coordinates from an ortep .tep file.
5083  *  Return true if successful.
5084  */
5085 static VALUE
5086 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5087 {
5088         VALUE fname;
5089         char *fstr;
5090         Molecule *mol;
5091         int retval;
5092         Data_Get_Struct(self, Molecule, mol);
5093         rb_scan_args(argc, argv, "1", &fname);
5094         MoleculeClearLoadSaveErrorMessage();
5095         fstr = FileStringValuePtr(fname);
5096         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5097         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5098         return Qtrue;   
5099 }
5100
5101 /*
5102  *  call-seq:
5103  *     loadres(file)       -> bool
5104  *
5105  *  Read coordinates from a shelx .res file.
5106  *  Return true if successful.
5107  */
5108 static VALUE
5109 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5110 {
5111         VALUE fname;
5112         char *fstr;
5113         Molecule *mol;
5114         int retval;
5115         Data_Get_Struct(self, Molecule, mol);
5116         rb_scan_args(argc, argv, "1", &fname);
5117         MoleculeClearLoadSaveErrorMessage();
5118         fstr = FileStringValuePtr(fname);
5119         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5120         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5121         return Qtrue;   
5122 }
5123
5124 /*
5125  *  call-seq:
5126  *     loadfchk(file)       -> bool
5127  *
5128  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5129  *  Return true if successful.
5130  */
5131 static VALUE
5132 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5133 {
5134         VALUE fname;
5135         char *fstr;
5136         Molecule *mol;
5137         int retval;
5138         Data_Get_Struct(self, Molecule, mol);
5139         rb_scan_args(argc, argv, "1", &fname);
5140         MoleculeClearLoadSaveErrorMessage();
5141         fstr = FileStringValuePtr(fname);
5142         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5143         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5144         return Qtrue;   
5145 }
5146
5147 /*
5148  *  call-seq:
5149  *     loaddat(file)       -> bool
5150  *
5151  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5152  *  Return true if successful.
5153  */
5154 static VALUE
5155 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5156 {
5157         VALUE fname;
5158         char *fstr;
5159         Molecule *mol;
5160         int retval;
5161         Data_Get_Struct(self, Molecule, mol);
5162         rb_scan_args(argc, argv, "1", &fname);
5163         MoleculeClearLoadSaveErrorMessage();
5164         fstr = FileStringValuePtr(fname);
5165         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5166         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5167         MyAppCallback_hideProgressPanel();
5168         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5169         return Qtrue;   
5170 }
5171
5172 /*
5173  *  call-seq:
5174  *     savembsf(file)       -> bool
5175  *
5176  *  Write structure as a mbsf file. Returns true if successful.
5177  */
5178 static VALUE
5179 s_Molecule_Savembsf(VALUE self, VALUE fname)
5180 {
5181         char *fstr;
5182     Molecule *mol;
5183         int retval;
5184     Data_Get_Struct(self, Molecule, mol);
5185         MoleculeClearLoadSaveErrorMessage();
5186         fstr = FileStringValuePtr(fname);
5187         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5188         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5189         return Qtrue;
5190 }
5191
5192 /*
5193  *  call-seq:
5194  *     savepsf(file)       -> bool
5195  *
5196  *  Write structure as a psf file. Returns true if successful.
5197  */
5198 static VALUE
5199 s_Molecule_Savepsf(VALUE self, VALUE fname)
5200 {
5201         char *fstr;
5202     Molecule *mol;
5203         int retval;
5204     Data_Get_Struct(self, Molecule, mol);
5205         MoleculeClearLoadSaveErrorMessage();
5206         fstr = FileStringValuePtr(fname);
5207         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5208         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5209         return Qtrue;
5210 }
5211
5212 /*
5213  *  call-seq:
5214  *     savepdb(file)       -> bool
5215  *
5216  *  Write coordinates as a pdb file. Returns true if successful.
5217  */
5218 static VALUE
5219 s_Molecule_Savepdb(VALUE self, VALUE fname)
5220 {
5221         char *fstr;
5222     Molecule *mol;
5223         int retval;
5224     Data_Get_Struct(self, Molecule, mol);
5225         MoleculeClearLoadSaveErrorMessage();
5226         fstr = FileStringValuePtr(fname);
5227         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5228         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5229         return Qtrue;
5230 }
5231
5232 /*
5233  *  call-seq:
5234  *     savedcd(file)       -> bool
5235  *
5236  *  Write coordinates as a dcd file. Returns true if successful.
5237  */
5238 static VALUE
5239 s_Molecule_Savedcd(VALUE self, VALUE fname)
5240 {
5241         char *fstr;
5242     Molecule *mol;
5243         int retval;
5244     Data_Get_Struct(self, Molecule, mol);
5245         MoleculeClearLoadSaveErrorMessage();
5246         fstr = FileStringValuePtr(fname);
5247         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5248         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5249         return Qtrue;
5250 }
5251
5252 /*  load([ftype, ] fname, ...)  */
5253 static VALUE
5254 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5255 {
5256         VALUE rval;
5257         char *argstr, *methname, *p, *type = "";
5258         ID mid = 0;
5259         int i;
5260         const char *ls = (loadFlag ? "load" : "save");
5261         int lslen = strlen(ls);
5262
5263         if (argc == 0)
5264                 return Qnil;
5265
5266         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5267                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5268         if (argstr[0] == ':') {
5269                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5270                 methname = ALLOC_N(char, lslen + strlen(argstr));
5271                 strcpy(methname, ls);
5272                 strcat(methname, argstr + 1);
5273                 type = argstr + 1;
5274                 for (i = lslen; methname[i] != 0; i++)
5275                         methname[i] = tolower(methname[i]);
5276                 mid = rb_intern(methname);
5277                 xfree(methname);
5278                 argc--;
5279                 argv++;
5280                 rval = rb_funcall2(self, mid, argc, argv);
5281                 if (rval == Qnil)
5282                         goto failure;
5283                 else
5284                         goto success;
5285         }
5286         /*  Guess file type from extension  */
5287         p = strrchr(argstr, '.');
5288         if (p != NULL) {
5289                 p++;
5290                 type = p;
5291                 for (methname = p; *methname != 0; methname++) {
5292                         if (!isalpha(*methname))
5293                                 break;
5294                 }
5295                 if (*methname == 0) {
5296                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5297                         if (methname == NULL)
5298                                 rb_raise(rb_eMolbyError, "Low memory");
5299                         strcpy(methname, ls);
5300                         strcat(methname, p);
5301                         for (i = lslen; methname[i] != 0; i++)
5302                                 methname[i] = tolower(methname[i]);
5303                         mid = rb_intern(methname);
5304                         xfree(methname);
5305                         if (loadFlag) {
5306                                 if (rb_respond_to(self, mid)) {
5307                                         /*  Load: try to call the load procedure only if it is available  */
5308                                         rval = rb_funcall2(self, mid, argc, argv);
5309                                         if (rval != Qnil)
5310                                                 goto success;
5311                                 }
5312                         } else {
5313                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5314                                 rval = rb_funcall2(self, mid, argc, argv);
5315                                 if (rval != Qnil)
5316                                         goto success;
5317                         }
5318                 }
5319         }
5320 failure:
5321         rval = rb_str_to_str(argv[0]);
5322         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5323         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5324         return Qnil;  /*  Does not reach here  */
5325
5326 success:
5327         {
5328                 /*  Register the path  */
5329                 Molecule *mol;
5330         /*      Atom *ap; */
5331                 Data_Get_Struct(self, Molecule, mol);
5332                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5333                 
5334                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5335         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5336                         if (ap->occupancy != 0.0)
5337                                 break;
5338                 }
5339                 if (i == mol->natoms) {
5340                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5341                                 ap->occupancy = 1.0;
5342                         }
5343                 } */
5344         }
5345         return rval;
5346 }
5347
5348 /*
5349  *  call-seq:
5350  *     molload(file, *args)       -> bool
5351  *
5352  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5353  *  file type given by the extension). If this method fails, then all defined (public)
5354  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5355  */
5356 static VALUE
5357 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5358 {
5359         return s_Molecule_LoadSave(argc, argv, self, 1);
5360 }
5361
5362 /*
5363  *  call-seq:
5364  *     molsave(file, *args)       -> bool
5365  *
5366  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5367  *  (XXX is the file type given by the extension).
5368  */
5369 static VALUE
5370 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5371 {
5372         return s_Molecule_LoadSave(argc, argv, self, 0);
5373 }
5374
5375 /*
5376  *  call-seq:
5377  *     open        -> Molecule
5378  *     open(file)  -> Molecule
5379  *
5380  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5381  */
5382 static VALUE
5383 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5384 {
5385         VALUE fname;
5386         const char *p;
5387         Molecule *mp;
5388         VALUE iflag;
5389         rb_scan_args(argc, argv, "01", &fname);
5390         if (NIL_P(fname))
5391                 p = NULL;
5392         else
5393                 p = FileStringValuePtr(fname);
5394         iflag = Ruby_SetInterruptFlag(Qfalse);
5395         mp = MoleculeCallback_openNewMolecule(p);
5396         Ruby_SetInterruptFlag(iflag);
5397         if (mp == NULL) {
5398                 if (p == NULL)
5399                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5400                 else
5401                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5402         }
5403         return ValueFromMolecule(mp);
5404 }
5405
5406 /*
5407  *  call-seq:
5408  *     new  -> Molecule
5409  *     new(file, *args)  -> Molecule
5410  *
5411  *  Create a new molecule and call "load" method with the same arguments.
5412  */
5413 static VALUE
5414 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5415 {
5416         if (argc > 0)
5417                 return s_Molecule_Load(argc, argv, self);
5418         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5419 }
5420
5421 /*
5422  *  call-seq:
5423  *     error_message       -> String
5424  *
5425  *  Get the error_message from the last load/save method. If no error, returns nil.
5426  */
5427 static VALUE
5428 s_Molecule_ErrorMessage(VALUE klass)
5429 {
5430         if (gLoadSaveErrorMessage == NULL)
5431                 return Qnil;
5432         else return rb_str_new2(gLoadSaveErrorMessage);
5433 }
5434
5435 /*
5436  *  call-seq:
5437  *     set_error_message(String)
5438  *     Molecule.error_message = String
5439  *
5440  *  Get the error_message from the last load/save method. If no error, returns nil.
5441  */
5442 static VALUE
5443 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5444 {
5445         if (gLoadSaveErrorMessage != NULL) {
5446                 free(gLoadSaveErrorMessage);
5447                 gLoadSaveErrorMessage = NULL;
5448         }
5449         if (sval != Qnil) {
5450                 sval = rb_str_to_str(sval);
5451                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5452         }
5453         return sval;
5454 }
5455
5456 /*
5457  *  call-seq:
5458  *     set_molecule(Molecule)
5459  *
5460  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5461  *  This method is exclusively used for associating a new document with an existing molecule.
5462  */
5463 static VALUE
5464 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5465 {
5466         Molecule *mp1, *mp2;
5467         Data_Get_Struct(self, Molecule, mp1);
5468         mp2 = MoleculeFromValue(mval);
5469         MoleculeInitWithMolecule(mp1, mp2);
5470         MoleculeCallback_notifyModification(mp1, 1);
5471         return self;
5472 }
5473
5474 #pragma mark ------ Name attributes ------
5475
5476 /*
5477  *  call-seq:
5478  *     name       -> String
5479  *
5480  *  Returns the display name of the molecule. If the molecule has no associated
5481  *  document, then returns nil.
5482  */
5483 static VALUE
5484 s_Molecule_Name(VALUE self)
5485 {
5486     Molecule *mol;
5487         char buf[1024];
5488     Data_Get_Struct(self, Molecule, mol);
5489         MoleculeCallback_displayName(mol, buf, sizeof buf);
5490         if (buf[0] == 0)
5491                 return Qnil;
5492         else
5493                 return rb_str_new2(buf);
5494 }
5495
5496 /*
5497  *  call-seq:
5498  *     set_name(string) -> self
5499  *
5500  *  Set the name of an untitled molecule. If the molecule is not associated with window
5501  *  or it already has an associated file, then exception is thrown.
5502  */
5503 static VALUE
5504 s_Molecule_SetName(VALUE self, VALUE nval)
5505 {
5506     Molecule *mol;
5507     Data_Get_Struct(self, Molecule, mol);
5508         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5509                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5510         return self;
5511 }
5512
5513
5514 /*
5515  *  call-seq:
5516  *     path       -> String
5517  *
5518  *  Returns the full path name of the molecule, if it is associated with a file.
5519  *  If the molecule has no associated file, then returns nil.
5520  */
5521 static VALUE
5522 s_Molecule_Path(VALUE self)
5523 {
5524     Molecule *mol;
5525         char buf[1024];
5526     Data_Get_Struct(self, Molecule, mol);
5527         MoleculeCallback_pathName(mol, buf, sizeof buf);
5528         if (buf[0] == 0)
5529                 return Qnil;
5530         else
5531                 return Ruby_NewFileStringValue(buf);
5532 }
5533
5534 /*
5535  *  call-seq:
5536  *     dir       -> String
5537  *
5538  *  Returns the full path name of the directory in which the file associated with the
5539  *  molecule is located. If the molecule has no associated file, then returns nil.
5540  */
5541 static VALUE
5542 s_Molecule_Dir(VALUE self)
5543 {
5544     Molecule *mol;
5545         char buf[1024], *p;
5546     Data_Get_Struct(self, Molecule, mol);
5547         MoleculeCallback_pathName(mol, buf, sizeof buf);
5548 #if __WXMSW__
5549         translate_char(buf, '\\', '/');
5550 #endif
5551         if (buf[0] == 0)
5552                 return Qnil;
5553         else {
5554                 p = strrchr(buf, '/');
5555                 if (p != NULL)
5556                         *p = 0;
5557                 return rb_str_new2(buf);
5558         }
5559 }
5560
5561 /*
5562  *  call-seq:
5563  *     inspect       -> String
5564  *
5565  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5566  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5567  *  the Molecule structure) is returned.
5568  */
5569 static VALUE
5570 s_Molecule_Inspect(VALUE self)
5571 {
5572     Molecule *mol;
5573         char buf[256];
5574     Data_Get_Struct(self, Molecule, mol);
5575         MoleculeCallback_displayName(mol, buf, sizeof buf);
5576         if (buf[0] == 0) {
5577                 /*  No associated document  */
5578                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5579                 return rb_str_new2(buf);
5580         } else {
5581                 /*  Check whether the document name is duplicate  */
5582                 char buf2[256];
5583                 int idx, k, k2;
5584                 Molecule *mol2;
5585                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5586                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5587                         if (strcmp(buf, buf2) == 0) {
5588                                 k++;
5589                                 if (mol == mol2)
5590                                         k2 = k;
5591                         }
5592                 }
5593                 if (k > 1) {
5594                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5595                 } else {
5596                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5597                 }
5598                 return rb_str_new2(buf2);
5599         }
5600 }
5601
5602 #pragma mark ------ MolEnumerables ------
5603
5604 static VALUE
5605 s_Molecule_MolEnumerable(VALUE self, int kind)
5606 {
5607     Molecule *mol;
5608         MolEnumerable *mseq;
5609     Data_Get_Struct(self, Molecule, mol);
5610         mseq = MolEnumerableNew(mol, kind);
5611         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5612 }
5613
5614 /*
5615  *  call-seq:
5616  *     atoms       -> MolEnumerable
5617  *
5618  *  Returns a MolEnumerable object representing the array of atoms.
5619  */
5620 static VALUE
5621 s_Molecule_Atoms(VALUE self)
5622 {
5623         return s_Molecule_MolEnumerable(self, kAtomKind);
5624 }
5625
5626 /*
5627  *  call-seq:
5628  *     bonds       -> MolEnumerable
5629  *
5630  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5631  *  by an array of two atom indices.
5632  */
5633 static VALUE
5634 s_Molecule_Bonds(VALUE self)
5635 {
5636         return s_Molecule_MolEnumerable(self, kBondKind);
5637 }
5638
5639 /*
5640  *  call-seq:
5641  *     angles       -> MolEnumerable
5642  *
5643  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5644  *  by an array of three atom indices.
5645  */
5646 static VALUE
5647 s_Molecule_Angles(VALUE self)
5648 {
5649         return s_Molecule_MolEnumerable(self, kAngleKind);
5650 }
5651
5652 /*
5653  *  call-seq:
5654  *     dihedrals       -> MolEnumerable
5655  *
5656  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5657  *  by an array of four atom indices.
5658  */
5659 static VALUE
5660 s_Molecule_Dihedrals(VALUE self)
5661 {
5662         return s_Molecule_MolEnumerable(self, kDihedralKind);
5663 }
5664
5665 /*
5666  *  call-seq:
5667  *     impropers       -> MolEnumerable
5668  *
5669  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5670  *  by an array of four atom indices.
5671  */
5672 static VALUE
5673 s_Molecule_Impropers(VALUE self)
5674 {
5675         return s_Molecule_MolEnumerable(self, kImproperKind);
5676 }
5677
5678 /*
5679  *  call-seq:
5680  *     residues       -> MolEnumerable
5681  *
5682  *  Returns a MolEnumerable object representing the array of residue names.
5683  */
5684 static VALUE
5685 s_Molecule_Residues(VALUE self)
5686 {
5687         return s_Molecule_MolEnumerable(self, kResidueKind);
5688 }
5689
5690 /*
5691  *  call-seq:
5692  *     natoms       -> Integer
5693  *
5694  *  Returns the number of atoms.
5695  */
5696 static VALUE
5697 s_Molecule_Natoms(VALUE self)
5698 {
5699     Molecule *mol;
5700     Data_Get_Struct(self, Molecule, mol);
5701         return INT2NUM(mol->natoms);
5702 }
5703
5704 /*
5705  *  call-seq:
5706  *     nbonds       -> Integer
5707  *
5708  *  Returns the number of bonds.
5709  */
5710 static VALUE
5711 s_Molecule_Nbonds(VALUE self)
5712 {
5713     Molecule *mol;
5714     Data_Get_Struct(self, Molecule, mol);
5715         return INT2NUM(mol->nbonds);
5716 }
5717
5718 /*
5719  *  call-seq:
5720  *     nangles       -> Integer
5721  *
5722  *  Returns the number of angles.
5723  */
5724 static VALUE
5725 s_Molecule_Nangles(VALUE self)
5726 {
5727     Molecule *mol;
5728     Data_Get_Struct(self, Molecule, mol);
5729         return INT2NUM(mol->nangles);
5730 }
5731
5732 /*
5733  *  call-seq:
5734  *     ndihedrals       -> Integer
5735  *
5736  *  Returns the number of dihedrals.
5737  */
5738 static VALUE
5739 s_Molecule_Ndihedrals(VALUE self)
5740 {
5741     Molecule *mol;
5742     Data_Get_Struct(self, Molecule, mol);
5743         return INT2NUM(mol->ndihedrals);
5744 }
5745
5746 /*
5747  *  call-seq:
5748  *     nimpropers       -> Integer
5749  *
5750  *  Returns the number of impropers.
5751  */
5752 static VALUE
5753 s_Molecule_Nimpropers(VALUE self)
5754 {
5755     Molecule *mol;
5756     Data_Get_Struct(self, Molecule, mol);
5757         return INT2NUM(mol->nimpropers);
5758 }
5759
5760 /*
5761  *  call-seq:
5762  *     nresidues       -> Integer
5763  *
5764  *  Returns the number of residues.
5765  */
5766 static VALUE
5767 s_Molecule_Nresidues(VALUE self)
5768 {
5769     Molecule *mol;
5770     Data_Get_Struct(self, Molecule, mol);
5771         return INT2NUM(mol->nresidues);
5772 }
5773
5774 /*
5775  *  call-seq:
5776  *     nresidues = Integer
5777  *
5778  *  Change the number of residues.
5779  */
5780 static VALUE
5781 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5782 {
5783     Molecule *mol;
5784         int ival = NUM2INT(val);
5785     Data_Get_Struct(self, Molecule, mol);
5786         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5787         if (ival != mol->nresidues)
5788                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5789         return val;
5790 }
5791
5792 /*
5793  *  call-seq:
5794  *     max_residue_number(atom_group = nil)     -> Integer
5795  *
5796  *  Returns the maximum residue number actually used. If an atom group is given, only
5797  *  these atoms are examined. If no atom is present, nil is returned.
5798  */
5799 static VALUE
5800 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5801 {
5802     Molecule *mol;
5803         VALUE gval;
5804         int maxSeq;
5805         IntGroup *ig;
5806     Data_Get_Struct(self, Molecule, mol);
5807         rb_scan_args(argc, argv, "01", &gval);
5808         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5809         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5810         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5811 }
5812
5813 /*
5814  *  call-seq:
5815  *     min_residue_number(atom_group = nil)     -> Integer
5816  *
5817  *  Returns the minimum residue number actually used. If an atom group is given, only
5818  *  these atoms are examined. If no atom is present, nil is returned.
5819  */
5820 static VALUE
5821 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5822 {
5823     Molecule *mol;
5824         VALUE gval;
5825         int minSeq;
5826         IntGroup *ig;
5827     Data_Get_Struct(self, Molecule, mol);
5828         rb_scan_args(argc, argv, "01", &gval);
5829         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5830         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5831         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5832 }
5833
5834 /*
5835  *  call-seq:
5836  *     each_atom(atom_group = nil) {|aref| ...}
5837  *
5838  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5839  *  group is given, only these atoms are processed.
5840  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5841  *  is self (a Molecule object).
5842  */
5843 static VALUE
5844 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5845 {
5846         int i;
5847     Molecule *mol;
5848         AtomRef *aref;
5849         VALUE arval;
5850         VALUE gval;
5851         IntGroup *ig;
5852     Data_Get_Struct(self, Molecule, mol);
5853         rb_scan_args(argc, argv, "01", &gval);
5854         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5855         arval = ValueFromMoleculeAndIndex(mol, 0);
5856         Data_Get_Struct(arval, AtomRef, aref);
5857         for (i = 0; i < mol->natoms; i++) {
5858                 aref->idx = i;
5859                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5860                         rb_yield(arval);
5861         }
5862         if (ig != NULL)
5863                 IntGroupRelease(ig);
5864     return self;
5865 }
5866
5867 #pragma mark ------ Atom Group ------
5868
5869 static VALUE
5870 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5871 {
5872         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5873         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5874         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5875         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5876         return Qnil;
5877 }
5878
5879 /*
5880  *  call-seq:
5881  *     atom_group
5882  *     atom_group {|aref| ...}
5883  *     atom_group(arg1, arg2, ...)
5884  *     atom_group(arg1, arg2, ...) {|aref| ...}
5885  *
5886  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5887  *  If arguments are given, then the atoms reprensented by the arguments are added to the
5888  *  group. For a conversion of a string to an atom index, see the description
5889  *  of Molecule#atom_index.
5890  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
5891  *  representing each atom, and the atoms are removed from the result if the block returns false.
5892  *
5893  */
5894 static VALUE
5895 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5896 {
5897         IntGroup *ig1, *ig2;
5898     Molecule *mol;
5899         Int i, startPt, interval;
5900         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5901         Data_Get_Struct(retval, IntGroup, ig1);
5902     Data_Get_Struct(self, Molecule, mol);
5903         if (argc == 0) {
5904                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5905         } else {
5906                 while (argc > 0) {
5907                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5908                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5909                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5910                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5911                                 ig2 = IntGroupFromValue(*argv);
5912                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5913                                         interval = IntGroupGetInterval(ig2, i);
5914                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5915                                 }
5916                                 IntGroupRelease(ig2);
5917                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
5918                                 VALUE values[2];
5919                                 values[0] = (VALUE)mol;
5920                                 values[1] = (VALUE)ig1;
5921                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5922                         } else
5923                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5924                         argc--;
5925                         argv++;
5926                 }
5927         }
5928         if (rb_block_given_p()) {
5929                 /*  Evaluate the given block with an AtomRef as the argument, and delete
5930                         the index if the block returns false  */
5931                 AtomRef *aref = AtomRefNew(mol, 0);
5932                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5933                 ig2 = IntGroupNew();
5934                 IntGroupCopy(ig2, ig1);
5935                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5936                         VALUE resval;
5937                         if (startPt >= mol->natoms)
5938                                 break;
5939                         aref->idx = startPt;
5940                         resval = rb_yield(arval);
5941                         if (!RTEST(resval))
5942                                 IntGroupRemove(ig1, startPt, 1);
5943                 }
5944                 IntGroupRelease(ig2);
5945         }
5946         
5947         /*  Remove points that are out of bounds */
5948         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5949
5950         return retval;                  
5951 }
5952
5953 /*
5954  *  call-seq:
5955  *     selection       -> IntGroup
5956  *
5957  *  Returns the current selection.
5958  */
5959 static VALUE
5960 s_Molecule_Selection(VALUE self)
5961 {
5962     Molecule *mol;
5963         IntGroup *ig;
5964         VALUE val;
5965     Data_Get_Struct(self, Molecule, mol);
5966         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
5967                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
5968                 val = ValueFromIntGroup(ig);
5969                 IntGroupRelease(ig);
5970         } else {
5971                 val = IntGroup_Alloc(rb_cIntGroup);
5972         }
5973         return val;
5974 }
5975
5976 static VALUE
5977 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
5978 {
5979     Molecule *mol;
5980         IntGroup *ig;
5981     Data_Get_Struct(self, Molecule, mol);
5982         if (val == Qnil)
5983                 ig = NULL;
5984         else
5985                 ig = s_Molecule_AtomGroupFromValue(self, val);
5986         if (undoable)
5987                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
5988         else
5989                 MoleculeSetSelection(mol, ig);
5990         if (ig != NULL)
5991                 IntGroupRelease(ig);
5992         return val;
5993 }
5994
5995 /*
5996  *  call-seq:
5997  *     selection = IntGroup
5998  *
5999  *  Set the current selection. The right-hand operand may be nil.
6000  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6001  */
6002 static VALUE
6003 s_Molecule_SetSelection(VALUE self, VALUE val)
6004 {
6005         return s_Molecule_SetSelectionSub(self, val, 0);
6006 }
6007
6008 /*
6009  *  call-seq:
6010  *     set_undoable_selection(IntGroup)
6011  *
6012  *  Set the current selection with undo registration. The right-hand operand may be nil.
6013  *  This operation is undoable.
6014  */
6015 static VALUE
6016 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6017 {
6018         return s_Molecule_SetSelectionSub(self, val, 1);
6019 }
6020
6021 #pragma mark ------ Editing ------
6022
6023 /*
6024  *  call-seq:
6025  *     extract(group, dummy_flag = nil)       -> Molecule
6026  *
6027  *  Extract the atoms given by group and return as a new molecule object.
6028  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6029  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6030  *  names beginning with an underscore) and included in the new molecule object.
6031  */
6032 static VALUE
6033 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6034 {
6035     Molecule *mol1, *mol2;
6036         IntGroup *ig;
6037         VALUE group, dummy_flag, retval;
6038     Data_Get_Struct(self, Molecule, mol1);
6039         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6040         ig = s_Molecule_AtomGroupFromValue(self, group);
6041         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6042                 retval = Qnil;
6043         } else {
6044                 retval = ValueFromMolecule(mol2);
6045         }
6046         IntGroupRelease(ig);
6047         return retval;
6048 }
6049
6050 /*
6051  *  call-seq:
6052  *     add(molecule2)       -> self
6053  *
6054  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6055     conflicts.
6056     This operation is undoable.
6057  */
6058 static VALUE
6059 s_Molecule_Add(VALUE self, VALUE val)
6060 {
6061     Molecule *mol1, *mol2;
6062     Data_Get_Struct(self, Molecule, mol1);
6063         mol2 = MoleculeFromValue(val);
6064         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6065         return self; 
6066 }
6067
6068 /*
6069  *  call-seq:
6070  *     remove(group)       -> Molecule
6071  *
6072  *  The atoms designated by the given group are removed from the molecule.
6073  *  This operation is undoable.
6074  */
6075 static VALUE
6076 s_Molecule_Remove(VALUE self, VALUE group)
6077 {
6078     Molecule *mol1;
6079         IntGroup *ig, *bg;
6080         Int i;
6081         IntGroupIterator iter;
6082
6083     Data_Get_Struct(self, Molecule, mol1);
6084         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6085         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6086                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6087         Data_Get_Struct(group, IntGroup, ig);
6088
6089         /*  Remove the bonds between the two fragments  */
6090         /*  (This is necessary for undo to work correctly)  */
6091         IntGroupIteratorInit(ig, &iter);
6092         bg = NULL;
6093         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6094                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6095                 Int j, *cp;
6096                 cp = AtomConnectData(&ap->connect);
6097                 for (j = 0; j < ap->connect.count; j++) {
6098                         int n = cp[j];
6099                         if (!IntGroupLookup(ig, n, NULL)) {
6100                                 /*  bond i-n, i is in ig and n is not  */
6101                                 int k = MoleculeLookupBond(mol1, i, n);
6102                                 if (k >= 0) {
6103                                         if (bg == NULL)
6104                                                 bg = IntGroupNew();
6105                                         IntGroupAdd(bg, k, 1);
6106                                 }
6107                         }
6108                 }
6109         }
6110         IntGroupIteratorRelease(&iter);
6111         if (bg != NULL) {
6112                 /*  Remove bonds  */
6113                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6114                 IntGroupRelease(bg);
6115         }
6116         /*  Remove atoms  */
6117         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6118                 return Qnil;
6119         return self;
6120 }
6121
6122 /*
6123  *  call-seq:
6124  *     create_atom(name, pos = -1)  -> AtomRef
6125  *
6126  *  Create a new atom with the specified name (may contain residue 
6127  *  information) and position (if position is out of range, the atom is appended at
6128  *  the end). Returns the reference to the new atom.
6129  *  This operation is undoable.
6130  */
6131 static VALUE
6132 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6133 {
6134     Molecule *mol;
6135     Int i, pos;
6136         VALUE name, ival;
6137     Atom arec;
6138     AtomRef *aref;
6139         char *p, resName[6], atomName[6];
6140         int resSeq;
6141     Data_Get_Struct(self, Molecule, mol);
6142         rb_scan_args(argc, argv, "02", &name, &ival);
6143         if (ival != Qnil)
6144                 pos = NUM2INT(rb_Integer(ival));
6145         else pos = -1;
6146         if (name != Qnil) {
6147                 p = StringValuePtr(name);
6148                 if (p[0] != 0) {
6149                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6150                         if (atomName[0] == 0)
6151                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6152                 }
6153         } else p = NULL;
6154         if (p == NULL || p[0] == 0) {
6155                 memset(atomName, 0, 4);
6156                 resSeq = -1;
6157         }
6158     memset(&arec, 0, sizeof(arec));
6159     strncpy(arec.aname, atomName, 4);
6160     if (resSeq >= 0) {
6161       strncpy(arec.resName, resName, 4);
6162       arec.resSeq = resSeq;
6163     }
6164         arec.occupancy = 1.0;
6165 //    i = MoleculeCreateAnAtom(mol, &arec);
6166         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6167                 return Qnil;
6168     aref = AtomRefNew(mol, pos);
6169     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6170 }
6171
6172 /*
6173  *  call-seq:
6174  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6175  *
6176  *  Create a new atom with the same attributes (but no bonding information)
6177  *  with the specified atom. Returns the reference to the new atom.
6178  *  This operation is undoable.
6179  */
6180 static VALUE
6181 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6182 {
6183     Molecule *mol;
6184         const Atom *apsrc;
6185     Atom arec;
6186         AtomRef *aref;
6187         VALUE retval, aval, ival;
6188         Int pos;
6189     Data_Get_Struct(self, Molecule, mol);
6190         rb_scan_args(argc, argv, "11", &aval, &ival);
6191         if (FIXNUM_P(aval)) {
6192                 int idx = NUM2INT(aval);
6193                 if (idx < 0 || idx >= mol->natoms)
6194                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6195                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6196         } else {
6197                 apsrc = s_AtomFromValue(aval);
6198         }
6199         if (apsrc == NULL)
6200                 rb_raise(rb_eMolbyError, "bad atom specification");
6201         if (ival != Qnil)
6202                 pos = NUM2INT(rb_Integer(ival));
6203         else pos = -1;
6204         AtomDuplicate(&arec, apsrc);
6205         arec.connect.count = 0;
6206         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6207                 retval = Qnil;
6208         else {
6209                 aref = AtomRefNew(mol, pos);
6210                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6211         }
6212         AtomClean(&arec);
6213         return retval;
6214 }
6215
6216 /*
6217  *  call-seq:
6218  *     create_bond(n1, n2, ...)       -> Integer
6219  *
6220  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6221  *  do nothing for that pair. Returns the number of bonds actually created.
6222  *  This operation is undoable.
6223  */
6224 static VALUE
6225 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6226 {
6227     Molecule *mol;
6228         Int i, j, k, *ip, old_nbonds;
6229         if (argc == 0)
6230                 rb_raise(rb_eMolbyError, "missing arguments");
6231         if (argc % 2 != 0)
6232                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6233     Data_Get_Struct(self, Molecule, mol);
6234         ip = ALLOC_N(Int, argc + 1);
6235         for (i = j = 0; i < argc; i++, j++) {
6236                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6237                 if (i % 2 == 1) {
6238                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6239                                 j -= 2;  /*  This bond is already present: skip it  */
6240                         else {
6241                                 for (k = 0; k < j - 1; k += 2) {
6242                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6243                                                 j -= 2;   /*  The same entry is already in the argument  */
6244                                                 break;
6245                                         }
6246                                 }
6247                         }
6248                 }
6249         }
6250         old_nbonds = mol->nbonds;
6251         if (j > 0) {
6252                 ip[j] = kInvalidIndex;
6253                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6254         } else i = 0;
6255         xfree(ip);
6256         if (i == -1)
6257                 rb_raise(rb_eMolbyError, "atom index out of range");
6258         else if (i == -2)
6259                 rb_raise(rb_eMolbyError, "too many bonds");
6260         else if (i == -3)
6261                 rb_raise(rb_eMolbyError, "duplicate bonds");
6262         else if (i == -5)
6263                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6264         else if (i != 0)
6265                 rb_raise(rb_eMolbyError, "error in creating bonds");
6266         return INT2NUM(mol->nbonds - old_nbonds);
6267 }
6268
6269 /*
6270  *  call-seq:
6271  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6272  *
6273  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6274  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6275  *  This operation is undoable.
6276  */
6277 static VALUE
6278 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6279 {
6280     Molecule *mol;
6281         Int i, j, n[2];
6282         IntGroup *bg;
6283         if (argc == 0)
6284                 rb_raise(rb_eMolbyError, "missing arguments");
6285         if (argc % 2 != 0)
6286                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6287     Data_Get_Struct(self, Molecule, mol);
6288         bg = NULL;
6289         for (i = j = 0; i < argc; i++, j = 1 - j) {
6290                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6291                 if (j == 1) {
6292                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6293                         if (k >= 0) {
6294                                 if (bg == NULL)
6295                                         bg = IntGroupNew();
6296                                 IntGroupAdd(bg, k, 1);
6297                         }
6298                 }
6299         }
6300         if (bg != NULL) {
6301                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6302                 i = IntGroupGetCount(bg);
6303                 IntGroupRelease(bg);
6304         } else i = 0;
6305         return INT2NUM(i);
6306 }
6307
6308 /*
6309  *  call-seq:
6310  *     assign_bond_order(idx, d1)
6311  *     assign_bond_orders(group, [d1, d2, ...])
6312  *
6313  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6314  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6315  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6316  *  (This may change in the future)
6317  *  This operation is undoable.
6318  */
6319 static VALUE
6320 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6321 {
6322     Molecule *mol;
6323         IntGroup *ig;
6324     Data_Get_Struct(self, Molecule, mol);
6325         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6326                 /*  The first form  */
6327                 Int idx = NUM2INT(rb_Integer(idxval));
6328                 Double d1 = NUM2DBL(rb_Float(dval));
6329                 if (idx < 0 || idx >= mol->nbonds)
6330                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6331                 ig = IntGroupNewWithPoints(idx, 1, -1);
6332                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6333                 IntGroupRelease(ig);
6334         } else {
6335                 Int i, n;
6336                 Double *dp;
6337                 ig = IntGroupFromValue(idxval);
6338                 n = IntGroupGetCount(ig);
6339                 if (n == 0)
6340                         rb_raise(rb_eMolbyError, "the bond index is empty");
6341                 dval = rb_ary_to_ary(dval);
6342                 dp = (Double *)calloc(sizeof(Double), n);
6343                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6344                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6345                 }
6346                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6347                 free(dp);
6348                 IntGroupRelease(ig);
6349         }
6350         return self;
6351 }
6352
6353 /*
6354  *  call-seq:
6355  *     get_bond_order(idx) -> Float
6356  *     get_bond_orders(group) -> Array
6357  *
6358  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6359  *  In the second form, the bond orders at the indices in the group are returned as an array.
6360  *  If no bond order information have been assigned, returns nil (the first form)
6361  *  or an empty array (the second form).
6362  */
6363 static VALUE
6364 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6365 {
6366     Molecule *mol;
6367         IntGroup *ig;
6368         Double *dp;
6369         VALUE retval;
6370         Int i, n, numericArg;
6371     Data_Get_Struct(self, Molecule, mol);
6372         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6373                 /*  The first form  */
6374                 Int idx = NUM2INT(rb_Integer(idxval));
6375                 if (idx < 0 || idx >= mol->nbonds)
6376                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6377                 if (mol->bondOrders == NULL)
6378                         return Qnil;
6379                 ig = IntGroupNewWithPoints(idx, 1, -1);
6380                 n = 1;
6381                 numericArg = 1;
6382         } else {
6383                 if (mol->bondOrders == NULL)
6384                         return rb_ary_new();
6385                 ig = IntGroupFromValue(idxval);
6386                 n = IntGroupGetCount(ig);
6387                 if (n == 0)
6388                         rb_raise(rb_eMolbyError, "the bond index is empty");
6389                 numericArg = 0;
6390         }
6391         dp = (Double *)calloc(sizeof(Double), n);
6392         MoleculeGetBondOrders(mol, dp, ig);
6393         if (numericArg)
6394                 retval = rb_float_new(dp[0]);
6395         else {
6396                 retval = rb_ary_new();
6397                 for (i = 0; i < n; i++)
6398                         rb_ary_push(retval, rb_float_new(dp[i]));
6399         }
6400         free(dp);
6401         IntGroupRelease(ig);
6402         return retval;
6403 }
6404
6405 /*
6406  *  call-seq:
6407  *     bond_exist?(idx1, idx2) -> bool
6408  *
6409  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6410  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6411  */
6412 static VALUE
6413 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6414 {
6415         Molecule *mol;
6416         Int idx1, idx2, i;
6417         Atom *ap;
6418         Int *cp;
6419     Data_Get_Struct(self, Molecule, mol);
6420         idx1 = NUM2INT(rb_Integer(ival1));
6421         idx2 = NUM2INT(rb_Integer(ival2));
6422         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6423                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6424         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6425         cp = AtomConnectData(&ap->connect);
6426         for (i = 0; i < ap->connect.count; i++) {
6427                 if (cp[i] == idx2)
6428                         return Qtrue;
6429         }
6430         return Qfalse;
6431 }
6432
6433 /*
6434  *  call-seq:
6435  *     add_angle(n1, n2, n3)       -> Molecule
6436  *
6437  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6438  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6439  *  This operation is undoable.
6440  */
6441 static VALUE
6442 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6443 {
6444         Int n[4];
6445     Molecule *mol;
6446     Data_Get_Struct(self, Molecule, mol);
6447         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6448         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6449         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6450         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6451                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6452         n[3] = kInvalidIndex;
6453         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6454         return self;
6455 }
6456
6457 /*
6458  *  call-seq:
6459  *     remove_angle(n1, n2, n3)       -> Molecule
6460  *
6461  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6462  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6463  *  This operation is undoable.
6464  */
6465 static VALUE
6466 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6467 {
6468         Int n[4];
6469     Molecule *mol;
6470         IntGroup *ig;
6471     Data_Get_Struct(self, Molecule, mol);
6472         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6473         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6474         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6475         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6476                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6477         ig = IntGroupNewWithPoints(n[3], 1, -1);
6478         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6479         IntGroupRelease(ig);
6480         return self;
6481 }
6482
6483 /*
6484  *  call-seq:
6485  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6486  *
6487  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6488  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6489  *  This operation is undoable.
6490  */
6491 static VALUE
6492 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6493 {
6494         Int n[5];
6495     Molecule *mol;
6496     Data_Get_Struct(self, Molecule, mol);
6497         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6498         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6499         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6500         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6501         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6502                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6503         n[4] = kInvalidIndex;
6504         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6505         return self;
6506 }
6507
6508 /*
6509  *  call-seq:
6510  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6511  *
6512  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6513  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6514  *  This operation is undoable.
6515  */
6516 static VALUE
6517 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6518 {
6519         Int n[5];
6520     Molecule *mol;
6521         IntGroup *ig;
6522     Data_Get_Struct(self, Molecule, mol);
6523         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6524         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6525         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6526         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6527         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6528                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6529         ig = IntGroupNewWithPoints(n[4], 1, -1);
6530         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6531         IntGroupRelease(ig);
6532         return self;
6533 }
6534
6535 /*
6536  *  call-seq:
6537  *     add_improper(n1, n2, n3, n4)       -> Molecule
6538  *
6539  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6540  *  not automatically added when a new bond is created, so this method is more useful than
6541  *  the angle/dihedral counterpart.
6542  *  This operation is undoable.
6543  */
6544 static VALUE
6545 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6546 {
6547         Int n[5];
6548     Molecule *mol;
6549     Data_Get_Struct(self, Molecule, mol);
6550         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6551         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6552         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6553         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6554         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6555                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6556         n[4] = kInvalidIndex;
6557         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6558         return self;
6559 }
6560
6561 /*
6562  *  call-seq:
6563  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6564  *     remove_improper(intgroup)             -> Molecule
6565  *
6566  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6567  *  Returns self. Unlike angles and dihedrals, impropers are
6568  *  not automatically added when a new bond is created, so this method is more useful than
6569  *  the angle/dihedral counterpart.
6570  *  This operation is undoable.
6571  */
6572 static VALUE
6573 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6574 {
6575         Int n[5];
6576         VALUE v1, v2, v3, v4;
6577     Molecule *mol;
6578         IntGroup *ig;
6579     Data_Get_Struct(self, Molecule, mol);
6580         if (argc == 1) {
6581                 ig = IntGroupFromValue(argv[0]);
6582         } else {
6583                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6584                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6585                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6586                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6587                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6588                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6589                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6590                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6591         }
6592         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6593         IntGroupRelease(ig);
6594         return self;
6595 }
6596
6597 /*
6598  *  call-seq:
6599  *     assign_residue(group, res)       -> Molecule
6600  *
6601  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6602  *  or "resname.resno". When the residue number is not specified, the residue number of
6603  *  the first atom in the group is used.
6604  *  This operation is undoable.
6605  */
6606 static VALUE
6607 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6608 {
6609     Molecule *mol;
6610         IntGroup *ig;
6611         char *p, *pp, buf[16];
6612         Int resid, n;
6613         Atom *ap;
6614     Data_Get_Struct(self, Molecule, mol);
6615         
6616         /*  Parse the argument res  */
6617         if (FIXNUM_P(res)) {
6618                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6619                 resid = NUM2INT(res);
6620                 buf[0] = 0;
6621         } else {
6622                 p = StringValuePtr(res);
6623                 pp = strchr(p, '.');
6624                 if (pp != NULL) {
6625                         resid = atoi(pp + 1);
6626                         n = pp - p;
6627                 } else {
6628                         resid = -1;
6629                         n = strlen(p);
6630                 }
6631                 if (n > sizeof buf - 1)
6632                         n = sizeof buf - 1;
6633                 strncpy(buf, p, n);
6634                 buf[n] = 0;
6635         }
6636         ig = s_Molecule_AtomGroupFromValue(self, range);
6637         if (ig == NULL || IntGroupGetCount(ig) == 0)
6638                 return Qnil;
6639
6640         if (resid < 0) {
6641                 /*  Use the residue number of the first specified atom  */
6642                 n = IntGroupGetNthPoint(ig, 0);
6643                 if (n >= mol->natoms)
6644                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6645                 ap = ATOM_AT_INDEX(mol->atoms, n);
6646                 resid = ap->resSeq;
6647         }
6648         /*  Change the residue number  */
6649         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6650         /*  Change the residue name if necessary  */
6651         if (buf[0] != 0) {
6652         /*      Int seqs[2];
6653                 seqs[0] = resid;
6654                 seqs[1] = kInvalidIndex; */
6655                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6656         }
6657         IntGroupRelease(ig);
6658         return self;
6659 }
6660
6661 /*
6662  *  call-seq:
6663  *     offset_residue(group, offset)       -> Molecule
6664  *
6665  *  Offset the residue number of the specified atoms. If any of the residue number gets
6666  *  negative, then exception is thrown.
6667  *  This operation is undoable.
6668  */
6669 static VALUE
6670 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6671 {
6672     Molecule *mol;
6673         IntGroup *ig;
6674         int ofs, result;
6675     Data_Get_Struct(self, Molecule, mol);
6676         ig = s_Molecule_AtomGroupFromValue(self, range);
6677         ofs = NUM2INT(offset);
6678         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6679         if (result > 0)
6680                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6681         IntGroupRelease(ig);
6682         return self;
6683 }
6684
6685 /*
6686  *  call-seq:
6687  *     renumber_atoms(array)       -> IntGroup
6688  *
6689  *  Change the order of atoms so that the atoms specified in the array argument appear
6690  *  in this order from the top of the molecule. The atoms that are not included in array
6691  *  are placed after these atoms, and these atoms are returned as an intGroup.
6692  *  This operation is undoable.
6693  */
6694 static VALUE
6695 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6696 {
6697     Molecule *mol;
6698         Int *new2old;
6699         IntGroup *ig;
6700         int i, n;
6701         VALUE *valp, retval;
6702     Data_Get_Struct(self, Molecule, mol);
6703         if (TYPE(array) != T_ARRAY)
6704                 array = rb_funcall(array, rb_intern("to_a"), 0);
6705         n = RARRAY_LEN(array);
6706         valp = RARRAY_PTR(array);
6707         new2old = ALLOC_N(Int, n + 1);
6708         for (i = 0; i < n; i++)
6709                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6710         new2old[i] = kInvalidIndex;
6711         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6712         if (i == 1)
6713                 rb_raise(rb_eMolbyError, "Atom index out of range");
6714         else if (i == 2)
6715                 rb_raise(rb_eMolbyError, "Duplicate entry");
6716         else if (i == 3)
6717                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6718         retval = IntGroup_Alloc(rb_cIntGroup);
6719         Data_Get_Struct(retval, IntGroup, ig);
6720         if (mol->natoms > n)
6721                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6722         xfree(new2old);
6723         return retval;
6724 }
6725
6726 /*
6727  *  call-seq:
6728  *     set_atom_attr(index, key, value)
6729  *
6730  *  Set the atom attribute for the specified atom.
6731  *  This operation is undoable.
6732  */
6733 static VALUE
6734 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6735 {
6736         Molecule *mol;
6737         VALUE aref, oldval;
6738     Data_Get_Struct(self, Molecule, mol);
6739         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6740         oldval = s_AtomRef_GetAttr(aref, key);
6741         if (val == Qundef)
6742                 return oldval;
6743         s_AtomRef_SetAttr(aref, key, val);
6744         return val;
6745 }
6746
6747 /*
6748  *  call-seq:
6749  *     get_atom_attr(index, key)
6750  *
6751  *  Get the atom attribute for the specified atom.
6752  */
6753 static VALUE
6754 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6755 {
6756         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6757 }
6758
6759 #pragma mark ------ Undo Support ------
6760
6761 /*
6762  *  call-seq:
6763  *     register_undo(script, *args)
6764  *
6765  *  Register an undo operation with the current molecule.
6766  */
6767 static VALUE
6768 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6769 {
6770         Molecule *mol;
6771         VALUE script, args;
6772         MolAction *act;
6773     Data_Get_Struct(self, Molecule, mol);
6774         rb_scan_args(argc, argv, "1*", &script, &args);
6775         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6776         MolActionCallback_registerUndo(mol, act);
6777         return script;
6778 }
6779
6780 /*
6781  *  call-seq:
6782  *     undo_enabled? -> bool
6783  *
6784  *  Returns true if undo is enabled for this molecule; otherwise no.
6785  */
6786 static VALUE
6787 s_Molecule_UndoEnabled(VALUE self)
6788 {
6789     Molecule *mol;
6790     Data_Get_Struct(self, Molecule, mol);
6791         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6792                 return Qtrue;
6793         else return Qfalse;
6794 }
6795
6796 /*
6797  *  call-seq:
6798  *     undo_enabled = bool
6799  *
6800  *  Enable or disable undo.
6801  */
6802 static VALUE
6803 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6804 {
6805     Molecule *mol;
6806     Data_Get_Struct(self, Molecule, mol);
6807         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6808         return val;
6809 }
6810
6811 #pragma mark ------ Measure ------
6812
6813 static void
6814 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6815 {
6816         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6817                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6818                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6819                 case 0: break;
6820                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6821         }
6822 }
6823
6824 /*
6825  *  call-seq:
6826  *     center_of_mass(group = nil)       -> Vector3D
6827  *
6828  *  Calculate the center of mass for the given set of atoms. The argument
6829  *  group is null, then all atoms are considered.
6830  */
6831 static VALUE
6832 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6833 {
6834     Molecule *mol;
6835         VALUE group;
6836         IntGroup *ig;
6837         Vector v;
6838     Data_Get_Struct(self, Molecule, mol);
6839         rb_scan_args(argc, argv, "01", &group);
6840         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6841         s_Molecule_DoCenterOfMass(mol, &v, ig);
6842         if (ig != NULL)
6843                 IntGroupRelease(ig);
6844         return ValueFromVector(&v);
6845 }
6846
6847 /*
6848  *  call-seq:
6849  *     centralize(group = nil)       -> self
6850  *
6851  *  Translate the molecule so that the center of mass of the given group is located
6852  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
6853  */
6854 static VALUE
6855 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
6856 {
6857     Molecule *mol;
6858         VALUE group;
6859         IntGroup *ig;
6860         Vector v;
6861     Data_Get_Struct(self, Molecule, mol);
6862         rb_scan_args(argc, argv, "01", &group);
6863         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6864         s_Molecule_DoCenterOfMass(mol, &v, ig);
6865         if (ig != NULL)
6866                 IntGroupRelease(ig);
6867         v.x = -v.x;
6868         v.y = -v.y;
6869         v.z = -v.z;
6870         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
6871         return self;
6872 }
6873
6874 /*
6875  *  call-seq:
6876  *     bounds(group = nil)       -> [min, max]
6877  *
6878  *  Calculate the boundary. The return value is an array of two Vector3D objects.
6879  */
6880 static VALUE
6881 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
6882 {
6883     Molecule *mol;
6884         VALUE group;
6885         IntGroup *ig;
6886         Vector vmin, vmax;
6887         int n;
6888         Atom *ap;
6889     Data_Get_Struct(self, Molecule, mol);
6890         rb_scan_args(argc, argv, "01", &group);
6891         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6892         if (ig != NULL && IntGroupGetCount(ig) == 0)
6893                 rb_raise(rb_eMolbyError, "atom group is empty");
6894         vmin.x = vmin.y = vmin.z = 1e30;
6895         vmax.x = vmax.y = vmax.z = -1e30;
6896         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
6897                 Vector r;
6898                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
6899                         continue;
6900                 r = ap->r;
6901                 if (r.x < vmin.x)
6902                         vmin.x = r.x;
6903                 if (r.y < vmin.y)
6904                         vmin.y = r.y;
6905                 if (r.z < vmin.z)
6906                         vmin.z = r.z;
6907                 if (r.x > vmax.x)
6908                         vmax.x = r.x;
6909                 if (r.y > vmax.y)
6910                         vmax.y = r.y;
6911                 if (r.z > vmax.z)
6912                         vmax.z = r.z;
6913         }
6914         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
6915 }
6916
6917 /*  Get atom position or a vector  */
6918 static void
6919 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
6920 {
6921         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
6922                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
6923                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
6924         } else {
6925                 VectorFromValue(val, vp);
6926         }
6927 }
6928
6929 /*
6930  *  call-seq:
6931  *     measure_bond(n1, n2)       -> Float
6932  *
6933  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
6934  *  or Vector3D values.
6935  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6936  */
6937 static VALUE
6938 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
6939 {
6940     Molecule *mol;
6941         Vector v1, v2;
6942     Data_Get_Struct(self, Molecule, mol);
6943         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6944         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6945         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
6946 }
6947
6948 /*
6949  *  call-seq:
6950  *     measure_angle(n1, n2, n3)       -> Float
6951  *
6952  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
6953  *  or Vector3D values. The return value is in degree.
6954  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6955  */
6956 static VALUE
6957 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
6958 {
6959     Molecule *mol;
6960         Vector v1, v2, v3;
6961         Double d;
6962     Data_Get_Struct(self, Molecule, mol);
6963         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6964         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6965         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
6966         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
6967         if (isnan(d))
6968                 return Qnil;  /*  Cannot define  */
6969         else return rb_float_new(d);
6970 }
6971
6972 /*
6973  *  call-seq:
6974  *     measure_dihedral(n1, n2, n3, n4)       -> Float
6975  *
6976  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
6977  *  or Vector3D values. The return value is in degree.
6978  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
6979  */
6980 static VALUE
6981 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
6982 {
6983     Molecule *mol;
6984         Vector v1, v2, v3, v4;
6985         Double d;
6986     Data_Get_Struct(self, Molecule, mol);
6987         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
6988         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
6989         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
6990         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
6991         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
6992         if (isnan(d))
6993                 return Qnil;  /*  Cannot define  */
6994         else return rb_float_new(d);
6995 }
6996
6997 /*
6998  *  call-seq:
6999  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7000  *
7001  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7002  *  first and second atom in the pair should belong to group1 and group2, respectively.
7003  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7004  */
7005 static VALUE
7006 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7007 {
7008     Molecule *mol;
7009         VALUE limval, gval1, gval2, rval, igval;
7010         IntGroup *ig1, *ig2;
7011         IntGroupIterator iter1, iter2;
7012         Int npairs, *pairs;
7013         Int n[2], i;
7014         Double lim;
7015         Vector r1;
7016         Atom *ap1, *ap2;
7017         MDExclusion *exinfo;
7018         Int *exlist;
7019         
7020     Data_Get_Struct(self, Molecule, mol);
7021         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7022         lim = NUM2DBL(rb_Float(limval));
7023         if (lim <= 0.0)
7024                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7025         if (gval1 != Qnil)
7026                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7027         else
7028                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7029         if (gval2 != Qnil)
7030                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7031         else
7032                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7033         
7034         if (!RTEST(igval)) {
7035                 /*  Use the exclusion table in MDArena  */
7036                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7037                         VALUE mval = ValueFromMolecule(mol);
7038                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7039                 }
7040                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7041                 exlist = mol->arena->exlist;    
7042         } else {
7043                 exinfo = NULL;
7044                 exlist = NULL;
7045         }
7046         IntGroupIteratorInit(ig1, &iter1);
7047         IntGroupIteratorInit(ig2, &iter2);
7048         npairs = 0;
7049         pairs = NULL;
7050         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7051                 Int exn1, exn2;
7052                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7053                 r1 = ap1->r;
7054                 if (exinfo != NULL) {
7055                         exn1 = exinfo[n[0]].index1;
7056                         exn2 = exinfo[n[0] + 1].index1;
7057                 } else exn1 = exn2 = -1;
7058                 IntGroupIteratorReset(&iter2);
7059                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7060                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7061                         if (n[0] == n[1])
7062                                 continue;  /*  Same atom  */
7063                         if (exinfo != NULL) {
7064                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7065                                 for (i = exn1; i < exn2; i++) {
7066                                         if (exlist[i] == n[1])
7067                                                 break;
7068                                 }
7069                                 if (i < exn2)
7070                                         continue;  /*  Should be excluded  */
7071                         }
7072                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7073                                 /*  Is this pair already registered?  */
7074                                 Int *ip;
7075                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7076                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7077                                                 break;
7078                                 }
7079                                 if (i >= npairs) {
7080                                         /*  Not registered yet  */
7081                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7082                                 }
7083                         }
7084                 }
7085         }
7086         IntGroupIteratorRelease(&iter2);
7087         IntGroupIteratorRelease(&iter1);
7088         IntGroupRelease(ig2);
7089         IntGroupRelease(ig1);
7090         rval = rb_ary_new2(npairs);
7091         if (pairs != NULL) {
7092                 for (i = 0; i < npairs; i++) {
7093                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7094                 }
7095                 free(pairs);
7096         }
7097         return rval;
7098 }
7099
7100 /*
7101  *  call-seq:
7102  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7103  *
7104  *  Find atoms that are within the threshold distance from the given atom.
7105  *  (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.)
7106  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7107  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7108  *  If limit is not given, a default value of 1.2 is used.
7109  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7110  */
7111 static VALUE
7112 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7113 {
7114     Molecule *mol;
7115         VALUE aval, limval, radval;
7116         double limit, radius;
7117         Int n1, nbonds, *bonds, an;
7118         Vector v;
7119     Data_Get_Struct(self, Molecule, mol);
7120         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7121         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)) {
7122                 VectorFromValue(aval, &v);
7123                 if (radval == Qnil)
7124                         radius = gElementParameters[6].radius;
7125                 else
7126                         radius = NUM2DBL(rb_Float(radval));
7127                 n1 = mol->natoms;
7128         } else {
7129                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7130                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7131                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7132                 if (an >= 0 && an < gCountElementParameters)
7133                         radius = gElementParameters[an].radius;
7134                 else radius = gElementParameters[6].radius;
7135         }
7136         if (limval == Qnil)
7137                 limit = 1.2;
7138         else
7139                 limit = NUM2DBL(rb_Float(limval));
7140         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7141         bonds = NULL;
7142         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7143         aval = rb_ary_new();
7144         if (nbonds > 0) {
7145                 for (n1 = 0; n1 < nbonds; n1++)
7146                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7147                 free(bonds);
7148         }
7149         return aval;
7150 }
7151
7152 /*
7153  *  call-seq:
7154  *     guess_bonds(limit = 1.2)       -> Integer
7155  *
7156  *  Create bonds between atoms that are within the threshold distance.
7157  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7158  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7159  *  If limit is not given, a default value of 1.2 is used.
7160  *  The number of the newly created bonds is returned.
7161  *  This operation is undoable.
7162  */
7163 static VALUE
7164 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7165 {
7166     Molecule *mol;
7167         VALUE limval;
7168         double limit;
7169         Int nbonds, *bonds;
7170     Data_Get_Struct(self, Molecule, mol);
7171         rb_scan_args(argc, argv, "01", &limval);
7172         if (limval == Qnil)
7173                 limit = 1.2;
7174         else
7175                 limit = NUM2DBL(rb_Float(limval));
7176         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7177         if (nbonds > 0) {
7178                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7179                 free(bonds);
7180         }
7181         return INT2NUM(nbonds);
7182 }
7183
7184 #pragma mark ------ Cell and Symmetry ------
7185
7186 /*
7187  *  call-seq:
7188  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7189  *
7190  *  Returns the unit cell parameters. If cell is not set, returns nil.
7191  */
7192 static VALUE
7193 s_Molecule_Cell(VALUE self)
7194 {
7195     Molecule *mol;
7196         int i;
7197         VALUE val;
7198     Data_Get_Struct(self, Molecule, mol);
7199         if (mol->cell == NULL)
7200                 return Qnil;
7201         val = rb_ary_new2(6);
7202         for (i = 0; i < 6; i++)
7203                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7204         if (mol->cell->has_sigma) {
7205                 for (i = 0; i < 6; i++) {
7206                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7207                 }
7208         }
7209         return val;
7210 }
7211
7212 /*
7213  *  call-seq:
7214  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7215  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7216  *
7217  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7218  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7219  This operation is undoable.
7220  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7221  */
7222 static VALUE
7223 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7224 {
7225     Molecule *mol;
7226         VALUE val, cval;
7227         int i, convert_coord, n;
7228         double d[12];
7229     Data_Get_Struct(self, Molecule, mol);
7230         rb_scan_args(argc, argv, "11", &val, &cval);
7231         if (val == Qnil) {
7232                 n = 0;
7233         } else {
7234                 int len;
7235                 val = rb_ary_to_ary(val);
7236                 len = RARRAY_LEN(val);
7237                 if (len >= 12) {
7238                         n = 12;
7239                 } else if (len >= 6) {
7240                         n = 6;
7241                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7242                 for (i = 0; i < n; i++)
7243                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7244         }
7245         convert_coord = (RTEST(cval) ? 1 : 0);
7246         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7247         return val;
7248 }
7249
7250 /*
7251  *  call-seq:
7252  *     box -> [avec, bvec, cvec, origin, flags]
7253  *
7254  *  Get the unit cell information in the form of a periodic bounding box.
7255  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7256  *  Integers which define whether the system is periodic along the axis.
7257  *  If no unit cell is defined, nil is returned.
7258  */
7259 static VALUE
7260 s_Molecule_Box(VALUE self)
7261 {
7262     Molecule *mol;
7263         VALUE v[5], val;
7264     Data_Get_Struct(self, Molecule, mol);
7265         if (mol == NULL || mol->cell == NULL)
7266                 return Qnil;
7267         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7268         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7269         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7270         v[3] = ValueFromVector(&(mol->cell->origin));
7271         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7272         val = rb_ary_new4(5, v);
7273         return val;
7274 }
7275
7276 /*
7277  *  call-seq:
7278  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7279  *     set_box(d, origin = [0, 0, 0])
7280  *     set_box
7281  *
7282  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7283  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7284  as the box vector.
7285  Flags, if present, is a 3-member array of Integers defining whether the system is
7286  periodic along the axis.
7287  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7288  In the second form, an isotropic box with cell-length d is set.
7289  In the third form, the existing box is cleared.
7290  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7291  */
7292 static VALUE
7293 s_Molecule_SetBox(VALUE self, VALUE aval)
7294 {
7295     Molecule *mol;
7296         VALUE v[6];
7297         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7298         Vector vv[3];
7299         Vector origin = {0, 0, 0};
7300         char flags[3];
7301         Double d;
7302         int i, convertCoordinates = 0;
7303     Data_Get_Struct(self, Molecule, mol);
7304         if (aval == Qnil) {
7305                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7306                 return self;
7307         }
7308         aval = rb_ary_to_ary(aval);
7309         for (i = 0; i < 6; i++) {
7310                 if (i < RARRAY_LEN(aval))
7311                         v[i] = (RARRAY_PTR(aval))[i];
7312                 else v[i] = Qnil;
7313         }
7314         if (v[0] == Qnil) {
7315                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7316                 return self;
7317         }
7318         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7319                 d = NUM2DBL(rb_Float(v[0]));
7320                 for (i = 0; i < 3; i++)
7321                         VecScale(vv[i], ax[i], d);
7322                 if (v[1] != Qnil)
7323                         VectorFromValue(v[1], &origin);
7324                 flags[0] = flags[1] = flags[2] = 1;
7325         } else {
7326                 for (i = 0; i < 3; i++) {
7327                         if (v[i] == Qnil) {
7328                                 VecZero(vv[i]);
7329                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7330                                 d = NUM2DBL(rb_Float(v[i]));
7331                                 VecScale(vv[i], ax[i], d);
7332                         } else {
7333                                 VectorFromValue(v[i], &vv[i]);
7334                         }
7335                         flags[i] = (VecLength2(vv[i]) > 0.0);
7336                 }
7337                 if (v[3] != Qnil)
7338                         VectorFromValue(v[3], &origin);
7339                 if (v[4] != Qnil) {
7340                         for (i = 0; i < 3; i++) {
7341                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7342                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7343                         }
7344                 }
7345                 if (RTEST(v[5]))
7346                         convertCoordinates = 1;
7347         }
7348         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7349         return self;
7350 }
7351
7352 /*
7353  *  call-seq:
7354  *     cell_periodicity -> [n1, n2, n3]
7355  *
7356  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7357  *  nil is returned.
7358  */
7359 static VALUE
7360 s_Molecule_CellPeriodicity(VALUE self)
7361 {
7362     Molecule *mol;
7363     Data_Get_Struct(self, Molecule, mol);
7364         if (mol->cell == NULL)
7365                 return Qnil;
7366         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7367 }
7368
7369 /*
7370  *  call-seq:
7371  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7372  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7373  *
7374  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7375  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7376  *  If cell is not defined, exception is raised.
7377  *  This operation is undoable.
7378  */
7379 static VALUE
7380 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7381 {
7382     Molecule *mol;
7383         Int flag;
7384     Data_Get_Struct(self, Molecule, mol);
7385         if (mol->cell == NULL)
7386                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7387         if (arg == Qnil)
7388                 flag = 0;
7389         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7390                 flag = NUM2INT(rb_Integer(arg));
7391         else {
7392                 Int i;
7393                 VALUE arg0;
7394                 arg = rb_ary_to_ary(arg);
7395                 flag = 0;
7396                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7397                         arg0 = RARRAY_PTR(arg)[i];
7398                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7399                                 flag |= (1 << (2 - i));
7400                 }
7401         }
7402         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7403         return arg;
7404 }
7405
7406 /*
7407  *  call-seq:
7408  *     cell_flexibility -> bool
7409  *
7410  *  Returns the unit cell is flexible or not
7411  */
7412 static VALUE
7413 s_Molecule_CellFlexibility(VALUE self)
7414 {
7415         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7416         return Qtrue;
7417         /*    Molecule *mol;
7418          Data_Get_Struct(self, Molecule, mol);
7419          if (mol->cell == NULL)
7420          return Qfalse;
7421          if (mol->useFlexibleCell)
7422          return Qtrue;
7423          else return Qfalse; */
7424 }
7425
7426 /*
7427  *  call-seq:
7428  *     self.cell_flexibility = bool
7429  *     set_cell_flexibility(bool)
7430  *
7431  *  Change the unit cell is flexible or not
7432  */
7433 static VALUE
7434 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7435 {
7436         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7437         return self;
7438         /*    Molecule *mol;
7439          Data_Get_Struct(self, Molecule, mol);
7440          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7441          return self; */
7442 }
7443
7444 /*
7445  *  call-seq:
7446  *     cell_transform -> Transform
7447  *
7448  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7449  *  If cell is not defined, nil is returned.
7450  */
7451 static VALUE
7452 s_Molecule_CellTransform(VALUE self)
7453 {
7454     Molecule *mol;
7455     Data_Get_Struct(self, Molecule, mol);
7456         if (mol == NULL || mol->cell == NULL)
7457                 return Qnil;
7458         return ValueFromTransform(&(mol->cell->tr));
7459 }
7460
7461 /*
7462  *  call-seq:
7463  *     symmetry -> Array of Transforms
7464  *     symmetries -> Array of Transforms
7465  *
7466  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7467  *  returns an empty array.
7468  */
7469 static VALUE
7470 s_Molecule_Symmetry(VALUE self)
7471 {
7472     Molecule *mol;
7473         VALUE val;
7474         int i;
7475     Data_Get_Struct(self, Molecule, mol);
7476         if (mol->nsyms <= 0)
7477                 return rb_ary_new();
7478         val = rb_ary_new2(mol->nsyms);
7479         for (i = 0; i < mol->nsyms; i++) {
7480                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7481         }
7482         return val;
7483 }
7484
7485 /*
7486  *  call-seq:
7487  *     nsymmetries -> Integer
7488  *
7489  *  Get the number of currently defined symmetry operations.
7490  */
7491 static VALUE
7492 s_Molecule_Nsymmetries(VALUE self)
7493 {
7494     Molecule *mol;
7495     Data_Get_Struct(self, Molecule, mol);
7496         return INT2NUM(mol->nsyms);
7497 }
7498
7499 /*
7500  *  call-seq:
7501  *     add_symmetry(Transform) -> Integer
7502  *
7503  *  Add a new symmetry operation. If no symmetry operation is defined and the
7504  *  given argument is not an identity transform, then also add an identity
7505  *  transform at the index 0.
7506  *  Returns the total number of symmetries after operation.
7507  */
7508 static VALUE
7509 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7510 {
7511     Molecule *mol;
7512         Transform tr;
7513     Data_Get_Struct(self, Molecule, mol);
7514         TransformFromValue(trans, &tr);
7515         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7516         return INT2NUM(mol->nsyms);
7517 }
7518
7519 /*
7520  *  call-seq:
7521  *     remove_symmetry(count = nil) -> Integer
7522  *     remove_symmetries(count = nil) -> Integer
7523  *
7524  *  Remove the specified number of symmetry operations. The last added ones are removed
7525  *  first. If count is nil, then all symmetry operations are removed. Returns the
7526  *  number of leftover symmetries.
7527  */
7528 static VALUE
7529 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7530 {
7531     Molecule *mol;
7532         VALUE cval;
7533         int i, n;
7534     Data_Get_Struct(self, Molecule, mol);
7535         rb_scan_args(argc, argv, "01", &cval);
7536         if (cval == Qnil)
7537                 n = mol->nsyms - 1;
7538         else {
7539                 n = NUM2INT(rb_Integer(cval));
7540                 if (n < 0 || n > mol->nsyms)
7541                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7542                 if (n == mol->nsyms)
7543                         n = mol->nsyms - 1;
7544         }
7545         for (i = 0; i < n; i++)
7546                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7547         return INT2NUM(mol->nsyms);
7548 }
7549
7550 /*
7551  *  call-seq:
7552  *     wrap_unit_cell(group) -> Vector3D
7553  *
7554  *  Move the specified group so that the center of mass of the group is within the
7555  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7556  *  exception is raised.
7557  */
7558 static VALUE
7559 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7560 {
7561     Molecule *mol;
7562         IntGroup *ig;
7563         Vector v, cv, dv;
7564     Data_Get_Struct(self, Molecule, mol);
7565         if (mol->cell == NULL)
7566                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7567         ig = s_Molecule_AtomGroupFromValue(self, gval);
7568         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7569         TransformVec(&v, mol->cell->rtr, &cv);
7570         if (mol->cell->flags[0])
7571                 v.x -= floor(v.x);
7572         if (mol->cell->flags[1])
7573                 v.y -= floor(v.y);
7574         if (mol->cell->flags[2])
7575                 v.z -= floor(v.z);
7576         TransformVec(&dv, mol->cell->tr, &v);
7577         VecDec(dv, cv);
7578         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7579         IntGroupRelease(ig);
7580         return ValueFromVector(&dv);
7581 }
7582
7583 /*
7584  *  call-seq:
7585  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7586  *
7587  *  Expand the specified part of the molecule by the given symmetry operation.
7588  *  Returns the array of atom indices corresponding to the expanded atoms.
7589  *  If allow_overlap is true, then new atoms are created even when the
7590  *  coordinates coincide with the some other atom (special position) of the
7591  *  same element; otherwise, such atom will not be created and the index of the
7592  *  existing atom is given in the returned array.
7593  */
7594 static VALUE
7595 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7596 {
7597     Molecule *mol;
7598         VALUE gval, sval, xval, yval, zval, rval, oval;
7599         IntGroup *ig;
7600         Int n[4], allow_overlap;
7601         Int natoms;
7602         Int nidx, *idx;
7603         
7604     Data_Get_Struct(self, Molecule, mol);
7605         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7606         n[0] = NUM2INT(rb_Integer(sval));
7607         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7608         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7609         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7610         allow_overlap = (RTEST(oval) ? 1 : 0);
7611         ig = s_Molecule_AtomGroupFromValue(self, gval);
7612         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7613                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7614         natoms = mol->natoms;
7615         
7616         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7617         
7618         rval = rb_ary_new2(nidx);
7619         while (--nidx >= 0) {
7620                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7621         }
7622         /*      if (natoms == mol->natoms)
7623          rval = Qnil;
7624          else {
7625          rval = IntGroup_Alloc(rb_cIntGroup);
7626          Data_Get_Struct(rval, IntGroup, ig);
7627          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7628          } */
7629         return rval;
7630 }
7631
7632 /*
7633  *  call-seq:
7634  *     amend_by_symmetry(group = nil) -> IntGroup
7635  *
7636  *  Expand the specified part of the molecule by the given symmetry operation.
7637  *  Returns an IntGroup containing the added atoms.
7638  */
7639 static VALUE
7640 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7641 {
7642     Molecule *mol;
7643         IntGroup *ig, *ig2;
7644         VALUE rval, gval;
7645     Data_Get_Struct(self, Molecule, mol);
7646         rb_scan_args(argc, argv, "01", &gval);
7647         if (gval != Qnil)
7648                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7649         else ig = NULL;
7650         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7651         rval = ValueFromIntGroup(ig2);
7652         IntGroupRelease(ig2);
7653         return rval;
7654 }
7655
7656 #pragma mark ------ Transforms ------
7657
7658 /*
7659  *  call-seq:
7660  *     translate(vec, group = nil)       -> Molecule
7661  *
7662  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7663  *  This operation is undoable.
7664  */
7665 static VALUE
7666 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7667 {
7668     Molecule *mol;
7669         VALUE vec, group;
7670         Vector v;
7671         IntGroup *ig;
7672     Data_Get_Struct(self, Molecule, mol);
7673         rb_scan_args(argc, argv, "11", &vec, &group);
7674         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7675         VectorFromValue(vec, &v);
7676         //      MoleculeTranslate(mol, &v, ig);
7677         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7678         if (ig != NULL)
7679                 IntGroupRelease(ig);
7680         return self;
7681 }
7682
7683 /*
7684  *  call-seq:
7685  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7686  *
7687  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7688  *  If group is given, only atoms in the group are moved.
7689  *  This operation is undoable.
7690  */
7691 static VALUE
7692 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7693 {
7694     Molecule *mol;
7695         volatile VALUE aval, anval, cval, gval;
7696         Double angle;
7697         Vector av, cv;
7698         Transform tr;
7699         IntGroup *ig;
7700     Data_Get_Struct(self, Molecule, mol);
7701         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7702         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7703         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7704         VectorFromValue(aval, &av);
7705         if (NIL_P(cval))
7706                 cv.x = cv.y = cv.z = 0.0;
7707         else
7708                 VectorFromValue(cval, &cv);
7709         if (TransformForRotation(tr, &av, angle, &cv))
7710                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7711         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7712         if (ig != NULL)
7713                 IntGroupRelease(ig);
7714         return self;
7715 }
7716
7717 /*
7718  *  call-seq:
7719  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7720  *
7721  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7722  *  axis must not be a zero vector.
7723  *  If group is given, only atoms in the group are moved.
7724  *  This operation is undoable.
7725  */
7726 static VALUE
7727 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7728 {
7729     Molecule *mol;
7730         volatile VALUE aval, cval, gval;
7731         Vector av, cv;
7732         Transform tr;
7733         IntGroup *ig;
7734     Data_Get_Struct(self, Molecule, mol);
7735         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7736         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7737         VectorFromValue(aval, &av);
7738         if (NIL_P(cval))
7739                 cv.x = cv.y = cv.z = 0.0;
7740         else
7741                 VectorFromValue(cval, &cv);
7742         if (TransformForReflection(tr, &av, &cv))
7743                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7744         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7745         if (ig != NULL)
7746                 IntGroupRelease(ig);
7747         return self;
7748 }
7749
7750 /*
7751  *  call-seq:
7752  *     invert(center = [0,0,0], group = nil)       -> Molecule
7753  *
7754  *  Invert the molecule with the given center.
7755  *  If group is given, only atoms in the group are moved.
7756  *  This operation is undoable.
7757  */
7758 static VALUE
7759 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7760 {
7761         Molecule *mol;
7762         volatile VALUE cval, gval;
7763         Vector cv;
7764         Transform tr;
7765         IntGroup *ig;
7766     Data_Get_Struct(self, Molecule, mol);
7767         rb_scan_args(argc, argv, "02", &cval, &gval);
7768         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7769         if (NIL_P(cval))
7770                 cv.x = cv.y = cv.z = 0.0;
7771         else
7772                 VectorFromValue(cval, &cv);
7773         TransformForInversion(tr, &cv);
7774         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7775         if (ig != NULL)
7776                 IntGroupRelease(ig);
7777         return self;
7778 }
7779
7780 /*
7781  *  call-seq:
7782  *     transform(transform, group = nil)       -> Molecule
7783  *
7784  *  Transform the molecule by the given Transform object.
7785  *  If group is given, only atoms in the group are moved.
7786  *  This operation is undoable.
7787  */
7788 static VALUE
7789 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7790 {
7791     Molecule *mol;
7792         VALUE trans, group;
7793         Transform tr;
7794         IntGroup *ig;
7795     Data_Get_Struct(self, Molecule, mol);
7796         rb_scan_args(argc, argv, "11", &trans, &group);
7797         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7798         TransformFromValue(trans, &tr);
7799         /*      MoleculeTransform(mol, tr, ig); */
7800         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7801         if (ig != NULL)
7802                 IntGroupRelease(ig);
7803         return self;
7804 }
7805
7806 /*
7807  *  call-seq:
7808  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7809  *
7810  *  Get the transform corresponding to the symmetry operation. The symop can either be
7811  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7812  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7813  *  Otherwise, the returned transform is for fractional coordinates.
7814  *  Raises exception when no cell or no transform are defined.
7815  */
7816 static VALUE
7817 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7818 {
7819     Molecule *mol;
7820         VALUE sval, fval;
7821         Symop symop;
7822         Transform tr;
7823     Data_Get_Struct(self, Molecule, mol);
7824         if (mol->cell == NULL)
7825                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7826         if (mol->nsyms == 0)
7827                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7828         rb_scan_args(argc, argv, "11", &sval, &fval);
7829         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7830                 symop.sym = NUM2INT(rb_Integer(sval));
7831                 symop.dx = symop.dy = symop.dz = 0;
7832         } else {
7833                 sval = rb_ary_to_ary(sval);
7834                 if (RARRAY_LEN(sval) < 4)
7835                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7836                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7837                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7838                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7839                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7840         }
7841         if (symop.sym >= mol->nsyms)
7842                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7843         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7844         return ValueFromTransform(&tr);
7845 }
7846
7847 /*
7848  *  call-seq:
7849  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7850  *
7851  *  Get the symmetry operation corresponding to the given transform.
7852  *  If is_cartesian is true, the given transform is for cartesian coordinates.
7853  *  Otherwise, the given transform is for fractional coordinates.
7854  *  Raises exception when no cell or no transform are defined.
7855  */
7856 static VALUE
7857 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7858 {
7859     Molecule *mol;
7860         VALUE tval, fval;
7861         Symop symop;
7862         Transform tr;
7863         int n;
7864     Data_Get_Struct(self, Molecule, mol);
7865         if (mol->cell == NULL)
7866                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7867         if (mol->nsyms == 0)
7868                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7869         rb_scan_args(argc, argv, "11", &tval, &fval);
7870         TransformFromValue(tval, &tr);
7871         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7872         if (n == 0) {
7873                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7874         } else {
7875                 return Qnil;  /*  Not found  */
7876         }
7877 }
7878
7879 #pragma mark ------ Frames ------
7880
7881 /*
7882  *  call-seq:
7883  *     select_frame(index)
7884  *     frame = index
7885  *
7886  *  Select the specified frame. If successful, returns true, otherwise returns false.
7887  */
7888 static VALUE
7889 s_Molecule_SelectFrame(VALUE self, VALUE val)
7890 {
7891     Molecule *mol;
7892         int ival = NUM2INT(val);
7893     Data_Get_Struct(self, Molecule, mol);
7894         ival = MoleculeSelectFrame(mol, ival, 1);
7895         if (ival >= 0)
7896                 return Qtrue;
7897         else return Qfalse;
7898 }
7899
7900 /*
7901  *  call-seq:
7902  *     frame -> Integer
7903  *
7904  *  Get the current frame.
7905  */
7906 static VALUE
7907 s_Molecule_Frame(VALUE self)
7908 {
7909     Molecule *mol;
7910     Data_Get_Struct(self, Molecule, mol);
7911         return INT2NUM(mol->cframe);
7912 }
7913
7914 /*
7915  *  call-seq:
7916  *     nframes -> Integer
7917  *
7918  *  Get the number of frames.
7919  */
7920 static VALUE
7921 s_Molecule_Nframes(VALUE self)
7922 {
7923     Molecule *mol;
7924     Data_Get_Struct(self, Molecule, mol);
7925         return INT2NUM(MoleculeGetNumberOfFrames(mol));
7926 }
7927
7928 /*
7929  *  call-seq:
7930  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7931  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7932  *
7933  *  Insert new frames at the indices specified by the intGroup. If the first argument is
7934  *  an integer, a single new frame is inserted at that index. If the first argument is 
7935  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7936  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
7937  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
7938  *  to the new frame.
7939  *  Returns an intGroup representing the inserted frames if successful, nil if not.
7940  */
7941 static VALUE
7942 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7943 {
7944         VALUE val, coords, cells;
7945     Molecule *mol;
7946         IntGroup *ig;
7947         int count, ival, i, j, len, len_c, len2, nframes;
7948         VALUE *ptr, *ptr2;
7949         Vector *vp, *vp2;
7950     Data_Get_Struct(self, Molecule, mol);
7951         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7952         if (coords != Qnil) {
7953                 if (TYPE(coords) != T_ARRAY)
7954                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7955                 len = RARRAY_LEN(coords);
7956         } else len = 0;
7957         if (cells != Qnil) {
7958                 if (mol->cell == NULL)
7959                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7960                 if (TYPE(cells) != T_ARRAY)
7961                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7962                 len_c = RARRAY_LEN(cells);
7963         } else len_c = 0;
7964         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
7965         nframes = MoleculeGetNumberOfFrames(mol);
7966         if (val == Qnil) {
7967                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7968                 val = ValueFromIntGroup(ig);
7969         } else {
7970                 ig = IntGroupFromValue(val);
7971         }
7972         count = IntGroupGetCount(ig);  /*  Count is updated here  */
7973         vp = ALLOC_N(Vector, mol->natoms * count);
7974         if (cells != Qnil)
7975                 vp2 = ALLOC_N(Vector, 4 * count);
7976         else vp2 = NULL;
7977         if (len > 0) {
7978                 if (len < count)
7979                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7980                 ptr = RARRAY_PTR(coords);
7981                 for (i = 0; i < count; i++) {
7982                         if (TYPE(ptr[i]) != T_ARRAY)
7983                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7984                         len2 = RARRAY_LEN(ptr[i]);
7985                         if (len2 < mol->natoms)
7986                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7987                         ptr2 = RARRAY_PTR(ptr[i]);
7988                         for (j = 0; j < mol->natoms; j++)
7989                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7990                 }
7991         } else {
7992                 Atom *ap;
7993                 for (i = 0; i < count; i++) {
7994                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7995                                 vp[i * mol->natoms + j] = ap->r;
7996                         }
7997                 }
7998         }
7999         if (len_c > 0) {
8000                 if (len_c < count)
8001                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8002                 ptr = RARRAY_PTR(cells);
8003                 for (i = 0; i < count; i++) {
8004                         if (TYPE(ptr[i]) != T_ARRAY)
8005                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8006                         len2 = RARRAY_LEN(ptr[i]);
8007                         if (len2 < 4)
8008                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8009                         ptr2 = RARRAY_PTR(ptr[i]);
8010                         for (j = 0; j < 4; j++)
8011                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8012                 }
8013         }
8014         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8015         IntGroupRelease(ig);
8016         xfree(vp);
8017         if (vp2 != NULL)
8018                 xfree(vp2);
8019         return (ival >= 0 ? val : Qnil);
8020 }
8021
8022 /*
8023  *  call-seq:
8024  *     create_frame(coordinates = nil) -> Integer
8025  *     create_frames(coordinates = nil) -> Integer
8026  *
8027  *  Same as molecule.insert_frames(nil, coordinates).
8028  */
8029 static VALUE
8030 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8031 {
8032         VALUE vals[3];
8033         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8034         vals[0] = Qnil;
8035         return s_Molecule_InsertFrames(3, vals, self);
8036 }
8037
8038 /*
8039  *  call-seq:
8040  *     remove_frames(IntGroup, wantCoordinates = false)
8041  *
8042  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8043  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8044  *  removed frames is returned if operation is successful.
8045  */
8046 static VALUE
8047 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8048 {
8049         VALUE val, flag;
8050         VALUE retval;
8051     Molecule *mol;
8052         IntGroup *ig;
8053         int count;
8054     Data_Get_Struct(self, Molecule, mol);
8055         rb_scan_args(argc, argv, "11", &val, &flag);
8056         ig = IntGroupFromValue(val);
8057         count = IntGroupGetCount(ig);
8058         if (RTEST(flag)) {
8059                 /*  Create return value before removing frames  */
8060                 VALUE coords;
8061                 int i, j, n;
8062                 Atom *ap;
8063                 Vector v;
8064                 retval = rb_ary_new2(count);
8065                 for (i = 0; i < count; i++) {
8066                         n = IntGroupGetNthPoint(ig, i);
8067                         coords = rb_ary_new2(mol->natoms);
8068                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8069                                 if (n < ap->nframes && n != mol->cframe)
8070                                         v = ap->frames[n];
8071                                 else v = ap->r;
8072                                 rb_ary_push(coords, ValueFromVector(&v));
8073                         }
8074                         rb_ary_push(retval, coords);
8075                 }
8076         } else retval = Qtrue;
8077         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8078                 return retval;
8079         else return Qnil;
8080 }
8081
8082 /*
8083  *  call-seq:
8084  *     each_frame {|n| ...}
8085  *
8086  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8087  *  the frame number. After completion, the original frame number is restored.
8088  */
8089 static VALUE
8090 s_Molecule_EachFrame(VALUE self)
8091 {
8092         int i, cframe, nframes;
8093     Molecule *mol;
8094     Data_Get_Struct(self, Molecule, mol);
8095         cframe = mol->cframe;
8096         nframes = MoleculeGetNumberOfFrames(mol);
8097         if (nframes > 0) {
8098                 for (i = 0; i < nframes; i++) {
8099                         MoleculeSelectFrame(mol, i, 1);
8100                         rb_yield(INT2NUM(i));
8101                 }
8102                 MoleculeSelectFrame(mol, cframe, 1);
8103         }
8104     return self;
8105 }
8106
8107 /*
8108  *  call-seq:
8109  *     get_coord_from_frame(index, group = nil)
8110  *
8111  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8112  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8113  *  copied; now they are always copied)
8114  */
8115 static VALUE
8116 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8117 {
8118         Molecule *mol;
8119         VALUE ival, gval, cval;
8120         Int index, i, j, n, nn;
8121         IntGroup *ig;
8122         IntGroupIterator iter;
8123         Atom *ap;
8124         Vector *vp;
8125     Data_Get_Struct(self, Molecule, mol);
8126         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8127         if (argc == 3)
8128                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8129         index = NUM2INT(rb_Integer(ival));
8130         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8131                 if (n == 0)
8132                         rb_raise(rb_eMolbyError, "No frame is present");
8133                 else
8134                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8135         }
8136         if (gval == Qnil) {
8137                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8138         } else {
8139                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8140         }
8141         n = IntGroupGetCount(ig);
8142         if (n > 0) {
8143                 vp = (Vector *)calloc(sizeof(Vector), n);
8144                 IntGroupIteratorInit(ig, &iter);
8145                 j = 0;
8146                 nn = 0;
8147                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8148                         ap = ATOM_AT_INDEX(mol->atoms, i);
8149                         if (index < ap->nframes) {
8150                                 vp[j] = ap->frames[index];
8151                                 nn++;
8152                         } else {
8153                                 vp[j] = ap->r;
8154                         }
8155                         j++;
8156                 }
8157                 if (nn > 0)
8158                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8159                 free(vp);
8160                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8161                         vp = mol->frame_cells + index * 4;
8162                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8163                 }
8164                 IntGroupIteratorRelease(&iter);
8165         }
8166         /*  Copy the extra properties  */
8167         IntGroupRelease(ig);
8168         for (i = 0; i < mol->nmolprops; i++) {
8169                 Double *dp = (Double *)malloc(sizeof(Double));
8170                 ig = IntGroupNew();
8171                 IntGroupAdd(ig, mol->cframe, 1);
8172                 *dp = mol->molprops[i].propvals[index];
8173                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8174                 free(dp);
8175                 IntGroupRelease(ig);
8176         }
8177         
8178         return self;
8179 }
8180
8181 /*
8182  *  call-seq:
8183  *     reorder_frames(old_indices)
8184  *
8185  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8186  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8187  *  same as the old frames 2/0/1, respectively.
8188  *  The argument must have the same number of integers as the number of frames.
8189  */
8190 static VALUE
8191 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8192 {
8193         Molecule *mol;
8194         Int *ip, *ip2, i, n, nframes;
8195     Data_Get_Struct(self, Molecule, mol);
8196         aval = rb_ary_to_ary(aval);
8197         nframes = MoleculeGetNumberOfFrames(mol);
8198         if (RARRAY_LEN(aval) != nframes)
8199                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8200         ip2 = (Int *)calloc(sizeof(Int), nframes);
8201         ip = (Int *)calloc(sizeof(Int), nframes);
8202         for (i = 0; i < nframes; i++) {
8203                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8204                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8205                         free(ip2);
8206                         free(ip);
8207                         if (n < 0 || n >= nframes)
8208                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8209                         else
8210                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8211                 }
8212                 ip2[n] = 1;
8213                 ip[i] = n;
8214         }
8215         free(ip2);
8216         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8217         free(ip);
8218         return self;
8219 }
8220
8221 #pragma mark ------ Fragments ------
8222
8223 /*
8224  *  call-seq:
8225  *     fragment(n1, *exatoms)  -> IntGroup
8226  *     fragment(group, *exatoms)  -> IntGroup
8227  *
8228  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8229  *  those atoms will not be counted during the search.
8230  */
8231 static VALUE
8232 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8233 {
8234     Molecule *mol;
8235         IntGroup *baseg, *ig, *exatoms;
8236         int n;
8237         volatile VALUE nval, exval;
8238     Data_Get_Struct(self, Molecule, mol);
8239         rb_scan_args(argc, argv, "1*", &nval, &exval);
8240         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8241                 baseg = NULL;
8242                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8243         } else {
8244                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8245         }
8246         if (RARRAY_LEN(exval) == 0) {
8247                 exatoms = NULL;
8248         } else {
8249                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8250                 Data_Get_Struct(exval, IntGroup, exatoms);
8251         }
8252         if (baseg == NULL) {
8253                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8254         } else {
8255                 IntGroupIterator iter;
8256                 IntGroupIteratorInit(baseg, &iter);
8257                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8258                         ig = IntGroupNew();
8259                 } else {
8260                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8261                         if (ig != NULL) {
8262                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8263                                         IntGroup *subg;
8264                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8265                                         if (subg != NULL) {
8266                                                 IntGroupAddIntGroup(ig, subg);
8267                                                 IntGroupRelease(subg);
8268                                         }
8269                                 }
8270                         }
8271                 }
8272                 IntGroupIteratorRelease(&iter);
8273                 IntGroupRelease(baseg);
8274         }
8275         if (ig == NULL)
8276                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8277         nval = ValueFromIntGroup(ig);
8278         IntGroupRelease(ig);
8279         return nval;
8280 }
8281
8282 /*
8283  *  call-seq:
8284  *     fragments(exclude = nil)
8285  *
8286  *  Returns the fragments as an array of IntGroups. 
8287  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8288  *  in defining the fragment.
8289  */
8290 static VALUE
8291 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8292 {
8293     Molecule *mol;
8294         IntGroup *ag, *fg, *eg;
8295         VALUE gval, exval, retval;
8296     Data_Get_Struct(self, Molecule, mol);
8297         if (mol == NULL)
8298                 return Qnil;
8299         if (mol->natoms == 0)
8300                 return rb_ary_new();
8301         rb_scan_args(argc, argv, "01", &exval);
8302         if (exval == Qnil)
8303                 eg = NULL;
8304         else
8305                 eg = IntGroupFromValue(exval);
8306         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8307         if (eg != NULL)
8308                 IntGroupRemoveIntGroup(ag, eg);
8309         retval = rb_ary_new();
8310         while (IntGroupGetCount(ag) > 0) {
8311                 int n = IntGroupGetNthPoint(ag, 0);
8312                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8313                 if (fg == NULL)
8314                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8315                 gval = ValueFromIntGroup(fg);
8316                 rb_ary_push(retval, gval);
8317                 IntGroupRemoveIntGroup(ag, fg);
8318                 IntGroupRelease(fg);
8319         }
8320         IntGroupRelease(ag);
8321         if (eg != NULL)
8322                 IntGroupRelease(eg);
8323         return retval;
8324 }
8325
8326 /*
8327  *  call-seq:
8328  *     each_fragment(exclude = nil) {|group| ...}
8329  *
8330  *  Execute the block, with the IntGroup object for each fragment as the argument.
8331  *  Atoms or bonds should not be added or removed during the execution of the block.
8332  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8333  *  in defining the fragment.
8334  */
8335 static VALUE
8336 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8337 {
8338     Molecule *mol;
8339         IntGroup *ag, *fg, *eg;
8340         VALUE gval, exval;
8341     Data_Get_Struct(self, Molecule, mol);
8342         if (mol == NULL || mol->natoms == 0)
8343                 return self;
8344         rb_scan_args(argc, argv, "01", &exval);
8345         if (exval == Qnil)
8346                 eg = NULL;
8347         else
8348                 eg = IntGroupFromValue(exval);
8349         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8350         if (eg != NULL)
8351                 IntGroupRemoveIntGroup(ag, eg);
8352         while (IntGroupGetCount(ag) > 0) {
8353                 int n = IntGroupGetNthPoint(ag, 0);
8354                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8355                 if (fg == NULL)
8356                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8357                 gval = ValueFromIntGroup(fg);
8358                 rb_yield(gval);
8359                 IntGroupRemoveIntGroup(ag, fg);
8360                 IntGroupRelease(fg);
8361         }
8362         IntGroupRelease(ag);
8363         if (eg != NULL)
8364                 IntGroupRelease(eg);
8365         return self;
8366 }
8367
8368 /*
8369  *  call-seq:
8370  *     detachable?(group)  -> [n1, n2]
8371  *
8372  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8373  *  of the molecule via only one bond. If it is, then the indices of the atoms
8374  *  belonging to the bond is returned, the first element being the atom included
8375  *  in the fragment. Otherwise, Qnil is returned.
8376  */
8377 static VALUE
8378 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8379 {
8380         Molecule *mol;
8381         IntGroup *ig;
8382         int n1, n2;
8383         VALUE retval;
8384     Data_Get_Struct(self, Molecule, mol);
8385         ig = s_Molecule_AtomGroupFromValue(self, gval);
8386         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8387                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8388         } else retval = Qnil;
8389         IntGroupRelease(ig);
8390         return retval;
8391 }
8392
8393 /*
8394  *  call-seq:
8395  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8396  *
8397  *  Returns an array of bonds that connect an atom in the group and an atom out
8398  *  of the group. The first atom in the bond always belongs to the group. If no
8399  *  such bonds are present, an empty array is returned.
8400  */
8401 static VALUE
8402 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8403 {
8404         Molecule *mol;
8405         IntGroup *ig, *bg;
8406         VALUE gval, retval;
8407     Data_Get_Struct(self, Molecule, mol);
8408         rb_scan_args(argc, argv, "01", &gval);
8409         if (gval == Qnil) {
8410                 ig = MoleculeGetSelection(mol);
8411                 if (ig != NULL)
8412                         IntGroupRetain(ig);
8413         } else {
8414                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8415         }
8416         retval = rb_ary_new();
8417         if (ig == NULL)
8418                 return retval;
8419         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8420         if (bg != NULL) {
8421                 IntGroupIterator iter;
8422                 Int i;
8423                 IntGroupIteratorInit(bg, &iter);
8424                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8425                         /*  The atoms at the border  */
8426                         Int n1, n2;
8427                         n1 = mol->bonds[i * 2];
8428                         n2 = mol->bonds[i * 2 + 1];
8429                         if (IntGroupLookupPoint(ig, n1) < 0) {
8430                                 int w = n1;
8431                                 n1 = n2;
8432                                 n2 = w;
8433                                 if (IntGroupLookupPoint(ig, n1) < 0)
8434                                         continue;  /*  Actually this is an internal error  */
8435                         }
8436                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8437                 }
8438                 IntGroupIteratorRelease(&iter);
8439         }
8440         IntGroupRelease(bg);
8441         IntGroupRelease(ig);
8442         return retval;
8443 }
8444
8445 /*  Calculate the transform that moves the current coordinates to the reference
8446  coordinates with least displacements.   */
8447 static Double
8448 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8449 {
8450         Atom *ap, *ap1;
8451         Int natoms, nn;
8452         Vector org1, org2;
8453         Int i, in, j, k;
8454         Double w, w1;
8455         Mat33 r, q, u;
8456         Double eigen_val[3];
8457         Vector eigen_vec[3];
8458         Vector s[3];
8459         IntGroupIterator iter;
8460
8461         natoms = mol->natoms;
8462         ap = mol->atoms;
8463         IntGroupIteratorInit(ig, &iter);
8464         
8465         /*  Calculate the weighted center  */
8466         VecZero(org1);
8467         VecZero(org2);
8468         w = 0.0;
8469         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8470                 ap1 = ATOM_AT_INDEX(ap, in);
8471                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8472                 VecScaleInc(org1, ap1->r, w1);
8473                 VecScaleInc(org2, ref[i], w1);
8474                 w += w1;
8475         }
8476         w = 1.0 / w;
8477         VecScaleSelf(org1, w);
8478         VecScaleSelf(org2, w);
8479
8480     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8481     /*  Matrix to diagonalize = R * tR    */
8482         memset(r, 0, sizeof(Mat33));
8483         memset(q, 0, sizeof(Mat33));
8484         memset(u, 0, sizeof(Mat33));
8485         nn = 0;
8486         IntGroupIteratorReset(&iter);
8487         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8488                 Vector v1, v2;
8489                 ap1 = ATOM_AT_INDEX(ap, in);
8490                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8491                 w1 *= w1;
8492                 VecSub(v1, ap1->r, org1);
8493                 VecSub(v2, ref[i], org2);
8494                 r[0] += w1 * v1.x * v2.x;
8495                 r[1] += w1 * v1.y * v2.x;
8496                 r[2] += w1 * v1.z * v2.x;
8497                 r[3] += w1 * v1.x * v2.y;
8498                 r[4] += w1 * v1.y * v2.y;
8499                 r[5] += w1 * v1.z * v2.y;
8500                 r[6] += w1 * v1.x * v2.z;
8501                 r[7] += w1 * v1.y * v2.z;
8502                 r[8] += w1 * v1.z * v2.z;
8503                 nn++;
8504         }
8505         for (i = 0; i < 9; i++)
8506                 r[i] /= (nn * nn);
8507         for (i = 0; i < 3; i++) {
8508                 for (j = 0; j < 3; j++) {
8509                         for (k = 0; k < 3; k++) {
8510                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8511                         }
8512                 }
8513         }
8514         
8515         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8516                 IntGroupIteratorRelease(&iter);
8517                 return -1.0;  /*  Cannot determine the eigenvector  */
8518         }
8519
8520     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8521     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8522         MatrixTranspose(r, r);
8523         for (i = 0; i < 3; i++) {
8524                 MatrixVec(&s[i], r, &eigen_vec[i]);
8525                 w1 = 1.0 / sqrt(eigen_val[i]);
8526                 VecScaleSelf(s[i], w1);
8527         }
8528         for (k = 0; k < 3; k++) {
8529                 u[0] += s[k].x * eigen_vec[k].x;
8530                 u[1] += s[k].y * eigen_vec[k].x;
8531                 u[2] += s[k].z * eigen_vec[k].x;
8532                 u[3] += s[k].x * eigen_vec[k].y;
8533                 u[4] += s[k].y * eigen_vec[k].y;
8534                 u[5] += s[k].z * eigen_vec[k].y;
8535                 u[6] += s[k].x * eigen_vec[k].z;
8536                 u[7] += s[k].y * eigen_vec[k].z;
8537                 u[8] += s[k].z * eigen_vec[k].z;
8538         }
8539         
8540         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8541         MatrixVec(&org1, u, &org1);
8542         VecDec(org2, org1);
8543         for (i = 0; i < 9; i++)
8544                 trans[i] = u[i];
8545         trans[9] = org2.x;
8546         trans[10] = org2.y;
8547         trans[11] = org2.z;
8548         
8549         /*  Calculate rmsd  */
8550         IntGroupIteratorReset(&iter);
8551         w = 0.0;
8552         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8553                 Vector tv;
8554                 ap1 = ATOM_AT_INDEX(ap, in);
8555                 TransformVec(&tv, trans, &ap1->r);
8556                 VecDec(tv, ref[i]);
8557                 w += VecLength2(tv);
8558         }
8559         w = sqrt(w / nn);
8560         IntGroupIteratorRelease(&iter);
8561         return w;
8562 }
8563
8564 /*
8565  *  call-seq:
8566  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8567  *
8568  *  Calculate the transform to fit the given group to the set of reference coordinates.
8569  *  The reference coordinates ref is given as either a frame number, an array of
8570  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8571  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8572  *  Return values are the transform (that converts the present coordinates to the
8573  *  target coordinates) and root mean square deviation (without weight).
8574  */
8575 static VALUE
8576 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8577 {
8578         Molecule *mol;
8579         Atom *ap;
8580         VALUE gval, rval, wval;
8581         IntGroup *ig;
8582         IntGroupIterator iter;
8583         int nn, errno, i, j, in, status;
8584         Vector *ref;
8585         Double *weights, dval[3];
8586         Transform tr;
8587
8588         Data_Get_Struct(self, Molecule, mol);
8589         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8590         if (gval == Qnil)
8591                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8592         else
8593                 ig = IntGroupFromValue(gval);
8594         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8595                 IntGroupRelease(ig);
8596                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8597         }
8598         ref = (Vector *)calloc(sizeof(Vector), nn);
8599         weights = (Double *)calloc(sizeof(Double), nn);
8600         IntGroupIteratorInit(ig, &iter);
8601         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8602                 int fn = NUM2INT(rb_Integer(rval));
8603                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8604                         errno = 1;
8605                         status = fn;
8606                         goto err;
8607                 }
8608                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8609                         ap = ATOM_AT_INDEX(mol->atoms, in);
8610                         if (fn < ap->nframes)
8611                                 ref[i] = ap->frames[fn];
8612                         else ref[i] = ap->r;
8613                 }
8614         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8615                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8616                 if (m->row * m->column < nn * 3) {
8617                         errno = 2;
8618                         goto err;
8619                 }
8620                 for (i = 0; i < nn; i++) {
8621                         ref[i].x = m->data[i * 3];
8622                         ref[i].y = m->data[i * 3 + 1];
8623                         ref[i].z = m->data[i * 3 + 2];
8624                 }
8625         } else {
8626                 VALUE aval;
8627                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8628                 if (status != 0) {
8629                         errno = 3;
8630                         goto err;
8631                 }
8632                 if (RARRAY_LEN(rval) < nn) {
8633                         errno = 2;
8634                         goto err;
8635                 }
8636                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8637                         /*  Array of 3*nn numbers  */
8638                         if (RARRAY_LEN(rval) < nn * 3) {
8639                                 errno = 2;
8640                                 goto err;
8641                         }
8642                         for (i = 0; i < nn; i++) {
8643                                 for (j = 0; j < 3; j++) {
8644                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8645                                         if (status != 0) {
8646                                                 errno = 3;
8647                                                 goto err;
8648                                         }
8649                                         dval[j] = NUM2DBL(aval);
8650                                 }
8651                                 ref[i].x = dval[0];
8652                                 ref[i].y = dval[1];
8653                                 ref[i].z = dval[2];
8654                         }
8655                 } else {
8656                         /*  Array of nn Vector3Ds or Arrays  */
8657                         for (i = 0; i < nn; i++) {
8658                                 aval = (RARRAY_PTR(rval))[i];
8659                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8660                                         VectorFromValue(aval, &ref[i]);
8661                                 } else {
8662                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8663                                         if (status != 0) {
8664                                                 errno = 3;
8665                                                 goto err;
8666                                         }
8667                                         if (RARRAY_LEN(aval) < 3) {
8668                                                 errno = 4;
8669                                                 status = i;
8670                                                 goto err;
8671                                         }
8672                                         for (j = 0; j < 3; j++) {
8673                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8674                                                 if (status != 0) {
8675                                                         errno = 3;
8676                                                         goto err;
8677                                                 }
8678                                                 dval[j] = NUM2DBL(aaval);
8679                                         }
8680                                         ref[i].x = dval[0];
8681                                         ref[i].y = dval[1];
8682                                         ref[i].z = dval[2];
8683                                 }
8684                         }
8685                 }
8686         }
8687         if (wval == Qnil) {
8688                 /*  Use atomic weights  */
8689                 IntGroupIteratorReset(&iter);
8690                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8691                         ap = ATOM_AT_INDEX(mol->atoms, in);
8692                         weights[i] = ap->weight;
8693                 }
8694         } else {
8695                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8696                 if (status != 0) {
8697                         errno = 3;
8698                         goto err;
8699                 }
8700                 if (RARRAY_LEN(wval) < nn) {
8701                         errno = 5;
8702                         goto err;
8703                 }
8704                 for (i = 0; i < nn; i++) {
8705                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8706                         if (status != 0) {
8707                                 errno = 3;
8708                                 goto err;
8709                         }
8710                         weights[i] = NUM2DBL(wwval);
8711                 }
8712         }
8713         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8714         if (dval[0] < 0) {
8715                 errno = 6;
8716                 goto err;
8717         }
8718         errno = 0;
8719 err:
8720         IntGroupIteratorRelease(&iter);
8721         free(ref);
8722         free(weights);
8723         if (errno == 0) {
8724                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8725         } else if (errno == 1) {
8726                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8727         } else if (errno == 2) {
8728                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8729         } else if (errno == 3) {
8730                 rb_jump_tag(status);
8731         } else if (errno == 4) {
8732                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8733         } else if (errno == 5) {
8734                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8735         } else if (errno == 6) {
8736                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8737         }
8738         return Qnil;  /*  Not reached  */
8739 }
8740
8741 #pragma mark ------ Screen Display ------
8742
8743 /*
8744  *  call-seq:
8745  *     display
8746  *
8747  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8748  */
8749 static VALUE
8750 s_Molecule_Display(VALUE self)
8751 {
8752     Molecule *mol;
8753     Data_Get_Struct(self, Molecule, mol);
8754         if (mol->mview != NULL)
8755                 MainViewCallback_display(mol->mview);
8756         return Qnil;
8757 }
8758
8759 /*
8760  *  call-seq:
8761  *     make_front
8762  *
8763  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8764  */
8765 static VALUE
8766 s_Molecule_MakeFront(VALUE self)
8767 {
8768     Molecule *mol;
8769     Data_Get_Struct(self, Molecule, mol);
8770         if (mol->mview != NULL)
8771                 MainViewCallback_makeFront(mol->mview);
8772         return Qnil;
8773 }
8774
8775 /*
8776  *  call-seq:
8777  *     update_enabled? -> bool
8778  *
8779  *  Returns true if screen update is enabled; otherwise no.
8780  */
8781 static VALUE
8782 s_Molecule_UpdateEnabled(VALUE self)
8783 {
8784     Molecule *mol;
8785     Data_Get_Struct(self, Molecule, mol);
8786         if (mol->mview != NULL && !mol->mview->freezeScreen)
8787                 return Qtrue;
8788         else return Qfalse;
8789 }
8790
8791 /*
8792  *  call-seq:
8793  *     update_enabled = bool
8794  *
8795  *  Enable or disable screen update. This is effective for automatic update on modification.
8796  *  Explicit call to molecule.display() always updates the screen.
8797  */
8798 static VALUE
8799 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8800 {
8801     Molecule *mol;
8802     Data_Get_Struct(self, Molecule, mol);
8803         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8804         if (mol->mview != NULL)
8805                 mol->mview->freezeScreen = (val == Qfalse);
8806         else val = Qfalse;
8807         return val;
8808 }
8809
8810 /*
8811  *  call-seq:
8812  *     show_unitcell
8813  *     show_unitcell(bool)
8814  *     show_unitcell = bool
8815  *
8816  *  Set the flag whether to show the unit cell. If no argument is given, the
8817  *  current flag is returned.
8818  */
8819 static VALUE
8820 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8821 {
8822     Molecule *mol;
8823     Data_Get_Struct(self, Molecule, mol);
8824         if (mol->mview == NULL)
8825                 return Qnil;
8826         if (argc > 0) {
8827                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8828                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8829         }
8830         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8831 }
8832
8833 /*
8834  *  call-seq:
8835  *     show_hydrogens
8836  *     show_hydrogens(bool)
8837  *     show_hydrogens = bool
8838  *
8839  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8840  *  current flag is returned.
8841  */
8842 static VALUE
8843 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8844 {
8845     Molecule *mol;
8846     Data_Get_Struct(self, Molecule, mol);
8847         if (mol->mview == NULL)
8848                 return Qnil;
8849         if (argc > 0) {
8850                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8851                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8852         }
8853         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8854 }
8855
8856 /*
8857  *  call-seq:
8858  *     show_dummy_atoms
8859  *     show_dummy_atoms(bool)
8860  *     show_dummy_atoms = bool
8861  *
8862  *  Set the flag whether to show the dummy atoms. If no argument is given, the
8863  *  current flag is returned.
8864  */
8865 static VALUE
8866 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8867 {
8868     Molecule *mol;
8869     Data_Get_Struct(self, Molecule, mol);
8870         if (mol->mview == NULL)
8871                 return Qnil;
8872         if (argc > 0) {
8873                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8874                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8875         }
8876         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8877 }
8878
8879 /*
8880  *  call-seq:
8881  *     show_expanded
8882  *     show_expanded(bool)
8883  *     show_expanded = bool
8884  *
8885  *  Set the flag whether to show the expanded atoms. If no argument is given, the
8886  *  current flag is returned.
8887  */
8888 static VALUE
8889 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8890 {
8891     Molecule *mol;
8892     Data_Get_Struct(self, Molecule, mol);
8893         if (mol->mview == NULL)
8894                 return Qnil;
8895         if (argc > 0) {
8896                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8897                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8898         }
8899         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8900 }
8901
8902 /*
8903  *  call-seq:
8904  *     show_ellipsoids
8905  *     show_ellipsoids(bool)
8906  *     show_ellipsoids = bool
8907  *
8908  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8909  *  current flag is returned.
8910  */
8911 static VALUE
8912 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8913 {
8914     Molecule *mol;
8915     Data_Get_Struct(self, Molecule, mol);
8916         if (mol->mview == NULL)
8917                 return Qnil;
8918         if (argc > 0) {
8919                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8920                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8921         }
8922         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8923 }
8924
8925 /*
8926  *  call-seq:
8927  *     is_atom_visible(index)  -> Boolean
8928  *
8929  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8930  *  as well as the molecule attributes (showHydrogens, etc.)
8931  */
8932 static VALUE
8933 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8934 {
8935         Molecule *mol;
8936         Int idx;
8937         Atom *ap;
8938     Data_Get_Struct(self, Molecule, mol);
8939         idx = s_Molecule_AtomIndexFromValue(mol, ival);
8940         if (idx < 0 || idx >= mol->natoms)
8941                 return Qnil;
8942         ap = ATOM_AT_INDEX(mol->atoms, idx);
8943         if (mol->mview != NULL) {
8944                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8945                         return Qfalse;
8946                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8947                         return Qfalse;
8948                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8949                         return Qfalse;
8950         }
8951         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8952 }
8953
8954 /*
8955  *  call-seq:
8956  *     hidden_atoms       -> IntGroup
8957  *
8958  *  Returns the currently hidden atoms.
8959  */
8960 static VALUE
8961 s_Molecule_HiddenAtoms(VALUE self)
8962 {
8963         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8964         return Qnil;  /*  Not reached  */
8965 }
8966
8967 /*
8968  *  call-seq:
8969  *     set_hidden_atoms(IntGroup)
8970  *     self.hidden_atoms = IntGroup
8971  *
8972  *  Hide the specified atoms. This operation is _not_ undoable.
8973  */
8974 static VALUE
8975 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
8976 {
8977         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
8978         return Qnil;  /*  Not reached  */
8979 }
8980
8981 /*
8982  *  call-seq:
8983  *     show_graphite -> Integer
8984  *     show_graphite = Integer
8985  *     show_graphite = boolean
8986  *
8987  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
8988  *  number of rings to display for each direction.
8989  *  If the argument is boolean, only the show/hide flag is set.
8990  */
8991 static VALUE
8992 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
8993 {
8994     Molecule *mol;
8995     Data_Get_Struct(self, Molecule, mol);
8996         if (mol->mview == NULL)
8997                 return Qnil;
8998         if (argc > 0) {
8999                 if (argv[0] == Qnil || argv[0] == Qfalse)
9000                         mol->mview->showGraphiteFlag = 0;
9001                 else if (argv[0] == Qtrue)
9002                         mol->mview->showGraphiteFlag = 1;
9003                 else {
9004                         int n = NUM2INT(rb_Integer(argv[0]));
9005                         if (n < 0)
9006                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9007                         mol->mview->showGraphite = n;
9008                 }
9009                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9010         }
9011         return INT2NUM(mol->mview->showGraphite);
9012 }
9013
9014 /*
9015  *  call-seq:
9016  *     show_graphite? -> boolean
9017  *
9018  *  Return whether the graphite is set visible or not.
9019 */
9020 static VALUE
9021 s_Molecule_ShowGraphiteFlag(VALUE self)
9022 {
9023     Molecule *mol;
9024     Data_Get_Struct(self, Molecule, mol);
9025         if (mol->mview == NULL)
9026                 return Qnil;
9027         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9028 }
9029         
9030 /*
9031  *  call-seq:
9032  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9033  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9034  *     show_periodic_image = boolean
9035  *
9036  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9037  *  set but no visual effects are observed.
9038  *  If the argument is boolean, only the show/hide flag is modified.
9039  */
9040 static VALUE
9041 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9042 {
9043     Molecule *mol;
9044         VALUE val;
9045         int ival[6];
9046         int i;
9047     Data_Get_Struct(self, Molecule, mol);
9048         if (mol->mview == NULL)
9049                 return Qnil;
9050         rb_scan_args(argc, argv, "01", &val);
9051         if (argc > 0) {
9052                 /*  Change current settings  */
9053                 if (val == Qnil || val == Qfalse)
9054                         mol->mview->showPeriodicImageFlag = 0;
9055                 else if (val == Qtrue)
9056                         mol->mview->showPeriodicImageFlag = 1;
9057                 else {
9058                         val = rb_ary_to_ary(val);
9059                         for (i = 0; i < 6; i++) {
9060                                 if (i < RARRAY_LEN(val))
9061                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9062                         }
9063                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9064                                 rb_raise(rb_eMolbyError, "bad arguments");
9065                         for (i = 0; i < 6; i++)
9066                                 mol->mview->showPeriodicImage[i] = ival[i];
9067                 }
9068                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9069         }
9070         val = rb_ary_new();
9071         for (i = 0; i < 6; i++)
9072                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9073         return val;
9074 }
9075
9076 /*
9077  *  call-seq:
9078  *     show_periodic_image? -> boolean
9079  *
9080  *  Return whether the periodic images are set to visible or not. This flag is
9081  *  independent from the show_periodic_image settings.
9082  */
9083 static VALUE
9084 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9085 {
9086     Molecule *mol;
9087     Data_Get_Struct(self, Molecule, mol);
9088         if (mol->mview == NULL)
9089                 return Qnil;
9090         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9091 }
9092
9093 /*
9094  *  call-seq:
9095  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9096  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9097  *     show_rotation_center = boolean
9098  *
9099  *  Set to show the rotation center of the screen.
9100  *  If the argument is boolean, only the show/hide flag is modified.
9101  */
9102 static VALUE
9103 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9104 {
9105     Molecule *mol;
9106     Data_Get_Struct(self, Molecule, mol);
9107         if (mol->mview == NULL)
9108                 return Qnil;
9109         if (argc > 0) {
9110                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9111                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9112         }
9113         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9114 }
9115
9116 /*
9117  *  call-seq:
9118  *     line_mode
9119  *     line_mode(bool)
9120  *     line_mode = bool
9121  *
9122  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9123  *  current flag is returned.
9124  */
9125 static VALUE
9126 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9127 {
9128     Molecule *mol;
9129     Data_Get_Struct(self, Molecule, mol);
9130         if (mol->mview == NULL)
9131                 return Qnil;
9132         if (argc > 0) {
9133                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9134                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9135         }
9136         return (mol->mview->lineMode ? Qtrue : Qfalse);
9137 }
9138
9139 /*
9140  *  call-seq:
9141  *     atom_radius = float
9142  *     atom_radius
9143  *
9144  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9145  *  (Default = 0.4)
9146  *  If no argument is given, the current value is returned.
9147  */
9148 static VALUE
9149 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9150 {
9151     Molecule *mol;
9152     Data_Get_Struct(self, Molecule, mol);
9153         if (mol->mview == NULL)
9154                 return Qnil;
9155         if (argc > 0) {
9156                 double rad = NUM2DBL(rb_Float(argv[0]));
9157                 if (rad > 0.0) {
9158                         mol->mview->atomRadius = rad;
9159                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9160                 }
9161                 return argv[0];
9162         }
9163         return rb_float_new(mol->mview->atomRadius);
9164 }
9165
9166 /*
9167  *  call-seq:
9168  *     bond_radius = float
9169  *     bond_radius
9170  *
9171  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9172  *  If no argument is given, the current value is returned.
9173  */
9174 static VALUE
9175 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9176 {
9177     Molecule *mol;
9178     Data_Get_Struct(self, Molecule, mol);
9179         if (mol->mview == NULL)
9180                 return Qnil;
9181         if (argc > 0) {
9182                 double rad = NUM2DBL(rb_Float(argv[0]));
9183                 if (rad > 0.0) {
9184                         mol->mview->bondRadius = rad;
9185                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9186                 }
9187                 return argv[0];
9188         }
9189         return rb_float_new(mol->mview->bondRadius);
9190 }
9191
9192 /*
9193  *  call-seq:
9194  *     atom_resolution = integer
9195  *     atom_resolution
9196  *
9197  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9198  *  (Default = 12; minimum = 6)
9199  *  If no argument is given, the current value is returned.
9200  */
9201 static VALUE
9202 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9203 {
9204     Molecule *mol;
9205     Data_Get_Struct(self, Molecule, mol);
9206         if (mol->mview == NULL)
9207                 return Qnil;
9208         if (argc > 0) {
9209                 int res = NUM2INT(rb_Integer(argv[0]));
9210                 if (res < 6)
9211                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9212                 mol->mview->atomResolution = res;
9213                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9214                 return INT2NUM(res);
9215         }
9216         return INT2NUM(mol->mview->atomResolution);
9217 }
9218
9219 /*
9220  *  call-seq:
9221  *     bond_resolution = integer
9222  *     bond_resolution
9223  *
9224  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9225  *  (Default = 8; minimum = 4)
9226  *  If no argument is given, the current value is returned.
9227  */
9228 static VALUE
9229 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9230 {
9231     Molecule *mol;
9232     Data_Get_Struct(self, Molecule, mol);
9233         if (mol->mview == NULL)
9234                 return Qnil;
9235         if (argc > 0) {
9236                 int res = NUM2INT(rb_Integer(argv[0]));
9237                 if (res < 4)
9238                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9239                 mol->mview->bondResolution = res;
9240                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9241                 return INT2NUM(res);
9242         }
9243         return INT2NUM(mol->mview->bondResolution);
9244 }
9245
9246 /*
9247  *  call-seq:
9248  *     resize_to_fit
9249  *
9250  *  Resize the model drawing to fit in the window.
9251  */
9252 static VALUE
9253 s_Molecule_ResizeToFit(VALUE self)
9254 {
9255     Molecule *mol;
9256     Data_Get_Struct(self, Molecule, mol);
9257         if (mol->mview != NULL)
9258                 MainView_resizeToFit(mol->mview);
9259         return self;    
9260 }
9261
9262 /*
9263  *  call-seq:
9264  *     get_view_rotation -> [[ax, ay, az], angle]
9265  *
9266  *  Get the current rotation for the view. Angle is in degree, not radian.
9267  */
9268 static VALUE
9269 s_Molecule_GetViewRotation(VALUE self)
9270 {
9271     Molecule *mol;
9272         double f[4];
9273         Vector v;
9274     Data_Get_Struct(self, Molecule, mol);
9275         if (mol->mview == NULL)
9276                 return Qnil;
9277         TrackballGetRotate(mol->mview->track, f);
9278         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9279         v.x = f[1];
9280         v.y = f[2];
9281         v.z = f[3];
9282         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9283 }
9284
9285 /*
9286  *  call-seq:
9287  *     get_view_scale -> float
9288  *
9289  *  Get the current scale for the view.
9290  */
9291 static VALUE
9292 s_Molecule_GetViewScale(VALUE self)
9293 {
9294     Molecule *mol;
9295     Data_Get_Struct(self, Molecule, mol);
9296         if (mol->mview == NULL)
9297                 return Qnil;
9298         return rb_float_new(TrackballGetScale(mol->mview->track));
9299 }
9300
9301 /*
9302  *  call-seq:
9303  *     get_view_center -> Vector
9304  *
9305  *  Get the current center point of the view.
9306  */
9307 static VALUE
9308 s_Molecule_GetViewCenter(VALUE self)
9309 {
9310     Molecule *mol;
9311         double f[4];
9312         Vector v;
9313     Data_Get_Struct(self, Molecule, mol);
9314         if (mol->mview == NULL)
9315                 return Qnil;
9316         TrackballGetTranslate(mol->mview->track, f);
9317         v.x = -f[0] * mol->mview->dimension;
9318         v.y = -f[1] * mol->mview->dimension;
9319         v.z = -f[2] * mol->mview->dimension;
9320         return ValueFromVector(&v);
9321 }
9322
9323 /*
9324  *  call-seq:
9325  *     set_view_rotation([ax, ay, az], angle) -> self
9326  *
9327  *  Set the current rotation for the view. Angle is in degree, not radian.
9328  */
9329 static VALUE
9330 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9331 {
9332     Molecule *mol;
9333         double f[4];
9334         Vector v;
9335     Data_Get_Struct(self, Molecule, mol);
9336         if (mol->mview == NULL)
9337                 return Qnil;
9338         VectorFromValue(aval, &v);
9339         if (NormalizeVec(&v, &v))
9340                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9341         f[1] = v.x;
9342         f[2] = v.y;
9343         f[3] = v.z;
9344         f[0] = -NUM2DBL(rb_Float(angval));
9345         TrackballSetRotate(mol->mview->track, f);
9346         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9347         return self;
9348 }
9349
9350 /*
9351  *  call-seq:
9352  *     set_view_scale(scale) -> self
9353  *
9354  *  Set the current scale for the view.
9355  */
9356 static VALUE
9357 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9358 {
9359     Molecule *mol;
9360     Data_Get_Struct(self, Molecule, mol);
9361         if (mol->mview == NULL)
9362                 return Qnil;
9363         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9364         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9365         return self;
9366 }
9367
9368 /*
9369  *  call-seq:
9370  *     set_view_center(vec) -> self
9371  *
9372  *  Set the current center point of the view.
9373  */
9374 static VALUE
9375 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9376 {
9377     Molecule *mol;
9378         Vector v;
9379         double f[4];
9380     Data_Get_Struct(self, Molecule, mol);
9381         if (mol->mview == NULL)
9382                 return Qnil;
9383         VectorFromValue(aval, &v);
9384         f[0] = -v.x / mol->mview->dimension;
9385         f[1] = -v.y / mol->mview->dimension;
9386         f[2] = -v.z / mol->mview->dimension;
9387         TrackballSetTranslate(mol->mview->track, f);
9388         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9389         return self;
9390 }
9391
9392 /*
9393  *  call-seq:
9394  *     set_background_color(red, green, blue)
9395  *
9396  *  Set the background color of the model window.
9397  */
9398 static VALUE
9399 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9400 {
9401     Molecule *mol;
9402     Data_Get_Struct(self, Molecule, mol);
9403         if (mol->mview != NULL) {
9404                 VALUE rval, gval, bval;
9405                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9406                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9407         }
9408         return self;    
9409 }
9410
9411 /*
9412  *  call-seq:
9413  *     export_graphic(fname, scale = 1.0, bg_color = -1)
9414  *
9415  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9416  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9417  *  
9418  */
9419 static VALUE
9420 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9421 {
9422         Molecule *mol;
9423         VALUE fval, sval, bval;
9424         char *fname;
9425         float scale;
9426         int bg_color;
9427     Data_Get_Struct(self, Molecule, mol);
9428         if (mol->mview == NULL)
9429                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9430         rb_scan_args(argc, argv, "12", &fval, &sval, &bval);
9431         fname = FileStringValuePtr(fval);
9432         if (sval == Qnil)
9433                 scale = 1.0;
9434         else scale = NUM2DBL(rb_Float(sval));
9435         if (bval == Qnil)
9436                 bg_color = -1;
9437         else bg_color = NUM2INT(rb_Integer(bval));
9438         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color) == 0)
9439                 return fval;
9440         else return Qnil;
9441 }
9442
9443 #pragma mark ------ Graphics ------
9444
9445 static void
9446 s_CalculateGraphicNormals(MainViewGraphic *gp)
9447 {
9448         int i;
9449         Vector v1, v2, v3;
9450         if (gp == NULL || gp->npoints < 3)
9451                 return;
9452         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9453         v1.x = gp->points[3] - gp->points[0];
9454         v1.y = gp->points[4] - gp->points[1];
9455         v1.z = gp->points[5] - gp->points[2];
9456         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9457         for (i = 2; i < gp->npoints; i++) {
9458                 v2.x = gp->points[i * 3] - gp->points[0];
9459                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9460                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9461                 VecCross(v3, v1, v2);
9462                 NormalizeVec(&v3, &v3);
9463                 gp->normals[i * 3] = v3.x;
9464                 gp->normals[i * 3 + 1] = v3.y;
9465                 gp->normals[i * 3 + 2] = v3.z;
9466                 v1 = v2;
9467         }
9468         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9469         VecZero(v1);
9470         for (i = 2; i < gp->npoints; i++) {
9471                 v1.x += gp->normals[i * 3];
9472                 v1.y += gp->normals[i * 3 + 1];
9473                 v1.z += gp->normals[i * 3 + 2];
9474         }
9475         NormalizeVec(&v1, &v1);
9476         gp->normals[0] = v1.x;
9477         gp->normals[1] = v1.y;
9478         gp->normals[2] = v1.z;
9479         /*  normals[1] = nv[2].normalize  */
9480         v2.x = gp->normals[6];
9481         v2.y = gp->normals[7];
9482         v2.z = gp->normals[8];
9483         NormalizeVec(&v1, &v2);
9484         gp->normals[3] = v1.x;
9485         gp->normals[4] = v1.y;
9486         gp->normals[5] = v1.z;
9487         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9488         for (i = 2; i < gp->npoints; i++) {
9489                 if (i == gp->npoints - 1)
9490                         VecZero(v3);
9491                 else {
9492                         v3.x = gp->normals[i * 3 + 3];
9493                         v3.y = gp->normals[i * 3 + 4];
9494                         v3.z = gp->normals[i * 3 + 5];
9495                 }
9496                 VecInc(v2, v3);
9497                 NormalizeVec(&v1, &v2);
9498                 gp->normals[i * 3] = v1.x;
9499                 gp->normals[i * 3 + 1] = v1.y;
9500                 gp->normals[i * 3 + 2] = v1.z;
9501                 v2 = v3;
9502         }
9503 }
9504
9505 /*
9506  *  call-seq:
9507  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9508  *
9509  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9510  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9511  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9512  *   points: an array of Vectors
9513  *   
9514  */
9515 static VALUE
9516 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9517 {
9518     Molecule *mol;
9519         MainViewGraphic g;
9520         int i, n, ni, idx;
9521         const char *p;
9522         VALUE kval, cval, pval, fval, ival;
9523     Data_Get_Struct(self, Molecule, mol);
9524         if (mol->mview == NULL)
9525                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9526         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9527         idx = NUM2INT(rb_Integer(ival));
9528         if (idx == -1)
9529                 idx = mol->mview->ngraphics;
9530         else if (idx < 0 || idx > mol->mview->ngraphics)
9531                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9532         memset(&g, 0, sizeof(g));
9533         g.visible = 1;
9534         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9535                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9536         } else {
9537                 kval = rb_obj_as_string(kval);
9538                 p = StringValuePtr(kval);
9539                 if (strcmp(p, "line") == 0)
9540                         g.kind = kMainViewGraphicLine;
9541                 else if (strcmp(p, "poly") == 0)
9542                         g.kind = kMainViewGraphicPoly;
9543                 else if (strcmp(p, "cylinder") == 0)
9544                         g.kind = kMainViewGraphicCylinder;
9545                 else if (strcmp(p, "cone") == 0)
9546                         g.kind = kMainViewGraphicCone;
9547                 else if (strcmp(p, "ellipsoid") == 0)
9548                         g.kind = kMainViewGraphicEllipsoid;
9549                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9550         }
9551         g.closed = (RTEST(fval) ? 1 : 0);
9552         cval = rb_ary_to_ary(cval);
9553         n = RARRAY_LEN(cval);
9554         if (n < 3 || n >= 5)
9555                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9556         if (n == 3)
9557                 g.rgba[3] = 1.0;
9558         for (i = 0; i < n; i++)
9559                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9560         pval = rb_ary_to_ary(pval);
9561         n = RARRAY_LEN(pval);
9562         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9563         if (n <= 0)
9564                 rb_raise(rb_eArgError, "no control points are given");
9565         switch (g.kind) {
9566                 case kMainViewGraphicLine:
9567                         if (n < 2)
9568                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9569                         break;
9570                 case kMainViewGraphicPoly:
9571                         if (n < 3)
9572                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9573                         break;
9574                 case kMainViewGraphicCylinder:
9575                 case kMainViewGraphicCone:
9576                         if (n != 3)
9577                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9578                         ni = 2;
9579                         break;
9580                 case kMainViewGraphicEllipsoid:
9581                         if (n == 2) {
9582                                 ni = 1;
9583                         } else if (n != 4)
9584                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9585                         break;
9586         }
9587         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9588         for (i = 0; i < n; i++) {
9589                 Vector v;
9590                 VALUE rval = RARRAY_PTR(pval)[i];
9591                 if (i == ni) {
9592                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9593                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9594                                 VectorFromValue(rval, &v);
9595                         } else {
9596                                 v.x = NUM2DBL(rb_Float(rval));
9597                         }
9598                         v.y = v.z = 0;
9599                 } else {
9600                         VectorFromValue(rval, &v);
9601                 }
9602                 g.points[i * 3] = v.x;
9603                 g.points[i * 3 + 1] = v.y;
9604                 g.points[i * 3 + 2] = v.z;
9605         }
9606         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9607                 /*  Sphere  */
9608                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9609                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9610                 g.points[7] = g.points[11] = g.points[3];
9611         }
9612         if (g.kind == kMainViewGraphicPoly) {
9613                 /*  Calculate normals  */
9614                 s_CalculateGraphicNormals(&g);
9615         }
9616         MainView_insertGraphic(mol->mview, idx, &g);
9617         
9618         {
9619                 /*  Register undo  */
9620                 MolAction *act;
9621                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9622                 MolActionCallback_registerUndo(mol, act);
9623                 MolActionRelease(act);
9624         }
9625
9626         return INT2NUM(idx);    
9627 }
9628
9629 /*
9630  *  call-seq:
9631  *     create_graphic(kind, color, points, fill = nil) -> integer
9632  *
9633  *  Create a new graphic object. The arguments are similar as insert_graphic.
9634  */
9635 static VALUE
9636 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9637 {
9638         VALUE args[5];
9639         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9640         args[0] = INT2NUM(-1);
9641         return s_Molecule_InsertGraphic(argc + 1, args, self);
9642 }
9643
9644 /*
9645  *  call-seq:
9646  *     remove_graphic(index) -> integer
9647  *
9648  *  Remove a graphic object.
9649  */
9650 static VALUE
9651 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9652 {
9653     Molecule *mol;
9654         int i;
9655     Data_Get_Struct(self, Molecule, mol);
9656         if (mol->mview == NULL)
9657                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9658         i = NUM2INT(rb_Integer(ival));
9659         if (i < 0 || i >= mol->mview->ngraphics)
9660                 rb_raise(rb_eArgError, "graphic index is out of range");
9661         {
9662                 /*  Prepare data for undo  */
9663                 MainViewGraphic *gp;
9664                 Vector *vp;
9665                 MolAction *act;
9666                 double col[4];
9667                 int n;
9668                 gp = mol->mview->graphics + i;
9669                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9670                 for (n = 0; n < gp->npoints; n++) {
9671                         vp[n].x = gp->points[n * 3];
9672                         vp[n].y = gp->points[n * 3 + 1];
9673                         vp[n].z = gp->points[n * 3 + 2];
9674                 }
9675                 col[0] = gp->rgba[0];
9676                 col[1] = gp->rgba[1];
9677                 col[2] = gp->rgba[2];
9678                 col[3] = gp->rgba[3];
9679                 if (gp->visible == 0) {
9680                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9681                         MolActionCallback_registerUndo(mol, act);
9682                         MolActionRelease(act);
9683                 }
9684                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9685                 MolActionCallback_registerUndo(mol, act);
9686                 free(vp);
9687                 MolActionRelease(act);
9688         }
9689         MainView_removeGraphic(mol->mview, i);
9690         return ival;
9691 }
9692
9693 /*
9694  *  call-seq:
9695  *     ngraphics -> integer
9696  *
9697  *  Get the number of graphic objects.
9698  */
9699 static VALUE
9700 s_Molecule_NGraphics(VALUE self)
9701 {
9702     Molecule *mol;
9703     Data_Get_Struct(self, Molecule, mol);
9704         if (mol->mview == NULL)
9705                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9706         return INT2NUM(mol->mview->ngraphics);
9707 }
9708
9709 /*
9710  *  call-seq:
9711  *     get_graphic_point(graphic_index, point_index) -> value
9712  *     get_graphic_points(graphic_index) -> values
9713  *
9714  *  Get the point_index-th control point of graphic_index-th graphic object.
9715  *  Get an array of all control points with the given values.
9716  *   
9717  */
9718 static VALUE
9719 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9720 {
9721         MainViewGraphic *gp;
9722     Molecule *mol;
9723         int index, pindex;
9724         Vector v;
9725         VALUE gval, pval;
9726     Data_Get_Struct(self, Molecule, mol);
9727         if (mol->mview == NULL)
9728                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9729         rb_scan_args(argc, argv, "11", &gval, &pval);
9730         index = NUM2INT(rb_Integer(gval));
9731         if (index < 0 || index >= mol->mview->ngraphics)
9732                 rb_raise(rb_eArgError, "the graphic index is out of range");
9733         gp = mol->mview->graphics + index;
9734         if (pval != Qnil) {
9735                 pindex = NUM2INT(rb_Integer(pval));
9736                 if (pindex < 0 || pindex >= gp->npoints)
9737                         rb_raise(rb_eArgError, "the point index is out of range");
9738                 v.x = gp->points[pindex * 3];
9739                 v.y = gp->points[pindex * 3 + 1];
9740                 v.z = gp->points[pindex * 3 + 2];
9741                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9742                         return rb_float_new(v.x);
9743                 } else {
9744                         return ValueFromVector(&v);
9745                 }
9746         } else {
9747                 pval = rb_ary_new();
9748                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9749                         v.x = gp->points[pindex * 3];
9750                         v.y = gp->points[pindex * 3 + 1];
9751                         v.z = gp->points[pindex * 3 + 2];
9752                         rb_ary_push(pval, ValueFromVector(&v));
9753                 }
9754                 return pval;
9755         }
9756 }
9757
9758 /*
9759  *  call-seq:
9760  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9761  *     set_graphic_points(graphic_index, new_values) -> new_values
9762  *
9763  *  Change the point_index-th control point of graphic_index-th graphic object.
9764  *  Replace the control points with the given values.
9765  *   
9766  */
9767 static VALUE
9768 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9769 {
9770         MainViewGraphic *gp;
9771     Molecule *mol;
9772         int index, pindex;
9773         Vector v, v0;
9774         VALUE gval, pval, nval;
9775         MolAction *act;
9776     Data_Get_Struct(self, Molecule, mol);
9777         if (mol->mview == NULL)
9778                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9779         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9780         index = NUM2INT(rb_Integer(gval));
9781         if (index < 0 || index >= mol->mview->ngraphics)
9782                 rb_raise(rb_eArgError, "the graphic index is out of range");
9783         gp = mol->mview->graphics + index;
9784         if (nval != Qnil) {
9785                 pindex = NUM2INT(rb_Integer(pval));
9786                 if (pindex < 0 || pindex >= gp->npoints)
9787                         rb_raise(rb_eArgError, "the point index is out of range");
9788                 v0.x = gp->points[pindex * 3];
9789                 v0.y = gp->points[pindex * 3 + 1];
9790                 v0.z = gp->points[pindex * 3 + 2];
9791                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9792                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9793                                 v.x = NUM2DBL(rb_Float(nval));
9794                                 v.y = v.z = 0;
9795                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9796                                 v.x = NUM2DBL(rb_Float(nval));
9797                                 v.y = v.z = 0;
9798                                 gp->points[7] = gp->points[11] = v.x;
9799                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9800                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9801                 } else {
9802                         if (nval == Qnil) {
9803                                 v.x = kInvalidFloat;
9804                                 v.y = v.z = 0.0;
9805                         } else VectorFromValue(nval, &v);
9806                 }
9807                 gp->points[pindex * 3] = v.x;
9808                 gp->points[pindex * 3 + 1] = v.y;
9809                 gp->points[pindex * 3 + 2] = v.z;
9810                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9811         } else {
9812                 VALUE aval;
9813                 int len;
9814                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9815                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9816                         vp[pindex].x = gp->points[pindex * 3];
9817                         vp[pindex].y = gp->points[pindex * 3 + 1];
9818                         vp[pindex].z = gp->points[pindex * 3 + 2];
9819                 }
9820                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9821                 free(vp);
9822                 pval = rb_ary_to_ary(pval);
9823                 len = RARRAY_LEN(pval);
9824                 if (gp->npoints < len) {
9825                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9826                         gp->npoints = len;
9827                 } else if (gp->npoints > len) {
9828                         int len2 = 3;
9829                         switch (gp->kind) {
9830                                 case kMainViewGraphicLine: len2 = 2; break;
9831                                 case kMainViewGraphicPoly: len2 = 3; break;
9832                                 case kMainViewGraphicCylinder: len2 = 3; break;
9833                                 case kMainViewGraphicCone: len2 = 3; break;
9834                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9835                         }
9836                         if (len2 < len)
9837                                 len2 = len;
9838                         gp->npoints = len2;
9839                 }
9840                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9841                         aval = RARRAY_PTR(pval)[pindex];
9842                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9843                                 v.x = NUM2DBL(rb_Float(aval));
9844                                 v.y = v.z = 0;
9845                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
9846                                 v.x = NUM2DBL(rb_Float(aval));
9847                                 v.y = v.z = 0;
9848                                 gp->points[7] = gp->points[11] = v.x;
9849                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9850                                 break;
9851                         } else VectorFromValue(aval, &v);
9852                         gp->points[pindex * 3] = v.x;
9853                         gp->points[pindex * 3 + 1] = v.y;
9854                         gp->points[pindex * 3 + 2] = v.z;
9855                 }
9856         }
9857         if (gp->kind == kMainViewGraphicPoly) {
9858                 /*  Calculate normals  */
9859                 s_CalculateGraphicNormals(gp);
9860         }
9861         MolActionCallback_registerUndo(mol, act);
9862         MolActionRelease(act);          
9863         MoleculeCallback_notifyModification(mol, 0);
9864         return nval;
9865 }
9866
9867 /*
9868  *  call-seq:
9869  *     get_graphic_color(graphic_index) -> value
9870  *
9871  *  Get the color of graphic_index-th graphic object
9872  */
9873 static VALUE
9874 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
9875 {
9876         MainViewGraphic *gp;
9877     Molecule *mol;
9878         int index;
9879     Data_Get_Struct(self, Molecule, mol);
9880         if (mol->mview == NULL)
9881                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9882         index = NUM2INT(rb_Integer(gval));
9883         if (index < 0 || index >= mol->mview->ngraphics)
9884                 rb_raise(rb_eArgError, "the graphic index is out of range");
9885         gp = mol->mview->graphics + index;
9886         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]));
9887 }
9888
9889 /*
9890  *  call-seq:
9891  *     set_graphic_color(graphic_index, new_value) -> new_value
9892  *
9893  *  Change the color of graphic_index-th graphic object
9894  *   
9895  */
9896 static VALUE
9897 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9898 {
9899         MainViewGraphic *gp;
9900     Molecule *mol;
9901         MolAction *act;
9902         double c[4];
9903         int index, i, n;
9904     Data_Get_Struct(self, Molecule, mol);
9905         if (mol->mview == NULL)
9906                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9907         index = NUM2INT(rb_Integer(gval));
9908         if (index < 0 || index >= mol->mview->ngraphics)
9909                 rb_raise(rb_eArgError, "the graphic index is out of range");
9910         gp = mol->mview->graphics + index;
9911         for (i = 0; i < 4; i++)
9912                 c[i] = gp->rgba[i];
9913         cval = rb_ary_to_ary(cval);
9914         n = RARRAY_LEN(cval);
9915         if (n != 3 && n != 4)
9916                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9917
9918         for (i = 0; i < n; i++) {
9919                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9920         }
9921         if (n == 3)
9922                 gp->rgba[3] = 1.0;
9923         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
9924         MolActionCallback_registerUndo(mol, act);
9925         MolActionRelease(act);          
9926         MoleculeCallback_notifyModification(mol, 0);
9927         return cval;
9928 }
9929
9930 /*
9931  *  call-seq:
9932  *     show_graphic(graphic_index) -> self
9933  *
9934  *  Enable the visible flag of the graphic_index-th graphic object
9935  *   
9936  */
9937 static VALUE
9938 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9939 {
9940         MainViewGraphic *gp;
9941     Molecule *mol;
9942         int index;
9943     Data_Get_Struct(self, Molecule, mol);
9944         if (mol->mview == NULL)
9945                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9946         index = NUM2INT(rb_Integer(gval));
9947         if (index < 0 || index >= mol->mview->ngraphics)
9948                 rb_raise(rb_eArgError, "the graphic index is out of range");
9949         gp = mol->mview->graphics + index;
9950         gp->visible = 1;
9951         MoleculeCallback_notifyModification(mol, 0);
9952         return self;
9953 }
9954
9955 /*
9956  *  call-seq:
9957  *     hide_graphic(graphic_index) -> self
9958  *
9959  *  Disable the visible flag of the graphic_index-th graphic object
9960  *   
9961  */
9962 static VALUE
9963 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9964 {
9965         MainViewGraphic *gp;
9966     Molecule *mol;
9967         int index;
9968     Data_Get_Struct(self, Molecule, mol);
9969         if (mol->mview == NULL)
9970                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9971         index = NUM2INT(rb_Integer(gval));
9972         if (index < 0 || index >= mol->mview->ngraphics)
9973                 rb_raise(rb_eArgError, "the graphic index is out of range");
9974         gp = mol->mview->graphics + index;
9975         gp->visible = 0;
9976         MoleculeCallback_notifyModification(mol, 0);
9977         return self;
9978 }
9979
9980 /*
9981  *  call-seq:
9982  *     show_text(string)
9983  *
9984  *  Show the string in the info text box.
9985  */
9986 static VALUE
9987 s_Molecule_ShowText(VALUE self, VALUE arg)
9988 {
9989     Molecule *mol;
9990     Data_Get_Struct(self, Molecule, mol);
9991         if (mol->mview != NULL)
9992                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9993         return Qnil;
9994 }
9995
9996 #pragma mark ------ MD Support ------
9997
9998 /*
9999  *  call-seq:
10000  *     md_arena -> MDArena
10001  *
10002  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10003  *  this molecule, a new arena is created.
10004  */
10005 static VALUE
10006 s_Molecule_MDArena(VALUE self)
10007 {
10008     Molecule *mol;
10009         VALUE retval;
10010     Data_Get_Struct(self, Molecule, mol);
10011         if (mol->arena == NULL)
10012                 md_arena_new(mol);
10013         retval = ValueFromMDArena(mol->arena);
10014         return retval;
10015 }
10016
10017 /*
10018  *  call-seq:
10019  *     set_parameter_attr(type, index, key, value, src) -> value
10020  *
10021  *  This method is used only internally.
10022  */
10023 static VALUE
10024 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10025 {
10026         /*  This method is called from MolAction to change a MM parameter attribute.  */
10027     Molecule *mol;
10028         VALUE pval;
10029         ParameterRef *pref;
10030         UnionPar *up;
10031     Data_Get_Struct(self, Molecule, mol);
10032         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10033         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10034         
10035         /*  This is the special part of this method; it allows modification of the src field. */
10036         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10037         Data_Get_Struct(pval, ParameterRef, pref);
10038         up = ParameterRefGetPar(pref);
10039         up->bond.src = FIX2INT(sval);
10040         
10041         return vval;
10042 }
10043
10044 /*
10045  *  call-seq:
10046  *     parameter -> Parameter
10047  *
10048  *  Get the local parameter of this molecule. If not defined, returns nil.
10049  */
10050 static VALUE
10051 s_Molecule_Parameter(VALUE self)
10052 {
10053     Molecule *mol;
10054     Data_Get_Struct(self, Molecule, mol);
10055 /*      if (mol->par == NULL)
10056                 return Qnil; */
10057         return s_NewParameterValueFromValue(self);
10058 }
10059
10060 /*
10061  *  call-seq:
10062  *     start_step       -> Integer
10063  *
10064  *  Returns the start step (defined by dcd format).
10065  */
10066 static VALUE
10067 s_Molecule_StartStep(VALUE self)
10068 {
10069     Molecule *mol;
10070     Data_Get_Struct(self, Molecule, mol);
10071         return INT2NUM(mol->startStep);
10072 }
10073
10074 /*
10075  *  call-seq:
10076  *     start_step = Integer
10077  *
10078  *  Set the start step (defined by dcd format).
10079  */
10080 static VALUE
10081 s_Molecule_SetStartStep(VALUE self, VALUE val)
10082 {
10083     Molecule *mol;
10084     Data_Get_Struct(self, Molecule, mol);
10085         mol->startStep = NUM2INT(rb_Integer(val));
10086         return val;
10087 }
10088
10089 /*
10090  *  call-seq:
10091  *     steps_per_frame       -> Integer
10092  *
10093  *  Returns the number of steps between frames (defined by dcd format).
10094  */
10095 static VALUE
10096 s_Molecule_StepsPerFrame(VALUE self)
10097 {
10098     Molecule *mol;
10099     Data_Get_Struct(self, Molecule, mol);
10100         return INT2NUM(mol->stepsPerFrame);
10101 }
10102
10103 /*
10104  *  call-seq:
10105  *     steps_per_frame = Integer
10106  *
10107  *  Set the number of steps between frames (defined by dcd format).
10108  */
10109 static VALUE
10110 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10111 {
10112     Molecule *mol;
10113     Data_Get_Struct(self, Molecule, mol);
10114         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10115         return val;
10116 }
10117
10118 /*
10119  *  call-seq:
10120  *     ps_per_step       -> Float
10121  *
10122  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10123  */
10124 static VALUE
10125 s_Molecule_PsPerStep(VALUE self)
10126 {
10127     Molecule *mol;
10128     Data_Get_Struct(self, Molecule, mol);
10129         return rb_float_new(mol->psPerStep);
10130 }
10131
10132 /*
10133  *  call-seq:
10134  *     ps_per_step = Float
10135  *
10136  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10137  */
10138 static VALUE
10139 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10140 {
10141     Molecule *mol;
10142     Data_Get_Struct(self, Molecule, mol);
10143         mol->psPerStep = NUM2DBL(rb_Float(val));
10144         return val;
10145 }
10146
10147 static VALUE
10148 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10149 {
10150         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.");
10151 }
10152
10153 #pragma mark ------ MO Handling ------
10154
10155 /*
10156  *  call-seq:
10157  *     selectedMO -> IntGroup
10158  *
10159  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10160  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10161  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10162  */
10163 static VALUE
10164 s_Molecule_SelectedMO(VALUE self)
10165 {
10166     Molecule *mol;
10167         IntGroup *ig;
10168         VALUE val;
10169     Data_Get_Struct(self, Molecule, mol);
10170         if (mol->mview == NULL)
10171                 return Qnil;
10172         ig = MainView_selectedMO(mol->mview);
10173         if (ig == NULL)
10174                 return Qnil;
10175         IntGroupOffset(ig, 1);
10176         val = ValueFromIntGroup(ig);
10177         IntGroupRelease(ig);
10178         return val;
10179 }
10180
10181 /*
10182  *  call-seq:
10183  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10184  *
10185  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10186  *  If the molecule does not contain a basis set information, then returns nil.
10187  */
10188 static VALUE
10189 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10190 {
10191     Molecule *mol;
10192         Vector o, dx, dy, dz;
10193         Int nx, ny, nz;
10194         VALUE nval;
10195         Int npoints = 80 * 80 * 80;
10196     Data_Get_Struct(self, Molecule, mol);
10197         if (mol->bset == NULL)
10198                 return Qnil;
10199         rb_scan_args(argc, argv, "01", &nval);
10200         if (nval != Qnil)
10201                 npoints = NUM2INT(rb_Integer(nval));
10202         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10203                 return Qnil;
10204         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));
10205 }
10206
10207 static int
10208 s_Cubegen_callback(double progress, void *ref)
10209 {
10210         MyAppCallback_setProgressValue(progress);
10211         if (MyAppCallback_checkInterrupt())
10212                 return 1;
10213         else return 0;
10214 }
10215
10216 /*
10217  *  call-seq:
10218  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10219  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10220  *
10221  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10222  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10223  *  second form, the cube dimension is explicitly given.
10224  *  Returns fname when successful, nil otherwise.
10225  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10226  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10227  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10228  */
10229 static VALUE
10230 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10231 {
10232         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10233     Molecule *mol;
10234         Int mono, nx, ny, nz, npoints;
10235         Vector o, dx, dy, dz;
10236         int index, n;
10237         char buf[1024];
10238     Data_Get_Struct(self, Molecule, mol);
10239         if (mol->bset == NULL)
10240                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10241         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10242         
10243         /*  Set up parameters  */
10244         mono = NUM2INT(rb_Integer(mval));
10245         if (mono <= 0 || mono > mol->bset->ncomps)
10246                 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
10247         if (RTEST(bval)) {
10248                 if (mol->bset->rflag != 0)
10249                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10250                 mono += mol->bset->ncomps;
10251         }
10252                 
10253         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10254                 /*  Automatic grid formation  */
10255                 if (oval != Qnil)
10256                         npoints = NUM2INT(rb_Integer(oval));
10257                 else npoints = 0;
10258                 if (npoints == 0)
10259                         npoints = 1000000;
10260                 else if (npoints < 8)
10261                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10262                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10263                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10264                 ival = dxval;
10265                 bval = dyval;
10266         } else {
10267                 VectorFromValue(oval, &o);
10268                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10269                         VectorFromValue(dxval, &dx);
10270                 else {
10271                         dx.x = NUM2DBL(rb_Float(dxval));
10272                         dx.y = dx.z = 0.0;
10273                 }
10274                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10275                         VectorFromValue(dyval, &dy);
10276                 else {
10277                         dy.y = NUM2DBL(rb_Float(dyval));
10278                         dy.x = dy.z = 0.0;
10279                 }
10280                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10281                         VectorFromValue(dzval, &dz);
10282                 else {
10283                         dz.z = NUM2DBL(rb_Float(dzval));
10284                         dz.x = dz.y = 0.0;
10285                 }
10286                 nx = NUM2INT(rb_Integer(nxval));
10287                 ny = NUM2INT(rb_Integer(nyval));
10288                 nz = NUM2INT(rb_Integer(nzval));
10289                 if (nx <= 0 || ny <= 0 || nz <= 0)
10290                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10291                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10292                         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);
10293         }
10294         
10295         /*  Calc MO  */
10296         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10297         if (index == -2)
10298                 rb_interrupt();
10299         else if (index < 0)
10300                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10301         
10302         /*  Output to file  */
10303         MoleculeCallback_displayName(mol, buf, sizeof buf);
10304         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10305         if (n != 0)
10306                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10307         
10308         /*  Discard the cube  */
10309         MoleculeClearCubeAtIndex(mol, index);
10310         return fval;
10311 }
10312
10313 /*
10314  *  call-seq:
10315  *     clear_surface
10316  *
10317  *  Clear the MO surface if present.
10318  */
10319 static VALUE
10320 s_Molecule_ClearSurface(VALUE self)
10321 {
10322     Molecule *mol;
10323     Data_Get_Struct(self, Molecule, mol);
10324         if (mol->mcube != NULL)
10325                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10326         return self;
10327 }
10328
10329 /*
10330  *  call-seq:
10331  *     hide_surface
10332  *
10333  *  Hide the MO surface if present.
10334  */
10335 static VALUE
10336 s_Molecule_HideSurface(VALUE self)
10337 {
10338     Molecule *mol;
10339     Data_Get_Struct(self, Molecule, mol);
10340         if (mol->mcube != NULL) {
10341                 mol->mcube->hidden = 1;
10342                 MoleculeCallback_notifyModification(mol, 0);
10343         }
10344         return self;
10345 }
10346
10347 /*
10348  *  call-seq:
10349  *     show_surface
10350  *
10351  *  Show the MO surface if present.
10352  */
10353 static VALUE
10354 s_Molecule_ShowSurface(VALUE self)
10355 {
10356     Molecule *mol;
10357     Data_Get_Struct(self, Molecule, mol);
10358         if (mol->mcube != NULL) {
10359                 mol->mcube->hidden = 0;
10360                 MoleculeCallback_notifyModification(mol, 0);
10361         }
10362         return self;
10363 }
10364
10365 /*
10366  *  call-seq:
10367  *     create_surface(mo, attr = nil)
10368  *
10369  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10370  *  then it denotes the beta orbital.
10371  *  If mo is nil, then the attributes of the current surface are modified.
10372  *  Attributes:
10373  *    :npoints : the approximate number of grid points
10374  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10375  *    :thres   : the threshold for the isovalue surface
10376  *  If the molecule does not contain MO information, raises exception.
10377  */
10378 static VALUE
10379 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10380 {
10381     Molecule *mol;
10382         Vector o, dx, dy, dz;
10383         Int nmo, nx, ny, nz, i;
10384         Int need_recalc = 0;
10385         VALUE nval, hval, aval;
10386         Int npoints;
10387         Double expand;
10388         Double thres;
10389         Double d[4];
10390     Data_Get_Struct(self, Molecule, mol);
10391         rb_scan_args(argc, argv, "11", &nval, &hval);
10392         if (mol->bset == NULL)
10393                 rb_raise(rb_eMolbyError, "No MO information is given");
10394         if (nval == Qnil) {
10395                 nmo = -1;
10396         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10397                 nmo = mol->bset->nmos + 1;
10398         } else {
10399                 nmo = NUM2INT(rb_Integer(nval));
10400                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10401                         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);
10402                 if (nmo < 0)
10403                         nmo = -nmo + mol->bset->ncomps;
10404         }
10405         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10406                 npoints = NUM2INT(rb_Integer(aval));
10407                 need_recalc = 1;
10408         } else if (mol->mcube != NULL) {
10409                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10410         } else npoints = 80 * 80 * 80;
10411         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10412                 expand = NUM2DBL(rb_Float(aval));
10413         } else if (mol->mcube != NULL) {
10414                 expand = mol->mcube->expand;
10415         } else expand = 1.0;
10416         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10417                 thres = NUM2DBL(rb_Float(aval));
10418         } else if (mol->mcube != NULL) {
10419                 thres = mol->mcube->thres;
10420         } else thres = 0.05;
10421         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10422                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10423                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10424                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10425                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10426         }
10427         for (nx = 0; nx < 2; nx++) {
10428                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10429                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10430                         aval = rb_ary_to_ary(aval);
10431                         if (RARRAY_LEN(aval) < 3) {
10432                         raise:
10433                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10434                         }
10435                         for (i = 0; i < 4; i++)
10436                                 d[i] = mol->mcube->c[nx].rgba[i];
10437                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10438                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10439                                 if (d[i] < 0.0 && d[i] > 1.0)
10440                                         goto raise;
10441                         }
10442                         for (i = 0; i < 4; i++)
10443                                 mol->mcube->c[nx].rgba[i] = d[i];
10444                 }
10445         }
10446         if (mol->mcube->expand != expand)
10447                 need_recalc = 1;
10448         mol->mcube->thres = thres;
10449         mol->mcube->expand = expand;
10450         if (nmo < 0) {
10451                 if (mol->mcube->idn < 0)
10452                         return self;  /*  Only set attributes for now  */
10453                 if (need_recalc)
10454                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10455         }
10456         if (MoleculeUpdateMCube(mol, nmo) != 0)
10457                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10458         return self;
10459 }
10460
10461 /*
10462  *  call-seq:
10463  *     set_surface_attr(attr = nil)
10464  *
10465  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10466  */
10467 static VALUE
10468 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10469 {
10470         VALUE args[2];
10471         args[0] = Qnil;
10472         args[1] = hval;
10473         return s_Molecule_CreateSurface(2, args, self);
10474 }
10475
10476 /*
10477  *  call-seq:
10478  *     nelpots
10479  *
10480  *  Get the number of electrostatic potential info.
10481  */
10482 static VALUE
10483 s_Molecule_NElpots(VALUE self)
10484 {
10485         Molecule *mol;
10486     Data_Get_Struct(self, Molecule, mol);
10487         return INT2NUM(mol->nelpots);
10488 }
10489
10490 /*
10491  *  call-seq:
10492  *     elpot(idx)
10493  *
10494  *  Get the electrostatic potential info at the given index. If present, then the
10495  *  return value is [Vector, Float] (position and potential). If not present, then
10496  *  returns nil.
10497  */
10498 static VALUE
10499 s_Molecule_Elpot(VALUE self, VALUE ival)
10500 {
10501         Molecule *mol;
10502         int idx;
10503     Data_Get_Struct(self, Molecule, mol);
10504         idx = NUM2INT(rb_Integer(ival));
10505         if (idx < 0 || idx >= mol->nelpots)
10506                 return Qnil;
10507         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10508 }
10509
10510 /*
10511  *  call-seq:
10512  *     clear_basis_set
10513  *
10514  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10515  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10516  */
10517 static VALUE
10518 s_Molecule_ClearBasisSet(VALUE self)
10519 {
10520         Molecule *mol;
10521     Data_Get_Struct(self, Molecule, mol);
10522         if (mol != NULL) {
10523                 if (mol->bset != NULL) {
10524                         BasisSetRelease(mol->bset);
10525                         mol->bset = NULL;
10526                 }
10527                 if (mol->mcube != NULL) {
10528                         MoleculeDeallocateMCube(mol->mcube);
10529                         mol->mcube = NULL;
10530                 }
10531         }
10532         return self;
10533 }
10534
10535 /*
10536  *  call-seq:
10537  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10538  *
10539  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10540  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10541  *  -2, D5-type.
10542  */
10543 static VALUE
10544 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10545 {
10546         Molecule *mol;
10547         int sym, nprims, a_idx, n;
10548     Data_Get_Struct(self, Molecule, mol);
10549         a_idx = NUM2INT(rb_Integer(aval));
10550         sym = NUM2INT(rb_Integer(symval));
10551         nprims = NUM2INT(rb_Integer(npval));
10552         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10553         if (n == -1)
10554                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10555         else if (n == -2)
10556                 rb_raise(rb_eMolbyError, "Low memory");
10557         else if (n == -3)
10558                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10559         else if (n != 0)
10560                 rb_raise(rb_eMolbyError, "Unknown error");
10561         return self;
10562 }
10563
10564 /*
10565  *  call-seq:
10566  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10567  *
10568  *  To be used internally. Add a gaussian primitive coefficients.
10569  */
10570 static VALUE
10571 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10572 {
10573         Molecule *mol;
10574         Int n;
10575         Double exponent, contraction, contraction_sp;
10576     Data_Get_Struct(self, Molecule, mol);
10577         exponent = NUM2DBL(rb_Float(expval));
10578         contraction = NUM2DBL(rb_Float(cval));
10579         contraction_sp = NUM2DBL(rb_Float(cspval));
10580         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10581         if (n == -1)
10582                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10583         else if (n == -2)
10584                 rb_raise(rb_eMolbyError, "Low memory");
10585         else if (n != 0)
10586                 rb_raise(rb_eMolbyError, "Unknown error");
10587         return self;
10588 }
10589
10590 /*
10591  *  call-seq:
10592  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10593  *
10594  *  Get the Gaussian shell information for the given MO coefficient index.
10595  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10596  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10597  *  is the number of MO component belonging to this shell.
10598  */
10599 static VALUE
10600 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10601 {
10602         Molecule *mol;
10603         ShellInfo *sp;
10604         int s_idx, sym;
10605     Data_Get_Struct(self, Molecule, mol);
10606         if (mol->bset == NULL)
10607                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10608         s_idx = NUM2INT(rb_Integer(sval));
10609         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10610                 return Qnil;
10611         sp = mol->bset->shells + s_idx;
10612         sym = sp->sym;
10613         switch (sym) {
10614                 case kGTOType_S:  sym = 0;  break;
10615                 case kGTOType_SP: sym = -1; break;
10616                 case kGTOType_P:  sym = 1;  break;
10617                 case kGTOType_D:  sym = 2;  break;
10618                 case kGTOType_D5: sym = -2; break;
10619                 case kGTOType_F:  sym = 3;  break;
10620                 case kGTOType_F7: sym = -3; break;
10621                 case kGTOType_G:  sym = 4;  break;
10622                 case kGTOType_G9: sym = -4; break;
10623                 default:
10624                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10625         }
10626         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10627 }
10628
10629 /*
10630  *  call-seq:
10631  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10632  *
10633  *  Get the Gaussian primitive coefficients for the given MO component.
10634  */
10635 static VALUE
10636 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10637 {
10638         Molecule *mol;
10639         ShellInfo *sp;
10640         PrimInfo *pp;
10641         int s_idx, i;
10642         VALUE retval, aval;
10643     Data_Get_Struct(self, Molecule, mol);
10644         if (mol->bset == NULL)
10645                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10646         s_idx = NUM2INT(rb_Integer(sval));
10647         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10648                 return Qnil;
10649         sp = mol->bset->shells + s_idx;
10650         pp = mol->bset->priminfos + sp->p_idx;
10651         retval = rb_ary_new2(sp->nprim);
10652         for (i = 0; i < sp->nprim; i++) {
10653                 if (sp->sym == kGTOType_SP) {
10654                         /*  With P contraction coefficient  */
10655                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10656                 } else {
10657                         /*  Without P contraction coefficient  */
10658                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10659                 }
10660                 rb_ary_store(retval, i, aval);
10661         }
10662         return retval;
10663 }
10664
10665 /*
10666  *  call-seq:
10667  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10668  *
10669  *  Get the Gaussian shell information for the given MO coefficient index.
10670  */
10671 static VALUE
10672 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10673 {
10674         Molecule *mol;
10675         Int n, c, atom_idx, shell_idx;
10676         char label[32];
10677     Data_Get_Struct(self, Molecule, mol);
10678         if (mol->bset == NULL)
10679                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10680         c = NUM2INT(rb_Integer(cval));
10681         if (c < 0 || c >= mol->bset->ncomps)
10682                 return Qnil;
10683         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10684         if (n != 0)
10685                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10686         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10687 }
10688
10689 /*
10690  *  call-seq:
10691  *     clear_mo_coefficients
10692  *
10693  *  Clear the existing MO coefficients.
10694  */
10695 static VALUE
10696 s_Molecule_ClearMOCoefficients(VALUE self)
10697 {
10698         Molecule *mol;
10699         Data_Get_Struct(self, Molecule, mol);
10700         if (mol->bset != NULL) {
10701                 if (mol->bset->moenergies != NULL) {
10702                         free(mol->bset->moenergies);
10703                         mol->bset->moenergies = NULL;
10704                 }
10705                 if (mol->bset->mo != NULL) {
10706                         free(mol->bset->mo);
10707                         mol->bset->mo = NULL;
10708                 }
10709                 mol->bset->nmos = 0;
10710         }
10711         return self;
10712 }
10713
10714 /*
10715  *  call-seq:
10716  *     set_mo_coefficients(idx, energy, coefficients)
10717  *
10718  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10719  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10720  *  Energy is the MO energy, and coefficients is an array
10721  *  of MO coefficients.
10722  */
10723 static VALUE
10724 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10725 {
10726         Molecule *mol;
10727         Int idx, ncomps, i;
10728         Double energy;
10729         Double *coeffs;
10730     Data_Get_Struct(self, Molecule, mol);
10731         idx = NUM2INT(rb_Integer(ival));
10732         energy = NUM2DBL(rb_Float(eval));
10733         aval = rb_ary_to_ary(aval);
10734         ncomps = RARRAY_LEN(aval);
10735         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10736         if (coeffs == NULL) {
10737                 i = -2;
10738                 goto end;
10739         }
10740         for (i = 0; i < ncomps; i++)
10741                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10742         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10743 end:
10744         if (i == -1)
10745                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10746         else if (i == -2)
10747                 rb_raise(rb_eMolbyError, "Low memory");
10748         else if (i == -3)
10749                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10750         else if (i == -4)
10751                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10752         else if (i == -5)
10753                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10754         else if (i != 0)
10755                 rb_raise(rb_eMolbyError, "Unknown error");
10756         return self;
10757 }
10758
10759 /*
10760  *  call-seq:
10761  *     get_mo_coefficients(idx)
10762  *
10763  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10764  */
10765 static VALUE
10766 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10767 {
10768         Molecule *mol;
10769         Int idx, ncomps, n;
10770         Double energy;
10771         Double *coeffs;
10772         VALUE retval;
10773     Data_Get_Struct(self, Molecule, mol);
10774         idx = NUM2INT(rb_Integer(ival));
10775         ncomps = 0;
10776         coeffs = NULL;
10777         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10778         if (n == -1)
10779                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10780         else if (n == -2)
10781                 rb_raise(rb_eMolbyError, "No basis set information is present");
10782         else if (n == -3)
10783                 return Qnil;  /*  Silently returns nil  */
10784         retval = rb_ary_new2(ncomps);
10785         for (n = 0; n < ncomps; n++)
10786                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10787         free(coeffs);
10788         return retval;
10789 }
10790
10791 /*
10792  *  call-seq:
10793  *     get_mo_energy(idx)
10794  *
10795  *  To be used internally. Get the MO energy for the given MO index (1-based).
10796  */
10797 static VALUE
10798 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10799 {
10800         Molecule *mol;
10801         Int idx, n;
10802         Double energy;
10803     Data_Get_Struct(self, Molecule, mol);
10804         idx = NUM2INT(rb_Integer(ival));
10805         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10806         if (n == -1)
10807                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10808         else if (n == -2)
10809                 rb_raise(rb_eMolbyError, "No basis set information is present");
10810         else if (n == -3)
10811                 return Qnil;
10812         return rb_float_new(energy);
10813 }
10814
10815 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10816
10817 static inline void
10818 s_InitMOInfoKeys(void)
10819 {
10820         if (sTypeSym == 0) {
10821                 sTypeSym = ID2SYM(rb_intern("type"));
10822                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10823                 sBetaSym = ID2SYM(rb_intern("beta"));
10824                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10825                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10826         }
10827 }
10828
10829 /*
10830  *  call-seq:
10831  *     set_mo_info(hash)
10832  *
10833  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10834  *  :alpha=>integer, :beta=>integer
10835  */
10836 static VALUE
10837 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10838 {
10839         Molecule *mol;
10840         VALUE aval;
10841         Int rflag, na, nb, n;
10842         char *s;
10843     Data_Get_Struct(self, Molecule, mol);
10844         if (mol->bset != NULL) {
10845                 rflag = mol->bset->rflag;
10846                 na = mol->bset->ne_alpha;
10847                 nb = mol->bset->ne_beta;
10848         } else {
10849                 rflag = 1;
10850                 na = 0;
10851                 nb = 0;
10852         }
10853         if (hval != Qnil) {
10854                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10855                         s = StringValuePtr(aval);
10856                         if (strcasecmp(s, "RHF") == 0)
10857                                 rflag = 1;
10858                         else if (strcasecmp(s, "UHF") == 0)
10859                                 rflag = 0;
10860                         else if (strcasecmp(s, "ROHF") == 0)
10861                                 rflag = 2;
10862                 }
10863                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10864                         n = NUM2INT(rb_Integer(aval));
10865                         if (n >= 0)
10866                                 na = n;
10867                 }
10868                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10869                         n = NUM2INT(rb_Integer(aval));
10870                         if (n >= 0)
10871                                 nb = n;
10872                 }
10873                 MoleculeSetMOInfo(mol, rflag, na, nb);
10874         }
10875         return self;
10876 }
10877
10878 /*
10879  *  call-seq:
10880  *     get_mo_info(key)
10881  *
10882  *  Get the MO info. The key is as described in set_mo_info.
10883  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10884  */
10885 static VALUE
10886 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10887 {
10888         Molecule *mol;
10889     Data_Get_Struct(self, Molecule, mol);
10890         if (mol->bset == NULL)
10891                 return Qnil;
10892         if (kval == sTypeSym) {
10893                 switch (mol->bset->rflag) {
10894                         case 0: return rb_str_new2("UHF");
10895                         case 1: return rb_str_new2("RHF");
10896                         case 2: return rb_str_new2("ROHF");
10897                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10898                 }
10899         } else if (kval == sAlphaSym) {
10900                 return INT2NUM(mol->bset->ne_alpha);
10901         } else if (kval == sBetaSym) {
10902                 return INT2NUM(mol->bset->ne_beta);
10903         } else if (kval == sNcompsSym) {
10904                 return INT2NUM(mol->bset->ncomps);
10905         } else if (kval == sNshellsSym) {
10906                 return INT2NUM(mol->bset->nshells);
10907         } else {
10908                 kval = rb_inspect(kval);
10909                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10910                 return Qnil;  /*  Does not reach here  */
10911         }
10912 }
10913
10914 /*
10915  *  call-seq:
10916  *     mo_type
10917  *
10918  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10919  */
10920 static VALUE
10921 s_Molecule_MOType(VALUE self)
10922 {
10923         return s_Molecule_GetMOInfo(self, sTypeSym);
10924 }
10925
10926 #pragma mark ------ Molecular Topology ------
10927
10928 /*
10929  *  call-seq:
10930  *     search_equivalent_atoms(ig = nil)
10931  *
10932  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
10933  */
10934 static VALUE
10935 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10936 {
10937         Molecule *mol;
10938         Int *result, i;
10939         VALUE val;
10940         IntGroup *ig;
10941     Data_Get_Struct(self, Molecule, mol);
10942         if (mol->natoms == 0)
10943                 return Qnil;
10944         rb_scan_args(argc, argv, "01", &val);
10945         if (val != Qnil)
10946                 ig = IntGroupFromValue(val);
10947         else ig = NULL;
10948         result = MoleculeSearchEquivalentAtoms(mol, ig);
10949         if (result == NULL)
10950                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10951         if (ig != NULL)
10952                 IntGroupRelease(ig);
10953         val = rb_ary_new2(mol->natoms);
10954         for (i = 0; i < mol->natoms; i++)
10955                 rb_ary_push(val, INT2NUM(result[i]));
10956         free(result);
10957         return val;
10958 }
10959
10960 /*
10961  *  call-seq:
10962  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10963  *
10964  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10965  *  Name is the name of the new pi anchor, and group is the atoms that define
10966  *  the pi system. Type (a String) is an atom type for MM implementation.
10967  *  Weights represent the relative significance of the component atoms; if omitted, then
10968  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
10969  *  The weight values will be normalized so that the sum of the weights is 1.0.
10970  *  The weight values must be positive.
10971  *  Index is the atom index where the created pi-anchor is inserted in the 
10972  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
10973  *  having the largest index.
10974  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
10975  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10976  */
10977 static VALUE
10978 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10979 {
10980         Molecule *mol;
10981         VALUE nval, gval;
10982         IntGroup *ig;
10983         Int i, n, idx, last_component;
10984         Atom a, *ap;
10985         PiAnchor an;
10986         AtomRef *aref;
10987         if (argc < 2 || argc >= 6)
10988                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10989         nval = *argv++;
10990         gval = *argv++;
10991         argc -= 2;
10992     Data_Get_Struct(self, Molecule, mol);
10993         ig = IntGroupFromValue(gval);
10994         memset(&a, 0, sizeof(a));
10995         memset(&an, 0, sizeof(an));
10996         strncpy(a.aname, StringValuePtr(nval), 4);
10997         if (a.aname[0] == '_')
10998                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10999         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11000         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11001                 if (n >= mol->natoms) {
11002                         AtomConnectResize(&an.connect, 0);
11003                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11004                 }
11005                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11006                 last_component = n;
11007         }
11008         if (an.connect.count == 0)
11009                 rb_raise(rb_eMolbyError, "no atoms are specified");
11010         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11011         for (i = 0; i < an.connect.count; i++) {
11012                 an.coeffs[i] = 1.0 / an.connect.count;
11013         }
11014         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11015                 /*  Atom type  */
11016                 if (argv[0] != Qnil)
11017                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11018                 argc--;
11019                 argv++;
11020         }
11021         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11022                 if (argv[0] != Qnil) {
11023                         VALUE aval = rb_ary_to_ary(argv[0]);
11024                         Double d, sum;
11025                         if (RARRAY_LEN(aval) != an.connect.count)
11026                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11027                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11028                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11029                                 if (d <= 0.0)
11030                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11031                                 sum += d;
11032                                 an.coeffs[i] = d;
11033                         }
11034                         for (i = 0; i < an.connect.count; i++)
11035                                 an.coeffs[i] /= sum;
11036                 }
11037                 argc--;
11038                 argv++;
11039         }
11040         if (argc > 0 && argv[0] != Qnil) {
11041                 /*  Index  */
11042                 idx = NUM2INT(rb_Integer(argv[0]));
11043         } else idx = -1;
11044         if (idx < 0 || idx > mol->natoms) {
11045                 /*  Immediately after the last specified atom  */
11046                 idx = last_component + 1;
11047         }
11048         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11049         memmove(a.anchor, &an, sizeof(PiAnchor));
11050         /*  Use residue information of the last specified atom  */
11051         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11052         a.resSeq = ap->resSeq;
11053         strncpy(a.resName, ap->resName, 4);
11054         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11055                 return Qnil;
11056         MoleculeCalculatePiAnchorPosition(mol, idx);
11057     aref = AtomRefNew(mol, idx);
11058     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11059 }
11060
11061 #pragma mark ------ Molecular Properties ------
11062
11063 /*
11064  *  call-seq:
11065  *     set_property(name, value[, index]) -> value
11066  *     set_property(name, values, group) -> values
11067  *
11068  *  Set molecular property. A property is a floating-point number with a specified name,
11069  *  and can be set for each frame separately. The name of the property is given as a String.
11070  *  The value can be a single floating point number, which is set to the current frame.
11071  *  
11072  */
11073 static VALUE
11074 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11075 {
11076         Molecule *mol;
11077         VALUE nval, vval, ival;
11078         char *name;
11079         IntGroup *ig;
11080         Int i, n, idx, fidx;
11081         Double *dp;
11082         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11083     Data_Get_Struct(self, Molecule, mol);
11084         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11085                 idx = NUM2INT(rb_Integer(nval));
11086                 if (idx < 0 || idx >= mol->nmolprops)
11087                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11088         } else {
11089                 name = StringValuePtr(nval);
11090                 idx = MoleculeLookUpProperty(mol, name);
11091                 if (idx < 0) {
11092                         idx = MoleculeCreateProperty(mol, name);
11093                         if (idx < 0)
11094                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11095                 }
11096         }
11097         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11098                 if (ival == Qnil)
11099                         fidx = mol->cframe;
11100                 else {
11101                         fidx = NUM2INT(rb_Integer(ival));
11102                         n = MoleculeGetNumberOfFrames(mol);
11103                         if (fidx < 0 || fidx >= n)
11104                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11105                 }
11106                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11107                 dp = (Double *)malloc(sizeof(Double));
11108                 *dp = NUM2DBL(rb_Float(vval));
11109                 n = 1;
11110         } else {
11111                 vval = rb_ary_to_ary(vval);
11112                 ig = IntGroupFromValue(ival);
11113                 n = IntGroupGetCount(ig);
11114                 if (n == 0)
11115                         rb_raise(rb_eMolbyError, "No frames are specified");
11116                 if (RARRAY_LEN(vval) < n)
11117                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11118                 dp = (Double *)calloc(sizeof(Double), n);
11119                 for (i = 0; i < n; i++)
11120                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11121         }
11122         
11123         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11124         free(dp);
11125         IntGroupRelease(ig);
11126         return self;
11127 }
11128
11129 /*
11130  *  call-seq:
11131  *     get_property(name[, index]) -> value
11132  *     get_property(name, group) -> values
11133  *
11134  *  Get molecular property. In the first form, a property value for a single frame is returned.
11135  *  (If index is omitted, then the value for the current frame is given)
11136  *  In the second form, an array of property values for the given frames is returned.
11137  *  If name is not one of known properties or a valid index integer, exception is raised.
11138  */
11139 static VALUE
11140 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11141 {
11142         Molecule *mol;
11143         VALUE nval, ival;
11144         char *name;
11145         IntGroup *ig;
11146         Int i, n, idx, fidx;
11147         Double *dp;
11148         rb_scan_args(argc, argv, "11", &nval, &ival);
11149     Data_Get_Struct(self, Molecule, mol);
11150         if (mol->nmolprops == 0)
11151                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11152         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11153                 idx = NUM2INT(rb_Integer(nval));
11154                 if (idx < 0 || idx >= mol->nmolprops)
11155                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11156         } else {
11157                 name = StringValuePtr(nval);
11158                 idx = MoleculeLookUpProperty(mol, name);
11159                 if (idx < 0)
11160                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11161         }
11162         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11163                 if (ival == Qnil)
11164                         fidx = mol->cframe;
11165                 else {
11166                         fidx = NUM2INT(rb_Integer(ival));
11167                         n = MoleculeGetNumberOfFrames(mol);
11168                         if (fidx < 0 || fidx >= n)
11169                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11170                 }
11171                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11172                 ival = INT2FIX(fidx);
11173                 n = 1;
11174         } else {
11175                 ig = IntGroupFromValue(ival);
11176                 n = IntGroupGetCount(ig);
11177                 if (n == 0)
11178                         return rb_ary_new();
11179         }
11180         dp = (Double *)calloc(sizeof(Double), n);
11181         MoleculeGetProperty(mol, idx, ig, dp);  
11182         if (FIXNUM_P(ival))
11183                 ival = rb_float_new(dp[0]);
11184         else {
11185                 ival = rb_ary_new();
11186                 for (i = n - 1; i >= 0; i--) {
11187                         nval = rb_float_new(dp[i]);
11188                         rb_ary_store(ival, i, nval);
11189                 }
11190         }
11191         free(dp);
11192         IntGroupRelease(ig);
11193         return ival;
11194 }
11195
11196 /*
11197  *  call-seq:
11198  *     property_names -> Array
11199  *
11200  *  Get an array of property names.
11201  */
11202 static VALUE
11203 s_Molecule_PropertyNames(VALUE self)
11204 {
11205         Molecule *mol;
11206         VALUE rval, nval;
11207         int i;
11208     Data_Get_Struct(self, Molecule, mol);
11209         rval = rb_ary_new();
11210         for (i = mol->nmolprops - 1; i >= 0; i--) {
11211                 nval = rb_str_new2(mol->molprops[i].propname);
11212                 rb_ary_store(rval, i, nval);
11213         }
11214         return rval;
11215 }
11216
11217 #pragma mark ------ Class methods ------
11218
11219 /*
11220  *  call-seq:
11221  *     current       -> Molecule
11222  *
11223  *  Get the currently "active" molecule.
11224  */
11225 static VALUE
11226 s_Molecule_Current(VALUE klass)
11227 {
11228         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11229 }
11230
11231 /*
11232  *  call-seq:
11233  *     Molecule[]          -> Molecule
11234  *     Molecule[n]         -> Molecule
11235  *     Molecule[name]      -> Molecule
11236  *     Molecule[name, k]   -> Molecule
11237  *     Molecule[regex]     -> Molecule
11238  *     Molecule[regex, k]  -> Molecule
11239  *
11240  *  Molecule[] is equivalent to Molecule.current.
11241  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11242  *  Molecule[name] gives the first document (in the order of creation time) that has
11243  *  the given name. If a second argument (k) is given, the k-th document that has the
11244  *  given name is returned.
11245  *  Molecule[regex] gives the first document (in the order of creation time) that
11246  *  has a name matching the regular expression. If a second argument (k) is given, 
11247  *  the k-th document that has a name matching the re is returned.
11248  */
11249 static VALUE
11250 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11251 {
11252         VALUE val, kval;
11253         int idx, k;
11254         Molecule *mol;
11255         char buf[1024];
11256         rb_scan_args(argc, argv, "02", &val, &kval);
11257         if (val == Qnil)
11258                 return s_Molecule_Current(klass);
11259         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11260                 idx = NUM2INT(val);
11261                 mol = MoleculeCallback_moleculeAtIndex(idx);
11262         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11263                 char *p = StringValuePtr(val);
11264                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11265                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11266                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11267                         if (strcmp(buf, p) == 0 && --k == 0)
11268                                 break;
11269                 }
11270         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11271                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11272                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11273                         VALUE name;
11274                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11275                         name = rb_str_new2(buf);
11276                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11277                                 break;
11278                 }       
11279         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11280         
11281         if (mol == NULL)
11282                 return Qnil;
11283         else return ValueFromMolecule(mol);
11284 }
11285
11286 /*
11287  *  call-seq:
11288  *     list         -> array of Molecules
11289  *
11290  *  Get the list of molecules associated to the documents, in the order of creation
11291  *  time of the document. If no document is open, returns an empry array.
11292  */
11293 static VALUE
11294 s_Molecule_List(VALUE klass)
11295 {
11296         Molecule *mol;
11297         int i;
11298         VALUE ary;
11299         i = 0;
11300         ary = rb_ary_new();
11301         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11302                 rb_ary_push(ary, ValueFromMolecule(mol));
11303                 i++;
11304         }
11305         return ary;
11306 }
11307
11308 /*
11309  *  call-seq:
11310  *     ordered_list         -> array of Molecules
11311  *
11312  *  Get the list of molecules associated to the documents, in the order of front-to-back
11313  *  ordering of the associated window. If no document is open, returns an empry array.
11314  */
11315 static VALUE
11316 s_Molecule_OrderedList(VALUE klass)
11317 {
11318         Molecule *mol;
11319         int i;
11320         VALUE ary;
11321         i = 0;
11322         ary = rb_ary_new();
11323         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11324                 rb_ary_push(ary, ValueFromMolecule(mol));
11325                 i++;
11326         }
11327         return ary;
11328 }
11329
11330 #pragma mark ------ Call Subprocess ------
11331
11332 /*  The callback functions for call_subprocess_async  */
11333 static int
11334 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11335 {
11336         int ruby_status;
11337         VALUE procval, retval, args[2];
11338         args[0] = ValueFromMolecule(mol);
11339         args[1] = INT2NUM(status);
11340         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11341         if (procval != Qnil) {
11342                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11343                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11344                         return 1;
11345         }
11346         return 0;
11347 }
11348
11349 static int
11350 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11351 {
11352         int ruby_status;
11353         VALUE procval, retval, args[2];
11354         args[0] = ValueFromMolecule(mol);
11355         args[1] = INT2NUM(tcount);
11356         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11357         if (procval != Qnil) {
11358                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11359                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11360                         return 1;
11361         }
11362         return 0;
11363 }
11364
11365 /*
11366  *  call-seq:
11367  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11368  *
11369  *  Call subprocess asynchronically.
11370  *  If end_callback is given, it will be called (with two arguments self and termination status)
11371  *  when the subprocess terminated.
11372  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11373  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11374  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11375  *  filename begins with ">>", then the message will be appended to the file.
11376  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11377  *  If the argument is nil, then the message will be sent to the Ruby console.
11378  *  Returns the process ID as an integer.
11379  */
11380 static VALUE
11381 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11382 {
11383         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11384         Molecule *mol;
11385         char *sout, *serr;
11386         int n;
11387         FILE *fpout, *fperr;
11388         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11389         Data_Get_Struct(self, Molecule, mol);
11390
11391         if (stdout_val == Qnil) {
11392                 fpout = (FILE *)1;
11393         } else {
11394                 sout = StringValuePtr(stdout_val);
11395                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11396                         fpout = NULL;
11397                 else {
11398                         if (strncmp(sout, ">>", 2) == 0) {
11399                                 sout += 2;
11400                                 fpout = fopen(sout, "a");
11401                         } else {
11402                                 if (*sout == '>')
11403                                         sout++;
11404                                 fpout = fopen(sout, "w");
11405                         }
11406                         if (fpout == NULL)
11407                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11408                 }
11409         }
11410         if (stderr_val == Qnil) {
11411                 fperr = (FILE *)1;
11412         } else {
11413                 serr = StringValuePtr(stderr_val);
11414                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11415                         fperr = NULL;
11416                 else {
11417                         if (strncmp(serr, ">>", 2) == 0) {
11418                                 serr += 2;
11419                                 fpout = fopen(serr, "a");
11420                         } else {
11421                                 if (*serr == '>')
11422                                         serr++;
11423                                 fperr = fopen(serr, "w");
11424                         }
11425                         if (fperr == NULL)
11426                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11427                 }
11428         }
11429         
11430         /*  Register procs as instance variables  */
11431         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11432         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11433         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11434         if (fpout != NULL && fpout != (FILE *)1)
11435                 fclose(fpout);
11436         if (fperr != NULL && fperr != (FILE *)1)
11437                 fclose(fperr);
11438         return INT2NUM(n);
11439 }
11440
11441 #pragma mark ====== Define Molby Classes ======
11442
11443 void
11444 Init_Molby(void)
11445 {
11446         int i;
11447         
11448         /*  Define module Molby  */
11449         rb_mMolby = rb_define_module("Molby");
11450         
11451         /*  Define Vector3D, Transform, IntGroup  */
11452         Init_MolbyTypes();
11453         
11454         /*  Define MDArena  */
11455         Init_MolbyMDTypes();
11456
11457         /*  class Molecule  */
11458         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11459
11460         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11461     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11462     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11463         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11464         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11465
11466     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11467     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11468     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11469     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11470     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11471     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11472     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11473     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11474     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11475     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11476         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11477     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11478     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11479     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11480     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11481     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11482     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11483     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11484         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11485         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11486         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11487         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11488         
11489     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11490         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11491     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11492     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11493     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11494
11495     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11496     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11497     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11498     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11499     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11500     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11501         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11502         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11503         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11504         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11505         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11506         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11507         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11508         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11509         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11510         
11511         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11512         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11513         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11514         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11515         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11516         
11517         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11518     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11519         rb_define_alias(rb_cMolecule, "+", "add");
11520     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11521         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11522         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11523         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11524         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11525         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11526         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11527         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11528         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11529         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11530         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11531         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11532         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11533         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11534         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11535         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11536         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11537         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11538         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11539         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11540         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11541
11542         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11543         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11544         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11545         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11546         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11547
11548         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11549         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11550         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11551         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11552         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11553         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11554         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11555         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11556         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11557
11558         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11559         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11560         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11561         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11562         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11563         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11564         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11565         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11566         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11567         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11568         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11569         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11570         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11571         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11572         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11573         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11574         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11575         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11576         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11577         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11578         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11579         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11580
11581         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11582         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11583         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11584         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11585         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11586         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11587         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11588
11589         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11590         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11591         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11592         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11593         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11594         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11595         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11596         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11597         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11598         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11599         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11600         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11601         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11602
11603         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11604         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11605         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11606         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11607         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11608         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11609
11610         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11611         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11612         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11613         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11614         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11615         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11616         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11617         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11618         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11619         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11620         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11621         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11622         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11623         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11624         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11625         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11626         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11627         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11628         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11629         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11630         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11631         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11632         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11633         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11634         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11635         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11636         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11637         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11638         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11639         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11640         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11641         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11642         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11643         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11644         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11645         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11646         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11647         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11648         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11649         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11650         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11651         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11652         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11653         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11654         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11655         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11656         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11657         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11658         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11659         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11660         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11661         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11662         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11663         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11664         
11665         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11666         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11667         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11668         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11669         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11670         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11671         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11672         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11673         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11674         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11675         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11676         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11677         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11678
11679         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11680         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11681         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11682         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11683         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11684         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11685         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11686         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11687         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11688         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11689         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11690         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11691         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11692         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11693                 
11694         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11695         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11696         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11697         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11698         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11699         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11700         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11701         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11702         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11703         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11704         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11705         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11706         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11707         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11708         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11709         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11710         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11711         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11712         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11713         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11714         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11715         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11716         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11717
11718         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11719         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11720         
11721         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11722         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11723         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11724                 
11725         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11726         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11727         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11728         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11729         
11730         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11731         
11732         /*  class MolEnumerable  */
11733         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11734     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11735         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11736         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11737     rb_define_alias(rb_cMolEnumerable, "size", "length");
11738         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11739         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11740
11741         /*  class AtomRef  */
11742         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11743         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11744                 char buf[64];
11745                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11746                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11747                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11748                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11749                 strcat(buf, "=");
11750                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11751         }
11752         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11753         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11754         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11755         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11756         s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11757         rb_global_variable(&s_SetAtomAttrString);
11758         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11759         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11760
11761         /*  class Parameter  */
11762         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11763         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11764         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11765         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11766         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11767         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11768         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11769         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11770         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11771         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11772         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11773         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11774         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11775         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11776         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11777         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11778         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11779         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11780         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11781         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11782         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11783         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11784         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11785         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11786         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11787         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11788         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11789         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11790         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11791         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11792         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11793         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11794         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11795         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11796         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11797         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11798         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11799         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11800         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11801         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11802         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11803         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11804         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11805         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11806         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11807         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11808         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11809         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11810         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11811         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11812         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11813         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11814         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11815         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11816
11817         /*  class ParEnumerable  */
11818         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11819     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11820         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11821         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11822         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11823         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11824         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11825         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11826         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11827         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11828         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11829         
11830         /*  class ParameterRef  */
11831         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11832         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11833                 char buf[64];
11834                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11835                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11836                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11837                 if (s_ParameterAttrDefTable[i].symref != NULL)
11838                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11839                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11840                         strcat(buf, "=");
11841                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11842                 }
11843         }
11844         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11845         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11846         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11847         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11848         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11849         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11850         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11851         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11852
11853         /*  class MolbyError  */
11854         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11855
11856         /*  module Kernel  */
11857         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11858         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11859         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11860         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11861         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11862         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11863         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11864         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11865         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11866         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11867         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11868         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11869         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11870         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11871         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11872         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11873         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11874         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11875         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11876         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11877         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11878         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11879         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11880         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11881
11882         /*  class IO  */
11883         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11884         
11885         s_ID_equal = rb_intern("==");
11886         g_RubyID_call = rb_intern("call");
11887         
11888         s_InitMOInfoKeys();
11889         
11890         /*  Symbols for graphics  */
11891         s_LineSym = ID2SYM(rb_intern("line"));
11892         s_PolySym = ID2SYM(rb_intern("poly"));
11893         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
11894         s_ConeSym = ID2SYM(rb_intern("cone"));
11895         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
11896 }
11897
11898 #pragma mark ====== Interface with RubyDialog class ======
11899
11900 RubyValue
11901 RubyDialogCallback_parentModule(void)
11902 {
11903         return (RubyValue)rb_mMolby;
11904 }
11905
11906 #pragma mark ====== External functions ======
11907
11908 static VALUE s_ruby_top_self = Qfalse;
11909 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11910 static VALUE s_ruby_export_local_variables = Qfalse;
11911
11912 static VALUE
11913 s_evalRubyScriptOnMoleculeSub(VALUE val)
11914 {
11915         void **ptr = (void **)val;
11916         Molecule *mol = (Molecule *)ptr[1];
11917         VALUE sval, fnval, lnval, retval;
11918         VALUE binding;
11919
11920         /*  Clear the error information (store in the history array if necessary)  */
11921         sval = rb_errinfo();
11922         if (sval != Qnil) {
11923                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11924                 rb_set_errinfo(Qnil);
11925         }
11926
11927         if (s_ruby_top_self == Qfalse) {
11928                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11929         }
11930         if (s_ruby_get_binding_for_molecule == Qfalse) {
11931                 const char *s1 =
11932                  "lambda { |_mol_, _bind_| \n"
11933                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11934                  "  _proc_.call(_mol_) } ";
11935                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11936                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11937         }
11938         if (s_ruby_export_local_variables == Qfalse) {
11939                 const char *s2 =
11940                 "lambda { |_bind_| \n"
11941                 "   # find local variables newly defined in _bind_ \n"
11942                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11943                 " _a_.each { |_vsym_| \n"
11944                 "   _vname_ = _vsym_.to_s \n"
11945                 "   _vval_ = _bind_.eval(_vname_) \n"
11946                 "   #  Define local variable \n"
11947                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11948                 "   #  Then set value  \n"
11949                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11950                 " } \n"
11951                 "}";
11952                 s_ruby_export_local_variables = rb_eval_string(s2);
11953                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11954         }
11955         if (ptr[2] == NULL) {
11956                 char *scr;
11957                 /*  String literal: we need to specify string encoding  */
11958 #if defined(__WXMSW__)
11959                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11960 #else
11961                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11962 #endif
11963                 sval = rb_str_new2(scr);
11964                 free(scr);
11965                 fnval = rb_str_new2("(eval)");
11966                 lnval = INT2FIX(0);
11967         } else {
11968                 sval = rb_str_new2((char *)ptr[0]);
11969                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11970                 lnval = INT2FIX(1);
11971         }
11972         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11973         if (mol != NULL) {
11974                 VALUE mval = ValueFromMolecule(mol);
11975                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11976         }
11977         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11978         if (mol != NULL) {
11979                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11980         }
11981         return retval;
11982 }
11983
11984 RubyValue
11985 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
11986 {
11987         RubyValue retval;
11988         void *args[3];
11989         VALUE save_interrupt_flag;
11990 /*      char *save_ruby_sourcefile;
11991         int save_ruby_sourceline; */
11992         if (gMolbyIsCheckingInterrupt) {
11993                 MolActionAlertRubyIsRunning();
11994                 *status = -1;
11995                 return (RubyValue)Qnil;
11996         }
11997         gMolbyRunLevel++;
11998         args[0] = (void *)script;
11999         args[1] = (void *)mol;
12000         args[2] = (void *)fname;
12001         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12002 /*      save_ruby_sourcefile = ruby_sourcefile;
12003         save_ruby_sourceline = ruby_sourceline; */
12004         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12005         if (*status != 0) {
12006                 /*  Is this 'exit' exception?  */
12007                 VALUE last_exception = rb_gv_get("$!");
12008                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12009                         /*  Capture exit and return the status value  */
12010                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12011                         *status = 0;
12012                         rb_set_errinfo(Qnil);
12013                 }
12014         }
12015         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12016 /*      ruby_sourcefile = save_ruby_sourcefile;
12017         ruby_sourceline = save_ruby_sourceline; */
12018         gMolbyRunLevel--;
12019         return retval;
12020 }
12021
12022 int
12023 Ruby_showValue(RubyValue value, char **outValueString)
12024 {
12025         VALUE val = (VALUE)value;
12026         if (gMolbyIsCheckingInterrupt) {
12027                 MolActionAlertRubyIsRunning();
12028                 return 0;
12029         }
12030         if (val != Qnil) {
12031                 int status;
12032                 char *str;
12033                 gMolbyRunLevel++;
12034                 val = rb_protect(rb_inspect, val, &status);
12035                 gMolbyRunLevel--;
12036                 if (status != 0)
12037                         return status;
12038                 str = StringValuePtr(val);
12039                 if (outValueString != NULL)
12040                         *outValueString = strdup(str);
12041                 MyAppCallback_showScriptMessage("%s", str);
12042         } else {
12043                 if (outValueString != NULL)
12044                         *outValueString = NULL;
12045         }
12046         return 0;
12047 }
12048
12049 void
12050 Ruby_showError(int status)
12051 {
12052         static const int tag_raise = 6;
12053         char *msg = NULL, *msg2;
12054         VALUE val, backtrace;
12055         int interrupted = 0;
12056         if (status == tag_raise) {
12057                 VALUE errinfo = rb_errinfo();
12058                 VALUE eclass = CLASS_OF(errinfo);
12059                 if (eclass == rb_eInterrupt) {
12060                         msg = "Interrupt";
12061                         interrupted = 1;
12062                 }
12063         }
12064         gMolbyRunLevel++;
12065         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12066         if (msg == NULL) {
12067                 val = rb_eval_string_protect("$!.to_s", &status);
12068                 if (status == 0)
12069                         msg = RSTRING_PTR(val);
12070                 else msg = "(message not available)";
12071         }
12072         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12073         MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
12074         free(msg2);
12075         gMolbyRunLevel--;
12076 }
12077
12078 char *
12079 Molby_getDescription(void)
12080 {
12081         extern const char *gVersionString, *gCopyrightString;
12082         extern int gRevisionNumber;
12083         extern char *gLastBuildString;
12084         char *s;
12085         char *revisionString;
12086         if (gRevisionNumber > 0) {
12087                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12088         } else revisionString = "";
12089         asprintf(&s, 
12090                          "Molby %s%s\n%s\nLast compile: %s\n"
12091 #if !defined(__CMDMAC__)
12092                          "\nIncluding:\n"
12093                          "%s"
12094 #else
12095                          "Including "
12096 #endif
12097                          "ruby %s, http://www.ruby-lang.org/\n"
12098                          "%s\n"
12099                          "FFTW 3.3.2, http://www.fftw.org/\n"
12100                          "  Copyright (C) 2003, 2007-11 Matteo Frigo\n"
12101                          "  and Massachusetts Institute of Technology",
12102                          gVersionString, revisionString, gCopyrightString, gLastBuildString,
12103 #if !defined(__CMDMAC__)
12104                          MyAppCallback_getGUIDescriptionString(),
12105 #endif
12106                          gRubyVersion, gRubyCopyright);
12107         if (revisionString[0] != 0)
12108                 free(revisionString);
12109         return s;
12110 }
12111
12112 void
12113 Molby_startup(const char *script, const char *dir)
12114 {
12115         VALUE val;
12116         int status;
12117         char *libpath;
12118         char *respath, *p, *wbuf;
12119
12120         /*  Get version/copyright string from Ruby interpreter  */
12121         {
12122                 gRubyVersion = strdup(ruby_version);
12123                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12124 #if defined(__CMDMAC__)
12125                                  "",
12126 #else
12127                                  "  ",  /*  Indent for displaying in About dialog  */
12128 #endif
12129                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12130         }
12131         
12132         /*  Read build and revision information for Molby  */
12133 /*      {
12134                 char buf[200];
12135                 extern int gRevisionNumber;
12136                 extern char *gLastBuildString;
12137                 FILE *fp = fopen("../buildInfo.txt", "r");
12138                 gLastBuildString = "";
12139                 if (fp != NULL) {
12140                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12141                                 char *p1 = strchr(buf, '\"');
12142                                 char *p2 = strrchr(buf, '\"');
12143                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12144                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12145                                         buf[p2 - p1 - 1] = 0;
12146                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12147                                 }
12148                         }
12149                         fclose(fp);
12150                 }
12151                 fp = fopen("../revisionInfo.txt", "r");
12152                 gRevisionNumber = 0;
12153                 if (fp != NULL) {
12154                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12155                                 gRevisionNumber = strtol(buf, NULL, 0);
12156                         }
12157                         fclose(fp);
12158                 }
12159     } */
12160
12161 #if defined(__CMDMAC__)
12162         wbuf = Molby_getDescription();
12163         printf("%s\n", wbuf);
12164         free(wbuf);
12165 #endif
12166         
12167         /*  Read atom display parameters  */
12168         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12169 #if defined(__CMDMAC__)
12170                 fprintf(stderr, "%s\n", wbuf);
12171 #else
12172                 MyAppCallback_setConsoleColor(1);
12173                 MyAppCallback_showScriptMessage("%s", wbuf);
12174                 MyAppCallback_setConsoleColor(0);
12175 #endif
12176                 free(wbuf);
12177         }
12178         
12179         /*  Read default parameters  */
12180         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12181         if (wbuf != NULL) {
12182 #if defined(__CMDMAC__)
12183                 fprintf(stderr, "%s\n", wbuf);
12184 #else
12185                 MyAppCallback_setConsoleColor(1);
12186                 MyAppCallback_showScriptMessage("%s", wbuf);
12187                 MyAppCallback_setConsoleColor(0);
12188 #endif
12189                 free(wbuf);
12190         }
12191                 
12192         /*  Initialize Ruby interpreter  */
12193 #if __WXMSW__
12194         {
12195                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12196                     it causes rb_bug() (= fatal error) during ruby_init().
12197                     As a workaround, these standard streams are reopend as
12198                     NUL stream.  */
12199                 freopen("NUL", "r", stdin);
12200                 freopen("NUL", "w", stdout);
12201                 freopen("NUL", "w", stderr);
12202         }
12203 #endif
12204         ruby_init();
12205
12206         {
12207                 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12208                 Init_shift_jis();
12209                 Init_trans_japanese_sjis();
12210         }
12211         
12212         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12213         ruby_incpush(".");
12214         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12215         ruby_incpush(libpath);
12216         free(libpath);
12217         ruby_incpush(dir);
12218
12219         ruby_script("Molby");
12220         
12221         /*  Find the resource path (the parent directory of the given directory)  */
12222         respath = strdup(dir);
12223         p = strrchr(respath, '/');
12224         if (p == NULL && PATH_SEPARATOR != '/')
12225                 p = strrchr(respath, PATH_SEPARATOR);
12226         if (p != NULL)
12227                 *p = 0;
12228         val = Ruby_NewFileStringValue(respath);
12229         rb_define_global_const("MolbyResourcePath", val);
12230         free(respath);
12231
12232         /*  Define Molby classes  */
12233         Init_Molby();
12234         RubyDialogInitClass();
12235
12236         rb_define_const(rb_mMolby, "ResourcePath", val);
12237         val = Ruby_NewFileStringValue(dir);
12238         rb_define_const(rb_mMolby, "ScriptPath", val);
12239         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12240         val = Ruby_NewFileStringValue(p);
12241         rb_define_const(rb_mMolby, "MbsfPath", val);    
12242         free(p);
12243         
12244         p = MyAppCallback_getHomeDir();
12245         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12246         rb_define_const(rb_mMolby, "HomeDirectory", val);
12247         free(p);
12248         p = MyAppCallback_getDocumentHomeDir();
12249         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12250         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12251         free(p);
12252         
12253 #if defined(__CMDMAC__)
12254         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12255 #else
12256         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12257 #endif
12258
12259 #if !__CMDMAC__
12260         
12261         /*  Create objects for stdout and stderr  */
12262         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12263         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12264         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12265         rb_gv_set("$stdout", val);
12266         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12267         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12268         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12269         rb_gv_set("$stderr", val);
12270
12271         /*  Create objects for stdin  */
12272         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12273         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12274         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12275         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12276         rb_gv_set("$stdin", val);
12277         
12278 #endif
12279         
12280         /*  Global variable to hold error information  */
12281         rb_define_variable("$backtrace", &gMolbyBacktrace);
12282         rb_define_variable("$error_history", &gMolbyErrorHistory);
12283         gMolbyErrorHistory = rb_ary_new();
12284         
12285         /*  Global variables for script menus  */
12286         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12287         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12288         gScriptMenuCommands = rb_ary_new();
12289         gScriptMenuEnablers = rb_ary_new();
12290         
12291 #if !__CMDMAC__
12292         /*  Register interrupt check code  */
12293         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12294 #endif
12295         
12296 #if !__CMDMAC__
12297         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12298         s_SetIntervalTimer(0, 50);
12299 #endif
12300         
12301         /*  Read the startup script  */
12302         if (script != NULL && script[0] != 0) {
12303                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12304                 gMolbyRunLevel++;
12305                 rb_load_protect(rb_str_new2(script), 0, &status);
12306                 gMolbyRunLevel--;
12307                 if (status != 0)
12308                         Ruby_showError(status);
12309                 else
12310                         MyAppCallback_showScriptMessage("Done.\n");
12311         }
12312 }
12313
12314 void
12315 Molby_buildARGV(int argc, const char **argv)
12316 {
12317         int i;
12318     rb_ary_clear(rb_argv);
12319     for (i = 0; i < argc; i++) {
12320                 VALUE arg = rb_tainted_str_new2(argv[i]);
12321                 OBJ_FREEZE(arg);
12322                 rb_ary_push(rb_argv, arg);
12323     }
12324 }