OSDN Git Service

5692d28c9e2942412033a01be6a7f18064b9a76b
[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 /*
95  *  Utility function
96  *  Get ary[i] by calling "[]" method
97  */
98 VALUE
99 Ruby_ObjectAtIndex(VALUE ary, int idx)
100 {
101         static ID index_method = 0;
102         if (TYPE(ary) == T_ARRAY) {
103                 int len = RARRAY_LEN(ary);
104                 if (idx >= 0 && idx < len)
105                         return (RARRAY_PTR(ary))[idx];
106                 else return Qnil;
107         }
108         if (index_method == 0)
109                 index_method = rb_intern("[]");
110         return rb_funcall(ary, index_method, 1, INT2NUM(idx));
111 }
112
113 char *
114 Ruby_FileStringValuePtr(VALUE *valp)
115 {
116 #if __WXMSW__
117         char *p = strdup(StringValuePtr(*valp));
118         translate_char(p, '/', '\\');
119         *valp = rb_str_new2(p);
120         free(p);
121         return StringValuePtr(*valp);
122 #else
123         return StringValuePtr(*valp);
124 #endif
125 }
126
127 VALUE
128 Ruby_NewFileStringValue(const char *fstr)
129 {
130 #if __WXMSW__
131         VALUE retval;
132         char *p = strdup(fstr);
133         translate_char(p, '\\', '/');
134         retval = rb_enc_str_new(p, strlen(p), rb_default_external_encoding());
135         free(p);
136         return retval;
137 #else
138         return rb_str_new2(fstr);
139 #endif
140 }
141
142 char *
143 Ruby_EncodedStringValuePtr(VALUE *valp)
144 {
145         rb_string_value(valp);
146         *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
147         return RSTRING_PTR(*valp);
148 }
149
150 VALUE
151 Ruby_NewEncodedStringValue(const char *str, int len)
152 {
153         if (len <= 0)
154                 len = strlen(str);
155         return rb_enc_str_new(str, len, rb_default_external_encoding());
156 }
157
158 VALUE
159 Ruby_ObjToStringObj(VALUE val)
160 {
161         switch (TYPE(val)) {
162                 case T_STRING:
163                         return val;
164                 case T_SYMBOL:
165                         return rb_str_new2(rb_id2name(SYM2ID(val)));
166                 default:
167                         return rb_str_to_str(val);
168         }
169 }
170
171 #pragma mark ====== Message input/output ======
172
173 /*
174  *  call-seq:
175  *     message_box(str, title, button = nil, icon = :info)
176  *
177  *  Show a message box.
178  *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
179  *  Icon: :info, :warning, :error
180  */
181 static VALUE
182 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
183 {
184         char *str, *title, *s;
185         int buttons, icon;
186         VALUE sval, tval, bval, ival;
187         rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
188         str = StringValuePtr(sval);
189         title = StringValuePtr(tval);
190         if (bval != Qnil) {
191                 bval = Ruby_ObjToStringObj(bval);
192                 s = RSTRING_PTR(bval);
193                 if (strncmp(s, "ok", 2) == 0)
194                         buttons = 1;
195                 else if (strncmp(s, "cancel", 6) == 0)
196                         buttons = 2;
197                 else
198                         rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
199         } else buttons = 3;
200         if (ival != Qnil) {
201                 ival = Ruby_ObjToStringObj(ival);
202                 s = RSTRING_PTR(ival);
203                 if (strncmp(s, "info", 4) == 0)
204                         icon = 1;
205                 else if (strncmp(s, "warn", 4) == 0)
206                         icon = 2;
207                 else if (strncmp(s, "err", 3) == 0)
208                         icon = 3;
209                 else
210                         rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
211         } else icon = 1;
212         MyAppCallback_messageBox(str, title, buttons, icon);
213         return Qnil;
214 }
215
216 /*
217  *  call-seq:
218  *     error_message_box(str)
219  *
220  *  Show an error message box.
221  */
222 static VALUE
223 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
224 {
225         char *str = StringValuePtr(sval);
226         MyAppCallback_errorMessageBox("%s", str);
227         return Qnil;
228 }
229
230 /*
231  *  call-seq:
232  *     ask(prompt, default = nil) -> string
233  *
234  *  Open a modal dialog and get a line of text.
235  */
236 static VALUE
237 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
238 {
239         volatile VALUE prompt, message;
240         char buf[1024];
241         int retval;
242         rb_scan_args(argc, argv, "11", &prompt, &message);
243         if (message != Qnil) {
244                 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
245                 buf[sizeof buf - 1] = 0;
246         } else buf[0] = 0;
247         retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
248         if (retval)
249                 return rb_str_new2(buf);
250         else
251                 return Qnil;    
252 }
253
254 /*
255  *  call-seq:
256  *     show_console_window
257  *
258  *  Show the console window and bring to the front.
259  */
260 static VALUE
261 s_Kernel_ShowConsoleWindow(VALUE self)
262 {
263         MyAppCallback_showConsoleWindow();
264         return Qnil;
265 }
266
267 /*
268  *  call-seq:
269  *     hide_console_window
270  *
271  *  Hide the console window.
272  */
273 static VALUE
274 s_Kernel_HideConsoleWindow(VALUE self)
275 {
276         MyAppCallback_hideConsoleWindow();
277         return Qnil;
278 }
279
280 /*
281  *  call-seq:
282  *     bell
283  *
284  *  Ring the system bell.
285  */
286 static VALUE
287 s_Kernel_Bell(VALUE self)
288 {
289         MyAppCallback_bell();
290         return Qnil;
291 }
292
293 /*
294  *  call-seq:
295  *     play_sound(filename, flag = 0)
296  *
297  *  Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
298  *  1, play the sound asynchronously; 3, play the sound with loop asynchronously
299  */
300 static VALUE
301 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
302 {
303         VALUE fnval, flval;
304         int flag, retval;
305         char *fname;
306         rb_scan_args(argc, argv, "11", &fnval, &flval);
307         if (flval == Qnil)
308                 flag = 0;
309         else flag = NUM2INT(rb_Integer(flval));
310         fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
311         fname = StringValuePtr(fnval);
312         retval = MyAppCallback_playSound(fname, flag);
313         return (retval ? Qtrue : Qnil);
314 }
315
316 /*
317  *  call-seq:
318  *     stop_sound
319  *
320  *  Stop the sound if playing.
321  */
322 static VALUE
323 s_Kernel_StopSound(VALUE self)
324 {
325         MyAppCallback_stopSound();
326         return Qnil;
327 }
328
329 /*
330  *  call-seq:
331  *     export_to_clipboard(str)
332  *
333  *  Export the given string to clipboard.
334  */
335 static VALUE
336 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
337 {
338 #if !defined(__CMDMAC__)
339         const char *s = StringValuePtr(sval);
340         char *ns;
341 #if __WXMSW__
342         /*  Convert the end-of-line characters  */
343         {       const char *p; int nc; char *np;
344                 nc = 0;
345                 for (p = s; *p != 0; p++) {
346                         if (*p == '\n')
347                                 nc++;
348                 }       
349                 ns = (char *)malloc(strlen(s) + nc + 1);
350                 for (np = ns, p = s; *p != 0; p++, np++) {
351                         if (*p == '\n')
352                                 *np++ = '\r';
353                         *np = *p;
354                 }
355                 *np = 0;
356         }
357 #else
358         ns = (char *)malloc(strlen(s) + 1);
359         strcpy(ns, s);
360 #if __WXMAC__
361         {       char *np;
362                 /*  wxMac still has Carbon code. Oops.  */
363                 for (np = ns; *np != 0; np++) {
364                         if (*np == '\n')
365                                 *np = '\r';
366                 }
367         }
368 #endif
369 #endif
370         if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
371                 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
372 #endif
373         return Qnil;
374 }
375
376 /*
377  *  call-seq:
378  *     stdout.write(str)
379  *
380  *  Put the message in the main text view in black color.
381  */
382 static VALUE
383 s_StandardOutput(VALUE self, VALUE str)
384 {
385         int n;
386         MyAppCallback_setConsoleColor(0);
387         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
388         return INT2NUM(n);
389 }
390
391 /*
392  *  call-seq:
393  *     stderr.write(str)
394  *
395  *  Put the message in the main text view in red color.
396  */
397 static VALUE
398 s_StandardErrorOutput(VALUE self, VALUE str)
399 {
400         int n;
401         MyAppCallback_setConsoleColor(1);
402         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
403         MyAppCallback_setConsoleColor(0);
404         return INT2NUM(n);
405 }
406
407 /*
408  *  call-seq:
409  *     stdout.flush
410  *     stderr.flush
411  *
412  *  Flush the standard (error) output. Actually do nothing.
413  */
414 static VALUE
415 s_FlushConsoleOutput(VALUE self)
416 {
417         return self;
418 }
419
420 /*
421  *  call-seq:
422  *     stdin.gets(rs = $/)
423  *
424  *  Read one line message via dialog box.
425  */
426 static VALUE
427 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
428 {
429         VALUE pval, rval;
430         pval = rb_str_new2("Enter a line:");
431         rval = s_Kernel_Ask(1, &pval, self);
432         if (rval == Qnil)
433                 rb_interrupt();
434         rb_str_cat2(rval, "\n");
435         return rval;
436 }
437
438 /*
439  *  call-seq:
440  *     stdin.method_missing(name, args, ...)
441  *
442  *  Throw an exception, noting only gets and readline are defined.
443  */
444 static VALUE
445 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
446 {
447         VALUE nval;
448         rb_scan_args(argc, argv, "10", &nval);
449         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
450         return Qnil;  /*  Not reached  */
451 }
452
453 #pragma mark ====== Track key events ======
454
455 /*  User interrupt handling
456  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
457  *  key events. This polling should only be enabled during "normal" execution
458  *  of scripts and must be disabled when the rest of the application (or Ruby
459  *  script itself) is handling GUI. This is ensured by appropriate calls to
460  *  enable_interrupt and disable_interrupt.  */
461
462 static VALUE s_interrupt_flag = Qfalse;
463
464 static VALUE
465 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
466 {
467         volatile VALUE message;
468         const char *p;
469         if (Ruby_GetInterruptFlag() == Qtrue) {
470                 rb_scan_args(argc, argv, "01", &message);
471                 if (message != Qnil)
472                         p = StringValuePtr(message);
473                 else
474                         p = NULL;
475                 MyAppCallback_showProgressPanel(p);
476         }
477         return Qnil;
478 }
479
480 static VALUE
481 s_HideProgressPanel(VALUE self)
482 {
483         MyAppCallback_hideProgressPanel();
484         return Qnil;
485 }
486
487 static VALUE
488 s_SetProgressValue(VALUE self, VALUE val)
489 {
490         double dval = NUM2DBL(rb_Float(val));
491         MyAppCallback_setProgressValue(dval);
492         return Qnil;
493 }
494
495 static VALUE
496 s_SetProgressMessage(VALUE self, VALUE msg)
497 {
498         const char *p;
499         if (msg == Qnil)
500                 p = NULL;
501         else p = StringValuePtr(msg);
502         MyAppCallback_setProgressMessage(p);
503         return Qnil;
504 }
505
506 static VALUE
507 s_SetInterruptFlag(VALUE self, VALUE val)
508 {
509         VALUE oldval;
510         if (val != Qundef) {
511                 if (val == Qfalse || val == Qnil)
512                         val = Qfalse;
513                 else val = Qtrue;
514         }
515         oldval = s_interrupt_flag;
516         if (val != Qundef) {
517                 s_interrupt_flag = val;
518                 if (val == Qfalse) {
519                         s_HideProgressPanel(self);
520                 }
521         }
522         return oldval;
523 }
524
525 static VALUE
526 s_GetInterruptFlag(VALUE self)
527 {
528         return s_SetInterruptFlag(self, Qundef);
529 }
530
531 VALUE
532 Ruby_SetInterruptFlag(VALUE val)
533 {
534         return s_SetInterruptFlag(Qnil, val);
535 }
536
537 VALUE
538 Ruby_GetInterruptFlag(void)
539 {
540         return s_SetInterruptFlag(Qnil, Qundef);
541 }
542
543 /*
544  *  call-seq:
545  *     check_interrupt -> integer
546  *
547  *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
548  */
549 static VALUE
550 s_Kernel_CheckInterrupt(VALUE self)
551 {
552         if (Ruby_GetInterruptFlag() == Qfalse)
553                 return INT2NUM(-1);
554         else if (MyAppCallback_checkInterrupt())
555                 return INT2NUM(1);
556         else return INT2NUM(0);
557 }
558
559 static volatile unsigned long sITimerCount = 0;
560
561 #if __WXMSW__
562 static HANDLE sITimerEvent;
563 static HANDLE sITimerThread;
564 static int sITimerInterval;
565
566 static __stdcall unsigned
567 s_ITimerThreadFunc(void *p)
568 {
569         while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
570                 sITimerCount++;
571         }
572         return 0;
573 }
574
575 #elif USE_PTHREAD_FOR_TIMER
576
577 /*  Timer thread  */
578 static pthread_t sTimerThread;
579
580 /*  -1: uninitiated; 0: active, 1: inactive, -2: request to terminate  */
581 static volatile signed char sTimerFlag = -1;
582 static volatile int sTimerIntervalMicrosec = 0;
583
584 static void *
585 s_TimerThreadEntry(void *param)
586 {
587         while (1) {
588                 usleep(sTimerIntervalMicrosec);
589                 if (sTimerFlag == 0)
590                         sITimerCount++;
591                 else if (sTimerFlag == -2)
592                         break;
593         }
594         return NULL;    
595 }
596
597 #endif
598
599 static void
600 s_SignalAction(int n)
601 {
602         sITimerCount++;
603 }
604
605 static void
606 s_SetIntervalTimer(int n, int msec)
607 {
608 #if __WXMSW__
609         if (n == 0) {
610                 /*  Start interval timer  */
611                 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
612                 sITimerInterval = msec;
613                 if (sITimerEvent) {
614                         sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
615                 }
616         } else {
617                 /*  Stop interval timer  */
618                 if (sITimerEvent)
619                         SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
620                 if (sITimerThread) {
621                         WaitForSingleObject(sITimerThread, 1000);
622                         CloseHandle(sITimerThread);
623                 }
624                 if (sITimerEvent)
625                         CloseHandle(sITimerEvent);
626                 sITimerEvent = NULL;
627                 sITimerThread = NULL;
628         }
629 #elif USE_PTHREAD_FOR_TIMER
630         if (n == 0) {
631                 if (sTimerFlag == -1) {
632                         int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
633                         if (status != 0) {
634                                 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
635                         }
636                 }
637                 sTimerFlag = 0;  /*  Active  */
638                 sTimerIntervalMicrosec = msec * 1000;
639         } else if (sTimerFlag != -1)
640                 sTimerFlag = 1;  /*  Inactive  */       
641 #else
642         static struct itimerval sOldValue;
643         static struct sigaction sOldAction;
644         struct itimerval val;
645         struct sigaction act;
646         if (n == 0) {
647                 sITimerCount = 0;
648                 act.sa_handler = s_SignalAction;
649                 act.sa_mask = 0;
650                 act.sa_flags = 0;
651                 sigaction(SIGALRM, &act, &sOldAction);
652                 val.it_value.tv_sec = 0;
653                 val.it_value.tv_usec = msec * 1000;
654                 val.it_interval.tv_sec = 0;
655                 val.it_interval.tv_usec = msec * 1000;
656                 setitimer(ITIMER_REAL, &val, &sOldValue);
657         } else {
658                 setitimer(ITIMER_REAL, &sOldValue, &val);
659                 sigaction(SIGALRM, &sOldAction, &act);
660         }
661 #endif
662 }
663
664 static unsigned long
665 s_GetTimerCount(void)
666 {
667         return sITimerCount;
668 }
669
670 static void
671 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
672 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
673 {
674         if (s_interrupt_flag != Qfalse) {
675                 static unsigned long sLastTime = 0;
676                 unsigned long currentTime;
677                 int flag;
678                 currentTime = s_GetTimerCount();
679                 if (currentTime != sLastTime) {
680                         sLastTime = currentTime;
681                         gMolbyIsCheckingInterrupt = 1;
682                         flag = MyAppCallback_checkInterrupt();
683                         gMolbyIsCheckingInterrupt = 0;
684                         if (flag) {
685                                 s_SetInterruptFlag(Qnil, Qfalse);
686                                 rb_interrupt();
687                         }
688                 }
689         }
690 }
691
692 #pragma mark ====== Menu handling ======
693
694 /*
695  *  call-seq:
696  *     register_menu(title, method, enable_proc = nil)
697  *
698  *  Register the method (specified as a symbol) in the script menu.
699  *  The method must be either an instance method of Molecule with no argument,
700  *  or a class method of Molecule with one argument (the current molecule),
701  *  or a proc object with one argument (the current molecule).
702  *  The menu associated with the class method can be invoked even when no document
703  *  is open (the argument is set to Qnil in this case). On the other hand, the
704  *  menu associated with the instance method can only be invoked when at least one 
705  *  document is active.
706  *  If enable_proc is non-nil, then it is called whenever the availability of
707  *  the menu command is tested. It is usually a proc object with one argument
708  *  (the current molecule or nil). As a special case, the following symbols can
709  *  be given; :mol (enabled when any molecule is open), :non_empty (enabled when
710  *  the top-level molecule has at least one atom), :selection (enabled when
711  *  the top-level molecule has one or more selected atoms).
712  */
713 static VALUE
714 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
715 {
716         int n, mtype = 0;
717         VALUE tval, mval, pval;
718         static VALUE sMolSym, sNonEmptySym, sSelectionSym;
719         static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
720         rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
721         tval = rb_str_to_str(tval);
722         n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
723         if (n < 0)
724                 return Qnil;
725         if (TYPE(mval) == T_SYMBOL) {
726                 /*  Create an appropriate proc object  */
727                 const char *name = rb_id2name(SYM2ID(mval));
728                 char *s;
729                 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
730                         /*  Defined as a Molecule method  */
731                         asprintf(&s, "lambda { |m| m.%s }", name);
732                         mtype = 1;
733                 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
734                         /*  Defined as a Molecule class method  */
735                         asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
736                         mtype = 2;
737                 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
738                 mval = rb_eval_string(s);
739                 free(s);
740         }
741         if (sMolSym == Qfalse) {
742                 sMolSym = ID2SYM(rb_intern("mol"));
743                 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
744                 sSelectionSym = ID2SYM(rb_intern("selection"));
745                 sMolProc = rb_eval_string("lambda { |m| m != nil }");
746                 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
747                 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
748                 sTrueProc = rb_eval_string("lambda { |m| true }");
749                 rb_global_variable(&sMolProc);
750                 rb_global_variable(&sNonEmptyProc);
751                 rb_global_variable(&sSelectionProc);
752                 rb_global_variable(&sTrueProc);
753         }
754         
755         if (pval == Qnil) {
756                 if (mtype == 1)
757                         pval = sMolProc;
758                 else
759                         pval = sTrueProc;
760         } else if (pval == sMolSym)
761                 pval = sMolProc;
762         else if (pval == sNonEmptySym)
763                 pval = sNonEmptyProc;
764         else if (pval == sSelectionSym)
765                 pval = sSelectionProc;
766         rb_ary_store(gScriptMenuCommands, n, mval);
767         rb_ary_store(gScriptMenuEnablers, n, pval);
768         return INT2NUM(n);
769 }
770
771 static VALUE
772 s_Kernel_LookupMenu(VALUE self, VALUE title)
773 {
774         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
775         return INT2NUM(n);
776 }
777
778 static VALUE
779 s_Ruby_UpdateUI_handler(VALUE data)
780 {
781         void **p = (void **)data;
782         int index = (int)p[0];
783         Molecule *mol = (Molecule *)p[1];
784         int *outChecked = (int *)p[2];
785         char **outTitle = (char **)p[3];
786         VALUE mval = ValueFromMolecule(mol);
787         VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
788         static ID call_id = 0;
789         if (call_id == 0)
790                 call_id = rb_intern("call");
791         if (pval == Qnil)
792                 return Qnil;
793         pval = rb_funcall(pval, call_id, 1, mval);
794         if (rb_obj_is_kind_of(pval, rb_cArray)) {
795                 VALUE val;
796                 if (outChecked != NULL) {
797                         val = rb_ary_entry(pval, 1);  /*  Checked or not  */
798                         *outChecked = (RTEST(val) ? 1 : 0);
799                 }
800                 if (outTitle != NULL) {
801                         val = rb_ary_entry(pval, 2);  /*  Text  */
802                         if (TYPE(val) == T_STRING) {
803                                 *outTitle = strdup(StringValuePtr(val));
804                         }
805                 }
806                 pval = rb_ary_entry(pval, 0);
807         }
808         return pval;
809 }
810
811 int
812 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
813 {
814         int status;
815         void *p[4];
816         VALUE retval;
817         p[0] = (void *)index;
818         p[1] = mol;
819         p[2] = outChecked;
820         p[3] = outTitle;
821         retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
822         return (RTEST(retval) ? 1 : 0);
823 }
824
825 /*
826 static VALUE
827 s_Ruby_methodType_sub(VALUE data)
828 {
829         const char **p = (const char **)data;
830         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
831         ID mid = rb_intern(p[1]);
832         int ival;
833         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
834                 ival = 1;
835         else if (rb_respond_to(klass, mid))
836                 ival = 2;
837         else ival = 0;
838         return INT2FIX(ival);
839 }
840 */      
841 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
842     has the singleton method (class method) with the given name, 0 otherwise.  */
843 /*int
844 Ruby_methodType(const char *className, const char *methodName)
845 {
846         int status;
847         VALUE retval;
848         const char *p[2];
849         p[0] = className;
850         p[1] = methodName;
851         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
852         if (status == 0)
853                 return FIX2INT(retval);
854         else return 0;
855 }
856 */
857
858 /*
859  *  call-seq:
860  *     execute_script_file(fname)
861  *
862  *  Execute the script in the given file. If a molecule is active, then
863  *  the script is evaluated as Molecule.current.instance_eval(script).
864  *  Before entering the script, the current directory is set to the parent
865  *  directory of the script.
866  */
867 static VALUE
868 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
869 {
870         int status;
871         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
872         if (retval == (VALUE)6 && status == -1)
873                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
874         if (status != 0)
875                 rb_jump_tag(status);
876         return retval;
877 }
878
879 /*
880  *  call-seq:
881  *     document_home
882  *
883  *  Get the directory suitable for storing user documents. On Windows
884  *  it is the home directory + "My Documents". On other platforms
885  *  it is the home directory.
886  */
887 static VALUE
888 s_Kernel_DocumentHome(VALUE self)
889 {
890         char *s = MyAppCallback_getDocumentHomeDir();
891         VALUE retval = Ruby_NewFileStringValue(s);
892         free(s);
893         return retval;
894 }
895
896 /*  The callback function for call_subprocess  */
897 static int
898 s_Kernel_CallSubProcess_Callback(void *data)
899 {
900         int status;
901         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
902         if (status != 0 || retval == Qnil || retval == Qfalse)
903                 return 1;
904         else return 0;
905 }
906
907 /*
908  *  call-seq:
909  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
910  *
911  *  Call subprocess. A progress dialog window is displayed, with a message
912  *  "Running #{process_name}...".
913  *  A callback proc can be given, which is called periodically during execution. If the proc returns
914  *  nil or false, then the execution will be interrupted.
915  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
916  *  filename begins with ">>", then the message will be appended to the file.
917  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
918  *  If the argument is nil, then the message will be sent to the Ruby console.
919  */
920 static VALUE
921 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
922 {
923         VALUE cmd, procname, cproc, stdout_val, stderr_val;
924         int n;
925         char *sout, *serr;
926         FILE *fpout, *fperr;
927
928         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
929
930         if (stdout_val == Qnil) {
931                 fpout = (FILE *)1;
932         } else {
933                 sout = StringValuePtr(stdout_val);
934                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
935                         fpout = NULL;
936                 else {
937                         if (strncmp(sout, ">>", 2) == 0) {
938                                 sout += 2;
939                                 fpout = fopen(sout, "a");
940                         } else {
941                                 if (*sout == '>')
942                                         sout++;
943                                 fpout = fopen(sout, "w");
944                         }
945                         if (fpout == NULL)
946                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
947                 }
948         }
949         if (stderr_val == Qnil) {
950                 fperr = (FILE *)1;
951         } else {
952                 serr = StringValuePtr(stderr_val);
953                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
954                         fperr = NULL;
955                 else {
956                         if (strncmp(serr, ">>", 2) == 0) {
957                                 serr += 2;
958                                 fpout = fopen(serr, "a");
959                         } else {
960                                 if (*serr == '>')
961                                         serr++;
962                                 fperr = fopen(serr, "w");
963                         }
964                         if (fperr == NULL)
965                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
966                 }
967         }
968
969         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
970         
971         if (fpout != NULL && fpout != (FILE *)1)
972                 fclose(fpout);
973         if (fperr != NULL && fperr != (FILE *)1)
974                 fclose(fperr);
975
976         return INT2NUM(n);
977
978         
979 }
980
981 /*
982  *  call-seq:
983  *     backquote(cmd)
984  *
985  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
986  */
987 static VALUE
988 s_Kernel_Backquote(VALUE self, VALUE cmd)
989 {
990         char *buf;
991         int n;
992         VALUE val;
993         n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
994         if (n != 0)
995                 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
996         if (buf != NULL) {
997                 val = Ruby_NewEncodedStringValue(buf, 0);
998                 free(buf);
999         } else {
1000                 val = Ruby_NewEncodedStringValue("", 0);
1001         }
1002         return val;
1003 }
1004
1005 #pragma mark ====== User defaults ======
1006
1007 /*
1008  *  call-seq:
1009  *     get_global_settings(key)
1010  *
1011  *  Get a setting data for key from the application preferences.
1012  */
1013 static VALUE
1014 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1015 {
1016         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1017         if (p != NULL) {
1018                 VALUE retval = rb_eval_string(p);
1019                 free(p);
1020                 return retval;
1021         } else return Qnil;
1022 }
1023
1024 /*
1025  *  call-seq:
1026  *     set_global_settings(key, value)
1027  *
1028  *  Set a setting data for key to the application preferences.
1029  */
1030 static VALUE
1031 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1032 {
1033         VALUE sval = rb_inspect(value);
1034         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1035         return value;
1036 }
1037
1038 #pragma mark ====== IO extension ======
1039
1040 /*
1041  *  call-seq:
1042  *     gets_any_eol
1043  *
1044  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1045  */
1046 static VALUE
1047 s_IO_gets_any_eol(VALUE self)
1048 {
1049         VALUE val, cval;
1050         char buf[1024];
1051         int i, c;
1052         static ID id_getbyte = 0, id_ungetbyte;
1053         if (id_getbyte == 0) {
1054                 id_getbyte = rb_intern("getbyte");
1055                 id_ungetbyte = rb_intern("ungetbyte");
1056         }
1057         i = 0;
1058         val = Qnil;
1059         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1060                 c = NUM2INT(rb_Integer(cval));
1061                 if (c == 0x0d) {
1062                         cval = rb_funcall(self, id_getbyte, 0);
1063                         if (cval != Qnil) {
1064                                 c = NUM2INT(rb_Integer(cval));
1065                                 if (c != 0x0a)
1066                                         rb_funcall(self, id_ungetbyte, 1, cval);
1067                         }
1068                 } else if (c != 0x0a) {
1069                         buf[i++] = c;
1070                         if (i >= 1020) {
1071                                 buf[i] = 0;
1072                                 if (val == Qnil)
1073                                         val = rb_str_new(buf, i);
1074                                 else
1075                                         rb_str_append(val, rb_str_new(buf, i));
1076                                 i = 0;
1077                         }
1078                 } else break;
1079         }
1080         if (cval == Qnil && i == 0 && val == Qnil)
1081                 return Qnil;  /*  End of file  */
1082         buf[i] = 0;
1083         if (val == Qnil)
1084                 val = rb_str_new(buf, i);
1085         else if (i > 0)
1086                 rb_str_append(val, rb_str_new(buf, i));
1087         val = rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
1088         if (cval != Qnil) {
1089                 /*  Needs a end-of-line mark  */
1090                 cval = rb_gv_get("$/");
1091                 rb_str_append(val, cval);
1092         }
1093         rb_gv_set("$_", val);
1094         return val;
1095 }
1096
1097 #pragma mark ====== Utility functions (protected funcall) ======
1098
1099 struct Ruby_funcall2_record {
1100         VALUE recv;
1101         ID mid;
1102         int argc;
1103         VALUE *argv;
1104 };
1105
1106 static VALUE
1107 s_Ruby_funcall2_sub(VALUE data)
1108 {
1109         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1110         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1111 }
1112
1113 VALUE
1114 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1115 {
1116         struct Ruby_funcall2_record rec;
1117         rec.recv = recv;
1118         rec.mid = mid;
1119         rec.argc = argc;
1120         rec.argv = argv;
1121         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1122 }
1123
1124 RubyValue
1125 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1126 {
1127         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1128 }
1129
1130 #pragma mark ====== ParameterRef Class ======
1131
1132 static UnionPar *
1133 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1134 {
1135         UnionPar *up;
1136         ParameterRef *pref;
1137         Data_Get_Struct(self, ParameterRef, pref);
1138         if (typep != NULL)
1139                 *typep = pref->parType;
1140         if (pref->parType == kElementParType) {
1141                 up = (UnionPar *)&gElementParameters[pref->idx];
1142         } else {
1143                 up = ParameterRefGetPar(pref);
1144                 if (checkEditable) {
1145                         if (pref->idx < 0)
1146                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1147                         if (up->bond.src != 0 && up->bond.src != -1)
1148                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1149                 }
1150         }
1151         return up;
1152 }
1153
1154 static void
1155 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1156 {
1157         UnionPar *up;
1158         ParameterRef *pref;
1159         Data_Get_Struct(self, ParameterRef, pref);
1160         if (pref->mol == NULL)
1161                 return;
1162         up = ParameterRefGetPar(pref);
1163         if (key != s_SourceSym)
1164                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1165         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1166                 /*  Register undo  */
1167                 MolAction *act;
1168                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1169                 MolActionCallback_registerUndo(pref->mol, act);
1170                 MoleculeCallback_notifyModification(pref->mol, 0);
1171                 pref->mol->needsMDRebuild = 1;
1172         }
1173 }
1174
1175 VALUE
1176 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1177 {
1178         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1179         if (pref != NULL)
1180                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1181         else
1182                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1183 }
1184
1185 static int
1186 s_AtomTypeIndexFromValue(VALUE val)
1187 {
1188         if (rb_obj_is_kind_of(val, rb_cNumeric))
1189                 return NUM2INT(val);
1190         else
1191                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1192 }
1193
1194 static const char *s_ParameterTypeNames[] = {
1195         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1196 };
1197 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1198
1199 static int
1200 s_ParTypeFromValue(VALUE val)
1201 {
1202         int i, n;
1203         ID valid;
1204         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1205         if (s_ParameterTypeIDs[0] == 0) {
1206                 for (i = 0; i < n; i++)
1207                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1208         }
1209         valid = rb_to_id(val);
1210         for (i = 0; i < n; i++) {
1211                 if (valid == s_ParameterTypeIDs[i]) {
1212                         if (i == 7)
1213                                 return kElementParType;
1214                         else return kFirstParType + i;
1215                 }
1216         }
1217         return kInvalidParType;
1218 }
1219
1220 /*
1221  *  call-seq:
1222  *     index -> Integer
1223  *
1224  *  Get the index in the parameter list.
1225  */
1226 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1227         ParameterRef *pref;
1228         Data_Get_Struct(self, ParameterRef, pref);
1229         return INT2NUM(pref->idx);
1230 }
1231
1232 /*
1233  *  call-seq:
1234  *     par_type -> String
1235  *
1236  *  Get the parameter type, like "bond", "angle", etc.
1237  */
1238 static VALUE s_ParameterRef_GetParType(VALUE self) {
1239         Int tp;
1240         s_UnionParFromValue(self, &tp, 0);
1241         if (tp == kElementParType)
1242                 return rb_str_new2("element");
1243         tp -= kFirstParType;
1244         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1245                 return rb_str_new2(s_ParameterTypeNames[tp]);
1246         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1247 }
1248
1249 /*
1250  *  call-seq:
1251  *     atom_type -> String or Array of String
1252  *     atom_types -> String or Array of String
1253  *
1254  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1255  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1256  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1257  *  The atom type may be "X", which is a wildcard that matches any atom type.
1258  */
1259 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1260         UnionPar *up;
1261         Int tp, i, n;
1262         UInt types[4];
1263         VALUE vals[4];
1264         up = s_UnionParFromValue(self, &tp, 0);
1265         n = ParameterGetAtomTypes(tp, up, types);
1266         if (n == 0)
1267                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1268         for (i = 0; i < n; i++) {
1269                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1270                         vals[i] = INT2NUM(types[i]);
1271                 else
1272                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1273         }
1274         if (n == 1)
1275                 return vals[0];
1276         else
1277                 return rb_ary_new4(n, vals);
1278 }
1279
1280 /*
1281  *  call-seq:
1282  *     k -> Float
1283  *
1284  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1285  */
1286 static VALUE s_ParameterRef_GetK(VALUE self) {
1287         UnionPar *up;
1288         Int tp, i, n;
1289         VALUE vals[3];
1290         up = s_UnionParFromValue(self, &tp, 0);
1291         switch (tp) {
1292                 case kBondParType:
1293                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1294                 case kAngleParType:
1295                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1296                 case kDihedralParType:
1297                 case kImproperParType:
1298                         if (up->torsion.mult == 1)
1299                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1300                         n = up->torsion.mult;
1301                         if (n > 3)
1302                                 n = 3;
1303                         for (i = 0; i < n; i++)
1304                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1305                         return rb_ary_new4(n, vals);
1306                 default:
1307                         rb_raise(rb_eMolbyError, "invalid member k");
1308         }
1309 }
1310
1311 /*
1312  *  call-seq:
1313  *     r0 -> Float
1314  *
1315  *  Get the equilibrium bond length. Only available for bond parameters.
1316  */
1317 static VALUE s_ParameterRef_GetR0(VALUE self) {
1318         UnionPar *up;
1319         Int tp;
1320         up = s_UnionParFromValue(self, &tp, 0);
1321         if (tp == kBondParType)
1322                 return rb_float_new(up->bond.r0);
1323         else rb_raise(rb_eMolbyError, "invalid member r0");
1324 }
1325
1326 /*
1327  *  call-seq:
1328  *     a0 -> Float
1329  *
1330  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1331  */
1332 static VALUE s_ParameterRef_GetA0(VALUE self) {
1333         UnionPar *up;
1334         Int tp;
1335         up = s_UnionParFromValue(self, &tp, 0);
1336         if (tp == kAngleParType)
1337                 return rb_float_new(up->angle.a0 * kRad2Deg);
1338         else rb_raise(rb_eMolbyError, "invalid member a0");
1339 }
1340
1341 /*
1342  *  call-seq:
1343  *     mult -> Float
1344  *
1345  *  Get the multiplicity. Only available for dihedral and improper parameters.
1346  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1347  */
1348 static VALUE s_ParameterRef_GetMult(VALUE self) {
1349         UnionPar *up;
1350         Int tp;
1351         up = s_UnionParFromValue(self, &tp, 0);
1352         if (tp == kDihedralParType || tp == kImproperParType)
1353                 return rb_float_new(up->torsion.mult);
1354         else rb_raise(rb_eMolbyError, "invalid member mult");
1355 }
1356
1357 /*
1358  *  call-seq:
1359  *     period -> Integer or Array of Integers
1360  *
1361  *  Get the periodicity. Only available for dihedral and improper parameters.
1362  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1363  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1364  */
1365 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1366         UnionPar *up;
1367         Int tp, i, n;
1368         VALUE vals[3];
1369         up = s_UnionParFromValue(self, &tp, 0);
1370         if (tp == kDihedralParType || tp == kImproperParType) {
1371                 if (up->torsion.mult == 1)
1372                         return INT2NUM(up->torsion.period[0]);
1373                 n = up->torsion.mult;
1374                 if (n > 3)
1375                         n = 3;
1376                 for (i = 0; i < n; i++)
1377                         vals[i] = INT2NUM(up->torsion.period[i]);
1378                 return rb_ary_new4(n, vals);
1379         } else rb_raise(rb_eMolbyError, "invalid member period");
1380 }
1381
1382 /*
1383  *  call-seq:
1384  *     phi0 -> Float or Array of Floats
1385  *
1386  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1387  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1388  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1389  */
1390 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1391         UnionPar *up;
1392         Int tp, i, n;
1393         VALUE vals[3];
1394         up = s_UnionParFromValue(self, &tp, 0);
1395         if (tp == kDihedralParType || tp == kImproperParType) {
1396                 if (up->torsion.mult == 1)
1397                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1398                 n = up->torsion.mult;
1399                 if (n > 3)
1400                         n = 3;
1401                 for (i = 0; i < n; i++)
1402                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1403                 return rb_ary_new4(n, vals);
1404         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1405 }
1406
1407 /*
1408  *  call-seq:
1409  *     A -> Float
1410  *
1411  *  Get the "A" value for the van der Waals parameter.
1412  */
1413 /*
1414  static VALUE s_ParameterRef_GetA(VALUE self) {
1415         UnionPar *up;
1416         Int tp;
1417         up = s_UnionParFromValue(self, &tp, 0);
1418         if (tp == kVdwParType)
1419                 return rb_float_new(up->vdw.A);
1420         else if (tp == kVdwPairParType)
1421                 return rb_float_new(up->vdwp.A);
1422         else rb_raise(rb_eMolbyError, "invalid member A");
1423 }
1424 */
1425
1426 /*
1427  *  call-seq:
1428  *     B -> Float
1429  *
1430  *  Get the "B" value for the van der Waals parameter.
1431  */
1432 /*
1433 static VALUE s_ParameterRef_GetB(VALUE self) {
1434         UnionPar *up;
1435         Int tp;
1436         up = s_UnionParFromValue(self, &tp, 0);
1437         if (tp == kVdwParType)
1438                 return rb_float_new(up->vdw.B);
1439         else if (tp == kVdwPairParType)
1440                 return rb_float_new(up->vdwp.B);
1441         else rb_raise(rb_eMolbyError, "invalid member B");
1442 }
1443 */
1444
1445 /*
1446  *  call-seq:
1447  *     r_eq -> Float
1448  *
1449  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1450  */
1451 static VALUE s_ParameterRef_GetReq(VALUE self) {
1452         UnionPar *up;
1453         Int tp;
1454 /*      Double a, b, r; */
1455         Double r;
1456         up = s_UnionParFromValue(self, &tp, 0);
1457         if (tp == kVdwParType) {
1458         /*      a = up->vdw.A;
1459                 b = up->vdw.B;  */
1460                 r = up->vdw.r_eq;
1461         } else if (tp == kVdwPairParType) {
1462         /*      a = up->vdwp.A;
1463                 b = up->vdwp.B;  */
1464                 r = up->vdwp.r_eq;
1465         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1466 /*      if (a == 0.0 || b == 0.0) */
1467         return rb_float_new(r);
1468 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1469 }
1470
1471 /*
1472  *  call-seq:
1473  *     eps -> Float
1474  *
1475  *  Get the minimum energy for the van der Waals parameter.
1476  */
1477 static VALUE s_ParameterRef_GetEps(VALUE self) {
1478         UnionPar *up;
1479         Int tp;
1480 /*      Double a, b; */
1481         Double eps;
1482         up = s_UnionParFromValue(self, &tp, 0);
1483         if (tp == kVdwParType) {
1484         /*      a = up->vdw.A;
1485                 b = up->vdw.B;  */
1486                 eps = up->vdw.eps;
1487         } else if (tp == kVdwPairParType) {
1488         /*      a = up->vdwp.A;
1489                 b = up->vdwp.B; */
1490                 eps = up->vdwp.eps;
1491         } else rb_raise(rb_eMolbyError, "invalid member eps");
1492 /*      if (a == 0.0 || b == 0.0)  */
1493                 return rb_float_new(eps * INTERNAL2KCAL);
1494 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1495 }
1496
1497 /*
1498  *  call-seq:
1499  *     A14 -> Float
1500  *
1501  *  Get the "A" value for the 1-4 van der Waals parameter.
1502  */
1503 /*
1504 static VALUE s_ParameterRef_GetA14(VALUE self) {
1505         UnionPar *up;
1506         Int tp;
1507         up = s_UnionParFromValue(self, &tp, 0);
1508         if (tp == kVdwParType)
1509                 return rb_float_new(up->vdw.A14);
1510         else if (tp == kVdwPairParType)
1511                 return rb_float_new(up->vdwp.A14);
1512         else rb_raise(rb_eMolbyError, "invalid member A14");
1513 }
1514 */
1515
1516 /*
1517  *  call-seq:
1518  *     B14 -> Float
1519  *
1520  *  Get the "B" value for the 1-4 van der Waals parameter.
1521  */
1522 /*
1523 static VALUE s_ParameterRef_GetB14(VALUE self) {
1524         UnionPar *up;
1525         Int tp;
1526         up = s_UnionParFromValue(self, &tp, 0);
1527         if (tp == kVdwParType)
1528                 return rb_float_new(up->vdw.B14);
1529         else if (tp == kVdwPairParType)
1530                 return rb_float_new(up->vdwp.B14);
1531         else rb_raise(rb_eMolbyError, "invalid member B14");
1532 }
1533 */
1534
1535 /*
1536  *  call-seq:
1537  *     r_eq14 -> Float
1538  *
1539  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1540  */
1541 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1542         UnionPar *up;
1543         Int tp;
1544 /*      Double a, b, r; */
1545         Double r;
1546         up = s_UnionParFromValue(self, &tp, 0);
1547         if (tp == kVdwParType) {
1548         /*      a = up->vdw.A14;
1549                 b = up->vdw.B14; */
1550                 r = up->vdw.r_eq14;
1551         } else if (tp == kVdwPairParType) {
1552         /*      a = up->vdwp.A14;
1553                 b = up->vdwp.B14;  */
1554                 r = up->vdwp.r_eq14;
1555         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1556 /*      if (a == 0.0 || b == 0.0)  */
1557         return rb_float_new(r);
1558 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1559 }
1560
1561 /*
1562  *  call-seq:
1563  *     eps14 -> Float
1564  *
1565  *  Get the minimum energy for the 1-4 van der Waals parameter.
1566  */
1567 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1568         UnionPar *up;
1569         Int tp;
1570 /*      Double a, b;  */
1571         Double eps;
1572         up = s_UnionParFromValue(self, &tp, 0);
1573         if (tp == kVdwParType) {
1574         /*      a = up->vdw.A14;
1575                 b = up->vdw.B14;  */
1576                 eps = up->vdw.eps14;
1577         } else if (tp == kVdwPairParType) {
1578         /*      a = up->vdwp.A14;
1579                 b = up->vdwp.B14; */
1580                 eps = up->vdwp.eps14;
1581         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1582 /*      if (a == 0.0 || b == 0.0) */
1583         return rb_float_new(eps * INTERNAL2KCAL);
1584 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1585 }
1586
1587 /*
1588  *  call-seq:
1589  *     cutoff -> Float
1590  *
1591  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1592  */
1593 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1594         UnionPar *up;
1595         Int tp;
1596         up = s_UnionParFromValue(self, &tp, 0);
1597         if (tp == kVdwCutoffParType)
1598                 return rb_float_new(up->vdwcutoff.cutoff);
1599         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1600 }
1601
1602 /*
1603  *  call-seq:
1604  *     radius -> Float
1605  *
1606  *  Get the atomic (covalent) radius for the element parameter.
1607  */
1608 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1609         UnionPar *up;
1610         Int tp;
1611         up = s_UnionParFromValue(self, &tp, 0);
1612         if (tp == kElementParType)
1613                 return rb_float_new(up->atom.radius);
1614         else rb_raise(rb_eMolbyError, "invalid member radius");
1615 }
1616
1617 /*
1618  *  call-seq:
1619  *     vdw_radius -> Float
1620  *
1621  *  Get the van der Waals radius for the element parameter. (0 if not given)
1622  */
1623 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1624         UnionPar *up;
1625         Int tp;
1626         up = s_UnionParFromValue(self, &tp, 0);
1627         if (tp == kElementParType)
1628                 return rb_float_new(up->atom.vdw_radius);
1629         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1630 }
1631
1632 /*
1633  *  call-seq:
1634  *     color -> [Float, Float, Float]
1635  *
1636  *  Get the rgb color for the element parameter.
1637  */
1638 static VALUE s_ParameterRef_GetColor(VALUE self) {
1639         UnionPar *up;
1640         Int tp;
1641         up = s_UnionParFromValue(self, &tp, 0);
1642         if (tp == kElementParType)
1643                 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));
1644         else rb_raise(rb_eMolbyError, "invalid member color");
1645 }
1646
1647 /*
1648  *  call-seq:
1649  *     atomic_number -> Integer
1650  *
1651  *  Get the atomic number for the vdw or element parameter.
1652  */
1653 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1654         UnionPar *up;
1655         Int tp;
1656         up = s_UnionParFromValue(self, &tp, 0);
1657         if (tp == kElementParType)
1658                 return INT2NUM(up->atom.number);
1659         else if (tp == kVdwParType)
1660                 return INT2NUM(up->vdw.atomicNumber);
1661         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1662 }
1663
1664 /*
1665  *  call-seq:
1666  *     name -> String
1667  *
1668  *  Get the name for the element parameter.
1669  */
1670 static VALUE s_ParameterRef_GetName(VALUE self) {
1671         UnionPar *up;
1672         Int tp;
1673         up = s_UnionParFromValue(self, &tp, 0);
1674         if (tp == kElementParType) {
1675                 char name[5];
1676                 strncpy(name, up->atom.name, 4);
1677                 name[4] = 0;
1678                 return rb_str_new2(name);
1679         } else rb_raise(rb_eMolbyError, "invalid member name");
1680 }
1681
1682 /*
1683  *  call-seq:
1684  *     weight -> Float
1685  *
1686  *  Get the atomic weight for the element parameter.
1687  */
1688 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1689         UnionPar *up;
1690         Int tp;
1691         up = s_UnionParFromValue(self, &tp, 0);
1692         if (tp == kElementParType)
1693                 return rb_float_new(up->atom.weight);
1694         else if (tp == kVdwParType)
1695                 return rb_float_new(up->vdw.weight);
1696         else rb_raise(rb_eMolbyError, "invalid member weight");
1697 }
1698
1699 /*
1700  *  call-seq:
1701  *     fullname -> String
1702  *
1703  *  Get the full name for the element parameter.
1704  */
1705 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1706         UnionPar *up;
1707         Int tp;
1708         up = s_UnionParFromValue(self, &tp, 0);
1709         if (tp == kElementParType) {
1710                 char fullname[16];
1711                 strncpy(fullname, up->atom.fullname, 15);
1712                 fullname[15] = 0;
1713                 return rb_str_new2(fullname);
1714         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1715 }
1716
1717 /*
1718  *  call-seq:
1719  *     comment -> String
1720  *
1721  *  Get the comment for the parameter.
1722  */
1723 static VALUE s_ParameterRef_GetComment(VALUE self) {
1724         UnionPar *up;
1725         Int tp, com;
1726         up = s_UnionParFromValue(self, &tp, 0);
1727         com = up->bond.com;
1728         if (com == 0)
1729                 return Qnil;
1730         else return rb_str_new2(ParameterGetComment(com));
1731 }
1732
1733 /*
1734  *  call-seq:
1735  *     source -> String
1736  *
1737  *  Get the source string for the parameter. Returns false for undefined parameter,
1738  *  and nil for "local" parameter that is specific for the molecule.
1739  */
1740 static VALUE s_ParameterRef_GetSource(VALUE self) {
1741         UnionPar *up;
1742         Int tp, src;
1743         up = s_UnionParFromValue(self, &tp, 0);
1744         src = up->bond.src;
1745         if (src < 0)
1746                 return Qfalse;  /* undefined */
1747         else if (src == 0)
1748                 return Qnil;  /*  local  */
1749         else return rb_str_new2(ParameterGetComment(src));
1750 }
1751
1752 static void
1753 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1754 {
1755         VALUE *valp;
1756         int i;
1757         if (n == 1)
1758                 valp = &val;
1759         else {
1760                 if (rb_obj_is_kind_of(val, rb_cString)) {
1761                         char *s = StringValuePtr(val);
1762                         char *p;
1763                         for (i = 0; i < n; i++) {
1764                                 char buf[40];
1765                                 int len;
1766                                 /*  Skip leading separaters  */
1767                                 while (*s == '-' || *s == ' ' || *s == '\t')
1768                                         s++;
1769                                 for (p = s; *p != 0; p++) {
1770                                         if (*p == '-' || *p == ' ' || *p == '\t')
1771                                                 break;
1772                                 }
1773                                 len = p - s;
1774                                 if (len >= sizeof(buf))
1775                                         len = sizeof(buf) - 1;
1776                                 strncpy(buf, s, len);
1777                                 buf[len] = 0;
1778                                 /*  Skip trailing blanks  */
1779                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1780                                         buf[len] = 0;
1781                                 if (buf[0] == 0)
1782                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1783                                 if (buf[0] >= '0' && buf[0] <= '9')
1784                                         types[i] = atoi(buf);
1785                                 else
1786                                         types[i] = AtomTypeEncodeToUInt(buf);
1787                                 if (p == NULL || *p == 0) {
1788                                         i++;
1789                                         break;
1790                                 } else s = p + 1;
1791                         }
1792                         if (i < n)
1793                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1794                         return;
1795                 }
1796                 val = rb_ary_to_ary(val);
1797                 if (RARRAY_LEN(val) != n)
1798                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1799                 valp = RARRAY_PTR(val);
1800         }
1801         for (i = 0; i < n; i++) {
1802                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1803                         types[i] = NUM2INT(rb_Integer(valp[i]));
1804                 else {
1805                         VALUE sval = valp[i];
1806                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1807                 }
1808         }
1809 }
1810
1811 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1812         UnionPar *up;
1813         VALUE oldval;
1814         Int oldsrc, tp;
1815         UInt types[4];
1816         up = s_UnionParFromValue(self, &tp, 1);
1817         oldval = s_ParameterRef_GetAtomTypes(self);
1818         oldsrc = up->bond.src;
1819         switch (tp) {
1820                 case kBondParType:
1821                         s_ScanAtomTypes(val, 2, types);
1822                         up->bond.type1 = types[0];
1823                         up->bond.type2 = types[1];
1824                         break;
1825                 case kAngleParType:
1826                         s_ScanAtomTypes(val, 3, types);
1827                         up->angle.type1 = types[0];
1828                         up->angle.type2 = types[1];
1829                         up->angle.type3 = types[2];
1830                         break;
1831                 case kDihedralParType:
1832                 case kImproperParType:
1833                         s_ScanAtomTypes(val, 4, types);
1834                         up->torsion.type1 = types[0];
1835                         up->torsion.type2 = types[1];
1836                         up->torsion.type3 = types[2];
1837                         up->torsion.type4 = types[3];
1838                         break;
1839                 case kVdwParType:
1840                         s_ScanAtomTypes(val, 1, types);
1841                         up->vdw.type1 = types[0];
1842                         break;
1843                 case kVdwPairParType:
1844                         s_ScanAtomTypes(val, 2, types);
1845                         up->vdwp.type1 = types[0];
1846                         up->vdwp.type2 = types[1];
1847                         break;
1848                 case kVdwCutoffParType:
1849                         s_ScanAtomTypes(val, 2, types);
1850                         up->vdwcutoff.type1 = types[0];
1851                         up->vdwcutoff.type2 = types[1];
1852                         break;
1853                 default:
1854                         return Qnil;
1855         }
1856         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1857         return val;
1858 }
1859
1860 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1861         UnionPar *up;
1862         Int tp, i, n, oldsrc;
1863         VALUE *valp, oldval;
1864         up = s_UnionParFromValue(self, &tp, 1);
1865         oldval = s_ParameterRef_GetK(self);
1866         oldsrc = up->bond.src;
1867         switch (tp) {
1868                 case kBondParType:
1869                         val = rb_Float(val);
1870                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1871                         break;
1872                 case kAngleParType:
1873                         val = rb_Float(val);
1874                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1875                         break;
1876                 case kDihedralParType:
1877                 case kImproperParType:
1878                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1879                                 up->torsion.mult = 1;
1880                                 val = rb_Float(val);
1881                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1882                                 break;
1883                         }
1884                         n = up->torsion.mult;
1885                         if (n > 3)
1886                                 n = 3;
1887                         val = rb_ary_to_ary(val);
1888                         if (RARRAY_LEN(val) != n)
1889                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1890                         valp = RARRAY_PTR(val);
1891                         for (i = 0; i < n; i++) {
1892                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1893                         }
1894                         break;
1895                 default:
1896                         rb_raise(rb_eMolbyError, "invalid member k");
1897         }
1898         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1899         return val;
1900 }
1901
1902 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1903         UnionPar *up;
1904         Int tp, oldsrc;
1905         VALUE oldval;
1906         up = s_UnionParFromValue(self, &tp, 1);
1907         oldval = s_ParameterRef_GetR0(self);
1908         oldsrc = up->bond.src;
1909         if (tp == kBondParType) {
1910                 val = rb_Float(val);
1911                 up->bond.r0 = NUM2DBL(val);
1912         } else rb_raise(rb_eMolbyError, "invalid member r0");
1913         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1914         return val;
1915 }
1916
1917 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1918         UnionPar *up;
1919         Int tp, oldsrc;
1920         VALUE oldval;
1921         up = s_UnionParFromValue(self, &tp, 1);
1922         oldval = s_ParameterRef_GetA0(self);
1923         oldsrc = up->bond.src;
1924         if (tp == kAngleParType) {
1925                 val = rb_Float(val);
1926                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1927         } else rb_raise(rb_eMolbyError, "invalid member a0");
1928         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1929         return val;
1930 }
1931
1932 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1933         UnionPar *up;
1934         Int tp, oldsrc;
1935         VALUE oldval;
1936         up = s_UnionParFromValue(self, &tp, 1);
1937         oldval = s_ParameterRef_GetMult(self);
1938         oldsrc = up->bond.src;
1939         if (tp == kDihedralParType || tp == kImproperParType) {
1940                 int i;
1941                 val = rb_Integer(val);
1942                 i = NUM2INT(val);
1943                 if (i < 0 || i > 3)
1944                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1945                 up->torsion.mult = i;
1946         } else rb_raise(rb_eMolbyError, "invalid member mult");
1947         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1948         return val;
1949 }
1950
1951 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1952         UnionPar *up;
1953         Int tp, i, n, oldsrc;
1954         VALUE *valp, oldval;
1955         up = s_UnionParFromValue(self, &tp, 1);
1956         oldval = s_ParameterRef_GetPeriod(self);
1957         oldsrc = up->bond.src;
1958         if (tp == kDihedralParType || tp == kImproperParType) {
1959                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1960                         up->torsion.mult = 1;
1961                         val = rb_Integer(val);
1962                         up->torsion.period[0] = NUM2INT(val);
1963                 } else {
1964                         n = up->torsion.mult;
1965                         if (n > 3)
1966                                 n = 3;
1967                         val = rb_ary_to_ary(val);
1968                         if (RARRAY_LEN(val) != n)
1969                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1970                         valp = RARRAY_PTR(val);
1971                         for (i = 0; i < n; i++) {
1972                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1973                         }
1974                 }
1975         } else rb_raise(rb_eMolbyError, "invalid member period");
1976         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1977         return val;
1978 }
1979
1980 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1981         UnionPar *up;
1982         Int tp, i, n, oldsrc;
1983         VALUE *valp, oldval;
1984         up = s_UnionParFromValue(self, &tp, 1);
1985         oldval = s_ParameterRef_GetPhi0(self);
1986         oldsrc = up->bond.src;
1987         if (tp == kDihedralParType || tp == kImproperParType) {
1988                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1989                         up->torsion.mult = 1;
1990                         val = rb_Float(val);
1991                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1992                 } else {
1993                         n = up->torsion.mult;
1994                         if (n > 3)
1995                                 n = 3;
1996                         val = rb_ary_to_ary(val);
1997                         if (RARRAY_LEN(val) != n)
1998                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1999                         valp = RARRAY_PTR(val);
2000                         for (i = 0; i < n; i++)
2001                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2002                 }
2003         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2004         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2005         return val;
2006 }
2007
2008 /*
2009 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2010         UnionPar *up;
2011         Int tp, oldsrc;
2012         double d;
2013         VALUE oldval;
2014         up = s_UnionParFromValue(self, &tp, 1);
2015         oldval = s_ParameterRef_GetA(self);
2016         oldsrc = up->bond.src;
2017         val = rb_Float(val);
2018         d = NUM2DBL(val);
2019         if (tp == kVdwParType)
2020                 up->vdw.A = d;
2021         else if (tp == kVdwPairParType)
2022                 up->vdwp.A = d;
2023         else rb_raise(rb_eMolbyError, "invalid member A");
2024         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2025         return val;
2026 }
2027
2028 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2029         UnionPar *up;
2030         Int tp, oldsrc;
2031         double d;
2032         VALUE oldval;
2033         up = s_UnionParFromValue(self, &tp, 1);
2034         oldval = s_ParameterRef_GetB(self);
2035         oldsrc = up->bond.src;
2036         val = rb_Float(val);
2037         d = NUM2DBL(val);
2038         if (tp == kVdwParType)
2039                 up->vdw.B = d;
2040         else if (tp == kVdwPairParType)
2041                 up->vdwp.B = d;
2042         else rb_raise(rb_eMolbyError, "invalid member B");
2043         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2044         return val;
2045 }
2046 */
2047
2048 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2049         UnionPar *up;
2050         Int tp, oldsrc;
2051         Double r;
2052         VALUE oldval;
2053         up = s_UnionParFromValue(self, &tp, 1);
2054         oldval = s_ParameterRef_GetReq(self);
2055         oldsrc = up->bond.src;
2056         val = rb_Float(val);
2057         r = NUM2DBL(val);
2058         if (tp == kVdwParType) {
2059                 up->vdw.r_eq = r;
2060                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2061                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2062         } else if (tp == kVdwPairParType) {
2063                 up->vdwp.r_eq = r;
2064                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2065                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2066         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2067         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2068         return val;
2069 }
2070
2071 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2072         UnionPar *up;
2073         Int tp, oldsrc;
2074         Double e;
2075         VALUE oldval;
2076         up = s_UnionParFromValue(self, &tp, 1);
2077         oldval = s_ParameterRef_GetEps(self);
2078         oldsrc = up->bond.src;
2079         val = rb_Float(val);
2080         e = NUM2DBL(val) * KCAL2INTERNAL;
2081         if (tp == kVdwParType) {
2082                 up->vdw.eps = e;
2083                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2084                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2085         } else if (tp == kVdwPairParType) {
2086                 up->vdwp.eps = e;
2087                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2088                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2089         } else rb_raise(rb_eMolbyError, "invalid member eps");
2090         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2091         return val;
2092 }
2093
2094 /*
2095 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2096         UnionPar *up;
2097         Int tp, oldsrc;
2098         double d;
2099         VALUE oldval;
2100         up = s_UnionParFromValue(self, &tp, 1);
2101         oldval = s_ParameterRef_GetA14(self);
2102         oldsrc = up->bond.src;
2103         val = rb_Float(val);
2104         d = NUM2DBL(val);
2105         if (tp == kVdwParType)
2106                 up->vdw.A14 = d;
2107         else if (tp == kVdwPairParType)
2108                 up->vdwp.A14 = d;
2109         else rb_raise(rb_eMolbyError, "invalid member A14");
2110         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2111         return val;
2112 }
2113
2114 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2115         UnionPar *up;
2116         Int tp, oldsrc;
2117         double d;
2118         VALUE oldval;
2119         up = s_UnionParFromValue(self, &tp, 1);
2120         oldval = s_ParameterRef_GetB14(self);
2121         oldsrc = up->bond.src;
2122         val = rb_Float(val);
2123         d = NUM2DBL(val);
2124         if (tp == kVdwParType)
2125                 up->vdw.B14 = d;
2126         else if (tp == kVdwPairParType)
2127                 up->vdwp.B14 = d;
2128         else rb_raise(rb_eMolbyError, "invalid member B14");
2129         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2130         return val;
2131 }
2132 */
2133
2134 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2135         UnionPar *up;
2136         Int tp, oldsrc;
2137         Double r;
2138         VALUE oldval;
2139         up = s_UnionParFromValue(self, &tp, 1);
2140         oldval = s_ParameterRef_GetReq14(self);
2141         oldsrc = up->bond.src;
2142         val = rb_Float(val);
2143         r = NUM2DBL(val);
2144         if (tp == kVdwParType) {
2145                 up->vdw.r_eq14 = r;
2146                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2147                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2148         } else if (tp == kVdwPairParType) {
2149                 up->vdwp.r_eq14 = r;
2150                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2151                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2152         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2153         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2154         return val;
2155 }
2156
2157 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2158         UnionPar *up;
2159         Int tp, oldsrc;
2160         Double e;
2161         VALUE oldval;
2162         up = s_UnionParFromValue(self, &tp, 1);
2163         oldval = s_ParameterRef_GetEps14(self);
2164         oldsrc = up->bond.src;
2165         val = rb_Float(val);
2166         e = NUM2DBL(val) * KCAL2INTERNAL;
2167         if (tp == kVdwParType) {
2168                 up->vdw.eps14 = e;
2169                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2170                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2171         } else if (tp == kVdwPairParType) {
2172                 up->vdwp.eps14 = e;
2173                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2174                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2175         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2176         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2177         return val;
2178 }
2179
2180 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2181         UnionPar *up;
2182         Int tp, oldsrc;
2183         VALUE oldval;
2184         oldval = s_ParameterRef_GetCutoff(self);
2185         oldsrc = up->bond.src;
2186         up = s_UnionParFromValue(self, &tp, 1);
2187         val = rb_Float(val);
2188         if (tp == kVdwCutoffParType) {
2189                 up->vdwcutoff.cutoff = NUM2DBL(val);
2190         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2191         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2192         return val;
2193 }
2194
2195 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2196         UnionPar *up;
2197         Int tp, oldsrc;
2198         VALUE oldval;
2199         up = s_UnionParFromValue(self, &tp, 1);
2200         oldval = s_ParameterRef_GetRadius(self);
2201         oldsrc = up->bond.src;
2202         val = rb_Float(val);
2203         if (tp == kElementParType) {
2204                 up->atom.radius = NUM2DBL(val);
2205         } else rb_raise(rb_eMolbyError, "invalid member radius");
2206         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2207         return val;
2208 }
2209
2210 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2211         UnionPar *up;
2212         Int tp, oldsrc;
2213         VALUE oldval;
2214         up = s_UnionParFromValue(self, &tp, 1);
2215         oldval = s_ParameterRef_GetVdwRadius(self);
2216         oldsrc = up->bond.src;
2217         val = rb_Float(val);
2218         if (tp == kElementParType) {
2219                 up->atom.vdw_radius = NUM2DBL(val);
2220         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2221         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2222         return val;
2223 }
2224
2225 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2226         UnionPar *up;
2227         Int tp, oldsrc;
2228         VALUE *valp, oldval;
2229         up = s_UnionParFromValue(self, &tp, 1);
2230         oldval = s_ParameterRef_GetColor(self);
2231         oldsrc = up->bond.src;
2232         val = rb_ary_to_ary(val);
2233         if (RARRAY_LEN(val) != 3)
2234                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2235         valp = RARRAY_PTR(val);
2236         if (tp == kElementParType) {
2237                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2238                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2239                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2240         } else rb_raise(rb_eMolbyError, "invalid member color");
2241         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2242         return val;
2243 }
2244
2245 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2246         UnionPar *up;
2247         Int tp, oldsrc;
2248         VALUE oldval;
2249         up = s_UnionParFromValue(self, &tp, 1);
2250         oldval = s_ParameterRef_GetAtomicNumber(self);
2251         oldsrc = up->bond.src;
2252         val = rb_Integer(val);
2253         if (tp == kElementParType)
2254                 up->atom.number = NUM2INT(val);
2255         else if (tp == kVdwParType) {
2256                 up->vdw.atomicNumber = NUM2INT(val);
2257                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2258         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2259         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2260         return val;
2261 }
2262
2263 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2264         UnionPar *up;
2265         Int tp, oldsrc;
2266         VALUE oldval;
2267         up = s_UnionParFromValue(self, &tp, 1);
2268         oldval = s_ParameterRef_GetName(self);
2269         oldsrc = up->bond.src;
2270         if (tp == kElementParType) {
2271                 strncpy(up->atom.name, StringValuePtr(val), 4);
2272         } else rb_raise(rb_eMolbyError, "invalid member name");
2273         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2274         return val;
2275 }
2276
2277 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2278         UnionPar *up;
2279         Int tp, oldsrc;
2280         VALUE oldval;
2281         val = rb_Float(val);
2282         oldval = s_ParameterRef_GetWeight(self);
2283         up = s_UnionParFromValue(self, &tp, 1);
2284         oldsrc = up->bond.src;
2285         if (tp == kElementParType)
2286                 up->atom.weight = NUM2DBL(val);
2287         else if (tp == kVdwParType)
2288                 up->vdw.weight = NUM2DBL(val);
2289         else rb_raise(rb_eMolbyError, "invalid member weight");
2290         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2291         return val;
2292 }
2293
2294 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2295         UnionPar *up;
2296         Int tp, oldsrc;
2297         VALUE oldval;
2298         up = s_UnionParFromValue(self, &tp, 1);
2299         oldval = s_ParameterRef_GetFullName(self);
2300         oldsrc = up->bond.src;
2301         if (tp == kElementParType) {
2302                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2303                 up->atom.fullname[15] = 0;
2304         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2305         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2306         return val;
2307 }
2308
2309 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2310         UnionPar *up;
2311         Int tp, com, oldsrc;
2312         VALUE oldval;
2313         up = s_UnionParFromValue(self, &tp, 1);
2314         oldval = s_ParameterRef_GetComment(self);
2315         oldsrc = up->bond.src;
2316         if (val == Qnil)
2317                 up->bond.com = 0;
2318         else {
2319                 com = ParameterCommentIndex(StringValuePtr(val));
2320                 up->bond.com = com;
2321         }
2322         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2323         return val;     
2324 }
2325
2326 /*  Only false (undefined) and nil (local) can be set  */
2327 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2328         UnionPar *up;
2329         Int tp, oldsrc;
2330         VALUE oldval;
2331         up = s_UnionParFromValue(self, &tp, 1);
2332         if (val != Qfalse && val != Qnil)
2333                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2334         oldval = s_ParameterRef_GetSource(self);
2335         oldsrc = up->bond.src;
2336         if (oldsrc != 0 && oldsrc != -1)
2337                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2338         up->bond.src = (val == Qfalse ? -1 : 0);
2339         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2340         return val;     
2341 }
2342
2343 static struct s_ParameterAttrDef {
2344         char *name;
2345         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2346         ID id;                  /*  Will be set within InitMolby()  */
2347         VALUE (*getter)(VALUE);
2348         VALUE (*setter)(VALUE, VALUE);
2349 } s_ParameterAttrDefTable[] = {
2350         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2351         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2352         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2353         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2354         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2355         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2356         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2357         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2358         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2359         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2360 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2361         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2362         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2363         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2364 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2365         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2366         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2367         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2368         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2369         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2370         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2371         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2372         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2373         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2374         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2375         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2376         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2377         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2378         {NULL} /* Sentinel */
2379 };
2380
2381 static VALUE
2382 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2383 {
2384         int i;
2385         ID kid;
2386         if (TYPE(key) != T_SYMBOL) {
2387                 kid = rb_intern(StringValuePtr(key));
2388                 key = ID2SYM(kid);
2389         } else kid = SYM2ID(key);
2390         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2391                 if (s_ParameterAttrDefTable[i].id == kid) {
2392                         if (value == Qundef)
2393                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2394                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2395                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2396                         else
2397                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2398                 }
2399         }
2400         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2401         return Qnil; /* not reached */
2402 }
2403
2404 static VALUE
2405 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2406 {
2407         return s_ParameterRef_SetAttr(self, key, Qundef);
2408 }
2409
2410 /*
2411  *  call-seq:
2412  *     keys(idx)          -> array of valid parameter attributes
2413  *  
2414  *  Returns an array of valid parameter attributes (as Symbols).
2415  */
2416 static VALUE
2417 s_ParameterRef_Keys(VALUE self)
2418 {
2419         ParameterRef *pref;
2420         Data_Get_Struct(self, ParameterRef, pref);
2421         switch (pref->parType) {
2422                 case kBondParType:
2423                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2424                 case kAngleParType:
2425                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2426                 case kDihedralParType:
2427                 case kImproperParType:
2428                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2429                 case kVdwParType:
2430                         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);
2431                 case kVdwPairParType:
2432                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2433                 case kVdwCutoffParType:
2434                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2435                 case kElementParType:
2436                         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);
2437                 default:
2438                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2439         }
2440         return Qnil;  /*  Not reached  */
2441 }
2442
2443 /*
2444  *  call-seq:
2445  *     to_hash(idx)          -> Hash
2446  *  
2447  *  Returns a hash containing valid parameter names and values
2448  */
2449 static VALUE
2450 s_ParameterRef_ToHash(VALUE self)
2451 {
2452         VALUE keys = s_ParameterRef_Keys(self);
2453         VALUE retval;
2454         int i;
2455         if (keys == Qnil)
2456                 return Qnil;
2457         retval = rb_hash_new();
2458         for (i = 0; i < RARRAY_LEN(keys); i++) {
2459                 VALUE key = RARRAY_PTR(keys)[i];
2460                 VALUE val = s_ParameterRef_GetAttr(self, key);
2461                 rb_hash_aset(retval, key, val);
2462         }
2463         return retval;
2464 }
2465
2466 /*
2467  *  call-seq:
2468  *     parameter.to_s(idx)          -> String
2469  *  
2470  *  Returns a string representation of the given parameter
2471  */
2472 static VALUE
2473 s_ParameterRef_ToString(VALUE self)
2474 {
2475         Int tp, i, n;
2476         char buf[1024], types[4][8];
2477         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2478         switch (tp) {
2479                 case kBondParType:
2480                         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);
2481                         break;
2482                 case kAngleParType:
2483                         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);
2484                         break;
2485                 case kDihedralParType:
2486                 case kImproperParType:
2487                         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]));
2488                         n = strlen(buf);
2489                         for (i = 0; i < up->torsion.mult; i++) {
2490                                 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);
2491                                 n = strlen(buf);
2492                         }
2493                         break;
2494                 case kVdwParType:
2495                         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);
2496                         break;
2497                 case kVdwPairParType:
2498                         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);
2499                         break;
2500                 case kVdwCutoffParType:
2501                         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);
2502                         break;
2503                 case kElementParType:
2504                         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);
2505                         break;
2506         }
2507         return rb_str_new2(buf);
2508 }
2509
2510 /*
2511  *  call-seq:
2512  *     self == parameterRef -> boolean
2513  *  
2514  *  True if the parameters point to the same parameter record.
2515  */
2516 static VALUE
2517 s_ParameterRef_Equal(VALUE self, VALUE val)
2518 {
2519         Int tp1, tp2;
2520         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2521                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2522         } else return Qfalse;
2523 }
2524         
2525 #pragma mark ====== Parameter Class ======
2526
2527 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2528  *  is NULL, then the global parameters are looked for.  */
2529
2530 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2531 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2532 static void
2533 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2534 {
2535         Molecule *mol;
2536         Data_Get_Struct(val, Molecule, mol);
2537         if (mol == NULL)
2538                 rb_raise(rb_eMolbyError, "the molecule is empty");
2539         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2540                 /*  Do self.md_arena.prepare  */
2541                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2542                 if (val2 != Qnil)
2543                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2544         }
2545 }
2546
2547 static VALUE
2548 s_NewParameterValueFromValue(VALUE val)
2549 {
2550         Molecule *mol;
2551         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2552                 Data_Get_Struct(val, Molecule, mol);
2553                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2554                 MoleculeRetain(mol);
2555                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2556         } else {
2557                 mol = NULL;
2558                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2559         }
2560 }
2561
2562 static Molecule *
2563 s_MoleculeFromParameterValue(VALUE val)
2564 {
2565         Molecule *mol;
2566         Data_Get_Struct(val, Molecule, mol);
2567         return mol;
2568 }
2569
2570 static Parameter *
2571 s_ParameterFromParameterValue(VALUE val)
2572 {
2573         Molecule *mol;
2574         Data_Get_Struct(val, Molecule, mol);
2575         if (mol != NULL)
2576                 return mol->par;
2577         return gBuiltinParameters;
2578 }
2579
2580 /*  Forward declarations  */
2581 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2582 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2583
2584 static Molecule *
2585 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2586 {
2587         if (val == rb_cParameter) {
2588                 return NULL;  /*  Parameter class method: builtin parameters  */
2589         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2590                 return s_MoleculeFromParameterValue(val);
2591         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2592                 return s_MoleculeFromParEnumerableValue(val);
2593         } else return NULL;
2594 }
2595
2596 /*
2597  *  call-seq:
2598  *     builtin    -> Parameter
2599  *  
2600  *  Returns a parameter value that points to the global (builtin) parameters.
2601  *  Equivalent to Parameter::Builtin (constant).
2602  */
2603 static VALUE
2604 s_Parameter_Builtin(VALUE self)
2605 {
2606         static ID s_builtin_id = 0;
2607         if (s_builtin_id == 0)
2608                 s_builtin_id = rb_intern("Builtin");
2609         return rb_const_get(rb_cParameter, s_builtin_id);
2610 }
2611
2612 /*
2613  *  call-seq:
2614  *     bond(idx)          -> ParameterRef
2615  *  
2616  *  The index-th bond parameter record is returned.
2617  */
2618 static VALUE
2619 s_Parameter_Bond(VALUE self, VALUE ival)
2620 {
2621         Molecule *mol;
2622         int idx, n;
2623         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2624         idx = NUM2INT(rb_Integer(ival));
2625         if (mol == NULL)
2626                 n = gBuiltinParameters->nbondPars;
2627         else if (mol->par != NULL)
2628                 n = mol->par->nbondPars;
2629         else n = 0;
2630         if (idx < -n || idx >= n)
2631                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2632         if (idx < 0)
2633                 idx += n;
2634         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2635 }
2636
2637 /*
2638  *  call-seq:
2639  *     angle(idx)          -> ParameterRef
2640  *  
2641  *  The index-th angle parameter record is returned.
2642  */
2643 static VALUE
2644 s_Parameter_Angle(VALUE self, VALUE ival)
2645 {
2646         Molecule *mol;
2647         int idx, n;
2648         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2649         idx = NUM2INT(rb_Integer(ival));
2650         if (mol == NULL)
2651                 n = gBuiltinParameters->nanglePars;
2652         else if (mol->par != NULL)
2653                 n = mol->par->nanglePars;
2654         else n = 0;
2655         if (idx < -n || idx >= n)
2656                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2657         if (idx < 0)
2658                 idx += n;
2659         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2660 }
2661
2662 /*
2663  *  call-seq:
2664  *     dihedral(idx)          -> ParameterRef
2665  *  
2666  *  The index-th dihedral parameter record is returned.
2667  */
2668 static VALUE
2669 s_Parameter_Dihedral(VALUE self, VALUE ival)
2670 {
2671         Molecule *mol;
2672         int idx, n;
2673         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2674         idx = NUM2INT(rb_Integer(ival));
2675         if (mol == NULL)
2676                 n = gBuiltinParameters->ndihedralPars;
2677         else if (mol->par != NULL)
2678                 n = mol->par->ndihedralPars;
2679         else n = 0;
2680         if (idx < -n || idx >= n)
2681                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2682         if (idx < 0)
2683                 idx += n;
2684         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2685 }
2686
2687 /*
2688  *  call-seq:
2689  *     improper(idx)          -> ParameterRef
2690  *  
2691  *  The index-th improper parameter record is returned.
2692  */
2693 static VALUE
2694 s_Parameter_Improper(VALUE self, VALUE ival)
2695 {
2696         Molecule *mol;
2697         int idx, n;
2698         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2699         idx = NUM2INT(rb_Integer(ival));
2700         if (mol == NULL)
2701                 n = gBuiltinParameters->nimproperPars;
2702         else if (mol->par != NULL)
2703                 n = mol->par->nimproperPars;
2704         else n = 0;
2705         if (idx < -n || idx >= n)
2706                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2707         if (idx < 0)
2708                 idx += n;
2709         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2710 }
2711
2712 /*
2713  *  call-seq:
2714  *     vdw(idx)          -> ParameterRef
2715  *  
2716  *  The index-th vdw parameter record is returned.
2717  */
2718 static VALUE
2719 s_Parameter_Vdw(VALUE self, VALUE ival)
2720 {
2721         Molecule *mol;
2722         int idx, n;
2723         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2724         idx = NUM2INT(rb_Integer(ival));
2725         if (mol == NULL)
2726                 n = gBuiltinParameters->nvdwPars;
2727         else if (mol->par != NULL)
2728                 n = mol->par->nvdwPars;
2729         else n = 0;
2730         if (idx < -n || idx >= n)
2731                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2732         if (idx < 0)
2733                 idx += n;
2734         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2735 }
2736
2737 /*
2738  *  call-seq:
2739  *     vdw_pair(idx)          -> ParameterRef
2740  *  
2741  *  The index-th vdw pair parameter record is returned.
2742  */
2743 static VALUE
2744 s_Parameter_VdwPair(VALUE self, VALUE ival)
2745 {
2746         Molecule *mol;
2747         int idx, n;
2748         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2749         idx = NUM2INT(rb_Integer(ival));
2750         if (mol == NULL)
2751                 n = gBuiltinParameters->nvdwpPars;
2752         else if (mol->par != NULL)
2753                 n = mol->par->nvdwpPars;
2754         else n = 0;
2755         if (idx < -n || idx >= n)
2756                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2757         if (idx < 0)
2758                 idx += n;
2759         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2760 }
2761
2762 /*
2763  *  call-seq:
2764  *     vdw_cutoff(idx)          -> ParameterRef
2765  *  
2766  *  The index-th vdw cutoff parameter record is returned.
2767  */
2768 static VALUE
2769 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2770 {
2771         Molecule *mol;
2772         int idx, n;
2773         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2774         idx = NUM2INT(rb_Integer(ival));
2775         if (mol == NULL)
2776                 n = gBuiltinParameters->nvdwCutoffPars;
2777         else if (mol->par != NULL)
2778                 n = mol->par->nvdwCutoffPars;
2779         else n = 0;
2780         if (idx < -n || idx >= n)
2781                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2782         if (idx < 0)
2783                 idx += n;
2784         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2785 }
2786
2787 /*
2788  *  call-seq:
2789  *     element(idx)            -> ParameterRef
2790  *     element(t1)             -> ParameterRef
2791  *  
2792  *  In the first form, the index-th element parameter record is returned. In the second
2793  *  form, the element parameter for t1 is looked up (the last index first). t1
2794  *  is the element name string (up to 4 characters).
2795  *  Unlike other Parameter methods, this is used only for the global parameter.
2796  */
2797 static VALUE
2798 s_Parameter_Element(VALUE self, VALUE ival)
2799 {
2800         Int idx1;
2801         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2802                 int n = gCountElementParameters;
2803                 idx1 = NUM2INT(rb_Integer(ival));
2804                 if (idx1 < -n || idx1 >= n)
2805                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2806                 if (idx1 < 0)
2807                         idx1 += n;
2808                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2809         } else {
2810                 ElementPar *ep;
2811                 char name[6];
2812                 int i;
2813                 strncpy(name, StringValuePtr(ival), 4);
2814                 name[4] = 0;
2815                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2816                         if (strncmp(ep->name, name, 4) == 0)
2817                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2818                 }
2819                 return Qnil;
2820         }
2821 }
2822
2823 /*
2824  *  call-seq:
2825  *     nbonds          -> Integer
2826  *  
2827  *  Returns the number of bond parameters.
2828  */
2829 static VALUE
2830 s_Parameter_Nbonds(VALUE self)
2831 {
2832         Int n;
2833         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2834         if (mol == NULL)
2835                 n = gBuiltinParameters->nbondPars;
2836         else if (mol->par != NULL)
2837                 n = mol->par->nbondPars;
2838         else n = 0;
2839         return INT2NUM(n);
2840 }
2841
2842 /*
2843  *  call-seq:
2844  *     nangles          -> Integer
2845  *  
2846  *  Returns the number of angle parameters.
2847  */
2848 static VALUE
2849 s_Parameter_Nangles(VALUE self)
2850 {
2851         Int n;
2852         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2853         if (mol == NULL)
2854                 n = gBuiltinParameters->nanglePars;
2855         else if (mol->par != NULL)
2856                 n = mol->par->nanglePars;
2857         else n = 0;
2858         return INT2NUM(n);
2859 }
2860
2861 /*
2862  *  call-seq:
2863  *     ndihedrals          -> Integer
2864  *  
2865  *  Returns the number of dihedral parameters.
2866  */
2867 static VALUE
2868 s_Parameter_Ndihedrals(VALUE self)
2869 {
2870         Int n;
2871         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2872         if (mol == NULL)
2873                 n = gBuiltinParameters->ndihedralPars;
2874         else if (mol->par != NULL)
2875                 n = mol->par->ndihedralPars;
2876         else n = 0;
2877         return INT2NUM(n);
2878 }
2879
2880 /*
2881  *  call-seq:
2882  *     nimpropers          -> Integer
2883  *  
2884  *  Returns the number of improper parameters.
2885  */
2886 static VALUE
2887 s_Parameter_Nimpropers(VALUE self)
2888 {
2889         Int n;
2890         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2891         if (mol == NULL)
2892                 n = gBuiltinParameters->nimproperPars;
2893         else if (mol->par != NULL)
2894                 n = mol->par->nimproperPars;
2895         else n = 0;
2896         return INT2NUM(n);
2897 }
2898
2899 /*
2900  *  call-seq:
2901  *     nvdws          -> Integer
2902  *  
2903  *  Returns the number of vdw parameters.
2904  */
2905 static VALUE
2906 s_Parameter_Nvdws(VALUE self)
2907 {
2908         Int n;
2909         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2910         if (mol == NULL)
2911                 n = gBuiltinParameters->nvdwPars;
2912         else if (mol->par != NULL)
2913                 n = mol->par->nvdwPars;
2914         else n = 0;
2915         return INT2NUM(n);
2916 }
2917
2918 /*
2919  *  call-seq:
2920  *     nvdw_pairs          -> Integer
2921  *  
2922  *  Returns the number of vdw pair parameters.
2923  */
2924 static VALUE
2925 s_Parameter_NvdwPairs(VALUE self)
2926 {
2927         Int n;
2928         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2929         if (mol == NULL)
2930                 n = gBuiltinParameters->nvdwpPars;
2931         else if (mol->par != NULL)
2932                 n = mol->par->nvdwpPars;
2933         else n = 0;
2934         return INT2NUM(n);
2935 }
2936
2937 /*
2938  *  call-seq:
2939  *     nvdw_cutoffs          -> Integer
2940  *  
2941  *  Returns the number of vdw cutoff parameters.
2942  */
2943 static VALUE
2944 s_Parameter_NvdwCutoffs(VALUE self)
2945 {
2946         Int n;
2947         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2948         if (mol == NULL)
2949                 n = gBuiltinParameters->nvdwCutoffPars;
2950         else if (mol->par != NULL)
2951                 n = mol->par->nvdwCutoffPars;
2952         else n = 0;
2953         return INT2NUM(n);
2954 }
2955
2956 /*
2957  *  call-seq:
2958  *     nelements          -> Integer
2959  *  
2960  *  Returns the number of element parameters.
2961  */
2962 static VALUE
2963 s_Parameter_Nelements(VALUE self)
2964 {
2965         return INT2NUM(gCountElementParameters);
2966 }
2967
2968 /*
2969  *  call-seq:
2970  *     bonds          -> ParEnumerable
2971  *  
2972  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2973  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2974  *  useful when all accessible parameters should be examined by use of 'each' method.
2975  */
2976 static VALUE
2977 s_Parameter_Bonds(VALUE self)
2978 {
2979         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2980         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2981 }
2982
2983 /*
2984  *  call-seq:
2985  *     angles          -> ParEnumerable
2986  *  
2987  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2988  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2989  *  useful when all accessible parameters should be examined by use of 'each' method.
2990  */
2991 static VALUE
2992 s_Parameter_Angles(VALUE self)
2993 {
2994         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2995         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2996 }
2997
2998 /*
2999  *  call-seq:
3000  *     dihedrals          -> ParEnumerable
3001  *  
3002  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3003  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3004  *  useful when all accessible parameters should be examined by use of 'each' method.
3005  */
3006 static VALUE
3007 s_Parameter_Dihedrals(VALUE self)
3008 {
3009         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3010         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3011 }
3012
3013 /*
3014  *  call-seq:
3015  *     impropers          -> ParEnumerable
3016  *  
3017  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3018  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3019  *  useful when all accessible parameters should be examined by use of 'each' method.
3020  */
3021 static VALUE
3022 s_Parameter_Impropers(VALUE self)
3023 {
3024         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3025         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3026 }
3027
3028 /*
3029  *  call-seq:
3030  *     vdws          -> ParEnumerable
3031  *  
3032  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3033  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3034  *  useful when all accessible parameters should be examined by use of 'each' method.
3035  */
3036 static VALUE
3037 s_Parameter_Vdws(VALUE self)
3038 {
3039         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3040         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3041 }
3042
3043 /*
3044  *  call-seq:
3045  *     vdw_pairs          -> ParEnumerable
3046  *  
3047  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3048  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3049  *  useful when all accessible parameters should be examined by use of 'each' method.
3050  */
3051 static VALUE
3052 s_Parameter_VdwPairs(VALUE self)
3053 {
3054         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3055         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3056 }
3057
3058 /*
3059  *  call-seq:
3060  *     vdw_cutoffs          -> ParEnumerable
3061  *  
3062  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3063  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3064  *  useful when all accessible parameters should be examined by use of 'each' method.
3065  */
3066 static VALUE
3067 s_Parameter_VdwCutoffs(VALUE self)
3068 {
3069         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3070         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3071 }
3072
3073 /*
3074  *  call-seq:
3075  *     elements          -> ParEnumerable
3076  *  
3077  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3078  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3079  *  useful when all accessible parameters should be examined by use of 'each' method.
3080  */
3081 static VALUE
3082 s_Parameter_Elements(VALUE self)
3083 {
3084         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3085         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3086 }
3087
3088 static VALUE
3089 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3090 {
3091         VALUE atval, optval;
3092         UInt t[4];
3093         Int ii[4];
3094         int i, n, idx, flags, is_global;
3095
3096         rb_scan_args(argc, argv, "1*", &atval, &optval);
3097         
3098         /*  Get the atom types  */
3099         switch (parType) {
3100                 case kBondParType: n = 2; break;
3101                 case kAngleParType: n = 3; break;
3102                 case kDihedralParType: n = 4; break;
3103                 case kImproperParType: n = 4; break;
3104                 case kVdwParType: n = 1; break;
3105                 case kVdwPairParType: n = 2; break;
3106                 default: return Qnil;
3107         }
3108         s_ScanAtomTypes(atval, n, t);
3109         for (i = 0; i < n; i++) {
3110                 if (t[i] < kAtomTypeMinimum) {
3111                         /*  Explicit atom index  */
3112                         if (mol == NULL)
3113                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3114                         if (t[i] >= mol->natoms)
3115                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3116                         ii[i] = t[i];
3117                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3118                 } else ii[i] = -1;
3119         }
3120         
3121         /*  Analyze options  */
3122         flags = 0;
3123         n = RARRAY_LEN(optval);
3124         for (i = 0; i < n; i++) {
3125                 VALUE oval = RARRAY_PTR(optval)[i];
3126                 if (oval == ID2SYM(rb_intern("global")))
3127                         flags |= kParameterLookupGlobal;
3128                 else if (oval == ID2SYM(rb_intern("local")))
3129                         flags |= kParameterLookupLocal;
3130                 else if (oval == ID2SYM(rb_intern("missing")))
3131                         flags |= kParameterLookupMissing;
3132                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3133                         flags |= kParameterLookupNoWildcard;
3134                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3135                         flags |= kParameterLookupNoBaseAtomType;
3136                 else if (oval == ID2SYM(rb_intern("create")))
3137                         flags |= 256;
3138         }
3139         if (flags == 0)
3140                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3141         
3142         idx = -1;
3143         is_global = 0;
3144         switch (parType) {
3145                 case kBondParType: {
3146                         BondPar *bp;
3147                         if (mol != NULL) {
3148                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3149                                 if (bp != NULL) {
3150                                         idx = bp - mol->par->bondPars;
3151                                         break;
3152                                 }
3153                         }
3154                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3155                         if (bp != NULL) {
3156                                 idx = bp - gBuiltinParameters->bondPars;
3157                                 is_global = 1;
3158                         }
3159                         break;
3160                 }
3161                 case kAngleParType: {
3162                         AnglePar *ap;
3163                         if (mol != NULL) {
3164                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3165                                 if (ap != NULL) {
3166                                         idx = ap - mol->par->anglePars;
3167                                         break;
3168                                 }
3169                         }
3170                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3171                         if (ap != NULL) {
3172                                 idx = ap - gBuiltinParameters->anglePars;
3173                                 is_global = 1;
3174                         }
3175                         break;
3176                 }
3177                 case kDihedralParType: {
3178                         TorsionPar *tp;
3179                         if (mol != NULL) {
3180                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3181                                 if (tp != NULL) {
3182                                         idx = tp - mol->par->dihedralPars;
3183                                         break;
3184                                 }
3185                         }
3186                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3187                         if (tp != NULL) {
3188                                 idx = tp - gBuiltinParameters->dihedralPars;
3189                                 is_global = 1;
3190                         }
3191                         break;
3192                 }
3193                 case kImproperParType: {
3194                         TorsionPar *tp;
3195                         if (mol != NULL) {
3196                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3197                                 if (tp != NULL) {
3198                                         idx = tp - mol->par->improperPars;
3199                                         break;
3200                                 }
3201                         }
3202                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3203                         if (tp != NULL) {
3204                                 idx = tp - gBuiltinParameters->improperPars;
3205                                 is_global = 1;
3206                         }
3207                         break;
3208                 }       
3209                 case kVdwParType: {
3210                         VdwPar *vp;
3211                         if (mol != NULL) {
3212                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3213                                 if (vp != NULL) {
3214                                         idx = vp - mol->par->vdwPars;
3215                                         break;
3216                                 }
3217                         }
3218                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3219                         if (vp != NULL) {
3220                                 idx = vp - gBuiltinParameters->vdwPars;
3221                                 is_global = 1;
3222                         }
3223                         break;
3224                 }       
3225                 case kVdwPairParType: {
3226                         VdwPairPar *vp;
3227                         if (mol != NULL) {
3228                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3229                                 if (vp != NULL) {
3230                                         idx = vp - mol->par->vdwpPars;
3231                                         break;
3232                                 }
3233                         }
3234                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3235                         if (vp != NULL) {
3236                                 idx = vp - gBuiltinParameters->vdwpPars;
3237                                 is_global = 1;
3238                         }
3239                         break;
3240                 }
3241                 default:
3242                         return Qnil;
3243         }
3244         if (idx < 0) {
3245                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3246                         return Qnil;            
3247                 else {
3248                         /*  Insert a new parameter record  */
3249                         UnionPar *up;
3250                         Int count = ParameterGetCountForType(mol->par, parType);
3251                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3252                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3253                         IntGroupRelease(ig);
3254                         is_global = 0;
3255                         idx = count;
3256                         /*  Set atom types  */
3257                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3258                         if (up == NULL)
3259                                 return Qnil;
3260                         switch (parType) {
3261                                 case kBondParType:
3262                                         up->bond.type1 = t[0];
3263                                         up->bond.type2 = t[1];
3264                                         break;
3265                                 case kAngleParType:
3266                                         up->angle.type1 = t[0];
3267                                         up->angle.type2 = t[1];
3268                                         up->angle.type3 = t[2];
3269                                         break;
3270                                 case kDihedralParType:
3271                                 case kImproperParType:
3272                                         up->torsion.type1 = t[0];
3273                                         up->torsion.type2 = t[1];
3274                                         up->torsion.type3 = t[2];
3275                                         up->torsion.type4 = t[3];
3276                                         break;
3277                                 case kVdwParType:
3278                                         up->vdw.type1 = t[0];
3279                                         break;
3280                                 case kVdwPairParType:
3281                                         up->vdwp.type1 = t[0];
3282                                         up->vdwp.type2 = t[1];
3283                                         break;
3284                                 default:
3285                                         return Qnil;
3286                         }
3287                 }
3288         }
3289         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3290 }
3291
3292 /*
3293  *  call-seq:
3294  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3295  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3296  *
3297  *  Find the parameter record that matches the given atom types. The atom types are given
3298  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3299  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3300  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3301  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3302  */
3303 static VALUE
3304 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3305 {
3306         int parType;
3307         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3308         if (argc == 0)
3309                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3310         parType = s_ParTypeFromValue(argv[0]);
3311         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3312 }
3313
3314 /*
3315  *  call-seq:
3316  *     self == parameter -> boolean
3317  *  
3318  *  True if the parameters point to the same parameter table.
3319  */
3320 static VALUE
3321 s_Parameter_Equal(VALUE self, VALUE val)
3322 {
3323         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3324                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3325         } else return Qfalse;
3326 }
3327
3328 #pragma mark ====== ParEnumerable Class ======
3329
3330 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3331  and the parameter type. If the Molecule is NULL, then it refers to the
3332  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3333  the global parameters are always accessible. */
3334
3335 typedef struct ParEnumerable {
3336         Molecule *mol;
3337         Int parType;   /*  Same as parType in ParameterRef  */
3338 } ParEnumerable;
3339
3340 static ParEnumerable *
3341 s_ParEnumerableNew(Molecule *mol, Int parType)
3342 {
3343         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3344         if (pen != NULL) {
3345                 pen->mol = mol;
3346                 if (mol != NULL)
3347                         MoleculeRetain(mol);
3348                 pen->parType = parType;
3349         }
3350         return pen;
3351 }
3352
3353 static void
3354 s_ParEnumerableRelease(ParEnumerable *pen)
3355 {
3356         if (pen != NULL) {
3357                 if (pen->mol != NULL)
3358                         MoleculeRelease(pen->mol);
3359                 free(pen);
3360         }
3361 }
3362
3363 static Molecule *
3364 s_MoleculeFromParEnumerableValue(VALUE val)
3365 {
3366         ParEnumerable *pen;
3367     Data_Get_Struct(val, ParEnumerable, pen);
3368         return pen->mol;
3369 }
3370
3371 static VALUE
3372 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3373 {
3374         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3375         if (pen == NULL)
3376                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3377         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3378 }
3379
3380 /*
3381  *  call-seq:
3382  *     par_type -> String
3383  *
3384  *  Get the parameter type, like "bond", "angle", etc.
3385  */
3386 static VALUE
3387 s_ParEnumerable_ParType(VALUE self) {
3388         ParEnumerable *pen;
3389         Int tp;
3390     Data_Get_Struct(self, ParEnumerable, pen);
3391         tp = pen->parType;
3392         if (tp == kElementParType)
3393                 return rb_str_new2("element");
3394         tp -= kFirstParType;
3395         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3396                 return rb_str_new2(s_ParameterTypeNames[tp]);
3397         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3398 }
3399
3400 /*
3401  *  call-seq:
3402  *     self[idx]          -> ParameterRef
3403  *  
3404  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3405  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3406  *  parent Parameter object of self.
3407  *
3408  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3409  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3410  */
3411 static VALUE
3412 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3413 {
3414         ParEnumerable *pen;
3415     Data_Get_Struct(self, ParEnumerable, pen);
3416         switch (pen->parType) {
3417                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3418                 case kBondParType:      return s_Parameter_Bond(self, ival);
3419                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3420                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3421                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3422                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3423                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3424                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3425                 case kElementParType:   return s_Parameter_Element(self, ival);
3426                 default:
3427                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3428         }
3429         return Qnil;  /*  Not reached  */
3430 }
3431
3432 /*
3433  *  call-seq:
3434  *     length          -> Integer
3435  *  
3436  *  Returns the number of parameters included in this enumerable.
3437  */
3438 static VALUE
3439 s_ParEnumerable_Length(VALUE self)
3440 {
3441         ParEnumerable *pen;
3442     Data_Get_Struct(self, ParEnumerable, pen);
3443         switch (pen->parType) {
3444                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3445                 case kBondParType:      return s_Parameter_Nbonds(self);
3446                 case kAngleParType:     return s_Parameter_Nangles(self); 
3447                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3448                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3449                 case kVdwParType:       return s_Parameter_Nvdws(self);
3450                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3451                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3452                 case kElementParType:   return s_Parameter_Nelements(self);
3453                 default:
3454                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3455         }
3456         return Qnil;  /*  Not reached  */
3457 }
3458
3459 /*
3460  *  call-seq:
3461  *     each {|pref| ...}
3462  *  
3463  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3464  */
3465 VALUE
3466 s_ParEnumerable_Each(VALUE self)
3467 {
3468         VALUE aval;
3469         ParEnumerable *pen;
3470         ParameterRef *pref;
3471         int i, ofs, n;
3472     Data_Get_Struct(self, ParEnumerable, pen);
3473         if (pen->parType == kElementParType)
3474                 n = gCountElementParameters;
3475         else {
3476                 switch (pen->parType) {
3477                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3478                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3479                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3480                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3481                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3482                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3483                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3484                         default:
3485                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3486                 }
3487                 if (pen->mol == NULL)
3488                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3489                 else if (pen->mol->par != NULL)
3490                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3491                 else return self;
3492         }               
3493         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3494         Data_Get_Struct(aval, ParameterRef, pref);
3495         for (i = 0; i < n; i++) {
3496                 pref->idx = i;
3497                 rb_yield(aval);
3498         }
3499     return self;
3500 }
3501
3502 /*
3503  *  call-seq:
3504  *     reverse_each {|pref| ...}
3505  *  
3506  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3507  */
3508 VALUE
3509 s_ParEnumerable_ReverseEach(VALUE self)
3510 {
3511         VALUE aval;
3512         ParEnumerable *pen;
3513         ParameterRef *pref;
3514         int i, ofs, n;
3515     Data_Get_Struct(self, ParEnumerable, pen);
3516         if (pen->parType == kElementParType)
3517                 n = gCountElementParameters;
3518         else {
3519                 switch (pen->parType) {
3520                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3521                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3522                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3523                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3524                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3525                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3526                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3527                         default:
3528                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3529                 }
3530                 if (pen->mol == NULL)
3531                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3532                 else if (pen->mol->par != NULL)
3533                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3534                 else return self;
3535         }               
3536         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3537         Data_Get_Struct(aval, ParameterRef, pref);
3538         for (i = n - 1; i >= 0; i--) {
3539                 pref->idx = i;
3540                 rb_yield(aval);
3541         }
3542     return self;
3543 }
3544
3545 /*
3546  *  call-seq:
3547  *     insert(idx = nil, pref = nil)       -> ParameterRef
3548  *  
3549  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3550  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3551  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3552  *  parameter is left undefined.
3553  *  Throws an exception if ParEnumerable points to the global parameter.
3554  */
3555 static VALUE
3556 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3557 {
3558         VALUE ival, pval;
3559         ParEnumerable *pen;
3560         int i, n;
3561         IntGroup *ig;
3562         UnionPar u;
3563         MolAction *act;
3564     Data_Get_Struct(self, ParEnumerable, pen);
3565         if (pen->mol == NULL)
3566                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3567         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3568         rb_scan_args(argc, argv, "02", &ival, &pval);
3569         if (ival != Qnil) {
3570                 i = NUM2INT(rb_Integer(ival));
3571                 if (i < 0 || i > n)
3572                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3573                 n = i;
3574         }
3575         if (pval != Qnil) {
3576                 Int type;
3577                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3578                 if (up == NULL || type != pen->parType)
3579                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3580                 ParameterCopyOneWithType(&u, up, pen->parType);
3581                 u.bond.src = 0;
3582         } else {
3583                 memset(&u, 0, sizeof(u));
3584                 u.bond.src = 0;
3585         }
3586         ig = IntGroupNewWithPoints(n, 1, -1);
3587         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3588
3589         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3590         MolActionCallback_registerUndo(pen->mol, act);
3591         MolActionRelease(act);
3592         
3593         IntGroupRelease(ig);
3594         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3595 }
3596
3597 /*
3598  *  call-seq:
3599  *     delete(Integer)
3600  *     delete(IntGroup)
3601  *  
3602  *  Delete the parameter(s) specified by the argument.
3603  */
3604 static VALUE
3605 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3606 {
3607         ParEnumerable *pen;
3608         int i, n;
3609         IntGroup *ig;
3610     Data_Get_Struct(self, ParEnumerable, pen);
3611         if (pen->mol == NULL)
3612                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3613         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3614         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3615                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3616                 i = 1;
3617         } else {
3618                 ig = IntGroupFromValue(ival);
3619                 if ((i = IntGroupGetCount(ig)) == 0) {
3620                         IntGroupRelease(ig);
3621                         return Qnil;
3622                 }
3623         }
3624         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3625                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3626         n = i;
3627
3628         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3629         IntGroupRelease(ig);
3630         return ival;
3631 }
3632
3633 /*
3634  *  call-seq:
3635  *     lookup(atom_types, options, ...) -> ParameterRef
3636  *     lookup(atom_type_string, options, ...) -> ParameterRef
3637  *
3638  *  Find the parameter record that matches the given atom types. The arguments are
3639  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3640  *  specified.
3641  */
3642 static VALUE
3643 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3644 {
3645         ParEnumerable *pen;
3646     Data_Get_Struct(self, ParEnumerable, pen);
3647         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3648 }
3649
3650 /*
3651  *  call-seq:
3652  *     self == parEnumerable -> boolean
3653  *  
3654  *  True if the arguments point to the same parameter table and type.
3655  */
3656 static VALUE
3657 s_ParEnumerable_Equal(VALUE self, VALUE val)
3658 {
3659         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3660                 ParEnumerable *pen1, *pen2;
3661                 Data_Get_Struct(self, ParEnumerable, pen1);
3662                 Data_Get_Struct(val, ParEnumerable, pen2);
3663                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3664         } else return Qfalse;
3665 }
3666
3667 #pragma mark ====== AtomRef Class ======
3668
3669 /*  Forward declaration for register undo  */
3670 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3671
3672 /*  Ruby string "set_atom_attr"  */
3673 static VALUE s_SetAtomAttrString;
3674
3675 static int
3676 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3677 {
3678         AtomRef *aref;
3679         int idx;
3680         Data_Get_Struct(self, AtomRef, aref);
3681         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3682         if (idx < 0 || idx >= aref->mol->natoms)
3683                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3684         if (app != NULL)
3685                 *app = aref->mol->atoms + idx;
3686         if (mpp != NULL)
3687                 *mpp = aref->mol;
3688         return idx;
3689 }
3690
3691 static Atom *
3692 s_AtomFromValue(VALUE self)
3693 {
3694         Atom *ap;
3695         s_AtomIndexFromValue(self, &ap, NULL);
3696         return ap;
3697 }
3698
3699 static Atom *
3700 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3701 {
3702         Atom *ap;
3703         s_AtomIndexFromValue(self, &ap, mpp);
3704         return ap;
3705 }
3706
3707 static void
3708 s_NotifyModificationForAtomRef(VALUE self)
3709 {
3710         AtomRef *aref;
3711         Data_Get_Struct(self, AtomRef, aref);
3712         MoleculeIncrementModifyCount(aref->mol);
3713 }
3714
3715 static void
3716 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3717 {
3718         AtomRef *aref;
3719         Data_Get_Struct(self, AtomRef, aref);
3720         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3721                 /*  Register undo  */
3722                 MolAction *act;
3723                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3724                 MolActionCallback_registerUndo(aref->mol, act);
3725                 MoleculeCallback_notifyModification(aref->mol, 0);
3726                 /*  Request MD rebuilt if necessary  */
3727                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3728                         aref->mol->needsMDRebuild = 1;
3729         }
3730 }
3731
3732 VALUE
3733 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3734 {
3735         AtomRef *aref;
3736         aref = AtomRefNew(mol, idx);
3737         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3738 }
3739
3740 static VALUE
3741 s_AtomRef_GetMolecule(VALUE self)
3742 {
3743         Molecule *mpp;
3744         s_AtomIndexFromValue(self, NULL, &mpp);
3745         return ValueFromMolecule(mpp);
3746 }
3747
3748 static VALUE s_AtomRef_GetIndex(VALUE self) {
3749         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3750 }
3751
3752 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3753         return INT2NUM(s_AtomFromValue(self)->segSeq);
3754 }
3755
3756 static VALUE s_AtomRef_GetSegName(VALUE self) {
3757         char *p = s_AtomFromValue(self)->segName;
3758         return rb_str_new(p, strlen_limit(p, 4));
3759 }
3760
3761 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3762         return INT2NUM(s_AtomFromValue(self)->resSeq);
3763 }
3764
3765 static VALUE s_AtomRef_GetResName(VALUE self) {
3766         char *p = s_AtomFromValue(self)->resName;
3767         return rb_str_new(p, strlen_limit(p, 4));
3768 }
3769
3770 static VALUE s_AtomRef_GetName(VALUE self) {
3771         char *p = s_AtomFromValue(self)->aname;
3772         return rb_str_new(p, strlen_limit(p, 4));
3773 }
3774
3775 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3776         int type = s_AtomFromValue(self)->type;
3777         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3778         return rb_str_new(p, strlen_limit(p, 6));
3779 }
3780
3781 static VALUE s_AtomRef_GetCharge(VALUE self) {
3782         return rb_float_new(s_AtomFromValue(self)->charge);
3783 }
3784
3785 static VALUE s_AtomRef_GetWeight(VALUE self) {
3786         return rb_float_new(s_AtomFromValue(self)->weight);
3787 }
3788
3789 static VALUE s_AtomRef_GetElement(VALUE self) {
3790         char *p = s_AtomFromValue(self)->element;
3791         return rb_str_new(p, strlen_limit(p, 4));
3792 }
3793
3794 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3795         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3796 }
3797
3798 static VALUE s_AtomRef_GetConnects(VALUE self) {
3799         VALUE retval;
3800         Int i, *cp;
3801         Atom *ap = s_AtomFromValue(self);
3802         retval = rb_ary_new();
3803         cp = AtomConnectData(&ap->connect);
3804         for (i = 0; i < ap->connect.count; i++)
3805                 rb_ary_push(retval, INT2NUM(cp[i]));
3806         return retval;
3807 }
3808
3809 static VALUE s_AtomRef_GetR(VALUE self) {
3810         return ValueFromVector(&(s_AtomFromValue(self)->r));
3811 }
3812
3813 static VALUE s_AtomRef_GetX(VALUE self) {
3814         return rb_float_new(s_AtomFromValue(self)->r.x);
3815 }
3816
3817 static VALUE s_AtomRef_GetY(VALUE self) {
3818         return rb_float_new(s_AtomFromValue(self)->r.y);
3819 }
3820
3821 static VALUE s_AtomRef_GetZ(VALUE self) {
3822         return rb_float_new(s_AtomFromValue(self)->r.z);
3823 }
3824
3825 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3826         Atom *ap;
3827         Molecule *mp;
3828         Vector r1;
3829         s_AtomIndexFromValue(self, &ap, &mp);
3830         r1 = ap->r;
3831         if (mp->cell != NULL)
3832                 TransformVec(&r1, mp->cell->rtr, &r1);
3833         return r1;
3834 }
3835
3836 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3837         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3838         return ValueFromVector(&r1);
3839 }
3840
3841 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3842         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3843 }
3844
3845 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3846         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3847 }
3848
3849 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3850         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3851 }
3852
3853 static VALUE s_AtomRef_GetSigma(VALUE self) {
3854         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3855 }
3856
3857 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3858         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3859 }
3860
3861 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3862         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3863 }
3864
3865 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3866         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3867 }
3868
3869 static VALUE s_AtomRef_GetV(VALUE self) {
3870         return ValueFromVector(&(s_AtomFromValue(self)->v));
3871 }
3872
3873 static VALUE s_AtomRef_GetF(VALUE self) {
3874         Vector v = s_AtomFromValue(self)->f;
3875         VecScaleSelf(v, INTERNAL2KCAL);
3876         return ValueFromVector(&v);
3877 }
3878
3879 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3880         return rb_float_new(s_AtomFromValue(self)->occupancy);
3881 }
3882
3883 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3884         return rb_float_new(s_AtomFromValue(self)->tempFactor);
3885 }
3886
3887 static VALUE s_AtomRef_GetAniso(VALUE self) {
3888         VALUE retval;
3889         int i;
3890         Atom *ap = s_AtomFromValue(self);
3891         if (ap->aniso == NULL)
3892                 return Qnil;
3893         retval = rb_ary_new();
3894         for (i = 0; i < 6; i++)
3895                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3896         if (ap->aniso->has_bsig) {
3897                 rb_ary_push(retval, INT2NUM(0));
3898                 for (i = 0; i < 6; i++)
3899                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3900         }
3901         return retval;
3902 }
3903
3904 static VALUE s_AtomRef_GetSymop(VALUE self) {
3905         VALUE retval;
3906         Atom *ap = s_AtomFromValue(self);
3907         if (!ap->symop.alive)
3908                 return Qnil;
3909         retval = rb_ary_new();
3910         rb_ary_push(retval, INT2NUM(ap->symop.sym));
3911         rb_ary_push(retval, INT2NUM(ap->symop.dx));
3912         rb_ary_push(retval, INT2NUM(ap->symop.dy));
3913         rb_ary_push(retval, INT2NUM(ap->symop.dz));
3914         rb_ary_push(retval, INT2NUM(ap->symbase));
3915         return retval;
3916 }
3917
3918 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3919         return INT2NUM(s_AtomFromValue(self)->intCharge);
3920 }
3921
3922 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3923         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3924 }
3925
3926 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3927         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3928 }
3929
3930 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3931         Molecule *mol;
3932         Atom *ap;
3933         int idx, i;
3934         MDExclusion *exinfo;
3935         Int *exlist;
3936         VALUE retval, aval;
3937         idx = s_AtomIndexFromValue(self, &ap, &mol);
3938         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3939                 VALUE mval = ValueFromMolecule(mol);
3940                 s_RebuildMDParameterIfNecessary(mval, Qnil);
3941         }
3942         if (mol->arena->exinfo == NULL)
3943                 return Qnil;
3944         exinfo = mol->arena->exinfo + idx;
3945         exlist = mol->arena->exlist;
3946         retval = rb_ary_new();
3947         aval = rb_ary_new();
3948         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
3949                 rb_ary_push(aval, INT2NUM(exlist[i]));
3950         rb_ary_push(retval, aval);
3951         aval = rb_ary_new();
3952         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
3953                 rb_ary_push(aval, INT2NUM(exlist[i]));
3954         rb_ary_push(retval, aval);
3955         aval = rb_ary_new();
3956         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
3957                 rb_ary_push(aval, INT2NUM(exlist[i]));
3958         rb_ary_push(retval, aval);
3959         return retval;
3960 }
3961
3962 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3963         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3964 }
3965
3966 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3967         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3968 }
3969
3970 static VALUE s_AtomRef_GetHidden(VALUE self) {
3971         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3972 }
3973
3974 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3975         VALUE retval;
3976         Int i, count, *cp;
3977         Atom *ap = s_AtomFromValue(self);
3978         if (ap->anchor == NULL)
3979                 return Qnil;
3980         count = ap->anchor->connect.count;
3981         retval = rb_ary_new2(count * 2);
3982         cp = AtomConnectData(&ap->anchor->connect);
3983         for (i = 0; i < count; i++) {
3984                 rb_ary_store(retval, i, INT2NUM(cp[i]));
3985                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3986         }
3987         return retval;
3988 }
3989
3990 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3991         char *p = s_AtomFromValue(self)->uff_type;
3992         return rb_str_new(p, strlen_limit(p, 5));
3993 }
3994
3995 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3996         rb_raise(rb_eMolbyError, "index cannot be directly set");
3997         return Qnil;
3998 }
3999
4000 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4001         VALUE oval = s_AtomRef_GetSegSeq(self);
4002         val = rb_Integer(val);
4003         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4004         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4005         return val;
4006 }
4007
4008 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4009         char *p = StringValuePtr(val);
4010         VALUE oval = s_AtomRef_GetSegName(self);
4011         strncpy(s_AtomFromValue(self)->segName, p, 4);
4012         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4013         return val;
4014 }
4015
4016 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4017         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4018         return val; /* Not reached */
4019 }
4020
4021 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4022         Atom *ap = s_AtomFromValue(self);
4023         char *p = StringValuePtr(val);
4024         VALUE oval = s_AtomRef_GetName(self);
4025         if (ap->anchor != NULL && p[0] == '_')
4026                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4027         strncpy(ap->aname, p, 4);
4028         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4029         return val;
4030 }
4031
4032 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4033         Molecule *mp;
4034         char *p = StringValuePtr(val);
4035         VALUE oval = s_AtomRef_GetAtomType(self);
4036         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4037         if (type != 0 && type < kAtomTypeMinimum)
4038                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4039         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4040         mp->needsMDRebuild = 1;
4041         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4042         return val;
4043 }
4044
4045 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4046         Molecule *mp;
4047         VALUE oval = s_AtomRef_GetCharge(self);
4048         val = rb_Float(val);
4049         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4050         mp->needsMDRebuild = 1;
4051         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4052         return val;
4053 }
4054
4055 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4056         Molecule *mp;
4057         VALUE oval = s_AtomRef_GetWeight(self);
4058         val = rb_Float(val);
4059         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4060         mp->needsMDRebuild = 1;
4061         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4062         return val;
4063 }
4064
4065 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4066         Double w;
4067         Molecule *mp;
4068         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4069         char *p = StringValuePtr(val);
4070         VALUE oval = s_AtomRef_GetElement(self);
4071         ap->atomicNumber = ElementToInt(p);
4072         ElementToString(ap->atomicNumber, ap->element);
4073         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4074                 ap->weight = w;
4075         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4076         mp->needsMDRebuild = 1;
4077         return val;
4078 }
4079
4080 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4081         Double w;
4082         Molecule *mp;
4083         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4084         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4085         val = rb_Integer(val);
4086         ap->atomicNumber = NUM2INT(val);
4087         ElementToString(ap->atomicNumber, ap->element);
4088         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4089                 ap->weight = w;
4090         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4091         mp->needsMDRebuild = 1;
4092         return val;
4093 }
4094
4095 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4096         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4097         return val; /* Not reached */
4098 }
4099
4100 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4101         Vector v;
4102         Molecule *mp;
4103         VALUE oval = s_AtomRef_GetR(self);
4104         VectorFromValue(val, &v);
4105         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4106         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4107         mp->needsMDCopyCoordinates = 1;
4108         return val;
4109 }
4110
4111 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4112         Double f;
4113         Molecule *mp;
4114         VALUE oval = s_AtomRef_GetX(self);
4115         val = rb_Float(val);
4116         f = NUM2DBL(val);
4117         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4118         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4119         mp->needsMDCopyCoordinates = 1;
4120         return val;
4121 }
4122
4123 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4124         Double f;
4125         Molecule *mp;
4126         VALUE oval = s_AtomRef_GetY(self);
4127         val = rb_Float(val);
4128         f = NUM2DBL(val);
4129         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4130         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4131         mp->needsMDCopyCoordinates = 1;
4132         return val;
4133 }
4134
4135 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4136         Double f;
4137         Molecule *mp;
4138         VALUE oval = s_AtomRef_GetZ(self);
4139         val = rb_Float(val);
4140         f = NUM2DBL(val);
4141         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4142         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4143         mp->needsMDCopyCoordinates = 1;
4144         return val;
4145 }
4146
4147 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4148         Vector v, ov;
4149         Atom *ap;
4150         Molecule *mp;
4151         s_AtomIndexFromValue(self, &ap, &mp);
4152         ov = ap->r;
4153         VectorFromValue(val, &v);
4154         if (mp->cell != NULL)
4155                 TransformVec(&v, mp->cell->tr, &v);
4156         ap->r = v;
4157         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4158         mp->needsMDCopyCoordinates = 1;
4159         return val;
4160 }
4161
4162 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4163         double f;
4164         Vector v, ov;
4165         Atom *ap;
4166         Molecule *mp;
4167         s_AtomIndexFromValue(self, &ap, &mp);
4168         ov = v = ap->r;
4169         val = rb_Float(val);
4170         f = NUM2DBL(val);
4171         if (mp->cell != NULL) {
4172                 TransformVec(&v, mp->cell->rtr, &v);
4173                 v.x = f;
4174                 TransformVec(&v, mp->cell->tr, &v);
4175         } else v.x = f;
4176         ap->r = v;
4177         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4178         mp->needsMDCopyCoordinates = 1;
4179         return val;
4180 }
4181
4182 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4183         double f;
4184         Vector v, ov;
4185         Atom *ap;
4186         Molecule *mp;
4187         s_AtomIndexFromValue(self, &ap, &mp);
4188         ov = v = ap->r;
4189         val = rb_Float(val);
4190         f = NUM2DBL(val);
4191         if (mp->cell != NULL) {
4192                 TransformVec(&v, mp->cell->rtr, &v);
4193                 v.y = f;
4194                 TransformVec(&v, mp->cell->tr, &v);
4195         } else v.y = f;
4196         ap->r = v;
4197         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4198         mp->needsMDCopyCoordinates = 1;
4199         return val;
4200 }
4201
4202 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4203         double f;
4204         Vector v, ov;
4205         Atom *ap;
4206         Molecule *mp;
4207         s_AtomIndexFromValue(self, &ap, &mp);
4208         ov = v = ap->r;
4209         val = rb_Float(val);
4210         f = NUM2DBL(val);
4211         if (mp->cell != NULL) {
4212                 TransformVec(&v, mp->cell->rtr, &v);
4213                 v.z = f;
4214                 TransformVec(&v, mp->cell->tr, &v);
4215         } else v.z = f;
4216         ap->r = v;
4217         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4218         mp->needsMDCopyCoordinates = 1;
4219         return val;
4220 }
4221
4222 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4223         Vector v;
4224         Molecule *mp;
4225         VALUE oval = s_AtomRef_GetSigma(self);
4226         VectorFromValue(val, &v);
4227         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4228         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4229         mp->needsMDCopyCoordinates = 1;
4230         return val;
4231 }
4232
4233 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4234         Double f;
4235         Molecule *mp;
4236         VALUE oval = s_AtomRef_GetSigmaX(self);
4237         val = rb_Float(val);
4238         f = NUM2DBL(val);
4239         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4240         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4241         mp->needsMDCopyCoordinates = 1;
4242         return val;
4243 }
4244
4245 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4246         Double f;
4247         Molecule *mp;
4248         VALUE oval = s_AtomRef_GetSigmaY(self);
4249         val = rb_Float(val);
4250         f = NUM2DBL(val);
4251         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4252         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4253         mp->needsMDCopyCoordinates = 1;
4254         return val;
4255 }
4256
4257 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4258         Double f;
4259         Molecule *mp;
4260         VALUE oval = s_AtomRef_GetSigmaZ(self);
4261         val = rb_Float(val);
4262         f = NUM2DBL(val);
4263         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4264         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4265         mp->needsMDCopyCoordinates = 1;
4266         return val;
4267 }
4268
4269 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4270         Vector v;
4271         Atom *ap;
4272         Molecule *mp;
4273         VALUE oval = s_AtomRef_GetV(self);
4274         VectorFromValue(val, &v);
4275         s_AtomIndexFromValue(self, &ap, &mp);
4276         ap->v = v;
4277         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4278         mp->needsMDCopyCoordinates = 1;
4279         return val;
4280 }
4281
4282 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4283         Vector v;
4284         Molecule *mp;
4285         VALUE oval = s_AtomRef_GetF(self);
4286         VectorFromValue(val, &v);
4287         VecScaleSelf(v, KCAL2INTERNAL);
4288         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4289         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4290         mp->needsMDCopyCoordinates = 1;
4291         return val;
4292 }
4293
4294 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4295         VALUE oval = s_AtomRef_GetOccupancy(self);
4296         Molecule *mp;
4297         val = rb_Float(val);
4298         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4299         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4300         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4301         return val;
4302 }
4303
4304 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4305         VALUE oval = s_AtomRef_GetTempFactor(self);
4306         val = rb_Float(val);
4307         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4308         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4309         return val;
4310 }
4311
4312 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4313         AtomRef *aref;
4314         int i, n, type;
4315         VALUE *valp;
4316         Double f[12];
4317         VALUE oval = s_AtomRef_GetAniso(self);
4318         Data_Get_Struct(self, AtomRef, aref);
4319         val = rb_funcall(val, rb_intern("to_a"), 0);
4320         n = RARRAY_LEN(val);
4321         valp = RARRAY_PTR(val);
4322         for (i = 0; i < 6; i++) {
4323                 if (i < n)
4324                         f[i] = NUM2DBL(rb_Float(valp[i]));
4325                 else f[i] = 0.0;
4326         }
4327         if (n >= 7)
4328                 type = NUM2INT(rb_Integer(valp[6]));
4329         else type = 0;
4330         if (n >= 13) {
4331                 for (i = 0; i < 6; i++)
4332                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4333         } else {
4334                 for (i = 0; i < 6; i++)
4335                         f[i + 6] = 0.0;
4336         }
4337         i = s_AtomIndexFromValue(self, NULL, NULL);
4338         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4339         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4340         return val;
4341 }
4342
4343 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4344         Molecule *mol;
4345         Atom *ap;
4346         int i, n;
4347         VALUE *valp;
4348         Int ival[5];
4349         VALUE oval = s_AtomRef_GetSymop(self);
4350         i = s_AtomIndexFromValue(self, &ap, &mol);
4351         if (val == Qnil) {
4352                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4353         } else {
4354                 val = rb_funcall(val, rb_intern("to_a"), 0);
4355                 n = RARRAY_LEN(val);
4356                 valp = RARRAY_PTR(val);
4357                 for (i = 0; i < 5; i++) {
4358                         if (i < n) {
4359                                 if (valp[i] == Qnil)
4360                                         ival[i] = -100000;
4361                                 else 
4362                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4363                         } else ival[i] = -100000;
4364                 }
4365         }
4366         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4367                 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));
4368         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4369                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4370         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4371                 return val;  /*  No need to change  */
4372         if (ival[0] != -100000)
4373                 ap->symop.sym = ival[0];
4374         if (ival[1] != -100000)
4375                 ap->symop.dx = ival[1];
4376         if (ival[2] != -100000)
4377                 ap->symop.dy = ival[2];
4378         if (ival[3] != -100000)
4379                 ap->symop.dz = ival[3];
4380         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4381         if (ival[4] != -100000)
4382                 ap->symbase = ival[4];
4383         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4384                 /*  The anisotropic parameters should be recalculated  */
4385                 VALUE oaval = s_AtomRef_GetAniso(self);
4386                 MoleculeSetAnisoBySymop(mol, i);
4387                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4388         }
4389         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4390         return val;
4391 }
4392
4393 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4394         VALUE oval = s_AtomRef_GetIntCharge(self);
4395         val = rb_Integer(val);
4396         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4397         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4398         return val;
4399 }
4400
4401 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4402         Molecule *mp;
4403         VALUE oval = s_AtomRef_GetFixForce(self);
4404         val = rb_Float(val);
4405         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4406         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4407         mp->needsMDRebuild = 1;
4408         return val;
4409 }
4410
4411 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4412         Vector v;
4413         Molecule *mp;
4414         VALUE oval = s_AtomRef_GetFixPos(self);
4415         VectorFromValue(val, &v);
4416         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4417         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4418         mp->needsMDRebuild = 1;
4419         return val;
4420 }
4421
4422 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4423         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4424         return val; /* Not reached */
4425 }
4426
4427 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4428         VALUE oval = s_AtomRef_GetIntCharge(self);
4429         val = rb_Integer(val);
4430         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4431         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4432         return val;
4433 }
4434
4435 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4436         VALUE oval = s_AtomRef_GetIntCharge(self);
4437         val = rb_Integer(val);
4438         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4439         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4440         return val;
4441 }
4442
4443 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4444         Atom *ap = s_AtomFromValue(self);
4445         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4446         if (RTEST(val)) {
4447                 ap->exflags |= kAtomHiddenFlag;
4448         } else {
4449                 ap->exflags &= ~kAtomHiddenFlag;
4450         }
4451         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4452         return val;
4453 }
4454
4455 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4456         Int idx, i, j, k, n, *ip;
4457         Double *dp;
4458         Atom *ap;
4459         Molecule *mol;
4460         VALUE oval, v;
4461         AtomConnect ac;
4462         Int nUndoActions;
4463         MolAction **undoActions;
4464         memset(&ac, 0, sizeof(ac));
4465         idx = s_AtomIndexFromValue(self, &ap, &mol);
4466         oval = s_AtomRef_GetAnchorList(self);
4467         if (val != Qnil) {
4468                 val = rb_ary_to_ary(val);
4469                 n = RARRAY_LEN(val);
4470         } else n = 0;
4471         if (n == 0) {
4472                 if (ap->anchor != NULL) {
4473                         AtomConnectResize(&ap->anchor->connect, 0);
4474                         free(ap->anchor->coeffs);
4475                         free(ap->anchor);
4476                         ap->anchor = NULL;
4477                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4478                 }
4479                 return val;
4480         }
4481         if (n < 2)
4482                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4483         if (ap->aname[0] == '_')
4484                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4485         ip = (Int *)malloc(sizeof(Int) * n);
4486         dp = NULL;
4487         for (i = 0; i < n; i++) {
4488                 v = RARRAY_PTR(val)[i];
4489                 if (rb_obj_is_kind_of(v, rb_cFloat))
4490                         break;
4491                 j = NUM2INT(rb_Integer(v));
4492                 if (j < 0 || j >= mol->natoms)
4493                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4494                 for (k = 0; k < i; k++) {
4495                         if (ip[k] == j)
4496                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4497                 }
4498                 ip[i] = j;
4499         }
4500         if (i < n) {
4501                 if (i < 2)
4502                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4503                 else if (i * 2 != n)
4504                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4505                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4506                 for (i = 0; i < n / 2; i++) {
4507                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4508                         if (dp[i] <= 0.0)
4509                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4510                 }
4511                 n /= 2;
4512         }
4513         nUndoActions = 0;
4514         undoActions = NULL;
4515         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4516         free(dp);
4517         free(ip);
4518         if (i != 0)
4519                 rb_raise(rb_eMolbyError, "invalid argument");
4520         if (nUndoActions > 0) {
4521                 for (i = 0; i < nUndoActions; i++) {
4522                         MolActionCallback_registerUndo(mol, undoActions[i]);
4523                         MolActionRelease(undoActions[i]);
4524                 }
4525                 free(undoActions);
4526         }
4527         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4528         return val;
4529 }
4530
4531 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4532         Atom *ap = s_AtomFromValue(self);
4533         char *p = StringValuePtr(val);
4534         VALUE oval = s_AtomRef_GetUFFType(self);
4535         strncpy(ap->uff_type, p, 5);
4536         ap->uff_type[5] = 0;
4537         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4538         return val;
4539 }
4540
4541 static struct s_AtomAttrDef {
4542         char *name;
4543         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4544         ID id;                  /*  Will be set within InitMolby()  */
4545         VALUE (*getter)(VALUE);
4546         VALUE (*setter)(VALUE, VALUE);
4547 } s_AtomAttrDefTable[] = {
4548         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4549         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4550         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4551         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4552         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4553         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4554         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4555         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4556         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4557         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4558         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4559         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4560         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4561         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4562         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4563     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4564         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4565         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4566         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4567         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4568         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4569         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4570         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4571         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4572         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4573         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4574         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4575         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4576         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4577         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4578         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4579         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4580         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4581         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4582         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4583         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4584         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4585         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4586         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4587         {NULL} /* Sentinel */
4588 };
4589
4590 static VALUE
4591 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4592 {
4593         int i;
4594         ID kid;
4595         if (TYPE(key) != T_SYMBOL) {
4596                 kid = rb_intern(StringValuePtr(key));
4597                 key = ID2SYM(kid);
4598         } else kid = SYM2ID(key);
4599         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4600                 if (s_AtomAttrDefTable[i].id == kid) {
4601                         if (value == Qundef)
4602                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4603                         else
4604                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4605                 }
4606         }
4607         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4608         return Qnil; /* not reached */
4609 }
4610
4611 static VALUE
4612 s_AtomRef_GetAttr(VALUE self, VALUE key)
4613 {
4614         return s_AtomRef_SetAttr(self, key, Qundef);
4615 }
4616
4617 /*
4618  *  call-seq:
4619  *     self == atomRef -> boolean
4620  *
4621  *  True if the two references point to the same atom.
4622  */
4623 static VALUE
4624 s_AtomRef_Equal(VALUE self, VALUE val)
4625 {
4626         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4627                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4628         } else return Qfalse;
4629 }
4630
4631 #pragma mark ====== MolEnumerable Class ======
4632
4633 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4634
4635 /*
4636  *  call-seq:
4637  *     self[idx] -> AtomRef or Array of Integers
4638  *  
4639  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4640  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4641  *  value is a String. Otherwise, the return value is an Array of Integers.
4642  */
4643 static VALUE
4644 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4645 {
4646         MolEnumerable *mseq;
4647         Molecule *mol;
4648         int idx1, idx2;
4649     Data_Get_Struct(self, MolEnumerable, mseq);
4650         mol = mseq->mol;
4651         if (mseq->kind == kAtomKind) {
4652                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4653         }
4654         idx1 = NUM2INT(arg1);
4655         switch (mseq->kind) {
4656                 case kBondKind: {
4657                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4658                         if (idx2 < 0 || idx2 >= mol->nbonds)
4659                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4660                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4661                 }
4662                 case kAngleKind: {
4663                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4664                         if (idx2 < 0 || idx2 >= mol->nangles)
4665                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4666                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4667                 }
4668                 case kDihedralKind: {
4669                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4670                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4671                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4672                         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]));
4673                 }
4674                 case kImproperKind: {
4675                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4676                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4677                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4678                         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]));
4679                 }
4680                 case kResidueKind: {
4681                         char *p;
4682                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4683                         if (idx2 < 0 || idx2 >= mol->nresidues)
4684                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4685                         p = mol->residues[idx2];
4686                         return rb_str_new(p, strlen_limit(p, 4));
4687                 }
4688         }
4689         return Qnil;
4690 }
4691
4692 /*
4693  *  call-seq:
4694  *     length          -> Integer
4695  *  
4696  *  Returns the number of objects included in this enumerable.
4697  */
4698 static VALUE
4699 s_MolEnumerable_Length(VALUE self)
4700 {
4701         MolEnumerable *mseq;
4702     Data_Get_Struct(self, MolEnumerable, mseq);
4703         switch (mseq->kind) {
4704                 case kAtomKind:
4705                         return INT2NUM(mseq->mol->natoms);
4706                 case kBondKind:
4707                         return INT2NUM(mseq->mol->nbonds);
4708                 case kAngleKind:
4709                         return INT2NUM(mseq->mol->nangles);
4710                 case kDihedralKind:
4711                         return INT2NUM(mseq->mol->ndihedrals);
4712                 case kImproperKind:
4713                         return INT2NUM(mseq->mol->nimpropers);
4714                 case kResidueKind:
4715                         return INT2NUM(mseq->mol->nresidues);
4716         }
4717         return INT2NUM(-1);
4718 }
4719
4720 /*
4721  *  call-seq:
4722  *     each {|obj| ...}
4723  *  
4724  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4725  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4726  *  For the atoms, a same AtomRef object is passed (with different internal information)
4727  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4728  *  for each iteration.
4729  */
4730 VALUE
4731 s_MolEnumerable_Each(VALUE self)
4732 {
4733         MolEnumerable *mseq;
4734         int i;
4735         int len = NUM2INT(s_MolEnumerable_Length(self));
4736     Data_Get_Struct(self, MolEnumerable, mseq);
4737         if (mseq->kind == kAtomKind) {
4738                 /*  The same AtomRef object will be used during the loop  */
4739                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4740                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4741                 for (i = 0; i < len; i++) {
4742                         aref->idx = i;
4743                         rb_yield(arval);
4744                 }
4745     } else {
4746                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4747                 for (i = 0; i < len; i++) {
4748                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4749                 }
4750         }
4751     return self;
4752 }
4753
4754 /*
4755  *  call-seq:
4756  *     self == molEnumerable -> boolean
4757  *
4758  *  True if the two arguments point to the same molecule and enumerable type.
4759  */
4760 static VALUE
4761 s_MolEnumerable_Equal(VALUE self, VALUE val)
4762 {
4763         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4764                 MolEnumerable *mseq1, *mseq2;
4765                 Data_Get_Struct(self, MolEnumerable, mseq1);
4766                 Data_Get_Struct(val, MolEnumerable, mseq2);
4767                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4768         } else return Qfalse;
4769 }
4770
4771
4772 #pragma mark ====== Molecule Class ======
4773
4774 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method.  */
4775 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4776 char *gLoadSaveErrorMessage = NULL;
4777
4778 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4779
4780 Molecule *
4781 MoleculeFromValue(VALUE val)
4782 {
4783         Molecule *mol;
4784         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4785                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4786     Data_Get_Struct(val, Molecule, mol);
4787         return mol;
4788 }
4789
4790 static VALUE sMoleculeRetainArray = Qnil;
4791
4792 /*  The function is called from MoleculeRelease()  */
4793 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4794 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4795 /*  object is always returned for the same Molecule.  */
4796 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4797 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4798 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4799 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4800 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4801
4802 /*  Register/unregister the exmolobj Ruby object  */
4803 void
4804 MoleculeReleaseExternalObj(Molecule *mol)
4805 {
4806         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4807                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4808                 mol->exmolobjProtected = 0;
4809         }
4810 }
4811
4812 void
4813 MoleculeRetainExternalObj(Molecule *mol)
4814 {
4815         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4816                 if (sMoleculeRetainArray == Qnil) {
4817                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4818                         sMoleculeRetainArray = rb_ary_new();
4819                 }
4820                 
4821                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4822                 mol->exmolobjProtected = 1;
4823         }
4824 }
4825
4826 /*  Release hook function for Ruby  */
4827 void
4828 MoleculeReleaseHook(Molecule *mol)
4829 {
4830         if (mol->exmolobj != NULL) {
4831                 /*  No need to remove from sMoleculeRetainArray  */
4832                 mol->exmolobj = NULL;
4833                 mol->exmolobjProtected = 0;
4834         }
4835         MoleculeRelease(mol);
4836 }
4837
4838 VALUE
4839 ValueFromMolecule(Molecule *mol)
4840 {
4841         if (mol == NULL)
4842                 return Qnil;
4843         if (mol->exmolobj != NULL)
4844                 return (VALUE)mol->exmolobj;
4845         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4846         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4847         return (VALUE)mol->exmolobj;
4848 }
4849
4850 /*  Allocator  */
4851 static VALUE
4852 s_Molecule_Alloc(VALUE klass)
4853 {
4854         VALUE val;
4855         Molecule *mol = MoleculeNew();
4856         val = ValueFromMolecule(mol);
4857         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
4858         return val;
4859 }
4860
4861 static int
4862 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4863 {
4864         int n;
4865         char *p = "";
4866         if (FIXNUM_P(val)) {
4867                 n = FIX2INT(val);
4868                 if (n >= 0 && n < mol->natoms)
4869                         return n;
4870                 n = -1; /*  No such atom  */
4871                 val = rb_inspect(val);
4872         } else {
4873                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4874         }
4875         if (n >= 0 && n < mol->natoms)
4876                 return n;
4877         p = StringValuePtr(val);
4878         if (n == -1)
4879                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4880         else if (n == -2)
4881                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4882         else
4883                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4884         return 0; /* Not reached */
4885 }
4886
4887 static IntGroup *
4888 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4889 {
4890         IntGroup *ig;
4891         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4892         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4893                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4894         Data_Get_Struct(val, IntGroup, ig);
4895         IntGroupRetain(ig);
4896         return ig;
4897 }
4898
4899 static void
4900 s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
4901 {
4902         if (gLoadSaveErrorMessage != NULL) {
4903                 MyAppCallback_setConsoleColor(1);
4904                 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
4905                 MyAppCallback_setConsoleColor(0);
4906         }
4907         if (status != 0)
4908                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4909 }
4910
4911 /*
4912  *  call-seq:
4913  *     dup          -> Molecule
4914  *
4915  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
4916  *  created object does not affect the old object in any sense.
4917  */
4918 static VALUE
4919 s_Molecule_InitCopy(VALUE self, VALUE arg)
4920 {
4921         Molecule *mp1, *mp2;
4922         Data_Get_Struct(self, Molecule, mp1);
4923         mp2 = MoleculeFromValue(arg);
4924         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4925                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4926         return self;
4927 }
4928
4929 /*
4930  *  call-seq:
4931  *     loadmbsf(file)       -> bool
4932  *
4933  *  Read a structure from a mbsf file.
4934  *  Return true if successful.
4935  */
4936 static VALUE
4937 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4938 {
4939         VALUE fname;
4940         char *fstr;
4941         Molecule *mol;
4942         int retval;
4943         MoleculeClearLoadSaveErrorMessage();
4944         Data_Get_Struct(self, Molecule, mol);
4945         rb_scan_args(argc, argv, "1", &fname);
4946         fstr = FileStringValuePtr(fname);
4947         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4948         s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
4949         return Qtrue;   
4950 }
4951
4952 /*
4953  *  call-seq:
4954  *     loadpsf(file, pdbfile = nil)       -> bool
4955  *
4956  *  Read a structure from a psf file. molecule must be empty. The psf may be
4957  *  an "extended" version, which also contains coordinates. If pdbfile 
4958  *  is given, then atomic coordinates are read from that file.
4959  *  Return true if successful.
4960  */
4961 static VALUE
4962 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4963 {
4964         VALUE fname, pdbname;
4965         char *fstr, *pdbstr;
4966         Molecule *mol;
4967         int retval;
4968         Data_Get_Struct(self, Molecule, mol);
4969         if (mol->natoms > 0)
4970                 return Qnil;  /*  Must be a new molecule  */
4971         MoleculeClearLoadSaveErrorMessage();
4972         rb_scan_args(argc, argv, "11", &fname, &pdbname);
4973         fstr = FileStringValuePtr(fname);
4974         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4975         s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
4976         pdbstr = NULL;
4977         if (!NIL_P(pdbname)) {
4978                 pdbstr = strdup(FileStringValuePtr(pdbname));
4979                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
4980                 free(pdbstr);
4981                 s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
4982         }
4983         return Qtrue;
4984 }
4985
4986 /*
4987  *  call-seq:
4988  *     loadpdb(file)       -> bool
4989  *
4990  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
4991  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
4992  *  Return true if successful.
4993  */
4994 static VALUE
4995 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4996 {
4997         VALUE fname;
4998         char *fstr;
4999         Molecule *mol;
5000         int retval;
5001         Data_Get_Struct(self, Molecule, mol);
5002         rb_scan_args(argc, argv, "1", &fname);
5003         MoleculeClearLoadSaveErrorMessage();
5004         fstr = FileStringValuePtr(fname);
5005         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5006         s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
5007         return Qtrue;   
5008 }
5009
5010 /*
5011  *  call-seq:
5012  *     loaddcd(file)       -> bool
5013  *
5014  *  Read coordinates from a dcd file. The molecule should not empty.
5015  *  Return true if successful.
5016  */
5017 static VALUE
5018 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5019 {
5020         VALUE fname;
5021         char *fstr;
5022         Molecule *mol;
5023         int retval;
5024         Data_Get_Struct(self, Molecule, mol);
5025         rb_scan_args(argc, argv, "1", &fname);
5026         MoleculeClearLoadSaveErrorMessage();
5027         fstr = FileStringValuePtr(fname);
5028         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5029         s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
5030         return Qtrue;   
5031 }
5032
5033 /*
5034  *  call-seq:
5035  *     loadtep(file)       -> bool
5036  *
5037  *  Read coordinates from an ortep .tep file.
5038  *  Return true if successful.
5039  */
5040 static VALUE
5041 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5042 {
5043         VALUE fname;
5044         char *fstr;
5045         Molecule *mol;
5046         int retval;
5047         Data_Get_Struct(self, Molecule, mol);
5048         rb_scan_args(argc, argv, "1", &fname);
5049         MoleculeClearLoadSaveErrorMessage();
5050         fstr = FileStringValuePtr(fname);
5051         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5052         s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
5053         return Qtrue;   
5054 }
5055
5056 /*
5057  *  call-seq:
5058  *     loadres(file)       -> bool
5059  *
5060  *  Read coordinates from a shelx .res file.
5061  *  Return true if successful.
5062  */
5063 static VALUE
5064 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5065 {
5066         VALUE fname;
5067         char *fstr;
5068         Molecule *mol;
5069         int retval;
5070         Data_Get_Struct(self, Molecule, mol);
5071         rb_scan_args(argc, argv, "1", &fname);
5072         MoleculeClearLoadSaveErrorMessage();
5073         fstr = FileStringValuePtr(fname);
5074         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5075         s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
5076         return Qtrue;   
5077 }
5078
5079 /*
5080  *  call-seq:
5081  *     loadfchk(file)       -> bool
5082  *
5083  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5084  *  Return true if successful.
5085  */
5086 static VALUE
5087 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5088 {
5089         VALUE fname;
5090         char *fstr;
5091         Molecule *mol;
5092         int retval;
5093         Data_Get_Struct(self, Molecule, mol);
5094         rb_scan_args(argc, argv, "1", &fname);
5095         MoleculeClearLoadSaveErrorMessage();
5096         fstr = FileStringValuePtr(fname);
5097         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5098         s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
5099         return Qtrue;   
5100 }
5101
5102 /*
5103  *  call-seq:
5104  *     loaddat(file)       -> bool
5105  *
5106  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5107  *  Return true if successful.
5108  */
5109 static VALUE
5110 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5111 {
5112         VALUE fname;
5113         char *fstr;
5114         Molecule *mol;
5115         int retval;
5116         Data_Get_Struct(self, Molecule, mol);
5117         rb_scan_args(argc, argv, "1", &fname);
5118         MoleculeClearLoadSaveErrorMessage();
5119         fstr = FileStringValuePtr(fname);
5120         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5121         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5122         MyAppCallback_hideProgressPanel();
5123         s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
5124         return Qtrue;   
5125 }
5126
5127 /*
5128  *  call-seq:
5129  *     savembsf(file)       -> bool
5130  *
5131  *  Write structure as a mbsf file. Returns true if successful.
5132  */
5133 static VALUE
5134 s_Molecule_Savembsf(VALUE self, VALUE fname)
5135 {
5136         char *fstr;
5137     Molecule *mol;
5138         int retval;
5139     Data_Get_Struct(self, Molecule, mol);
5140         MoleculeClearLoadSaveErrorMessage();
5141         fstr = FileStringValuePtr(fname);
5142         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5143         s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
5144         return Qtrue;
5145 }
5146
5147 /*
5148  *  call-seq:
5149  *     savepsf(file)       -> bool
5150  *
5151  *  Write structure as a psf file. Returns true if successful.
5152  */
5153 static VALUE
5154 s_Molecule_Savepsf(VALUE self, VALUE fname)
5155 {
5156         char *fstr;
5157     Molecule *mol;
5158         int retval;
5159     Data_Get_Struct(self, Molecule, mol);
5160         MoleculeClearLoadSaveErrorMessage();
5161         fstr = FileStringValuePtr(fname);
5162         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5163         s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
5164         return Qtrue;
5165 }
5166
5167 /*
5168  *  call-seq:
5169  *     savepdb(file)       -> bool
5170  *
5171  *  Write coordinates as a pdb file. Returns true if successful.
5172  */
5173 static VALUE
5174 s_Molecule_Savepdb(VALUE self, VALUE fname)
5175 {
5176         char *fstr;
5177     Molecule *mol;
5178         int retval;
5179     Data_Get_Struct(self, Molecule, mol);
5180         MoleculeClearLoadSaveErrorMessage();
5181         fstr = FileStringValuePtr(fname);
5182         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5183         s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
5184         return Qtrue;
5185 }
5186
5187 /*
5188  *  call-seq:
5189  *     savedcd(file)       -> bool
5190  *
5191  *  Write coordinates as a dcd file. Returns true if successful.
5192  */
5193 static VALUE
5194 s_Molecule_Savedcd(VALUE self, VALUE fname)
5195 {
5196         char *fstr;
5197     Molecule *mol;
5198         int retval;
5199     Data_Get_Struct(self, Molecule, mol);
5200         MoleculeClearLoadSaveErrorMessage();
5201         fstr = FileStringValuePtr(fname);
5202         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5203         s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
5204         return Qtrue;
5205 }
5206
5207 /*
5208  *  call-seq:
5209  *     savetep(file)       -> bool
5210  *
5211  *  Write coordinates as an ORTEP file. Returns true if successful.
5212  */
5213 /*
5214 static VALUE
5215 s_Molecule_Savetep(VALUE self, VALUE fname)
5216 {
5217         char *fstr;
5218     Molecule *mol;
5219         char errbuf[128];
5220     Data_Get_Struct(self, Molecule, mol);
5221         fstr = FileStringValuePtr(fname);
5222         if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
5223                 rb_raise(rb_eMolbyError, errbuf);
5224         return Qtrue;
5225 }
5226 */
5227
5228 /*  load([ftype, ] fname, ...)  */
5229 static VALUE
5230 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5231 {
5232         VALUE rval;
5233         char *argstr, *methname, *p, *type = "";
5234         ID mid = 0;
5235         int i;
5236         const char *ls = (loadFlag ? "load" : "save");
5237         int lslen = strlen(ls);
5238
5239         if (argc == 0)
5240                 return Qnil;
5241
5242         if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5243                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5244         if (argstr[0] == ':') {
5245                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5246                 methname = ALLOC_N(char, lslen + strlen(argstr));
5247                 strcpy(methname, ls);
5248                 strcat(methname, argstr + 1);
5249                 type = argstr + 1;
5250                 for (i = lslen; methname[i] != 0; i++)
5251                         methname[i] = tolower(methname[i]);
5252                 mid = rb_intern(methname);
5253                 xfree(methname);
5254                 argc--;
5255                 argv++;
5256                 rval = rb_funcall2(self, mid, argc, argv);
5257                 if (rval == Qnil)
5258                         goto failure;
5259                 else
5260                         goto success;
5261         }
5262         /*  Guess file type from extension  */
5263         p = strrchr(argstr, '.');
5264         if (p != NULL) {
5265                 p++;
5266                 type = p;
5267                 for (methname = p; *methname != 0; methname++) {
5268                         if (!isalpha(*methname))
5269                                 break;
5270                 }
5271                 if (*methname == 0) {
5272                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5273                         if (methname == NULL)
5274                                 rb_raise(rb_eMolbyError, "Low memory");
5275                         strcpy(methname, ls);
5276                         strcat(methname, p);
5277                         for (i = lslen; methname[i] != 0; i++)
5278                                 methname[i] = tolower(methname[i]);
5279                         mid = rb_intern(methname);
5280                         xfree(methname);
5281                         if (loadFlag) {
5282                                 if (rb_respond_to(self, mid)) {
5283                                         /*  Load: try to call the load procedure only if it is available  */
5284                                         rval = rb_funcall2(self, mid, argc, argv);
5285                                         if (rval != Qnil)
5286                                                 goto success;
5287                                 }
5288                         } else {
5289                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5290                                 rval = rb_funcall2(self, mid, argc, argv);
5291                                 if (rval != Qnil)
5292                                         goto success;
5293                         }
5294                 }
5295         }
5296 failure:
5297         rval = rb_str_to_str(argv[0]);
5298         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5299         s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
5300         return Qnil;  /*  Does not reach here  */
5301
5302 success:
5303         {
5304                 /*  Register the path  */
5305                 Molecule *mol;
5306         /*      Atom *ap; */
5307                 Data_Get_Struct(self, Molecule, mol);
5308                 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5309                 
5310                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5311         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5312                         if (ap->occupancy != 0.0)
5313                                 break;
5314                 }
5315                 if (i == mol->natoms) {
5316                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5317                                 ap->occupancy = 1.0;
5318                         }
5319                 } */
5320         }
5321         return rval;
5322 }
5323
5324 /*
5325  *  call-seq:
5326  *     molload(file, *args)       -> bool
5327  *
5328  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5329  *  file type given by the extension). If this method fails, then all defined (public)
5330  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5331  */
5332 static VALUE
5333 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5334 {
5335         return s_Molecule_LoadSave(argc, argv, self, 1);
5336 }
5337
5338 /*
5339  *  call-seq:
5340  *     molsave(file, *args)       -> bool
5341  *
5342  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5343  *  (XXX is the file type given by the extension).
5344  */
5345 static VALUE
5346 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5347 {
5348         return s_Molecule_LoadSave(argc, argv, self, 0);
5349 }
5350
5351 /*
5352  *  call-seq:
5353  *     name       -> String
5354  *
5355  *  Returns the display name of the molecule. If the molecule has no associated
5356  *  document, then returns nil.
5357  */
5358 static VALUE
5359 s_Molecule_Name(VALUE self)
5360 {
5361     Molecule *mol;
5362         char buf[1024];
5363     Data_Get_Struct(self, Molecule, mol);
5364         MoleculeCallback_displayName(mol, buf, sizeof buf);
5365         if (buf[0] == 0)
5366                 return Qnil;
5367         else
5368                 return rb_str_new2(buf);
5369 }
5370
5371 /*
5372  *  call-seq:
5373  *     set_name(string) -> self
5374  *
5375  *  Set the name of an untitled molecule. If the molecule is not associated with window
5376  *  or it already has an associated file, then exception is thrown.
5377  */
5378 static VALUE
5379 s_Molecule_SetName(VALUE self, VALUE nval)
5380 {
5381     Molecule *mol;
5382     Data_Get_Struct(self, Molecule, mol);
5383         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5384                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5385         return self;
5386 }
5387
5388
5389 /*
5390  *  call-seq:
5391  *     path       -> String
5392  *
5393  *  Returns the full path name of the molecule, if it is associated with a file.
5394  *  If the molecule has no associated file, then returns nil.
5395  */
5396 static VALUE
5397 s_Molecule_Path(VALUE self)
5398 {
5399     Molecule *mol;
5400         char buf[1024];
5401     Data_Get_Struct(self, Molecule, mol);
5402         MoleculeCallback_pathName(mol, buf, sizeof buf);
5403         if (buf[0] == 0)
5404                 return Qnil;
5405         else
5406                 return Ruby_NewFileStringValue(buf);
5407 }
5408
5409 /*
5410  *  call-seq:
5411  *     dir       -> String
5412  *
5413  *  Returns the full path name of the directory in which the file associated with the
5414  *  molecule is located. If the molecule has no associated file, then returns nil.
5415  */
5416 static VALUE
5417 s_Molecule_Dir(VALUE self)
5418 {
5419     Molecule *mol;
5420         char buf[1024], *p;
5421     Data_Get_Struct(self, Molecule, mol);
5422         MoleculeCallback_pathName(mol, buf, sizeof buf);
5423 #if __WXMSW__
5424         translate_char(buf, '\\', '/');
5425 #endif
5426         if (buf[0] == 0)
5427                 return Qnil;
5428         else {
5429                 p = strrchr(buf, '/');
5430                 if (p != NULL)
5431                         *p = 0;
5432                 return rb_str_new2(buf);
5433         }
5434 }
5435
5436 /*
5437  *  call-seq:
5438  *     inspect       -> String
5439  *
5440  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5441  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5442  *  the Molecule structure) is returned.
5443  */
5444 static VALUE
5445 s_Molecule_Inspect(VALUE self)
5446 {
5447     Molecule *mol;
5448         char buf[256];
5449     Data_Get_Struct(self, Molecule, mol);
5450         MoleculeCallback_displayName(mol, buf, sizeof buf);
5451         if (buf[0] == 0) {
5452                 /*  No associated document  */
5453                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5454                 return rb_str_new2(buf);
5455         } else {
5456                 /*  Check whether the document name is duplicate  */
5457                 char buf2[256];
5458                 int idx, k, k2;
5459                 Molecule *mol2;
5460                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5461                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5462                         if (strcmp(buf, buf2) == 0) {
5463                                 k++;
5464                                 if (mol == mol2)
5465                                         k2 = k;
5466                         }
5467                 }
5468                 if (k > 1) {
5469                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5470                 } else {
5471                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5472                 }
5473                 return rb_str_new2(buf2);
5474         }
5475 }
5476
5477 /*
5478  *  call-seq:
5479  *     open        -> Molecule
5480  *     open(file)  -> Molecule
5481  *
5482  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5483  */
5484 static VALUE
5485 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5486 {
5487         VALUE fname;
5488         const char *p;
5489         Molecule *mp;
5490         VALUE iflag;
5491         rb_scan_args(argc, argv, "01", &fname);
5492         if (NIL_P(fname))
5493                 p = NULL;
5494         else
5495                 p = FileStringValuePtr(fname);
5496         iflag = Ruby_SetInterruptFlag(Qfalse);
5497         mp = MoleculeCallback_openNewMolecule(p);
5498         Ruby_SetInterruptFlag(iflag);
5499         if (mp == NULL) {
5500                 if (p == NULL)
5501                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5502                 else
5503                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5504         }
5505         return ValueFromMolecule(mp);
5506 }
5507
5508 /*
5509  *  call-seq:
5510  *     new  -> Molecule
5511  *     new(file, *args)  -> Molecule
5512  *
5513  *  Create a new molecule and call "load" method with the same arguments.
5514  */
5515 static VALUE
5516 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5517 {
5518         if (argc > 0)
5519                 return s_Molecule_Load(argc, argv, self);
5520         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5521 }
5522
5523 static VALUE
5524 s_Molecule_MolEnumerable(VALUE self, int kind)
5525 {
5526     Molecule *mol;
5527         MolEnumerable *mseq;
5528     Data_Get_Struct(self, Molecule, mol);
5529         mseq = MolEnumerableNew(mol, kind);
5530         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5531 }
5532
5533 /*
5534  *  call-seq:
5535  *     atoms       -> MolEnumerable
5536  *
5537  *  Returns a MolEnumerable object representing the array of atoms.
5538  */
5539 static VALUE
5540 s_Molecule_Atoms(VALUE self)
5541 {
5542         return s_Molecule_MolEnumerable(self, kAtomKind);
5543 }
5544
5545 /*
5546  *  call-seq:
5547  *     bonds       -> MolEnumerable
5548  *
5549  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5550  *  by an array of two atom indices.
5551  */
5552 static VALUE
5553 s_Molecule_Bonds(VALUE self)
5554 {
5555         return s_Molecule_MolEnumerable(self, kBondKind);
5556 }
5557
5558 /*
5559  *  call-seq:
5560  *     angles       -> MolEnumerable
5561  *
5562  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5563  *  by an array of three atom indices.
5564  */
5565 static VALUE
5566 s_Molecule_Angles(VALUE self)
5567 {
5568         return s_Molecule_MolEnumerable(self, kAngleKind);
5569 }
5570
5571 /*
5572  *  call-seq:
5573  *     dihedrals       -> MolEnumerable
5574  *
5575  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5576  *  by an array of four atom indices.
5577  */
5578 static VALUE
5579 s_Molecule_Dihedrals(VALUE self)
5580 {
5581         return s_Molecule_MolEnumerable(self, kDihedralKind);
5582 }
5583
5584 /*
5585  *  call-seq:
5586  *     impropers       -> MolEnumerable
5587  *
5588  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5589  *  by an array of four atom indices.
5590  */
5591 static VALUE
5592 s_Molecule_Impropers(VALUE self)
5593 {
5594         return s_Molecule_MolEnumerable(self, kImproperKind);
5595 }
5596
5597 /*
5598  *  call-seq:
5599  *     residues       -> MolEnumerable
5600  *
5601  *  Returns a MolEnumerable object representing the array of residue names.
5602  */
5603 static VALUE
5604 s_Molecule_Residues(VALUE self)
5605 {
5606         return s_Molecule_MolEnumerable(self, kResidueKind);
5607 }
5608
5609 /*
5610  *  call-seq:
5611  *     natoms       -> Integer
5612  *
5613  *  Returns the number of atoms.
5614  */
5615 static VALUE
5616 s_Molecule_Natoms(VALUE self)
5617 {
5618     Molecule *mol;
5619     Data_Get_Struct(self, Molecule, mol);
5620         return INT2NUM(mol->natoms);
5621 }
5622
5623 /*
5624  *  call-seq:
5625  *     nbonds       -> Integer
5626  *
5627  *  Returns the number of bonds.
5628  */
5629 static VALUE
5630 s_Molecule_Nbonds(VALUE self)
5631 {
5632     Molecule *mol;
5633     Data_Get_Struct(self, Molecule, mol);
5634         return INT2NUM(mol->nbonds);
5635 }
5636
5637 /*
5638  *  call-seq:
5639  *     nangles       -> Integer
5640  *
5641  *  Returns the number of angles.
5642  */
5643 static VALUE
5644 s_Molecule_Nangles(VALUE self)
5645 {
5646     Molecule *mol;
5647     Data_Get_Struct(self, Molecule, mol);
5648         return INT2NUM(mol->nangles);
5649 }
5650
5651 /*
5652  *  call-seq:
5653  *     ndihedrals       -> Integer
5654  *
5655  *  Returns the number of dihedrals.
5656  */
5657 static VALUE
5658 s_Molecule_Ndihedrals(VALUE self)
5659 {
5660     Molecule *mol;
5661     Data_Get_Struct(self, Molecule, mol);
5662         return INT2NUM(mol->ndihedrals);
5663 }
5664
5665 /*
5666  *  call-seq:
5667  *     nimpropers       -> Integer
5668  *
5669  *  Returns the number of impropers.
5670  */
5671 static VALUE
5672 s_Molecule_Nimpropers(VALUE self)
5673 {
5674     Molecule *mol;
5675     Data_Get_Struct(self, Molecule, mol);
5676         return INT2NUM(mol->nimpropers);
5677 }
5678
5679 /*
5680  *  call-seq:
5681  *     nresidues       -> Integer
5682  *
5683  *  Returns the number of residues.
5684  */
5685 static VALUE
5686 s_Molecule_Nresidues(VALUE self)
5687 {
5688     Molecule *mol;
5689     Data_Get_Struct(self, Molecule, mol);
5690         return INT2NUM(mol->nresidues);
5691 }
5692
5693 static VALUE
5694 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5695 {
5696         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.");
5697 }
5698
5699 /*
5700  *  call-seq:
5701  *     bond_par(idx)    -> ParameterRef
5702  *
5703  *  Returns the MD parameter for the idx-th bond.
5704  */
5705 /*
5706 static VALUE
5707 s_Molecule_BondPar(VALUE self, VALUE val)
5708 {
5709     Molecule *mol;
5710         BondPar *bp;
5711         UInt t1, t2;
5712         Int i1, i2;
5713         Int ival;
5714     Data_Get_Struct(self, Molecule, mol);
5715         ival = NUM2INT(rb_Integer(val));
5716         if (ival < -mol->nbonds || ival >= mol->nbonds)
5717                 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5718         if (ival < 0)
5719                 ival += mol->nbonds;
5720         s_RebuildMDParameterIfNecessary(self, Qtrue);
5721         i1 = mol->bonds[ival * 2];
5722         i2 = mol->bonds[ival * 2 + 1];
5723         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5724         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5725         bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5726         if (bp == NULL)
5727                 return Qnil;
5728         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5729 }
5730 */
5731
5732 /*
5733  *  call-seq:
5734  *     angle_par(idx)    -> ParameterRef
5735  *
5736  *  Returns the MD parameter for the idx-th angle.
5737  */
5738 /*
5739 static VALUE
5740 s_Molecule_AnglePar(VALUE self, VALUE val)
5741 {
5742     Molecule *mol;
5743         AnglePar *ap;
5744         UInt t1, t2, t3;
5745         Int i1, i2, i3;
5746         Int ival;
5747     Data_Get_Struct(self, Molecule, mol);
5748         ival = NUM2INT(rb_Integer(val));
5749         if (ival < -mol->nangles || ival >= mol->nangles)
5750                 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5751         if (ival < 0)
5752                 ival += mol->nangles;
5753         s_RebuildMDParameterIfNecessary(self, Qtrue);
5754         i1 = mol->angles[ival * 3];
5755         i2 = mol->angles[ival * 3 + 1];
5756         i3 = mol->angles[ival * 3 + 2];
5757         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5758         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5759         t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5760         ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5761         if (ap == NULL)
5762                 return Qnil;
5763         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5764 }
5765 */
5766 /*
5767  *  call-seq:
5768  *     dihedral_par(idx)    -> ParameterRef
5769  *
5770  *  Returns the MD parameter for the idx-th dihedral.
5771  */
5772 /*
5773 static VALUE
5774 s_Molecule_DihedralPar(VALUE self, VALUE val)
5775 {
5776     Molecule *mol;
5777         Int ival;
5778         TorsionPar *tp;
5779         UInt t1, t2, t3, t4;
5780         Int i1, i2, i3, i4;
5781     Data_Get_Struct(self, Molecule, mol);
5782         ival = NUM2INT(rb_Integer(val));
5783         if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5784                 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5785         if (ival < 0)
5786                 ival += mol->ndihedrals;
5787         s_RebuildMDParameterIfNecessary(self, Qtrue);
5788         i1 = mol->dihedrals[ival * 4];
5789         i2 = mol->dihedrals[ival * 4 + 1];
5790         i3 = mol->dihedrals[ival * 4 + 2];
5791         i4 = mol->dihedrals[ival * 4 + 3];
5792         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5793         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5794         t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5795         t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5796         tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5797         if (tp == NULL)
5798                 return Qnil;
5799         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5800 }
5801 */
5802 /*
5803  *  call-seq:
5804  *     improper_par(idx)    -> ParameterRef
5805  *
5806  *  Returns the MD parameter for the idx-th improper.
5807  */
5808 /*
5809 static VALUE
5810 s_Molecule_ImproperPar(VALUE self, VALUE val)
5811 {
5812     Molecule *mol;
5813         Int ival;
5814         TorsionPar *tp;
5815         UInt t1, t2, t3, t4;
5816         Int i1, i2, i3, i4;
5817     Data_Get_Struct(self, Molecule, mol);
5818         ival = NUM2INT(rb_Integer(val));
5819         if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5820                 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5821         if (ival < 0)
5822                 ival += mol->nimpropers;
5823         s_RebuildMDParameterIfNecessary(self, Qtrue);
5824         i1 = mol->impropers[ival * 4];
5825         i2 = mol->impropers[ival * 4 + 1];
5826         i3 = mol->impropers[ival * 4 + 2];
5827         i4 = mol->impropers[ival * 4 + 3];
5828         t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5829         t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5830         t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5831         t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5832         tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5833         if (tp == NULL)
5834                 return Qnil;
5835         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5836 }
5837 */
5838
5839 /*
5840  *  call-seq:
5841  *     start_step       -> Integer
5842  *
5843  *  Returns the start step (defined by dcd format).
5844  */
5845 static VALUE
5846 s_Molecule_StartStep(VALUE self)
5847 {
5848     Molecule *mol;
5849     Data_Get_Struct(self, Molecule, mol);
5850         return INT2NUM(mol->startStep);
5851 }
5852
5853 /*
5854  *  call-seq:
5855  *     start_step = Integer
5856  *
5857  *  Set the start step (defined by dcd format).
5858  */
5859 static VALUE
5860 s_Molecule_SetStartStep(VALUE self, VALUE val)
5861 {
5862     Molecule *mol;
5863     Data_Get_Struct(self, Molecule, mol);
5864         mol->startStep = NUM2INT(rb_Integer(val));
5865         return val;
5866 }
5867
5868 /*
5869  *  call-seq:
5870  *     steps_per_frame       -> Integer
5871  *
5872  *  Returns the number of steps between frames (defined by dcd format).
5873  */
5874 static VALUE
5875 s_Molecule_StepsPerFrame(VALUE self)
5876 {
5877     Molecule *mol;
5878     Data_Get_Struct(self, Molecule, mol);
5879         return INT2NUM(mol->stepsPerFrame);
5880 }
5881
5882 /*
5883  *  call-seq:
5884  *     steps_per_frame = Integer
5885  *
5886  *  Set the number of steps between frames (defined by dcd format).
5887  */
5888 static VALUE
5889 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5890 {
5891     Molecule *mol;
5892     Data_Get_Struct(self, Molecule, mol);
5893         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5894         return val;
5895 }
5896
5897 /*
5898  *  call-seq:
5899  *     ps_per_step       -> Float
5900  *
5901  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
5902  */
5903 static VALUE
5904 s_Molecule_PsPerStep(VALUE self)
5905 {
5906     Molecule *mol;
5907     Data_Get_Struct(self, Molecule, mol);
5908         return rb_float_new(mol->psPerStep);
5909 }
5910
5911 /*
5912  *  call-seq:
5913  *     ps_per_step = Float
5914  *
5915  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
5916  */
5917 static VALUE
5918 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5919 {
5920     Molecule *mol;
5921     Data_Get_Struct(self, Molecule, mol);
5922         mol->psPerStep = NUM2DBL(rb_Float(val));
5923         return val;
5924 }
5925
5926 /*
5927  *  call-seq:
5928  *     find_angles     -> Integer
5929  *
5930  *  Find the angles from the bonds. Returns the number of angles newly created.
5931  */
5932 /*
5933 static VALUE
5934 s_Molecule_FindAngles(VALUE self)
5935 {
5936     Molecule *mol;
5937         Atom *ap;
5938         int n1, i, j, nc;
5939         Int *ip, nip, n[3];
5940     Data_Get_Struct(self, Molecule, mol);
5941         if (mol == NULL || mol->natoms == 0)
5942                 return INT2NUM(0);
5943         ip = NULL;
5944         nip = 0;
5945         for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5946                 nc = ap->connect.count;
5947                 n[1] = n1;
5948                 for (i = 0; i < nc; i++) {
5949                         n[0] = ap->connects[i];
5950                         for (j = i + 1; j < nc; j++) {
5951                                 n[2] = ap->connects[j];
5952                                 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5953                                         AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5954                         }
5955                 }
5956         }
5957         if (nip > 0) {
5958                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);         
5959                 free(ip);
5960         }
5961         return INT2NUM(nip);
5962 }
5963 */
5964 /*
5965  *  call-seq:
5966  *     find_dihedrals     -> Integer
5967  *
5968  *  Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5969  */
5970 /*
5971 static VALUE
5972 s_Molecule_FindDihedrals(VALUE self)
5973 {
5974     Molecule *mol;
5975         Atom *ap1, *ap2;
5976         int n1, i, j, k, nc1, nc2;
5977         Int *ip, nip, n[4];
5978     Data_Get_Struct(self, Molecule, mol);
5979         if (mol == NULL || mol->natoms == 0)
5980                 return INT2NUM(0);
5981         ip = NULL;
5982         nip = 0;
5983         for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5984                 nc1 = ap1->connect.count;
5985                 n[1] = n1;
5986                 for (i = 0; i < nc1; i++) {
5987                         n[2] = ap1->connects[i];
5988                         if (n[1] > n[2])
5989                                 continue;
5990                         ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5991                         nc2 = ap2->connect.count;
5992                         for (j = 0; j < nc1; j++) {
5993                                 n[0] = ap1->connects[j];
5994                                 if (n[0] == n[2])
5995                                         continue;
5996                                 for (k = 0; k < nc2; k++) {
5997                                         n[3] = ap2->connects[k];
5998                                         if (n[3] == n1 || n[3] == n[0])
5999                                                 continue;
6000                                         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
6001                                                 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
6002                                 }
6003                         }
6004                 }
6005         }
6006         if (nip > 0) {
6007                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
6008                 free(ip);
6009         }
6010         return INT2NUM(nip);
6011 }
6012 */
6013
6014 /*
6015  *  call-seq:
6016  *     nresidues = Integer
6017  *
6018  *  Change the number of residues.
6019  */
6020 static VALUE
6021 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
6022 {
6023     Molecule *mol;
6024         int ival = NUM2INT(val);
6025     Data_Get_Struct(self, Molecule, mol);
6026         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
6027         if (ival != mol->nresidues)
6028                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
6029         return val;
6030 }
6031
6032 /*
6033  *  call-seq:
6034  *     max_residue_number(atom_group = nil)     -> Integer
6035  *
6036  *  Returns the maximum residue number actually used. If an atom group is given, only
6037  *  these atoms are examined. If no atom is present, nil is returned.
6038  */
6039 static VALUE
6040 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
6041 {
6042     Molecule *mol;
6043         VALUE gval;
6044         int maxSeq;
6045         IntGroup *ig;
6046     Data_Get_Struct(self, Molecule, mol);
6047         rb_scan_args(argc, argv, "01", &gval);
6048         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6049         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
6050         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
6051 }
6052
6053 /*
6054  *  call-seq:
6055  *     min_residue_number(atom_group = nil)     -> Integer
6056  *
6057  *  Returns the minimum residue number actually used. If an atom group is given, only
6058  *  these atoms are examined. If no atom is present, nil is returned.
6059  */
6060 static VALUE
6061 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
6062 {
6063     Molecule *mol;
6064         VALUE gval;
6065         int minSeq;
6066         IntGroup *ig;
6067     Data_Get_Struct(self, Molecule, mol);
6068         rb_scan_args(argc, argv, "01", &gval);
6069         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6070         minSeq = MoleculeMinimumResidueNumber(mol, ig);
6071         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
6072 }
6073
6074 /*
6075  *  call-seq:
6076  *     each_atom(atom_group = nil) {|aref| ...}
6077  *
6078  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
6079  *  group is given, only these atoms are processed.
6080  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
6081  *  is self (a Molecule object).
6082  */
6083 static VALUE
6084 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
6085 {
6086         int i;
6087     Molecule *mol;
6088         AtomRef *aref;
6089         VALUE arval;
6090         VALUE gval;
6091         IntGroup *ig;
6092     Data_Get_Struct(self, Molecule, mol);
6093         rb_scan_args(argc, argv, "01", &gval);
6094         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6095         arval = ValueFromMoleculeAndIndex(mol, 0);
6096         Data_Get_Struct(arval, AtomRef, aref);
6097         for (i = 0; i < mol->natoms; i++) {
6098                 aref->idx = i;
6099                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
6100                         rb_yield(arval);
6101         }
6102         if (ig != NULL)
6103                 IntGroupRelease(ig);
6104     return self;
6105 }
6106
6107 /*
6108  *  call-seq:
6109  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
6110  *
6111  *  Returns the unit cell parameters. If cell is not set, returns nil.
6112  */
6113 static VALUE
6114 s_Molecule_Cell(VALUE self)
6115 {
6116     Molecule *mol;
6117         int i;
6118         VALUE val;
6119     Data_Get_Struct(self, Molecule, mol);
6120         if (mol->cell == NULL)
6121                 return Qnil;
6122         val = rb_ary_new2(6);
6123         for (i = 0; i < 6; i++)
6124                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
6125         if (mol->cell->has_sigma) {
6126                 for (i = 0; i < 6; i++) {
6127                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
6128                 }
6129         }
6130         return val;
6131 }
6132
6133 /*
6134  *  call-seq:
6135  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
6136  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
6137  *
6138  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
6139     If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
6140     This operation is undoable.
6141     Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
6142  */
6143 static VALUE
6144 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
6145 {
6146     Molecule *mol;
6147         VALUE val, cval;
6148         int i, convert_coord, n;
6149         double d[12];
6150     Data_Get_Struct(self, Molecule, mol);
6151         rb_scan_args(argc, argv, "11", &val, &cval);
6152         if (val == Qnil) {
6153                 n = 0;
6154         } else {
6155                 int len;
6156                 val = rb_ary_to_ary(val);
6157                 len = RARRAY_LEN(val);
6158                 if (len >= 12) {
6159                         n = 12;
6160                 } else if (len >= 6) {
6161                         n = 6;
6162                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
6163                 for (i = 0; i < n; i++)
6164                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
6165         }
6166         convert_coord = (RTEST(cval) ? 1 : 0);
6167         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
6168         return val;
6169 }
6170
6171 /*
6172  *  call-seq:
6173  *     box -> [avec, bvec, cvec, origin, flags]
6174  *
6175  *  Get the unit cell information in the form of a periodic bounding box.
6176  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
6177  *  Integers which define whether the system is periodic along the axis.
6178  *  If no unit cell is defined, nil is returned.
6179  */
6180 static VALUE
6181 s_Molecule_Box(VALUE self)
6182 {
6183     Molecule *mol;
6184         VALUE v[5], val;
6185     Data_Get_Struct(self, Molecule, mol);
6186         if (mol == NULL || mol->cell == NULL)
6187                 return Qnil;
6188         v[0] = ValueFromVector(&(mol->cell->axes[0]));
6189         v[1] = ValueFromVector(&(mol->cell->axes[1]));
6190         v[2] = ValueFromVector(&(mol->cell->axes[2]));
6191         v[3] = ValueFromVector(&(mol->cell->origin));
6192         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
6193         val = rb_ary_new4(5, v);
6194         return val;
6195 }
6196
6197 /*
6198  *  call-seq:
6199  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
6200  *     set_box(d, origin = [0, 0, 0])
6201  *     set_box
6202  *
6203  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
6204  If it is a number, the x/y/z axis vector is multiplied with the given number and used
6205  as the box vector.
6206  Flags, if present, is a 3-member array of Integers defining whether the system is
6207  periodic along the axis.
6208  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
6209  In the second form, an isotropic box with cell-length d is set.
6210  In the third form, the existing box is cleared.
6211  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
6212  */
6213 static VALUE
6214 s_Molecule_SetBox(VALUE self, VALUE aval)
6215 {
6216     Molecule *mol;
6217         VALUE v[6];
6218         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
6219         Vector vv[3];
6220         Vector origin = {0, 0, 0};
6221         char flags[3];
6222         Double d;
6223         int i, convertCoordinates = 0;
6224     Data_Get_Struct(self, Molecule, mol);
6225         if (aval == Qnil) {
6226                 MolActionCreateAndPerform(mol, gMolActionClearBox);
6227                 return self;
6228         }
6229         aval = rb_ary_to_ary(aval);
6230         for (i = 0; i < 6; i++) {
6231                 if (i < RARRAY_LEN(aval))
6232                         v[i] = (RARRAY_PTR(aval))[i];
6233                 else v[i] = Qnil;
6234         }
6235         if (v[0] == Qnil) {
6236                 MolActionCreateAndPerform(mol, gMolActionClearBox);
6237                 return self;
6238         }
6239         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
6240                 d = NUM2DBL(rb_Float(v[0]));
6241                 for (i = 0; i < 3; i++)
6242                         VecScale(vv[i], ax[i], d);
6243                 if (v[1] != Qnil)
6244                         VectorFromValue(v[1], &origin);
6245                 flags[0] = flags[1] = flags[2] = 1;
6246         } else {
6247                 for (i = 0; i < 3; i++) {
6248                         if (v[i] == Qnil) {
6249                                 VecZero(vv[i]);
6250                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
6251                                 d = NUM2DBL(rb_Float(v[i]));
6252                                 VecScale(vv[i], ax[i], d);
6253                         } else {
6254                                 VectorFromValue(v[i], &vv[i]);
6255                         }
6256                         flags[i] = (VecLength2(vv[i]) > 0.0);
6257                 }
6258                 if (v[3] != Qnil)
6259                         VectorFromValue(v[3], &origin);
6260                 if (v[4] != Qnil) {
6261                         for (i = 0; i < 3; i++) {
6262                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
6263                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
6264                         }
6265                 }
6266                 if (RTEST(v[5]))
6267                         convertCoordinates = 1;
6268         }
6269         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
6270         return self;
6271 }
6272
6273 /*
6274  *  call-seq:
6275  *     cell_periodicity -> [n1, n2, n3]
6276  *
6277  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
6278  *  nil is returned.
6279  */
6280 static VALUE
6281 s_Molecule_CellPeriodicity(VALUE self)
6282 {
6283     Molecule *mol;
6284     Data_Get_Struct(self, Molecule, mol);
6285         if (mol->cell == NULL)
6286                 return Qnil;
6287         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
6288 }
6289
6290 /*
6291  *  call-seq:
6292  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
6293  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
6294  *
6295  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
6296  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
6297  *  If cell is not defined, exception is raised.
6298  *  This operation is undoable.
6299  */
6300 static VALUE
6301 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
6302 {
6303     Molecule *mol;
6304         Int flag;
6305     Data_Get_Struct(self, Molecule, mol);
6306         if (mol->cell == NULL)
6307                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
6308         if (arg == Qnil)
6309                 flag = 0;
6310         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
6311                 flag = NUM2INT(rb_Integer(arg));
6312         else {
6313                 Int i;
6314                 VALUE arg0;
6315                 arg = rb_ary_to_ary(arg);
6316                 flag = 0;
6317                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
6318                         arg0 = RARRAY_PTR(arg)[i];
6319                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
6320                                 flag |= (1 << (2 - i));
6321                 }
6322         }
6323         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
6324         return arg;
6325 }
6326
6327 /*
6328  *  call-seq:
6329  *     cell_flexibility -> bool
6330  *
6331  *  Returns the unit cell is flexible or not
6332  */
6333 static VALUE
6334 s_Molecule_CellFlexibility(VALUE self)
6335 {
6336         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
6337         return Qtrue;
6338 /*    Molecule *mol;
6339     Data_Get_Struct(self, Molecule, mol);
6340         if (mol->cell == NULL)
6341                 return Qfalse;
6342         if (mol->useFlexibleCell)
6343                 return Qtrue;
6344         else return Qfalse; */
6345 }
6346
6347 /*
6348  *  call-seq:
6349  *     self.cell_flexibility = bool
6350  *     set_cell_flexibility(bool)
6351  *
6352  *  Change the unit cell is flexible or not
6353  */
6354 static VALUE
6355 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
6356 {
6357         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
6358         return self;
6359 /*    Molecule *mol;
6360     Data_Get_Struct(self, Molecule, mol);
6361         MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
6362         return self; */
6363 }
6364
6365 /*
6366  *  call-seq:
6367  *     cell_transform -> Transform
6368  *
6369  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
6370  *  If cell is not defined, nil is returned.
6371  */
6372 static VALUE
6373 s_Molecule_CellTransform(VALUE self)
6374 {
6375     Molecule *mol;
6376     Data_Get_Struct(self, Molecule, mol);
6377         if (mol == NULL || mol->cell == NULL)
6378                 return Qnil;
6379         return ValueFromTransform(&(mol->cell->tr));
6380 }
6381
6382 /*
6383  *  call-seq:
6384  *     symmetry -> Array of Transforms
6385  *     symmetries -> Array of Transforms
6386  *
6387  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
6388  *  returns an empty array.
6389  */
6390 static VALUE
6391 s_Molecule_Symmetry(VALUE self)
6392 {
6393     Molecule *mol;
6394         VALUE val;
6395         int i;
6396     Data_Get_Struct(self, Molecule, mol);
6397         if (mol->nsyms <= 0)
6398                 return rb_ary_new();
6399         val = rb_ary_new2(mol->nsyms);
6400         for (i = 0; i < mol->nsyms; i++) {
6401                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
6402         }
6403         return val;
6404 }
6405
6406 /*
6407  *  call-seq:
6408  *     nsymmetries -> Integer
6409  *
6410  *  Get the number of currently defined symmetry operations.
6411  */
6412 static VALUE
6413 s_Molecule_Nsymmetries(VALUE self)
6414 {
6415     Molecule *mol;
6416     Data_Get_Struct(self, Molecule, mol);
6417         return INT2NUM(mol->nsyms);
6418 }
6419
6420 /*
6421  *  call-seq:
6422  *     add_symmetry(Transform) -> Integer
6423  *
6424  *  Add a new symmetry operation. If no symmetry operation is defined and the
6425  *  given argument is not an identity transform, then also add an identity
6426  *  transform at the index 0.
6427  *  Returns the total number of symmetries after operation.
6428  */
6429 static VALUE
6430 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
6431 {
6432     Molecule *mol;
6433         Transform tr;
6434     Data_Get_Struct(self, Molecule, mol);
6435         TransformFromValue(trans, &tr);
6436         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
6437         return INT2NUM(mol->nsyms);
6438 }
6439
6440 /*
6441  *  call-seq:
6442  *     remove_symmetry(count = nil) -> Integer
6443  *     remove_symmetries(count = nil) -> Integer
6444  *
6445  *  Remove the specified number of symmetry operations. The last added ones are removed
6446  *  first. If count is nil, then all symmetry operations are removed. Returns the
6447  *  number of leftover symmetries.
6448  */
6449 static VALUE
6450 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
6451 {
6452     Molecule *mol;
6453         VALUE cval;
6454         int i, n;
6455     Data_Get_Struct(self, Molecule, mol);
6456         rb_scan_args(argc, argv, "01", &cval);
6457         if (cval == Qnil)
6458                 n = mol->nsyms - 1;
6459         else {
6460                 n = NUM2INT(rb_Integer(cval));
6461                 if (n < 0 || n > mol->nsyms)
6462                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
6463                 if (n == mol->nsyms)
6464                         n = mol->nsyms - 1;
6465         }
6466         for (i = 0; i < n; i++)
6467                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
6468         return INT2NUM(mol->nsyms);
6469 }
6470
6471 static VALUE
6472 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6473 {
6474         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6475         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6476         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6477         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6478         return Qnil;
6479 }
6480
6481 /*
6482  *  call-seq:
6483  *     atom_group
6484  *     atom_group {|aref| ...}
6485  *     atom_group(arg1, arg2, ...)
6486  *     atom_group(arg1, arg2, ...) {|aref| ...}
6487  *
6488  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6489  *  If arguments are given, then the atoms reprensented by the arguments are added to the
6490  *  group. For a conversion of a string to an atom index, see the description
6491  *  of Molecule#atom_index.
6492  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
6493  *  representing each atom, and the atoms are removed from the result if the block returns false.
6494  *
6495  */
6496 static VALUE
6497 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6498 {
6499         IntGroup *ig1, *ig2;
6500     Molecule *mol;
6501         Int i, startPt, interval;
6502         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6503         Data_Get_Struct(retval, IntGroup, ig1);
6504     Data_Get_Struct(self, Molecule, mol);
6505         if (argc == 0) {
6506                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6507         } else {
6508                 while (argc > 0) {
6509                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6510                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6511                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6512                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6513                                 ig2 = IntGroupFromValue(*argv);
6514                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6515                                         interval = IntGroupGetInterval(ig2, i);
6516                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6517                                 }
6518                                 IntGroupRelease(ig2);
6519                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
6520                                 VALUE values[2];
6521                                 values[0] = (VALUE)mol;
6522                                 values[1] = (VALUE)ig1;
6523                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6524                         } else
6525                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6526                         argc--;
6527                         argv++;
6528                 }
6529         }
6530         if (rb_block_given_p()) {
6531                 /*  Evaluate the given block with an AtomRef as the argument, and delete
6532                         the index if the block returns false  */
6533                 AtomRef *aref = AtomRefNew(mol, 0);
6534                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6535                 ig2 = IntGroupNew();
6536                 IntGroupCopy(ig2, ig1);
6537                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6538                         VALUE resval;
6539                         if (startPt >= mol->natoms)
6540                                 break;
6541                         aref->idx = startPt;
6542                         resval = rb_yield(arval);
6543                         if (!RTEST(resval))
6544                                 IntGroupRemove(ig1, startPt, 1);
6545                 }
6546                 IntGroupRelease(ig2);
6547         }
6548         
6549         /*  Remove points that are out of bounds */
6550         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6551
6552         return retval;                  
6553 }
6554
6555 /*
6556  *  call-seq:
6557  *     atom_index(val)       -> Integer
6558  *
6559  *  Returns the atom index represented by val. val can be either a non-negative integer
6560  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
6561  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
6562  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
6563  *  If val is a string and multiple atoms match the description, the atom with the lowest index
6564  *  is returned.
6565  */
6566 static VALUE
6567 s_Molecule_AtomIndex(VALUE self, VALUE val)
6568 {
6569     Molecule *mol;
6570     Data_Get_Struct(self, Molecule, mol);
6571         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
6572 }
6573
6574 /*
6575  *  call-seq:
6576  *     extract(group, dummy_flag = nil)       -> Molecule
6577  *
6578  *  Extract the atoms given by group and return as a new molecule object.
6579  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6580  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6581  *  names beginning with an underscore) and included in the new molecule object.
6582  */
6583 static VALUE
6584 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6585 {
6586     Molecule *mol1, *mol2;
6587         IntGroup *ig;
6588         VALUE group, dummy_flag, retval;
6589     Data_Get_Struct(self, Molecule, mol1);
6590         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6591         ig = s_Molecule_AtomGroupFromValue(self, group);
6592         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6593                 retval = Qnil;
6594         } else {
6595                 retval = ValueFromMolecule(mol2);
6596         }
6597         IntGroupRelease(ig);
6598         return retval;
6599 }
6600
6601 /*
6602  *  call-seq:
6603  *     add(molecule2)       -> self
6604  *
6605  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6606     conflicts.
6607     This operation is undoable.
6608  */
6609 static VALUE
6610 s_Molecule_Add(VALUE self, VALUE val)
6611 {
6612     Molecule *mol1, *mol2;
6613     Data_Get_Struct(self, Molecule, mol1);
6614         mol2 = MoleculeFromValue(val);
6615         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6616         return self; 
6617 }
6618
6619 /*
6620  *  call-seq:
6621  *     remove(group)       -> Molecule
6622  *
6623  *  The atoms designated by the given group are removed from the molecule.
6624  *  This operation is undoable.
6625  */
6626 static VALUE
6627 s_Molecule_Remove(VALUE self, VALUE group)
6628 {
6629     Molecule *mol1;
6630         IntGroup *ig, *bg;
6631         Int i;
6632         IntGroupIterator iter;
6633
6634     Data_Get_Struct(self, Molecule, mol1);
6635         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6636         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6637                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6638         Data_Get_Struct(group, IntGroup, ig);
6639
6640         /*  Remove the bonds between the two fragments  */
6641         /*  (This is necessary for undo to work correctly)  */
6642         IntGroupIteratorInit(ig, &iter);
6643         bg = NULL;
6644         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6645                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6646                 Int j, *cp;
6647                 cp = AtomConnectData(&ap->connect);
6648                 for (j = 0; j < ap->connect.count; j++) {
6649                         int n = cp[j];
6650                         if (!IntGroupLookup(ig, n, NULL)) {
6651                                 /*  bond i-n, i is in ig and n is not  */
6652                                 int k = MoleculeLookupBond(mol1, i, n);
6653                                 if (k >= 0) {
6654                                         if (bg == NULL)
6655                                                 bg = IntGroupNew();
6656                                         IntGroupAdd(bg, k, 1);
6657                                 }
6658                         }
6659                 }
6660         }
6661         IntGroupIteratorRelease(&iter);
6662         if (bg != NULL) {
6663                 /*  Remove bonds  */
6664                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6665                 IntGroupRelease(bg);
6666         }
6667         /*  Remove atoms  */
6668         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6669                 return Qnil;
6670         return self;
6671 }
6672
6673 /*
6674  *  call-seq:
6675  *     create_atom(name, pos = -1)  -> AtomRef
6676  *
6677  *  Create a new atom with the specified name (may contain residue 
6678  *  information) and position (if position is out of range, the atom is appended at
6679  *  the end). Returns the reference to the new atom.
6680  *  This operation is undoable.
6681  */
6682 static VALUE
6683 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6684 {
6685     Molecule *mol;
6686     Int i, pos;
6687         VALUE name, ival;
6688     Atom arec;
6689     AtomRef *aref;
6690         char *p, resName[6], atomName[6];
6691         int resSeq;
6692     Data_Get_Struct(self, Molecule, mol);
6693         rb_scan_args(argc, argv, "02", &name, &ival);
6694         if (ival != Qnil)
6695                 pos = NUM2INT(rb_Integer(ival));
6696         else pos = -1;
6697         if (name != Qnil) {
6698                 p = StringValuePtr(name);
6699                 if (p[0] != 0) {
6700                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6701                         if (atomName[0] == 0)
6702                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6703                 }
6704         } else p = NULL;
6705         if (p == NULL || p[0] == 0) {
6706                 memset(atomName, 0, 4);
6707                 resSeq = -1;
6708         }
6709     memset(&arec, 0, sizeof(arec));
6710     strncpy(arec.aname, atomName, 4);
6711     if (resSeq >= 0) {
6712       strncpy(arec.resName, resName, 4);
6713       arec.resSeq = resSeq;
6714     }
6715         arec.occupancy = 1.0;
6716 //    i = MoleculeCreateAnAtom(mol, &arec);
6717         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6718                 return Qnil;
6719     aref = AtomRefNew(mol, pos);
6720     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6721 }
6722
6723 /*
6724  *  call-seq:
6725  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6726  *
6727  *  Create a new atom with the same attributes (but no bonding information)
6728  *  with the specified atom. Returns the reference to the new atom.
6729  *  This operation is undoable.
6730  */
6731 static VALUE
6732 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6733 {
6734     Molecule *mol;
6735         const Atom *apsrc;
6736     Atom arec;
6737         AtomRef *aref;
6738         VALUE retval, aval, ival;
6739         Int pos;
6740     Data_Get_Struct(self, Molecule, mol);
6741         rb_scan_args(argc, argv, "11", &aval, &ival);
6742         if (FIXNUM_P(aval)) {
6743                 int idx = NUM2INT(aval);
6744                 if (idx < 0 || idx >= mol->natoms)
6745                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6746                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6747         } else {
6748                 apsrc = s_AtomFromValue(aval);
6749         }
6750         if (apsrc == NULL)
6751                 rb_raise(rb_eMolbyError, "bad atom specification");
6752         if (ival != Qnil)
6753                 pos = NUM2INT(rb_Integer(ival));
6754         else pos = -1;
6755         AtomDuplicate(&arec, apsrc);
6756         arec.connect.count = 0;
6757         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6758                 retval = Qnil;
6759         else {
6760                 aref = AtomRefNew(mol, pos);
6761                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6762         }
6763         AtomClean(&arec);
6764         return retval;
6765 }
6766
6767 /*
6768  *  call-seq:
6769  *     create_bond(n1, n2, ...)       -> Integer
6770  *
6771  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6772  *  do nothing for that pair. Returns the number of bonds actually created.
6773  *  This operation is undoable.
6774  */
6775 static VALUE
6776 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6777 {
6778     Molecule *mol;
6779         Int i, j, k, *ip, old_nbonds;
6780         if (argc == 0)
6781                 rb_raise(rb_eMolbyError, "missing arguments");
6782         if (argc % 2 != 0)
6783                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6784     Data_Get_Struct(self, Molecule, mol);
6785         ip = ALLOC_N(Int, argc + 1);
6786         for (i = j = 0; i < argc; i++, j++) {
6787                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6788                 if (i % 2 == 1) {
6789                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6790                                 j -= 2;  /*  This bond is already present: skip it  */
6791                         else {
6792                                 for (k = 0; k < j - 1; k += 2) {
6793                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6794                                                 j -= 2;   /*  The same entry is already in the argument  */
6795                                                 break;
6796                                         }
6797                                 }
6798                         }
6799                 }
6800         }
6801         old_nbonds = mol->nbonds;
6802         if (j > 0) {
6803                 ip[j] = kInvalidIndex;
6804                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6805         } else i = 0;
6806         xfree(ip);
6807         if (i == -1)
6808                 rb_raise(rb_eMolbyError, "atom index out of range");
6809         else if (i == -2)
6810                 rb_raise(rb_eMolbyError, "too many bonds");
6811         else if (i == -3)
6812                 rb_raise(rb_eMolbyError, "duplicate bonds");
6813         else if (i == -5)
6814                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6815         else if (i != 0)
6816                 rb_raise(rb_eMolbyError, "error in creating bonds");
6817         return INT2NUM(mol->nbonds - old_nbonds);
6818 }
6819
6820 /*
6821  *  call-seq:
6822  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6823  *
6824  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6825  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6826  *  This operation is undoable.
6827  */
6828 static VALUE
6829 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6830 {
6831     Molecule *mol;
6832         Int i, j, n[2];
6833         IntGroup *bg;
6834         if (argc == 0)
6835                 rb_raise(rb_eMolbyError, "missing arguments");
6836         if (argc % 2 != 0)
6837                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6838     Data_Get_Struct(self, Molecule, mol);
6839         bg = NULL;
6840         for (i = j = 0; i < argc; i++, j = 1 - j) {
6841                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6842                 if (j == 1) {
6843                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6844                         if (k >= 0) {
6845                                 if (bg == NULL)
6846                                         bg = IntGroupNew();
6847                                 IntGroupAdd(bg, k, 1);
6848                         }
6849                 }
6850         }
6851         if (bg != NULL) {
6852                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6853                 i = IntGroupGetCount(bg);
6854                 IntGroupRelease(bg);
6855         } else i = 0;
6856         return INT2NUM(i);
6857 }
6858
6859 /*
6860  *  call-seq:
6861  *     assign_bond_order(idx, d1)
6862  *     assign_bond_orders(group, [d1, d2, ...])
6863  *
6864  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6865  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6866  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6867  *  (This may change in the future)
6868  *  This operation is undoable.
6869  */
6870 static VALUE
6871 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6872 {
6873     Molecule *mol;
6874         IntGroup *ig;
6875     Data_Get_Struct(self, Molecule, mol);
6876         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6877                 /*  The first form  */
6878                 Int idx = NUM2INT(rb_Integer(idxval));
6879                 Double d1 = NUM2DBL(rb_Float(dval));
6880                 if (idx < 0 || idx >= mol->nbonds)
6881                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6882                 ig = IntGroupNewWithPoints(idx, 1, -1);
6883                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6884                 IntGroupRelease(ig);
6885         } else {
6886                 Int i, n;
6887                 Double *dp;
6888                 ig = IntGroupFromValue(idxval);
6889                 n = IntGroupGetCount(ig);
6890                 if (n == 0)
6891                         rb_raise(rb_eMolbyError, "the bond index is empty");
6892                 dval = rb_ary_to_ary(dval);
6893                 dp = (Double *)calloc(sizeof(Double), n);
6894                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6895                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6896                 }
6897                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6898                 free(dp);
6899                 IntGroupRelease(ig);
6900         }
6901         return self;
6902 }
6903
6904 /*
6905  *  call-seq:
6906  *     get_bond_order(idx) -> Float
6907  *     get_bond_orders(group) -> Array
6908  *
6909  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6910  *  In the second form, the bond orders at the indices in the group are returned as an array.
6911  *  If no bond order information have been assigned, returns nil (the first form)
6912  *  or an empty array (the second form).
6913  */
6914 static VALUE
6915 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6916 {
6917     Molecule *mol;
6918         IntGroup *ig;
6919         Double *dp;
6920         VALUE retval;
6921         Int i, n, numericArg;
6922     Data_Get_Struct(self, Molecule, mol);
6923         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6924                 /*  The first form  */
6925                 Int idx = NUM2INT(rb_Integer(idxval));
6926                 if (idx < 0 || idx >= mol->nbonds)
6927                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6928                 if (mol->bondOrders == NULL)
6929                         return Qnil;
6930                 ig = IntGroupNewWithPoints(idx, 1, -1);
6931                 n = 1;
6932                 numericArg = 1;
6933         } else {
6934                 if (mol->bondOrders == NULL)
6935                         return rb_ary_new();
6936                 ig = IntGroupFromValue(idxval);
6937                 n = IntGroupGetCount(ig);
6938                 if (n == 0)
6939                         rb_raise(rb_eMolbyError, "the bond index is empty");
6940                 numericArg = 0;
6941         }
6942         dp = (Double *)calloc(sizeof(Double), n);
6943         MoleculeGetBondOrders(mol, dp, ig);
6944         if (numericArg)
6945                 retval = rb_float_new(dp[0]);
6946         else {
6947                 retval = rb_ary_new();
6948                 for (i = 0; i < n; i++)
6949                         rb_ary_push(retval, rb_float_new(dp[i]));
6950         }
6951         free(dp);
6952         IntGroupRelease(ig);
6953         return retval;
6954 }
6955
6956 /*
6957  *  call-seq:
6958  *     bond_exist?(idx1, idx2) -> bool
6959  *
6960  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6961  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6962  */
6963 static VALUE
6964 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6965 {
6966         Molecule *mol;
6967         Int idx1, idx2, i;
6968         Atom *ap;
6969         Int *cp;
6970     Data_Get_Struct(self, Molecule, mol);
6971         idx1 = NUM2INT(rb_Integer(ival1));
6972         idx2 = NUM2INT(rb_Integer(ival2));
6973         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6974                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6975         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6976         cp = AtomConnectData(&ap->connect);
6977         for (i = 0; i < ap->connect.count; i++) {
6978                 if (cp[i] == idx2)
6979                         return Qtrue;
6980         }
6981         return Qfalse;
6982 }
6983
6984 /*
6985  *  call-seq:
6986  *     add_angle(n1, n2, n3)       -> Molecule
6987  *
6988  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6989  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6990  *  This operation is undoable.
6991  */
6992 static VALUE
6993 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6994 {
6995         Int n[4];
6996     Molecule *mol;
6997     Data_Get_Struct(self, Molecule, mol);
6998         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6999         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7000         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7001         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
7002                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
7003         n[3] = kInvalidIndex;
7004         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
7005         return self;
7006 }
7007
7008 /*
7009  *  call-seq:
7010  *     remove_angle(n1, n2, n3)       -> Molecule
7011  *
7012  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
7013  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
7014  *  This operation is undoable.
7015  */
7016 static VALUE
7017 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
7018 {
7019         Int n[4];
7020     Molecule *mol;
7021         IntGroup *ig;
7022     Data_Get_Struct(self, Molecule, mol);
7023         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7024         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7025         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7026         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
7027                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
7028         ig = IntGroupNewWithPoints(n[3], 1, -1);
7029         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
7030         IntGroupRelease(ig);
7031         return self;
7032 }
7033
7034 /*
7035  *  call-seq:
7036  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
7037  *
7038  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
7039  *  when a bond is created, so it is rarely necessary to use this method explicitly.
7040  *  This operation is undoable.
7041  */
7042 static VALUE
7043 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7044 {
7045         Int n[5];
7046     Molecule *mol;
7047     Data_Get_Struct(self, Molecule, mol);
7048         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7049         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7050         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7051         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7052         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
7053                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
7054         n[4] = kInvalidIndex;
7055         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
7056         return self;
7057 }
7058
7059 /*
7060  *  call-seq:
7061  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
7062  *
7063  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
7064  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
7065  *  This operation is undoable.
7066  */
7067 static VALUE
7068 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7069 {
7070         Int n[5];
7071     Molecule *mol;
7072         IntGroup *ig;
7073     Data_Get_Struct(self, Molecule, mol);
7074         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7075         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7076         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7077         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7078         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
7079                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
7080         ig = IntGroupNewWithPoints(n[4], 1, -1);
7081         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
7082         IntGroupRelease(ig);
7083         return self;
7084 }
7085
7086 /*
7087  *  call-seq:
7088  *     add_improper(n1, n2, n3, n4)       -> Molecule
7089  *
7090  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
7091  *  not automatically added when a new bond is created, so this method is more useful than
7092  *  the angle/dihedral counterpart.
7093  *  This operation is undoable.
7094  */
7095 static VALUE
7096 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7097 {
7098         Int n[5];
7099     Molecule *mol;
7100     Data_Get_Struct(self, Molecule, mol);
7101         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7102         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7103         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7104         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7105         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
7106                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
7107         n[4] = kInvalidIndex;
7108         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
7109         return self;
7110 }
7111
7112 /*
7113  *  call-seq:
7114  *     remove_improper(n1, n2, n3, n4)       -> Molecule
7115  *     remove_improper(intgroup)             -> Molecule
7116  *
7117  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
7118  *  Returns self. Unlike angles and dihedrals, impropers are
7119  *  not automatically added when a new bond is created, so this method is more useful than
7120  *  the angle/dihedral counterpart.
7121  *  This operation is undoable.
7122  */
7123 static VALUE
7124 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
7125 {
7126         Int n[5];
7127         VALUE v1, v2, v3, v4;
7128     Molecule *mol;
7129         IntGroup *ig;
7130     Data_Get_Struct(self, Molecule, mol);
7131         if (argc == 1) {
7132                 ig = IntGroupFromValue(argv[0]);
7133         } else {
7134                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
7135                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7136                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7137                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7138                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7139                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
7140                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
7141                 ig = IntGroupNewWithPoints(n[4], 1, -1);
7142         }
7143         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
7144         IntGroupRelease(ig);
7145         return self;
7146 }
7147
7148 /*
7149  *  call-seq:
7150  *     assign_residue(group, res)       -> Molecule
7151  *
7152  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
7153  *  or "resname.resno". When the residue number is not specified, the residue number of
7154  *  the first atom in the group is used.
7155  *  This operation is undoable.
7156  */
7157 static VALUE
7158 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
7159 {
7160     Molecule *mol;
7161         IntGroup *ig;
7162         char *p, *pp, buf[16];
7163         Int resid, n;
7164         Atom *ap;
7165     Data_Get_Struct(self, Molecule, mol);
7166         
7167         /*  Parse the argument res  */
7168         if (FIXNUM_P(res)) {
7169                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
7170                 resid = NUM2INT(res);
7171                 buf[0] = 0;
7172         } else {
7173                 p = StringValuePtr(res);
7174                 pp = strchr(p, '.');
7175                 if (pp != NULL) {
7176                         resid = atoi(pp + 1);
7177                         n = pp - p;
7178                 } else {
7179                         resid = -1;
7180                         n = strlen(p);
7181                 }
7182                 if (n > sizeof buf - 1)
7183                         n = sizeof buf - 1;
7184                 strncpy(buf, p, n);
7185                 buf[n] = 0;
7186         }
7187         ig = s_Molecule_AtomGroupFromValue(self, range);
7188         if (ig == NULL || IntGroupGetCount(ig) == 0)
7189                 return Qnil;
7190
7191         if (resid < 0) {
7192                 /*  Use the residue number of the first specified atom  */
7193                 n = IntGroupGetNthPoint(ig, 0);
7194                 if (n >= mol->natoms)
7195                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
7196                 ap = ATOM_AT_INDEX(mol->atoms, n);
7197                 resid = ap->resSeq;
7198         }
7199         /*  Change the residue number  */
7200         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
7201         /*  Change the residue name if necessary  */
7202         if (buf[0] != 0) {
7203         /*      Int seqs[2];
7204                 seqs[0] = resid;
7205                 seqs[1] = kInvalidIndex; */
7206                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
7207         }
7208         IntGroupRelease(ig);
7209         return self;
7210 }
7211
7212 /*
7213  *  call-seq:
7214  *     offset_residue(group, offset)       -> Molecule
7215  *
7216  *  Offset the residue number of the specified atoms. If any of the residue number gets
7217  *  negative, then exception is thrown.
7218  *  This operation is undoable.
7219  */
7220 static VALUE
7221 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
7222 {
7223     Molecule *mol;
7224         IntGroup *ig;
7225         int ofs, result;
7226     Data_Get_Struct(self, Molecule, mol);
7227         ig = s_Molecule_AtomGroupFromValue(self, range);
7228         ofs = NUM2INT(offset);
7229         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
7230         if (result > 0)
7231                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
7232         IntGroupRelease(ig);
7233         return self;
7234 }
7235
7236 /*
7237  *  call-seq:
7238  *     renumber_atoms(array)       -> IntGroup
7239  *
7240  *  Change the order of atoms so that the atoms specified in the array argument appear
7241  *  in this order from the top of the molecule. The atoms that are not included in array
7242  *  are placed after these atoms, and these atoms are returned as an intGroup.
7243  *  This operation is undoable.
7244  */
7245 static VALUE
7246 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
7247 {
7248     Molecule *mol;
7249         Int *new2old;
7250         IntGroup *ig;
7251         int i, n;
7252         VALUE *valp, retval;
7253     Data_Get_Struct(self, Molecule, mol);
7254         if (TYPE(array) != T_ARRAY)
7255                 array = rb_funcall(array, rb_intern("to_a"), 0);
7256         n = RARRAY_LEN(array);
7257         valp = RARRAY_PTR(array);
7258         new2old = ALLOC_N(Int, n + 1);
7259         for (i = 0; i < n; i++)
7260                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
7261         new2old[i] = kInvalidIndex;
7262         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
7263         if (i == 1)
7264                 rb_raise(rb_eMolbyError, "Atom index out of range");
7265         else if (i == 2)
7266                 rb_raise(rb_eMolbyError, "Duplicate entry");
7267         else if (i == 3)
7268                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
7269         retval = IntGroup_Alloc(rb_cIntGroup);
7270         Data_Get_Struct(retval, IntGroup, ig);
7271         if (mol->natoms > n)
7272                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
7273         xfree(new2old);
7274         return retval;
7275 }
7276
7277 /*
7278  *  call-seq:
7279  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7280  *
7281  *  Find atoms that are within the threshold distance from the given atom.
7282  *  (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.)
7283  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7284  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7285  *  If limit is not given, a default value of 1.2 is used.
7286  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7287  */
7288 static VALUE
7289 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7290 {
7291     Molecule *mol;
7292         VALUE aval, limval, radval;
7293         double limit, radius;
7294         Int n1, nbonds, *bonds, an;
7295         Vector v;
7296     Data_Get_Struct(self, Molecule, mol);
7297         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7298         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)) {
7299                 VectorFromValue(aval, &v);
7300                 if (radval == Qnil)
7301                         radius = gElementParameters[6].radius;
7302                 else
7303                         radius = NUM2DBL(rb_Float(radval));
7304                 n1 = mol->natoms;
7305         } else {
7306                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7307                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7308                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7309                 if (an >= 0 && an < gCountElementParameters)
7310                         radius = gElementParameters[an].radius;
7311                 else radius = gElementParameters[6].radius;
7312         }
7313         if (limval == Qnil)
7314                 limit = 1.2;
7315         else
7316                 limit = NUM2DBL(rb_Float(limval));
7317         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7318         bonds = NULL;
7319         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7320         aval = rb_ary_new();
7321         if (nbonds > 0) {
7322                 for (n1 = 0; n1 < nbonds; n1++)
7323                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7324                 free(bonds);
7325         }
7326         return aval;
7327 }
7328
7329 /*
7330  *  call-seq:
7331  *     guess_bonds(limit = 1.2)       -> Integer
7332  *
7333  *  Create bonds between atoms that are within the threshold distance.
7334  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7335  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7336  *  If limit is not given, a default value of 1.2 is used.
7337  *  The number of the newly created bonds is returned.
7338  *  This operation is undoable.
7339  */
7340 static VALUE
7341 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7342 {
7343     Molecule *mol;
7344         VALUE limval;
7345         double limit;
7346         Int nbonds, *bonds;
7347     Data_Get_Struct(self, Molecule, mol);
7348         rb_scan_args(argc, argv, "01", &limval);
7349         if (limval == Qnil)
7350                 limit = 1.2;
7351         else
7352                 limit = NUM2DBL(rb_Float(limval));
7353         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7354         if (nbonds > 0) {
7355                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7356                 free(bonds);
7357         }
7358         return INT2NUM(nbonds);
7359 }
7360         
7361 /*
7362  *  call-seq:
7363  *     register_undo(script, *args)
7364  *
7365  *  Register an undo operation with the current molecule.
7366  */
7367 static VALUE
7368 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
7369 {
7370         Molecule *mol;
7371         VALUE script, args;
7372         MolAction *act;
7373     Data_Get_Struct(self, Molecule, mol);
7374         rb_scan_args(argc, argv, "1*", &script, &args);
7375         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
7376         MolActionCallback_registerUndo(mol, act);
7377         return script;
7378 }
7379
7380 /*
7381  *  call-seq:
7382  *     undo_enabled? -> bool
7383  *
7384  *  Returns true if undo is enabled for this molecule; otherwise no.
7385  */
7386 static VALUE
7387 s_Molecule_UndoEnabled(VALUE self)
7388 {
7389     Molecule *mol;
7390     Data_Get_Struct(self, Molecule, mol);
7391         if (MolActionCallback_isUndoRegistrationEnabled(mol))
7392                 return Qtrue;
7393         else return Qfalse;
7394 }
7395
7396 /*
7397  *  call-seq:
7398  *     undo_enabled = bool
7399  *
7400  *  Enable or disable undo.
7401  */
7402 static VALUE
7403 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
7404 {
7405     Molecule *mol;
7406     Data_Get_Struct(self, Molecule, mol);
7407         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
7408         return val;
7409 }
7410
7411 /*
7412  *  call-seq:
7413  *     selection       -> IntGroup
7414  *
7415  *  Returns the current selection.
7416  */
7417 static VALUE
7418 s_Molecule_Selection(VALUE self)
7419 {
7420     Molecule *mol;
7421         IntGroup *ig;
7422         VALUE val;
7423     Data_Get_Struct(self, Molecule, mol);
7424         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
7425                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
7426                 val = ValueFromIntGroup(ig);
7427                 IntGroupRelease(ig);
7428         } else {
7429                 val = IntGroup_Alloc(rb_cIntGroup);
7430         }
7431         return val;
7432 }
7433
7434 static VALUE
7435 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
7436 {
7437     Molecule *mol;
7438         IntGroup *ig;
7439     Data_Get_Struct(self, Molecule, mol);
7440         if (val == Qnil)
7441                 ig = NULL;
7442         else
7443                 ig = s_Molecule_AtomGroupFromValue(self, val);
7444         if (undoable)
7445                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
7446         else
7447                 MoleculeSetSelection(mol, ig);
7448         if (ig != NULL)
7449                 IntGroupRelease(ig);
7450         return val;
7451 }
7452
7453 /*
7454  *  call-seq:
7455  *     selection = IntGroup
7456  *
7457  *  Set the current selection. The right-hand operand may be nil.
7458  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
7459  */
7460 static VALUE
7461 s_Molecule_SetSelection(VALUE self, VALUE val)
7462 {
7463         return s_Molecule_SetSelectionSub(self, val, 0);
7464 }
7465
7466 /*
7467  *  call-seq:
7468  *     set_undoable_selection(IntGroup)
7469  *
7470  *  Set the current selection with undo registration. The right-hand operand may be nil.
7471  *  This operation is undoable.
7472  */
7473 static VALUE
7474 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
7475 {
7476         return s_Molecule_SetSelectionSub(self, val, 1);
7477 }
7478
7479 /*
7480  *  call-seq:
7481  *     hidden_atoms       -> IntGroup
7482  *
7483  *  Returns the currently hidden atoms.
7484  */
7485 static VALUE
7486 s_Molecule_HiddenAtoms(VALUE self)
7487 {
7488         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7489         return Qnil;  /*  Not reached  */
7490 /*    Molecule *mol;
7491         IntGroup *ig;
7492         VALUE val;
7493     Data_Get_Struct(self, Molecule, mol);
7494         if (mol != NULL) {
7495                 Atom *ap;
7496                 int i;
7497                 ig = IntGroupNew();
7498                 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7499                         if (ap->exflags & kAtomHiddenFlag)
7500                                 IntGroupAdd(ig, i, 1);
7501                 }
7502                 val = ValueFromIntGroup(ig);
7503                 IntGroupRelease(ig);
7504                 rb_obj_freeze(val);
7505                 return val;
7506         } else return Qnil; */
7507 }
7508
7509 /*
7510  *  call-seq:
7511  *     set_hidden_atoms(IntGroup)
7512  *     self.hidden_atoms = IntGroup
7513  *
7514  *  Hide the specified atoms. This operation is _not_ undoable.
7515  */
7516 static VALUE
7517 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
7518 {
7519         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7520         return Qnil;  /*  Not reached  */
7521 /*
7522         Molecule *mol;
7523     Data_Get_Struct(self, Molecule, mol);
7524         if (mol != NULL) {
7525                 Atom *ap;
7526                 int i;
7527                 IntGroup *ig;
7528                 if (val == Qnil)
7529                         ig = NULL;
7530                 else
7531                         ig = s_Molecule_AtomGroupFromValue(self, val);
7532                 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7533                         if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
7534                                 ap->exflags |= kAtomHiddenFlag;
7535                         } else {
7536                                 ap->exflags &= kAtomHiddenFlag;
7537                         }
7538                 }
7539                 if (ig != NULL)
7540                         IntGroupRelease(ig);
7541                 MoleculeCallback_notifyModification(mol, 0);
7542         }
7543         return val; */
7544 }
7545
7546 /*
7547  *  call-seq:
7548  *     select_frame(index)
7549  *     frame = index
7550  *
7551  *  Select the specified frame. If successful, returns true, otherwise returns false.
7552  */
7553 static VALUE
7554 s_Molecule_SelectFrame(VALUE self, VALUE val)
7555 {
7556     Molecule *mol;
7557         int ival = NUM2INT(val);
7558     Data_Get_Struct(self, Molecule, mol);
7559         ival = MoleculeSelectFrame(mol, ival, 1);
7560         if (ival >= 0)
7561                 return Qtrue;
7562         else return Qfalse;
7563 }
7564
7565 /*
7566  *  call-seq:
7567  *     frame -> Integer
7568  *
7569  *  Get the current frame.
7570  */
7571 static VALUE
7572 s_Molecule_Frame(VALUE self)
7573 {
7574     Molecule *mol;
7575     Data_Get_Struct(self, Molecule, mol);
7576         return INT2NUM(mol->cframe);
7577 }
7578
7579 /*
7580  *  call-seq:
7581  *     nframes -> Integer
7582  *
7583  *  Get the number of frames.
7584  */
7585 static VALUE
7586 s_Molecule_Nframes(VALUE self)
7587 {
7588     Molecule *mol;
7589     Data_Get_Struct(self, Molecule, mol);
7590         return INT2NUM(MoleculeGetNumberOfFrames(mol));
7591 }
7592
7593 /*
7594  *  call-seq:
7595  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7596  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7597  *
7598  *  Insert new frames at the indices specified by the intGroup. If the first argument is
7599  *  an integer, a single new frame is inserted at that index. If the first argument is 
7600  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7601  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
7602  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
7603  *  to the new frame.
7604  *  Returns an intGroup representing the inserted frames if successful, nil if not.
7605  */
7606 static VALUE
7607 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7608 {
7609         VALUE val, coords, cells;
7610     Molecule *mol;
7611         IntGroup *ig;
7612         int count, ival, i, j, len, len_c, len2, nframes;
7613         VALUE *ptr, *ptr2;
7614         Vector *vp, *vp2;
7615     Data_Get_Struct(self, Molecule, mol);
7616         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7617         if (coords != Qnil) {
7618                 if (TYPE(coords) != T_ARRAY)
7619                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7620                 len = RARRAY_LEN(coords);
7621         } else len = 0;
7622         if (cells != Qnil) {
7623                 if (mol->cell == NULL)
7624                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7625                 if (TYPE(cells) != T_ARRAY)
7626                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7627                 len_c = RARRAY_LEN(cells);
7628         } else len_c = 0;
7629         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
7630         nframes = MoleculeGetNumberOfFrames(mol);
7631         if (val == Qnil) {
7632                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7633                 val = ValueFromIntGroup(ig);
7634         } else {
7635                 ig = IntGroupFromValue(val);
7636         }
7637         count = IntGroupGetCount(ig);  /*  Count is updated here  */
7638         vp = ALLOC_N(Vector, mol->natoms * count);
7639         if (cells != Qnil)
7640                 vp2 = ALLOC_N(Vector, 4 * count);
7641         else vp2 = NULL;
7642         if (len > 0) {
7643                 if (len < count)
7644                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7645                 ptr = RARRAY_PTR(coords);
7646                 for (i = 0; i < count; i++) {
7647                         if (TYPE(ptr[i]) != T_ARRAY)
7648                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7649                         len2 = RARRAY_LEN(ptr[i]);
7650                         if (len2 < mol->natoms)
7651                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7652                         ptr2 = RARRAY_PTR(ptr[i]);
7653                         for (j = 0; j < mol->natoms; j++)
7654                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7655                 }
7656         } else {
7657                 Atom *ap;
7658                 for (i = 0; i < count; i++) {
7659                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7660                                 vp[i * mol->natoms + j] = ap->r;
7661                         }
7662                 }
7663         }
7664         if (len_c > 0) {
7665                 if (len_c < count)
7666                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7667                 ptr = RARRAY_PTR(cells);
7668                 for (i = 0; i < count; i++) {
7669                         if (TYPE(ptr[i]) != T_ARRAY)
7670                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7671                         len2 = RARRAY_LEN(ptr[i]);
7672                         if (len2 < 4)
7673                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7674                         ptr2 = RARRAY_PTR(ptr[i]);
7675                         for (j = 0; j < 4; j++)
7676                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7677                 }
7678         }
7679         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7680         IntGroupRelease(ig);
7681         xfree(vp);
7682         if (vp2 != NULL)
7683                 xfree(vp2);
7684         return (ival >= 0 ? val : Qnil);
7685 }
7686
7687 /*
7688  *  call-seq:
7689  *     create_frame(coordinates = nil) -> Integer
7690  *     create_frames(coordinates = nil) -> Integer
7691  *
7692  *  Same as molecule.insert_frames(nil, coordinates).
7693  */
7694 static VALUE
7695 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
7696 {
7697         VALUE vals[3];
7698         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
7699         vals[0] = Qnil;
7700         return s_Molecule_InsertFrames(3, vals, self);
7701 }
7702
7703 /*
7704  *  call-seq:
7705  *     remove_frames(IntGroup, wantCoordinates = false)
7706  *
7707  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
7708  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
7709  *  removed frames is returned if operation is successful.
7710  */
7711 static VALUE
7712 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
7713 {
7714         VALUE val, flag;
7715         VALUE retval;
7716     Molecule *mol;
7717         IntGroup *ig;
7718         int count;
7719     Data_Get_Struct(self, Molecule, mol);
7720         rb_scan_args(argc, argv, "11", &val, &flag);
7721         ig = IntGroupFromValue(val);
7722         count = IntGroupGetCount(ig);
7723         if (RTEST(flag)) {
7724                 /*  Create return value before removing frames  */
7725                 VALUE coords;
7726                 int i, j, n;
7727                 Atom *ap;
7728                 Vector v;
7729                 retval = rb_ary_new2(count);
7730                 for (i = 0; i < count; i++) {
7731                         n = IntGroupGetNthPoint(ig, i);
7732                         coords = rb_ary_new2(mol->natoms);
7733                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7734                                 if (n < ap->nframes && n != mol->cframe)
7735                                         v = ap->frames[n];
7736                                 else v = ap->r;
7737                                 rb_ary_push(coords, ValueFromVector(&v));
7738                         }
7739                         rb_ary_push(retval, coords);
7740                 }
7741         } else retval = Qtrue;
7742         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
7743                 return retval;
7744         else return Qnil;
7745 }
7746
7747 /*
7748  *  call-seq:
7749  *     each_frame {|n| ...}
7750  *
7751  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
7752  *  the frame number. After completion, the original frame number is restored.
7753  */
7754 static VALUE
7755 s_Molecule_EachFrame(VALUE self)
7756 {
7757         int i, cframe, nframes;
7758     Molecule *mol;
7759     Data_Get_Struct(self, Molecule, mol);
7760         cframe = mol->cframe;
7761         nframes = MoleculeGetNumberOfFrames(mol);
7762         if (nframes > 0) {
7763                 for (i = 0; i < nframes; i++) {
7764                         MoleculeSelectFrame(mol, i, 1);
7765                         rb_yield(INT2NUM(i));
7766                 }
7767                 MoleculeSelectFrame(mol, cframe, 1);
7768         }
7769     return self;
7770 }
7771
7772 /*
7773  *  call-seq:
7774  *     get_coord_from_frame(index, group = nil)
7775  *
7776  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7777  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
7778  *  copied; now they are always copied)
7779  */
7780 static VALUE
7781 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7782 {
7783         Molecule *mol;
7784         VALUE ival, gval, cval;
7785         Int index, i, j, n, nn;
7786         IntGroup *ig;
7787         IntGroupIterator iter;
7788         Atom *ap;
7789         Vector *vp;
7790     Data_Get_Struct(self, Molecule, mol);
7791         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7792         if (argc == 3)
7793                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
7794         index = NUM2INT(rb_Integer(ival));
7795         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7796                 if (n == 0)
7797                         rb_raise(rb_eMolbyError, "No frame is present");
7798                 else
7799                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7800         }
7801         if (gval == Qnil) {
7802                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7803         } else {
7804                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7805         }
7806         n = IntGroupGetCount(ig);
7807         if (n > 0) {
7808                 vp = (Vector *)calloc(sizeof(Vector), n);
7809                 IntGroupIteratorInit(ig, &iter);
7810                 j = 0;
7811                 nn = 0;
7812                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7813                         ap = ATOM_AT_INDEX(mol->atoms, i);
7814                         if (index < ap->nframes) {
7815                                 vp[j] = ap->frames[index];
7816                                 nn++;
7817                         } else {
7818                                 vp[j] = ap->r;
7819                         }
7820                         j++;
7821                 }
7822                 if (nn > 0)
7823                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7824                 free(vp);
7825                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7826                         vp = mol->frame_cells + index * 4;
7827                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7828                 }
7829                 IntGroupIteratorRelease(&iter);
7830         }
7831         /*  Copy the extra properties  */
7832         IntGroupRelease(ig);
7833         for (i = 0; i < mol->nmolprops; i++) {
7834                 Double *dp = (Double *)malloc(sizeof(Double));
7835                 ig = IntGroupNew();
7836                 IntGroupAdd(ig, mol->cframe, 1);
7837                 *dp = mol->molprops[i].propvals[index];
7838                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
7839                 free(dp);
7840                 IntGroupRelease(ig);
7841         }
7842         
7843         return self;
7844 }
7845
7846 /*
7847  *  call-seq:
7848  *     reorder_frames(old_indices)
7849  *
7850  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
7851  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
7852  *  same as the old frames 2/0/1, respectively.
7853  *  The argument must have the same number of integers as the number of frames.
7854  */
7855 static VALUE
7856 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
7857 {
7858         Molecule *mol;
7859         Int *ip, *ip2, i, n, nframes;
7860     Data_Get_Struct(self, Molecule, mol);
7861         aval = rb_ary_to_ary(aval);
7862         nframes = MoleculeGetNumberOfFrames(mol);
7863         if (RARRAY_LEN(aval) != nframes)
7864                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
7865         ip2 = (Int *)calloc(sizeof(Int), nframes);
7866         ip = (Int *)calloc(sizeof(Int), nframes);
7867         for (i = 0; i < nframes; i++) {
7868                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
7869                 if (n < 0 || n >= nframes || ip2[n] != 0) {
7870                         free(ip2);
7871                         free(ip);
7872                         if (n < 0 || n >= nframes)
7873                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
7874                         else
7875                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
7876                 }
7877                 ip2[n] = 1;
7878                 ip[i] = n;
7879         }
7880         free(ip2);
7881         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
7882         free(ip);
7883         return self;
7884 }
7885         
7886 /*
7887  *  call-seq:
7888  *     set_atom_attr(index, key, value)
7889  *
7890  *  Set the atom attribute for the specified atom.
7891  *  This operation is undoable.
7892  */
7893 static VALUE
7894 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7895 {
7896         Molecule *mol;
7897         VALUE aref, oldval;
7898     Data_Get_Struct(self, Molecule, mol);
7899         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7900         oldval = s_AtomRef_GetAttr(aref, key);
7901         if (val == Qundef)
7902                 return oldval;
7903         s_AtomRef_SetAttr(aref, key, val);
7904         return val;
7905 }
7906
7907 /*
7908  *  call-seq:
7909  *     get_atom_attr(index, key)
7910  *
7911  *  Get the atom attribute for the specified atom.
7912  */
7913 static VALUE
7914 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7915 {
7916         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7917 }
7918
7919 /*
7920  *  call-seq:
7921  *     fragment(n1, *exatoms)  -> IntGroup
7922  *     fragment(group, *exatoms)  -> IntGroup
7923  *
7924  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7925  *  those atoms will not be counted during the search.
7926  */
7927 static VALUE
7928 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7929 {
7930     Molecule *mol;
7931         IntGroup *baseg, *ig, *exatoms;
7932         int n;
7933         volatile VALUE nval, exval;
7934     Data_Get_Struct(self, Molecule, mol);
7935         rb_scan_args(argc, argv, "1*", &nval, &exval);
7936         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7937                 baseg = NULL;
7938                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7939         } else {
7940                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7941         }
7942         if (RARRAY_LEN(exval) == 0) {
7943                 exatoms = NULL;
7944         } else {
7945                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7946                 Data_Get_Struct(exval, IntGroup, exatoms);
7947         }
7948         if (baseg == NULL) {
7949                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7950         } else {
7951                 IntGroupIterator iter;
7952                 IntGroupIteratorInit(baseg, &iter);
7953                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7954                         ig = IntGroupNew();
7955                 } else {
7956                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7957                         if (ig != NULL) {
7958                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7959                                         IntGroup *subg;
7960                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7961                                         if (subg != NULL) {
7962                                                 IntGroupAddIntGroup(ig, subg);
7963                                                 IntGroupRelease(subg);
7964                                         }
7965                                 }
7966                         }
7967                 }
7968                 IntGroupIteratorRelease(&iter);
7969                 IntGroupRelease(baseg);
7970         }
7971         if (ig == NULL)
7972                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7973         nval = ValueFromIntGroup(ig);
7974         IntGroupRelease(ig);
7975         return nval;
7976 }
7977
7978 /*
7979  *  call-seq:
7980  *     fragments(exclude = nil)
7981  *
7982  *  Returns the fragments as an array of IntGroups. 
7983  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
7984  *  in defining the fragment.
7985  */
7986 static VALUE
7987 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
7988 {
7989     Molecule *mol;
7990         IntGroup *ag, *fg, *eg;
7991         VALUE gval, exval, retval;
7992     Data_Get_Struct(self, Molecule, mol);
7993         if (mol == NULL)
7994                 return Qnil;
7995         if (mol->natoms == 0)
7996                 return rb_ary_new();
7997         rb_scan_args(argc, argv, "01", &exval);
7998         if (exval == Qnil)
7999                 eg = NULL;
8000         else
8001                 eg = IntGroupFromValue(exval);
8002         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8003         if (eg != NULL)
8004                 IntGroupRemoveIntGroup(ag, eg);
8005         retval = rb_ary_new();
8006         while (IntGroupGetCount(ag) > 0) {
8007                 int n = IntGroupGetNthPoint(ag, 0);
8008                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8009                 if (fg == NULL)
8010                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8011                 gval = ValueFromIntGroup(fg);
8012                 rb_ary_push(retval, gval);
8013                 IntGroupRemoveIntGroup(ag, fg);
8014                 IntGroupRelease(fg);
8015         }
8016         IntGroupRelease(ag);
8017         if (eg != NULL)
8018                 IntGroupRelease(eg);
8019         return retval;
8020 }
8021
8022 /*
8023  *  call-seq:
8024  *     each_fragment(exclude = nil) {|group| ...}
8025  *
8026  *  Execute the block, with the IntGroup object for each fragment as the argument.
8027  *  Atoms or bonds should not be added or removed during the execution of the block.
8028  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8029  *  in defining the fragment.
8030  */
8031 static VALUE
8032 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8033 {
8034     Molecule *mol;
8035         IntGroup *ag, *fg, *eg;
8036         VALUE gval, exval;
8037     Data_Get_Struct(self, Molecule, mol);
8038         if (mol == NULL || mol->natoms == 0)
8039                 return self;
8040         rb_scan_args(argc, argv, "01", &exval);
8041         if (exval == Qnil)
8042                 eg = NULL;
8043         else
8044                 eg = IntGroupFromValue(exval);
8045         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8046         if (eg != NULL)
8047                 IntGroupRemoveIntGroup(ag, eg);
8048         while (IntGroupGetCount(ag) > 0) {
8049                 int n = IntGroupGetNthPoint(ag, 0);
8050                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8051                 if (fg == NULL)
8052                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8053                 gval = ValueFromIntGroup(fg);
8054                 rb_yield(gval);
8055                 IntGroupRemoveIntGroup(ag, fg);
8056                 IntGroupRelease(fg);
8057         }
8058         IntGroupRelease(ag);
8059         if (eg != NULL)
8060                 IntGroupRelease(eg);
8061         return self;
8062 }
8063
8064 /*
8065  *  call-seq:
8066  *     detachable?(group)  -> [n1, n2]
8067  *
8068  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8069  *  of the molecule via only one bond. If it is, then the indices of the atoms
8070  *  belonging to the bond is returned, the first element being the atom included
8071  *  in the fragment. Otherwise, Qnil is returned.
8072  */
8073 static VALUE
8074 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8075 {
8076         Molecule *mol;
8077         IntGroup *ig;
8078         int n1, n2;
8079         VALUE retval;
8080     Data_Get_Struct(self, Molecule, mol);
8081         ig = s_Molecule_AtomGroupFromValue(self, gval);
8082         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8083                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8084         } else retval = Qnil;
8085         IntGroupRelease(ig);
8086         return retval;
8087 }
8088
8089 /*
8090  *  call-seq:
8091  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8092  *
8093  *  Returns an array of bonds that connect an atom in the group and an atom out
8094  *  of the group. The first atom in the bond always belongs to the group. If no
8095  *  such bonds are present, an empty array is returned.
8096  */
8097 static VALUE
8098 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8099 {
8100         Molecule *mol;
8101         IntGroup *ig, *bg;
8102         VALUE gval, retval;
8103     Data_Get_Struct(self, Molecule, mol);
8104         rb_scan_args(argc, argv, "01", &gval);
8105         if (gval == Qnil) {
8106                 ig = MoleculeGetSelection(mol);
8107                 if (ig != NULL)
8108                         IntGroupRetain(ig);
8109         } else {
8110                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8111         }
8112         retval = rb_ary_new();
8113         if (ig == NULL)
8114                 return retval;
8115         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8116         if (bg != NULL) {
8117                 IntGroupIterator iter;
8118                 Int i;
8119                 IntGroupIteratorInit(bg, &iter);
8120                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8121                         /*  The atoms at the border  */
8122                         Int n1, n2;
8123                         n1 = mol->bonds[i * 2];
8124                         n2 = mol->bonds[i * 2 + 1];
8125                         if (IntGroupLookupPoint(ig, n1) < 0) {
8126                                 int w = n1;
8127                                 n1 = n2;
8128                                 n2 = w;
8129                                 if (IntGroupLookupPoint(ig, n1) < 0)
8130                                         continue;  /*  Actually this is an internal error  */
8131                         }
8132                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8133                 }
8134                 IntGroupIteratorRelease(&iter);
8135         }
8136         IntGroupRelease(bg);
8137         IntGroupRelease(ig);
8138         return retval;
8139 }
8140
8141 /*
8142  *  call-seq:
8143  *     translate(vec, group = nil)       -> Molecule
8144  *
8145  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
8146  *  This operation is undoable.
8147  */
8148 static VALUE
8149 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
8150 {
8151     Molecule *mol;
8152         VALUE vec, group;
8153         Vector v;
8154         IntGroup *ig;
8155     Data_Get_Struct(self, Molecule, mol);
8156         rb_scan_args(argc, argv, "11", &vec, &group);
8157         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8158         VectorFromValue(vec, &v);
8159 //      MoleculeTranslate(mol, &v, ig);
8160         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
8161         if (ig != NULL)
8162                 IntGroupRelease(ig);
8163         return self;
8164 }
8165
8166 /*
8167  *  call-seq:
8168  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
8169  *
8170  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
8171  *  If group is given, only atoms in the group are moved.
8172  *  This operation is undoable.
8173  */
8174 static VALUE
8175 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
8176 {
8177     Molecule *mol;
8178         volatile VALUE aval, anval, cval, gval;
8179         Double angle;
8180         Vector av, cv;
8181         Transform tr;
8182         IntGroup *ig;
8183     Data_Get_Struct(self, Molecule, mol);
8184         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
8185         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8186         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
8187         VectorFromValue(aval, &av);
8188         if (NIL_P(cval))
8189                 cv.x = cv.y = cv.z = 0.0;
8190         else
8191                 VectorFromValue(cval, &cv);
8192         if (TransformForRotation(tr, &av, angle, &cv))
8193                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
8194         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8195         if (ig != NULL)
8196                 IntGroupRelease(ig);
8197         return self;
8198 }
8199
8200 /*
8201  *  call-seq:
8202  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
8203  *
8204  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
8205  *  axis must not be a zero vector.
8206  *  If group is given, only atoms in the group are moved.
8207  *  This operation is undoable.
8208  */
8209 static VALUE
8210 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
8211 {
8212     Molecule *mol;
8213         volatile VALUE aval, cval, gval;
8214         Vector av, cv;
8215         Transform tr;
8216         IntGroup *ig;
8217     Data_Get_Struct(self, Molecule, mol);
8218         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
8219         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8220         VectorFromValue(aval, &av);
8221         if (NIL_P(cval))
8222                 cv.x = cv.y = cv.z = 0.0;
8223         else
8224                 VectorFromValue(cval, &cv);
8225         if (TransformForReflection(tr, &av, &cv))
8226                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
8227         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8228         if (ig != NULL)
8229                 IntGroupRelease(ig);
8230         return self;
8231 }
8232
8233 /*
8234  *  call-seq:
8235  *     invert(center = [0,0,0], group = nil)       -> Molecule
8236  *
8237  *  Invert the molecule with the given center.
8238  *  If group is given, only atoms in the group are moved.
8239  *  This operation is undoable.
8240  */
8241 static VALUE
8242 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
8243 {
8244         Molecule *mol;
8245         volatile VALUE cval, gval;
8246         Vector cv;
8247         Transform tr;
8248         IntGroup *ig;
8249     Data_Get_Struct(self, Molecule, mol);
8250         rb_scan_args(argc, argv, "02", &cval, &gval);
8251         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8252         if (NIL_P(cval))
8253                 cv.x = cv.y = cv.z = 0.0;
8254         else
8255                 VectorFromValue(cval, &cv);
8256         TransformForInversion(tr, &cv);
8257         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8258         if (ig != NULL)
8259                 IntGroupRelease(ig);
8260         return self;
8261 }
8262
8263 /*
8264  *  call-seq:
8265  *     transform(transform, group = nil)       -> Molecule
8266  *
8267  *  Transform the molecule by the given Transform object.
8268  *  If group is given, only atoms in the group are moved.
8269  *  This operation is undoable.
8270  */
8271 static VALUE
8272 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
8273 {
8274     Molecule *mol;
8275         VALUE trans, group;
8276         Transform tr;
8277         IntGroup *ig;
8278     Data_Get_Struct(self, Molecule, mol);
8279         rb_scan_args(argc, argv, "11", &trans, &group);
8280         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8281         TransformFromValue(trans, &tr);
8282 /*      MoleculeTransform(mol, tr, ig); */
8283         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8284         if (ig != NULL)
8285                 IntGroupRelease(ig);
8286         return self;
8287 }
8288
8289 static void
8290 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
8291 {
8292         switch (MoleculeCenterOfMass(mol, outv, ig)) {
8293                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
8294                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
8295                 case 0: break;
8296                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
8297         }
8298 }
8299
8300 /*
8301  *  call-seq:
8302  *     center_of_mass(group = nil)       -> Vector3D
8303  *
8304  *  Calculate the center of mass for the given set of atoms. The argument
8305  *  group is null, then all atoms are considered.
8306  */
8307 static VALUE
8308 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
8309 {
8310     Molecule *mol;
8311         VALUE group;
8312         IntGroup *ig;
8313         Vector v;
8314     Data_Get_Struct(self, Molecule, mol);
8315         rb_scan_args(argc, argv, "01", &group);
8316         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8317         s_Molecule_DoCenterOfMass(mol, &v, ig);
8318         if (ig != NULL)
8319                 IntGroupRelease(ig);
8320         return ValueFromVector(&v);
8321 }
8322
8323 /*
8324  *  call-seq:
8325  *     centralize(group = nil)       -> self
8326  *
8327  *  Translate the molecule so that the center of mass of the given group is located
8328  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
8329  */
8330 static VALUE
8331 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
8332 {
8333     Molecule *mol;
8334         VALUE group;
8335         IntGroup *ig;
8336         Vector v;
8337     Data_Get_Struct(self, Molecule, mol);
8338         rb_scan_args(argc, argv, "01", &group);
8339         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8340         s_Molecule_DoCenterOfMass(mol, &v, ig);
8341         if (ig != NULL)
8342                 IntGroupRelease(ig);
8343         v.x = -v.x;
8344         v.y = -v.y;
8345         v.z = -v.z;
8346         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
8347         return self;
8348 }
8349
8350 /*
8351  *  call-seq:
8352  *     bounds(group = nil)       -> [min, max]
8353  *
8354  *  Calculate the boundary. The return value is an array of two Vector3D objects.
8355  */
8356 static VALUE
8357 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
8358 {
8359     Molecule *mol;
8360         VALUE group;
8361         IntGroup *ig;
8362         Vector vmin, vmax;
8363         int n;
8364         Atom *ap;
8365     Data_Get_Struct(self, Molecule, mol);
8366         rb_scan_args(argc, argv, "01", &group);
8367         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8368         if (ig != NULL && IntGroupGetCount(ig) == 0)
8369                 rb_raise(rb_eMolbyError, "atom group is empty");
8370         vmin.x = vmin.y = vmin.z = 1e30;
8371         vmax.x = vmax.y = vmax.z = -1e30;
8372         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
8373                 Vector r;
8374                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
8375                         continue;
8376                 r = ap->r;
8377                 if (r.x < vmin.x)
8378                         vmin.x = r.x;
8379                 if (r.y < vmin.y)
8380                         vmin.y = r.y;
8381                 if (r.z < vmin.z)
8382                         vmin.z = r.z;
8383                 if (r.x > vmax.x)
8384                         vmax.x = r.x;
8385                 if (r.y > vmax.y)
8386                         vmax.y = r.y;
8387                 if (r.z > vmax.z)
8388                         vmax.z = r.z;
8389         }
8390         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
8391 }
8392
8393 /*  Get atom position or a vector  */
8394 static void
8395 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
8396 {
8397         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
8398                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
8399                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
8400         } else {
8401                 VectorFromValue(val, vp);
8402         }
8403 }
8404
8405 /*
8406  *  call-seq:
8407  *     measure_bond(n1, n2)       -> Float
8408  *
8409  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
8410  *  or Vector3D values.
8411  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8412  */
8413 static VALUE
8414 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
8415 {
8416     Molecule *mol;
8417         Vector v1, v2;
8418     Data_Get_Struct(self, Molecule, mol);
8419         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8420         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8421         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
8422 }
8423
8424 /*
8425  *  call-seq:
8426  *     measure_angle(n1, n2, n3)       -> Float
8427  *
8428  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
8429  *  or Vector3D values. The return value is in degree.
8430  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8431  */
8432 static VALUE
8433 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
8434 {
8435     Molecule *mol;
8436         Vector v1, v2, v3;
8437         Double d;
8438     Data_Get_Struct(self, Molecule, mol);
8439         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8440         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8441         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
8442         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
8443         if (isnan(d))
8444                 return Qnil;  /*  Cannot define  */
8445         else return rb_float_new(d);
8446 }
8447
8448 /*
8449  *  call-seq:
8450  *     measure_dihedral(n1, n2, n3, n4)       -> Float
8451  *
8452  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
8453  *  or Vector3D values. The return value is in degree.
8454  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8455  */
8456 static VALUE
8457 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
8458 {
8459     Molecule *mol;
8460         Vector v1, v2, v3, v4;
8461         Double d;
8462     Data_Get_Struct(self, Molecule, mol);
8463         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8464         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8465         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
8466         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
8467         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
8468         if (isnan(d))
8469                 return Qnil;  /*  Cannot define  */
8470         else return rb_float_new(d);
8471 }
8472
8473 /*
8474  *  call-seq:
8475  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
8476  *
8477  *  Expand the specified part of the molecule by the given symmetry operation.
8478  *  Returns the array of atom indices corresponding to the expanded atoms.
8479  *  If allow_overlap is true, then new atoms are created even when the
8480  *  coordinates coincide with the some other atom (special position) of the
8481  *  same element; otherwise, such atom will not be created and the index of the
8482  *  existing atom is given in the returned array.
8483  */
8484 static VALUE
8485 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
8486 {
8487     Molecule *mol;
8488         VALUE gval, sval, xval, yval, zval, rval, oval;
8489         IntGroup *ig;
8490         Int n[4], allow_overlap;
8491         Int natoms;
8492         Int nidx, *idx;
8493
8494     Data_Get_Struct(self, Molecule, mol);
8495         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
8496         n[0] = NUM2INT(rb_Integer(sval));
8497         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
8498         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
8499         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
8500         allow_overlap = (RTEST(oval) ? 1 : 0);
8501         ig = s_Molecule_AtomGroupFromValue(self, gval);
8502         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
8503                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
8504         natoms = mol->natoms;
8505         
8506         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
8507
8508         rval = rb_ary_new2(nidx);
8509         while (--nidx >= 0) {
8510                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
8511         }
8512 /*      if (natoms == mol->natoms)
8513                 rval = Qnil;
8514         else {
8515                 rval = IntGroup_Alloc(rb_cIntGroup);
8516                 Data_Get_Struct(rval, IntGroup, ig);
8517                 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
8518         } */
8519         return rval;
8520 }
8521
8522 /*
8523  *  call-seq:
8524  *     amend_by_symmetry(group = nil) -> IntGroup
8525  *
8526  *  Expand the specified part of the molecule by the given symmetry operation.
8527  *  Returns an IntGroup containing the added atoms.
8528  */
8529 static VALUE
8530 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
8531 {
8532     Molecule *mol;
8533         IntGroup *ig, *ig2;
8534         VALUE rval, gval;
8535     Data_Get_Struct(self, Molecule, mol);
8536         rb_scan_args(argc, argv, "01", &gval);
8537         if (gval != Qnil)
8538                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8539         else ig = NULL;
8540         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
8541         rval = ValueFromIntGroup(ig2);
8542         IntGroupRelease(ig2);
8543         return rval;
8544 }
8545
8546 /*
8547  *  call-seq:
8548  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
8549  *
8550  *  Get the transform corresponding to the symmetry operation. The symop can either be
8551  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
8552  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
8553  *  Otherwise, the returned transform is for fractional coordinates.
8554  *  Raises exception when no cell or no transform are defined.
8555  */
8556 static VALUE
8557 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
8558 {
8559     Molecule *mol;
8560         VALUE sval, fval;
8561         Symop symop;
8562         Transform tr;
8563     Data_Get_Struct(self, Molecule, mol);
8564         if (mol->cell == NULL)
8565                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8566         if (mol->nsyms == 0)
8567                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8568         rb_scan_args(argc, argv, "11", &sval, &fval);
8569         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
8570                 symop.sym = NUM2INT(rb_Integer(sval));
8571                 symop.dx = symop.dy = symop.dz = 0;
8572         } else {
8573                 sval = rb_ary_to_ary(sval);
8574                 if (RARRAY_LEN(sval) < 4)
8575                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
8576                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
8577                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
8578                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
8579                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
8580         }
8581         if (symop.sym >= mol->nsyms)
8582                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
8583         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
8584         return ValueFromTransform(&tr);
8585 }
8586         
8587 /*
8588  *  call-seq:
8589  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8590  *
8591  *  Get the symmetry operation corresponding to the given transform.
8592  *  If is_cartesian is true, the given transform is for cartesian coordinates.
8593  *  Otherwise, the given transform is for fractional coordinates.
8594  *  Raises exception when no cell or no transform are defined.
8595  */
8596 static VALUE
8597 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8598 {
8599     Molecule *mol;
8600         VALUE tval, fval;
8601         Symop symop;
8602         Transform tr;
8603         int n;
8604     Data_Get_Struct(self, Molecule, mol);
8605         if (mol->cell == NULL)
8606                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8607         if (mol->nsyms == 0)
8608                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8609         rb_scan_args(argc, argv, "11", &tval, &fval);
8610         TransformFromValue(tval, &tr);
8611         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8612         if (n == 0) {
8613                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8614         } else {
8615                 return Qnil;  /*  Not found  */
8616         }
8617 }
8618
8619 /*
8620  *  call-seq:
8621  *     wrap_unit_cell(group) -> Vector3D
8622  *
8623  *  Move the specified group so that the center of mass of the group is within the
8624  *  unit cell. The offset vector is returned. If no periodic box is defined, 
8625  *  exception is raised.
8626  */
8627 static VALUE
8628 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
8629 {
8630     Molecule *mol;
8631         IntGroup *ig;
8632         Vector v, cv, dv;
8633     Data_Get_Struct(self, Molecule, mol);
8634         if (mol->cell == NULL)
8635                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8636         ig = s_Molecule_AtomGroupFromValue(self, gval);
8637         s_Molecule_DoCenterOfMass(mol, &cv, ig);
8638         TransformVec(&v, mol->cell->rtr, &cv);
8639         if (mol->cell->flags[0])
8640                 v.x -= floor(v.x);
8641         if (mol->cell->flags[1])
8642                 v.y -= floor(v.y);
8643         if (mol->cell->flags[2])
8644                 v.z -= floor(v.z);
8645         TransformVec(&dv, mol->cell->tr, &v);
8646         VecDec(dv, cv);
8647         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
8648         IntGroupRelease(ig);
8649         return ValueFromVector(&dv);
8650 }
8651
8652 /*
8653  *  call-seq:
8654  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
8655  *
8656  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
8657  *  first and second atom in the pair should belong to group1 and group2, respectively.
8658  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
8659  */
8660 static VALUE
8661 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
8662 {
8663     Molecule *mol;
8664         VALUE limval, gval1, gval2, rval, igval;
8665         IntGroup *ig1, *ig2;
8666         IntGroupIterator iter1, iter2;
8667         Int npairs, *pairs;
8668         Int n[2], i;
8669         Double lim;
8670         Vector r1;
8671         Atom *ap1, *ap2;
8672         MDExclusion *exinfo;
8673         Int *exlist;
8674
8675     Data_Get_Struct(self, Molecule, mol);
8676         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
8677         lim = NUM2DBL(rb_Float(limval));
8678         if (lim <= 0.0)
8679                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
8680         if (gval1 != Qnil)
8681                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
8682         else
8683                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
8684         if (gval2 != Qnil)
8685                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
8686         else
8687                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
8688         
8689         if (!RTEST(igval)) {
8690                 /*  Use the exclusion table in MDArena  */
8691                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
8692                         VALUE mval = ValueFromMolecule(mol);
8693                         s_RebuildMDParameterIfNecessary(mval, Qnil);
8694                 }
8695                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
8696                 exlist = mol->arena->exlist;    
8697         } else {
8698                 exinfo = NULL;
8699                 exlist = NULL;
8700         }
8701         IntGroupIteratorInit(ig1, &iter1);
8702         IntGroupIteratorInit(ig2, &iter2);
8703         npairs = 0;
8704         pairs = NULL;
8705         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
8706                 Int exn1, exn2;
8707                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
8708                 r1 = ap1->r;
8709                 if (exinfo != NULL) {
8710                         exn1 = exinfo[n[0]].index1;
8711                         exn2 = exinfo[n[0] + 1].index1;
8712                 } else exn1 = exn2 = -1;
8713                 IntGroupIteratorReset(&iter2);
8714                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
8715                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
8716                         if (n[0] == n[1])
8717                                 continue;  /*  Same atom  */
8718                         if (exinfo != NULL) {
8719                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
8720                                 for (i = exn1; i < exn2; i++) {
8721                                         if (exlist[i] == n[1])
8722                                                 break;
8723                                 }
8724                                 if (i < exn2)
8725                                         continue;  /*  Should be excluded  */
8726                         }
8727                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
8728                                 /*  Is this pair already registered?  */
8729                                 Int *ip;
8730                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
8731                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
8732                                                 break;
8733                                 }
8734                                 if (i >= npairs) {
8735                                         /*  Not registered yet  */
8736                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
8737                                 }
8738                         }
8739                 }
8740         }
8741         IntGroupIteratorRelease(&iter2);
8742         IntGroupIteratorRelease(&iter1);
8743         IntGroupRelease(ig2);
8744         IntGroupRelease(ig1);
8745         rval = rb_ary_new2(npairs);
8746         if (pairs != NULL) {
8747                 for (i = 0; i < npairs; i++) {
8748                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
8749                 }
8750                 free(pairs);
8751         }
8752         return rval;
8753 }
8754
8755 /*  Calculate the transform that moves the current coordinates to the reference
8756  coordinates with least displacements.   */
8757 static Double
8758 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8759 {
8760         Atom *ap, *ap1;
8761         Int natoms, nn;
8762         Vector org1, org2;
8763         Int i, in, j, k;
8764         Double w, w1;
8765         Mat33 r, q, u;
8766         Double eigen_val[3];
8767         Vector eigen_vec[3];
8768         Vector s[3];
8769         IntGroupIterator iter;
8770
8771         natoms = mol->natoms;
8772         ap = mol->atoms;
8773         IntGroupIteratorInit(ig, &iter);
8774         
8775         /*  Calculate the weighted center  */
8776         VecZero(org1);
8777         VecZero(org2);
8778         w = 0.0;
8779         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8780                 ap1 = ATOM_AT_INDEX(ap, in);
8781                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8782                 VecScaleInc(org1, ap1->r, w1);
8783                 VecScaleInc(org2, ref[i], w1);
8784                 w += w1;
8785         }
8786         w = 1.0 / w;
8787         VecScaleSelf(org1, w);
8788         VecScaleSelf(org2, w);
8789
8790     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8791     /*  Matrix to diagonalize = R * tR    */
8792         memset(r, 0, sizeof(Mat33));
8793         memset(q, 0, sizeof(Mat33));
8794         memset(u, 0, sizeof(Mat33));
8795         nn = 0;
8796         IntGroupIteratorReset(&iter);
8797         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8798                 Vector v1, v2;
8799                 ap1 = ATOM_AT_INDEX(ap, in);
8800                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8801                 w1 *= w1;
8802                 VecSub(v1, ap1->r, org1);
8803                 VecSub(v2, ref[i], org2);
8804                 r[0] += w1 * v1.x * v2.x;
8805                 r[1] += w1 * v1.y * v2.x;
8806                 r[2] += w1 * v1.z * v2.x;
8807                 r[3] += w1 * v1.x * v2.y;
8808                 r[4] += w1 * v1.y * v2.y;
8809                 r[5] += w1 * v1.z * v2.y;
8810                 r[6] += w1 * v1.x * v2.z;
8811                 r[7] += w1 * v1.y * v2.z;
8812                 r[8] += w1 * v1.z * v2.z;
8813                 nn++;
8814         }
8815         for (i = 0; i < 9; i++)
8816                 r[i] /= (nn * nn);
8817         for (i = 0; i < 3; i++) {
8818                 for (j = 0; j < 3; j++) {
8819                         for (k = 0; k < 3; k++) {
8820                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8821                         }
8822                 }
8823         }
8824         
8825         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8826                 IntGroupIteratorRelease(&iter);
8827                 return -1.0;  /*  Cannot determine the eigenvector  */
8828         }
8829
8830     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8831     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8832         MatrixTranspose(r, r);
8833         for (i = 0; i < 3; i++) {
8834                 MatrixVec(&s[i], r, &eigen_vec[i]);
8835                 w1 = 1.0 / sqrt(eigen_val[i]);
8836                 VecScaleSelf(s[i], w1);
8837         }
8838         for (k = 0; k < 3; k++) {
8839                 u[0] += s[k].x * eigen_vec[k].x;
8840                 u[1] += s[k].y * eigen_vec[k].x;
8841                 u[2] += s[k].z * eigen_vec[k].x;
8842                 u[3] += s[k].x * eigen_vec[k].y;
8843                 u[4] += s[k].y * eigen_vec[k].y;
8844                 u[5] += s[k].z * eigen_vec[k].y;
8845                 u[6] += s[k].x * eigen_vec[k].z;
8846                 u[7] += s[k].y * eigen_vec[k].z;
8847                 u[8] += s[k].z * eigen_vec[k].z;
8848         }
8849         
8850         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8851         MatrixVec(&org1, u, &org1);
8852         VecDec(org2, org1);
8853         for (i = 0; i < 9; i++)
8854                 trans[i] = u[i];
8855         trans[9] = org2.x;
8856         trans[10] = org2.y;
8857         trans[11] = org2.z;
8858         
8859         /*  Calculate rmsd  */
8860         IntGroupIteratorReset(&iter);
8861         w = 0.0;
8862         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8863                 Vector tv;
8864                 ap1 = ATOM_AT_INDEX(ap, in);
8865                 TransformVec(&tv, trans, &ap1->r);
8866                 VecDec(tv, ref[i]);
8867                 w += VecLength2(tv);
8868         }
8869         w = sqrt(w / nn);
8870         IntGroupIteratorRelease(&iter);
8871         return w;
8872 }
8873
8874 /*
8875  *  call-seq:
8876  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8877  *
8878  *  Calculate the transform to fit the given group to the set of reference coordinates.
8879  *  The reference coordinates ref is given as either a frame number, an array of
8880  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8881  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8882  *  Return values are the transform (that converts the present coordinates to the
8883  *  target coordinates) and root mean square deviation (without weight).
8884  */
8885 static VALUE
8886 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8887 {
8888         Molecule *mol;
8889         Atom *ap;
8890         VALUE gval, rval, wval;
8891         IntGroup *ig;
8892         IntGroupIterator iter;
8893         int nn, errno, i, j, in, status;
8894         Vector *ref;
8895         Double *weights, dval[3];
8896         Transform tr;
8897
8898         Data_Get_Struct(self, Molecule, mol);
8899         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8900         if (gval == Qnil)
8901                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8902         else
8903                 ig = IntGroupFromValue(gval);
8904         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8905                 IntGroupRelease(ig);
8906                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8907         }
8908         ref = (Vector *)calloc(sizeof(Vector), nn);
8909         weights = (Double *)calloc(sizeof(Double), nn);
8910         IntGroupIteratorInit(ig, &iter);
8911         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8912                 int fn = NUM2INT(rb_Integer(rval));
8913                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8914                         errno = 1;
8915                         status = fn;
8916                         goto err;
8917                 }
8918                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8919                         ap = ATOM_AT_INDEX(mol->atoms, in);
8920                         if (fn < ap->nframes)
8921                                 ref[i] = ap->frames[fn];
8922                         else ref[i] = ap->r;
8923                 }
8924         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8925                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8926                 if (m->row * m->column < nn * 3) {
8927                         errno = 2;
8928                         goto err;
8929                 }
8930                 for (i = 0; i < nn; i++) {
8931                         ref[i].x = m->data[i * 3];
8932                         ref[i].y = m->data[i * 3 + 1];
8933                         ref[i].z = m->data[i * 3 + 2];
8934                 }
8935         } else {
8936                 VALUE aval;
8937                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8938                 if (status != 0) {
8939                         errno = 3;
8940                         goto err;
8941                 }
8942                 if (RARRAY_LEN(rval) < nn) {
8943                         errno = 2;
8944                         goto err;
8945                 }
8946                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8947                         /*  Array of 3*nn numbers  */
8948                         if (RARRAY_LEN(rval) < nn * 3) {
8949                                 errno = 2;
8950                                 goto err;
8951                         }
8952                         for (i = 0; i < nn; i++) {
8953                                 for (j = 0; j < 3; j++) {
8954                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8955                                         if (status != 0) {
8956                                                 errno = 3;
8957                                                 goto err;
8958                                         }
8959                                         dval[j] = NUM2DBL(aval);
8960                                 }
8961                                 ref[i].x = dval[0];
8962                                 ref[i].y = dval[1];
8963                                 ref[i].z = dval[2];
8964                         }
8965                 } else {
8966                         /*  Array of nn Vector3Ds or Arrays  */
8967                         for (i = 0; i < nn; i++) {
8968                                 aval = (RARRAY_PTR(rval))[i];
8969                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8970                                         VectorFromValue(aval, &ref[i]);
8971                                 } else {
8972                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8973                                         if (status != 0) {
8974                                                 errno = 3;
8975                                                 goto err;
8976                                         }
8977                                         if (RARRAY_LEN(aval) < 3) {
8978                                                 errno = 4;
8979                                                 status = i;
8980                                                 goto err;
8981                                         }
8982                                         for (j = 0; j < 3; j++) {
8983                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8984                                                 if (status != 0) {
8985                                                         errno = 3;
8986                                                         goto err;
8987                                                 }
8988                                                 dval[j] = NUM2DBL(aaval);
8989                                         }
8990                                         ref[i].x = dval[0];
8991                                         ref[i].y = dval[1];
8992                                         ref[i].z = dval[2];
8993                                 }
8994                         }
8995                 }
8996         }
8997         if (wval == Qnil) {
8998                 /*  Use atomic weights  */
8999                 IntGroupIteratorReset(&iter);
9000                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
9001                         ap = ATOM_AT_INDEX(mol->atoms, in);
9002                         weights[i] = ap->weight;
9003                 }
9004         } else {
9005                 wval = rb_protect(rb_ary_to_ary, wval, &status);
9006                 if (status != 0) {
9007                         errno = 3;
9008                         goto err;
9009                 }
9010                 if (RARRAY_LEN(wval) < nn) {
9011                         errno = 5;
9012                         goto err;
9013                 }
9014                 for (i = 0; i < nn; i++) {
9015                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
9016                         if (status != 0) {
9017                                 errno = 3;
9018                                 goto err;
9019                         }
9020                         weights[i] = NUM2DBL(wwval);
9021                 }
9022         }
9023         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
9024         if (dval[0] < 0) {
9025                 errno = 6;
9026                 goto err;
9027         }
9028         errno = 0;
9029 err:
9030         IntGroupIteratorRelease(&iter);
9031         free(ref);
9032         free(weights);
9033         if (errno == 0) {
9034                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
9035         } else if (errno == 1) {
9036                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
9037         } else if (errno == 2) {
9038                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
9039         } else if (errno == 3) {
9040                 rb_jump_tag(status);
9041         } else if (errno == 4) {
9042                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
9043         } else if (errno == 5) {
9044                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
9045         } else if (errno == 6) {
9046                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
9047         }
9048         return Qnil;  /*  Not reached  */
9049 }
9050
9051 /*
9052  *  call-seq:
9053  *     display
9054  *
9055  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
9056  */
9057 static VALUE
9058 s_Molecule_Display(VALUE self)
9059 {
9060     Molecule *mol;
9061     Data_Get_Struct(self, Molecule, mol);
9062         if (mol->mview != NULL)
9063                 MainViewCallback_display(mol->mview);
9064         return Qnil;
9065 }
9066
9067 /*
9068  *  call-seq:
9069  *     make_front
9070  *
9071  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
9072  */
9073 static VALUE
9074 s_Molecule_MakeFront(VALUE self)
9075 {
9076     Molecule *mol;
9077     Data_Get_Struct(self, Molecule, mol);
9078         if (mol->mview != NULL)
9079                 MainViewCallback_makeFront(mol->mview);
9080         return Qnil;
9081 }
9082
9083 /*
9084  *  call-seq:
9085  *     update_enabled? -> bool
9086  *
9087  *  Returns true if screen update is enabled; otherwise no.
9088  */
9089 static VALUE
9090 s_Molecule_UpdateEnabled(VALUE self)
9091 {
9092     Molecule *mol;
9093     Data_Get_Struct(self, Molecule, mol);
9094         if (mol->mview != NULL && !mol->mview->freezeScreen)
9095                 return Qtrue;
9096         else return Qfalse;
9097 }
9098
9099 /*
9100  *  call-seq:
9101  *     update_enabled = bool
9102  *
9103  *  Enable or disable screen update. This is effective for automatic update on modification.
9104  *  Explicit call to molecule.display() always updates the screen.
9105  */
9106 static VALUE
9107 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
9108 {
9109     Molecule *mol;
9110     Data_Get_Struct(self, Molecule, mol);
9111         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
9112         if (mol->mview != NULL)
9113                 mol->mview->freezeScreen = (val == Qfalse);
9114         else val = Qfalse;
9115         return val;
9116 }
9117
9118 /*
9119  *  call-seq:
9120  *     show_unitcell
9121  *     show_unitcell(bool)
9122  *     show_unitcell = bool
9123  *
9124  *  Set the flag whether to show the unit cell. If no argument is given, the
9125  *  current flag is returned.
9126  */
9127 static VALUE
9128 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
9129 {
9130     Molecule *mol;
9131     Data_Get_Struct(self, Molecule, mol);
9132         if (mol->mview == NULL)
9133                 return Qnil;
9134         if (argc > 0) {
9135                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
9136                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9137         }
9138         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
9139 }
9140
9141 /*
9142  *  call-seq:
9143  *     show_hydrogens
9144  *     show_hydrogens(bool)
9145  *     show_hydrogens = bool
9146  *
9147  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
9148  *  current flag is returned.
9149  */
9150 static VALUE
9151 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
9152 {
9153     Molecule *mol;
9154     Data_Get_Struct(self, Molecule, mol);
9155         if (mol->mview == NULL)
9156                 return Qnil;
9157         if (argc > 0) {
9158                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
9159                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9160         }
9161         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
9162 }
9163
9164 /*
9165  *  call-seq:
9166  *     show_dummy_atoms
9167  *     show_dummy_atoms(bool)
9168  *     show_dummy_atoms = bool
9169  *
9170  *  Set the flag whether to show the dummy atoms. If no argument is given, the
9171  *  current flag is returned.
9172  */
9173 static VALUE
9174 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
9175 {
9176     Molecule *mol;
9177     Data_Get_Struct(self, Molecule, mol);
9178         if (mol->mview == NULL)
9179                 return Qnil;
9180         if (argc > 0) {
9181                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9182                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9183         }
9184         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9185 }
9186
9187 /*
9188  *  call-seq:
9189  *     show_expanded
9190  *     show_expanded(bool)
9191  *     show_expanded = bool
9192  *
9193  *  Set the flag whether to show the expanded atoms. If no argument is given, the
9194  *  current flag is returned.
9195  */
9196 static VALUE
9197 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9198 {
9199     Molecule *mol;
9200     Data_Get_Struct(self, Molecule, mol);
9201         if (mol->mview == NULL)
9202                 return Qnil;
9203         if (argc > 0) {
9204                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9205                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9206         }
9207         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9208 }
9209
9210 /*
9211  *  call-seq:
9212  *     show_ellipsoids
9213  *     show_ellipsoids(bool)
9214  *     show_ellipsoids = bool
9215  *
9216  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9217  *  current flag is returned.
9218  */
9219 static VALUE
9220 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9221 {
9222     Molecule *mol;
9223     Data_Get_Struct(self, Molecule, mol);
9224         if (mol->mview == NULL)
9225                 return Qnil;
9226         if (argc > 0) {
9227                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9228                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9229         }
9230         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9231 }
9232
9233 /*
9234  *  call-seq:
9235  *     is_atom_visible(index)  -> Boolean
9236  *
9237  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9238  *  as well as the molecule attributes (showHydrogens, etc.)
9239  */
9240 static VALUE
9241 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9242 {
9243         Molecule *mol;
9244         Int idx;
9245         Atom *ap;
9246     Data_Get_Struct(self, Molecule, mol);
9247         idx = s_Molecule_AtomIndexFromValue(mol, ival);
9248         if (idx < 0 || idx >= mol->natoms)
9249                 return Qnil;
9250         ap = ATOM_AT_INDEX(mol->atoms, idx);
9251         if (mol->mview != NULL) {
9252                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9253                         return Qfalse;
9254                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9255                         return Qfalse;
9256                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9257                         return Qfalse;
9258         }
9259         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9260 }
9261
9262 /*
9263  *  call-seq:
9264  *     show_graphite -> Integer
9265  *     show_graphite = Integer
9266  *     show_graphite = boolean
9267  *
9268  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9269  *  number of rings to display for each direction.
9270  *  If the argument is boolean, only the show/hide flag is set.
9271  */
9272 static VALUE
9273 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9274 {
9275     Molecule *mol;
9276     Data_Get_Struct(self, Molecule, mol);
9277         if (mol->mview == NULL)
9278                 return Qnil;
9279         if (argc > 0) {
9280                 if (argv[0] == Qnil || argv[0] == Qfalse)
9281                         mol->mview->showGraphiteFlag = 0;
9282                 else if (argv[0] == Qtrue)
9283                         mol->mview->showGraphiteFlag = 1;
9284                 else {
9285                         int n = NUM2INT(rb_Integer(argv[0]));
9286                         if (n < 0)
9287                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9288                         mol->mview->showGraphite = n;
9289                 }
9290                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9291         }
9292         return INT2NUM(mol->mview->showGraphite);
9293 }
9294
9295 /*
9296  *  call-seq:
9297  *     show_graphite? -> boolean
9298  *
9299  *  Return whether the graphite is set visible or not.
9300 */
9301 static VALUE
9302 s_Molecule_ShowGraphiteFlag(VALUE self)
9303 {
9304     Molecule *mol;
9305     Data_Get_Struct(self, Molecule, mol);
9306         if (mol->mview == NULL)
9307                 return Qnil;
9308         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9309 }
9310         
9311 /*
9312  *  call-seq:
9313  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9314  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9315  *     show_periodic_image = boolean
9316  *
9317  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9318  *  set but no visual effects are observed.
9319  *  If the argument is boolean, only the show/hide flag is modified.
9320  */
9321 static VALUE
9322 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9323 {
9324     Molecule *mol;
9325         VALUE val;
9326         int ival[6];
9327         int i;
9328     Data_Get_Struct(self, Molecule, mol);
9329         if (mol->mview == NULL)
9330                 return Qnil;
9331         rb_scan_args(argc, argv, "01", &val);
9332         if (argc > 0) {
9333                 /*  Change current settings  */
9334                 if (val == Qnil || val == Qfalse)
9335                         mol->mview->showPeriodicImageFlag = 0;
9336                 else if (val == Qtrue)
9337                         mol->mview->showPeriodicImageFlag = 1;
9338                 else {
9339                         val = rb_ary_to_ary(val);
9340                         for (i = 0; i < 6; i++) {
9341                                 if (i < RARRAY_LEN(val))
9342                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9343                         }
9344                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9345                                 rb_raise(rb_eMolbyError, "bad arguments");
9346                         for (i = 0; i < 6; i++)
9347                                 mol->mview->showPeriodicImage[i] = ival[i];
9348                 }
9349                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9350         }
9351         val = rb_ary_new();
9352         for (i = 0; i < 6; i++)
9353                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9354         return val;
9355 }
9356
9357 /*
9358  *  call-seq:
9359  *     show_periodic_image? -> boolean
9360  *
9361  *  Return whether the periodic images are set to visible or not. This flag is
9362  *  independent from the show_periodic_image settings.
9363  */
9364 static VALUE
9365 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9366 {
9367     Molecule *mol;
9368     Data_Get_Struct(self, Molecule, mol);
9369         if (mol->mview == NULL)
9370                 return Qnil;
9371         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9372 }
9373
9374 /*
9375  *  call-seq:
9376  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9377  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9378  *     show_rotation_center = boolean
9379  *
9380  *  Set to show the rotation center of the screen.
9381  *  If the argument is boolean, only the show/hide flag is modified.
9382  */
9383 static VALUE
9384 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9385 {
9386     Molecule *mol;
9387     Data_Get_Struct(self, Molecule, mol);
9388         if (mol->mview == NULL)
9389                 return Qnil;
9390         if (argc > 0) {
9391                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9392                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9393         }
9394         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9395 }
9396
9397 /*
9398  *  call-seq:
9399  *     line_mode
9400  *     line_mode(bool)
9401  *     line_mode = bool
9402  *
9403  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9404  *  current flag is returned.
9405  */
9406 static VALUE
9407 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9408 {
9409     Molecule *mol;
9410     Data_Get_Struct(self, Molecule, mol);
9411         if (mol->mview == NULL)
9412                 return Qnil;
9413         if (argc > 0) {
9414                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9415                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9416         }
9417         return (mol->mview->lineMode ? Qtrue : Qfalse);
9418 }
9419
9420 /*
9421  *  call-seq:
9422  *     atom_radius = float
9423  *     atom_radius
9424  *
9425  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9426  *  (Default = 0.4)
9427  *  If no argument is given, the current value is returned.
9428  */
9429 static VALUE
9430 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9431 {
9432     Molecule *mol;
9433     Data_Get_Struct(self, Molecule, mol);
9434         if (mol->mview == NULL)
9435                 return Qnil;
9436         if (argc > 0) {
9437                 double rad = NUM2DBL(rb_Float(argv[0]));
9438                 if (rad > 0.0) {
9439                         mol->mview->atomRadius = rad;
9440                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9441                 }
9442                 return argv[0];
9443         }
9444         return rb_float_new(mol->mview->atomRadius);
9445 }
9446
9447 /*
9448  *  call-seq:
9449  *     bond_radius = float
9450  *     bond_radius
9451  *
9452  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9453  *  If no argument is given, the current value is returned.
9454  */
9455 static VALUE
9456 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9457 {
9458     Molecule *mol;
9459     Data_Get_Struct(self, Molecule, mol);
9460         if (mol->mview == NULL)
9461                 return Qnil;
9462         if (argc > 0) {
9463                 double rad = NUM2DBL(rb_Float(argv[0]));
9464                 if (rad > 0.0) {
9465                         mol->mview->bondRadius = rad;
9466                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9467                 }
9468                 return argv[0];
9469         }
9470         return rb_float_new(mol->mview->bondRadius);
9471 }
9472
9473 /*
9474  *  call-seq:
9475  *     atom_resolution = integer
9476  *     atom_resolution
9477  *
9478  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9479  *  (Default = 12; minimum = 6)
9480  *  If no argument is given, the current value is returned.
9481  */
9482 static VALUE
9483 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9484 {
9485     Molecule *mol;
9486     Data_Get_Struct(self, Molecule, mol);
9487         if (mol->mview == NULL)
9488                 return Qnil;
9489         if (argc > 0) {
9490                 int res = NUM2INT(rb_Integer(argv[0]));
9491                 if (res < 6)
9492                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9493                 mol->mview->atomResolution = res;
9494                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9495                 return INT2NUM(res);
9496         }
9497         return INT2NUM(mol->mview->atomResolution);
9498 }
9499
9500 /*
9501  *  call-seq:
9502  *     bond_resolution = integer
9503  *     bond_resolution
9504  *
9505  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9506  *  (Default = 8; minimum = 4)
9507  *  If no argument is given, the current value is returned.
9508  */
9509 static VALUE
9510 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9511 {
9512     Molecule *mol;
9513     Data_Get_Struct(self, Molecule, mol);
9514         if (mol->mview == NULL)
9515                 return Qnil;
9516         if (argc > 0) {
9517                 int res = NUM2INT(rb_Integer(argv[0]));
9518                 if (res < 4)
9519                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9520                 mol->mview->bondResolution = res;
9521                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9522                 return INT2NUM(res);
9523         }
9524         return INT2NUM(mol->mview->bondResolution);
9525 }
9526
9527 /*
9528  *  call-seq:
9529  *     resize_to_fit
9530  *
9531  *  Resize the model drawing to fit in the window.
9532  */
9533 static VALUE
9534 s_Molecule_ResizeToFit(VALUE self)
9535 {
9536     Molecule *mol;
9537     Data_Get_Struct(self, Molecule, mol);
9538         if (mol->mview != NULL)
9539                 MainView_resizeToFit(mol->mview);
9540         return self;    
9541 }
9542
9543 /*
9544  *  call-seq:
9545  *     get_view_rotation -> [[ax, ay, az], angle]
9546  *
9547  *  Get the current rotation for the view. Angle is in degree, not radian.
9548  */
9549 static VALUE
9550 s_Molecule_GetViewRotation(VALUE self)
9551 {
9552     Molecule *mol;
9553         double f[4];
9554         Vector v;
9555     Data_Get_Struct(self, Molecule, mol);
9556         if (mol->mview == NULL)
9557                 return Qnil;
9558         TrackballGetRotate(mol->mview->track, f);
9559         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9560         v.x = f[1];
9561         v.y = f[2];
9562         v.z = f[3];
9563         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9564 }
9565
9566 /*
9567  *  call-seq:
9568  *     get_view_scale -> float
9569  *
9570  *  Get the current scale for the view.
9571  */
9572 static VALUE
9573 s_Molecule_GetViewScale(VALUE self)
9574 {
9575     Molecule *mol;
9576     Data_Get_Struct(self, Molecule, mol);
9577         if (mol->mview == NULL)
9578                 return Qnil;
9579         return rb_float_new(TrackballGetScale(mol->mview->track));
9580 }
9581
9582 /*
9583  *  call-seq:
9584  *     get_view_center -> Vector
9585  *
9586  *  Get the current center point of the view.
9587  */
9588 static VALUE
9589 s_Molecule_GetViewCenter(VALUE self)
9590 {
9591     Molecule *mol;
9592         double f[4];
9593         Vector v;
9594     Data_Get_Struct(self, Molecule, mol);
9595         if (mol->mview == NULL)
9596                 return Qnil;
9597         TrackballGetTranslate(mol->mview->track, f);
9598         v.x = -f[0] * mol->mview->dimension;
9599         v.y = -f[1] * mol->mview->dimension;
9600         v.z = -f[2] * mol->mview->dimension;
9601         return ValueFromVector(&v);
9602 }
9603
9604 /*
9605  *  call-seq:
9606  *     set_view_rotation([ax, ay, az], angle) -> self
9607  *
9608  *  Set the current rotation for the view. Angle is in degree, not radian.
9609  */
9610 static VALUE
9611 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9612 {
9613     Molecule *mol;
9614         double f[4];
9615         Vector v;
9616     Data_Get_Struct(self, Molecule, mol);
9617         if (mol->mview == NULL)
9618                 return Qnil;
9619         VectorFromValue(aval, &v);
9620         if (NormalizeVec(&v, &v))
9621                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9622         f[1] = v.x;
9623         f[2] = v.y;
9624         f[3] = v.z;
9625         f[0] = -NUM2DBL(rb_Float(angval));
9626         TrackballSetRotate(mol->mview->track, f);
9627         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9628         return self;
9629 }
9630
9631 /*
9632  *  call-seq:
9633  *     set_view_scale(scale) -> self
9634  *
9635  *  Set the current scale for the view.
9636  */
9637 static VALUE
9638 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9639 {
9640     Molecule *mol;
9641     Data_Get_Struct(self, Molecule, mol);
9642         if (mol->mview == NULL)
9643                 return Qnil;
9644         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9645         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9646         return self;
9647 }
9648
9649 /*
9650  *  call-seq:
9651  *     set_view_center(vec) -> self
9652  *
9653  *  Set the current center point of the view.
9654  */
9655 static VALUE
9656 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9657 {
9658     Molecule *mol;
9659         Vector v;
9660         double f[4];
9661     Data_Get_Struct(self, Molecule, mol);
9662         if (mol->mview == NULL)
9663                 return Qnil;
9664         VectorFromValue(aval, &v);
9665         f[0] = -v.x / mol->mview->dimension;
9666         f[1] = -v.y / mol->mview->dimension;
9667         f[2] = -v.z / mol->mview->dimension;
9668         TrackballSetTranslate(mol->mview->track, f);
9669         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9670         return self;
9671 }
9672
9673 /*
9674  *  call-seq:
9675  *     set_background_color(red, green, blue)
9676  *
9677  *  Set the background color of the model window.
9678  */
9679 static VALUE
9680 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9681 {
9682     Molecule *mol;
9683     Data_Get_Struct(self, Molecule, mol);
9684         if (mol->mview != NULL) {
9685                 VALUE rval, gval, bval;
9686                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9687                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9688         }
9689         return self;    
9690 }
9691
9692 /*
9693  *  call-seq:
9694  *     create_graphic(kind, color, points, fill = nil) -> integer
9695  *
9696  *  Create a new graphic object.
9697  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9698  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9699  *   points: an array of Vectors
9700  *   
9701  */
9702 static VALUE
9703 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9704 {
9705     Molecule *mol;
9706         MainViewGraphic g;
9707         int i, n, ni;
9708         const char *p;
9709         VALUE kval, cval, pval, fval;
9710     Data_Get_Struct(self, Molecule, mol);
9711         if (mol->mview == NULL)
9712                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9713         rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
9714         kval = rb_obj_as_string(kval);
9715         memset(&g, 0, sizeof(g));
9716         g.visible = 1;
9717         p = RSTRING_PTR(kval);
9718         if (strcmp(p, "line") == 0)
9719                 g.kind = kMainViewGraphicLine;
9720         else if (strcmp(p, "poly") == 0)
9721                 g.kind = kMainViewGraphicPoly;
9722         else if (strcmp(p, "cylinder") == 0)
9723                 g.kind = kMainViewGraphicCylinder;
9724         else if (strcmp(p, "cone") == 0)
9725                 g.kind = kMainViewGraphicCone;
9726         else if (strcmp(p, "ellipsoid") == 0)
9727                 g.kind = kMainViewGraphicEllipsoid;
9728         else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9729         g.closed = (RTEST(fval) ? 1 : 0);
9730         cval = rb_ary_to_ary(cval);
9731         n = RARRAY_LEN(cval);
9732         if (n < 3 || n >= 5)
9733                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9734         if (n == 3)
9735                 g.rgba[3] = 1.0;
9736         for (i = 0; i < n; i++)
9737                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9738         pval = rb_ary_to_ary(pval);
9739         n = RARRAY_LEN(pval);
9740         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9741         if (n <= 0)
9742                 rb_raise(rb_eArgError, "no control points are given");
9743         switch (g.kind) {
9744                 case kMainViewGraphicLine:
9745                         if (n < 2)
9746                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9747                         break;
9748                 case kMainViewGraphicPoly:
9749                         if (n < 3)
9750                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9751                         break;
9752                 case kMainViewGraphicCylinder:
9753                 case kMainViewGraphicCone:
9754                         if (n != 3)
9755                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9756                         ni = 2;
9757                         break;
9758                 case kMainViewGraphicEllipsoid:
9759                         if (n == 2) {
9760                                 ni = 1;
9761                         } else if (n != 4)
9762                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9763                         break;
9764         }
9765         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9766         for (i = 0; i < n; i++) {
9767                 Vector v;
9768                 if (i == ni) {
9769                         v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
9770                         v.y = v.z = 0;
9771                 } else {
9772                         VectorFromValue(RARRAY_PTR(pval)[i], &v);
9773                 }
9774                 g.points[i * 3] = v.x;
9775                 g.points[i * 3 + 1] = v.y;
9776                 g.points[i * 3 + 2] = v.z;
9777         }
9778         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9779                 /*  Sphere  */
9780                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9781                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9782                 g.points[7] = g.points[11] = g.points[3];
9783         }
9784         MainView_insertGraphic(mol->mview, -1, &g);
9785         return INT2NUM(mol->mview->ngraphics - 1);      
9786 }
9787
9788 /*
9789  *  call-seq:
9790  *     remove_graphic(index) -> integer
9791  *
9792  *  Remove a graphic object.
9793  */
9794 static VALUE
9795 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9796 {
9797     Molecule *mol;
9798         int i;
9799     Data_Get_Struct(self, Molecule, mol);
9800         if (mol->mview == NULL)
9801                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9802         i = NUM2INT(rb_Integer(ival));
9803         if (i < 0 || i >= mol->mview->ngraphics)
9804                 rb_raise(rb_eArgError, "graphic index is out of range");
9805         MainView_removeGraphic(mol->mview, i);
9806         return ival;
9807 }
9808
9809 /*
9810  *  call-seq:
9811  *     ngraphics -> integer
9812  *
9813  *  Get the number of graphic objects.
9814  */
9815 static VALUE
9816 s_Molecule_NGraphics(VALUE self)
9817 {
9818     Molecule *mol;
9819     Data_Get_Struct(self, Molecule, mol);
9820         if (mol->mview == NULL)
9821                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9822         return INT2NUM(mol->mview->ngraphics);
9823 }
9824         
9825 /*
9826  *  call-seq:
9827  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9828  *
9829  *  Change the point_index-th control point of graphic_index-th graphic object
9830  *   
9831  */
9832 static VALUE
9833 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
9834 {
9835         MainViewGraphic *gp;
9836     Molecule *mol;
9837         int index;
9838         Vector v;
9839     Data_Get_Struct(self, Molecule, mol);
9840         if (mol->mview == NULL)
9841                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9842         index = NUM2INT(rb_Integer(gval));
9843         if (index < 0 || index >= mol->mview->ngraphics)
9844                 rb_raise(rb_eArgError, "the graphic index is out of range");
9845         gp = mol->mview->graphics + index;
9846         index = NUM2INT(rb_Integer(pval));
9847         if (index < 0 || index >= gp->npoints)
9848                 rb_raise(rb_eArgError, "the point index is out of range");
9849         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9850                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
9851                         v.x = NUM2DBL(rb_Float(nval));
9852                         v.y = v.z = 0;
9853                 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
9854                         gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
9855                         gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9856                         return nval;
9857                 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9858         } else {
9859                 if (nval == Qnil) {
9860                         v.x = kInvalidFloat;
9861                         v.y = v.z = 0.0;
9862                 } else VectorFromValue(nval, &v);
9863         }
9864         gp->points[index * 3] = v.x;
9865         gp->points[index * 3 + 1] = v.y;
9866         gp->points[index * 3 + 2] = v.z;
9867         MoleculeCallback_notifyModification(mol, 0);
9868         return nval;
9869 }
9870
9871 /*
9872  *  call-seq:
9873  *     set_graphic_color(graphic_index, new_value) -> new_value
9874  *
9875  *  Change the color of graphic_index-th graphic object
9876  *   
9877  */
9878 static VALUE
9879 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9880 {
9881         MainViewGraphic *gp;
9882     Molecule *mol;
9883         int index, n;
9884     Data_Get_Struct(self, Molecule, mol);
9885         if (mol->mview == NULL)
9886                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9887         index = NUM2INT(rb_Integer(gval));
9888         if (index < 0 || index >= mol->mview->ngraphics)
9889                 rb_raise(rb_eArgError, "the graphic index is out of range");
9890         gp = mol->mview->graphics + index;
9891         cval = rb_ary_to_ary(cval);
9892         n = RARRAY_LEN(cval);
9893         if (n != 3 && n != 4)
9894                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9895         for (index = 0; index < n; index++) {
9896                 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
9897         }
9898         if (n == 3)
9899                 gp->rgba[3] = 1.0;
9900         MoleculeCallback_notifyModification(mol, 0);
9901         return cval;
9902 }
9903
9904 /*
9905  *  call-seq:
9906  *     show_graphic(graphic_index) -> self
9907  *
9908  *  Enable the visible flag of the graphic_index-th graphic object
9909  *   
9910  */
9911 static VALUE
9912 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9913 {
9914         MainViewGraphic *gp;
9915     Molecule *mol;
9916         int index;
9917     Data_Get_Struct(self, Molecule, mol);
9918         if (mol->mview == NULL)
9919                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9920         index = NUM2INT(rb_Integer(gval));
9921         if (index < 0 || index >= mol->mview->ngraphics)
9922                 rb_raise(rb_eArgError, "the graphic index is out of range");
9923         gp = mol->mview->graphics + index;
9924         gp->visible = 1;
9925         MoleculeCallback_notifyModification(mol, 0);
9926         return self;
9927 }
9928
9929 /*
9930  *  call-seq:
9931  *     hide_graphic(graphic_index) -> self
9932  *
9933  *  Disable the visible flag of the graphic_index-th graphic object
9934  *   
9935  */
9936 static VALUE
9937 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9938 {
9939         MainViewGraphic *gp;
9940     Molecule *mol;
9941         int index;
9942     Data_Get_Struct(self, Molecule, mol);
9943         if (mol->mview == NULL)
9944                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9945         index = NUM2INT(rb_Integer(gval));
9946         if (index < 0 || index >= mol->mview->ngraphics)
9947                 rb_raise(rb_eArgError, "the graphic index is out of range");
9948         gp = mol->mview->graphics + index;
9949         gp->visible = 0;
9950         MoleculeCallback_notifyModification(mol, 0);
9951         return self;
9952 }
9953
9954 /*
9955  *  call-seq:
9956  *     show_text(string)
9957  *
9958  *  Show the string in the info text box.
9959  */
9960 static VALUE
9961 s_Molecule_ShowText(VALUE self, VALUE arg)
9962 {
9963     Molecule *mol;
9964     Data_Get_Struct(self, Molecule, mol);
9965         if (mol->mview != NULL)
9966                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9967         return Qnil;
9968 }
9969
9970 /*
9971  *  call-seq:
9972  *     md_arena -> MDArena
9973  *
9974  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
9975  *  this molecule, a new arena is created.
9976  */
9977 static VALUE
9978 s_Molecule_MDArena(VALUE self)
9979 {
9980     Molecule *mol;
9981         VALUE retval;
9982     Data_Get_Struct(self, Molecule, mol);
9983         if (mol->arena == NULL)
9984                 md_arena_new(mol);
9985         retval = ValueFromMDArena(mol->arena);
9986         return retval;
9987 }
9988
9989 /*
9990  *  call-seq:
9991  *     set_parameter_attr(type, index, key, value, src) -> value
9992  *
9993  *  This method is used only internally.
9994  */
9995 static VALUE
9996 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9997 {
9998         /*  This method is called from MolAction to change a MM parameter attribute.  */
9999     Molecule *mol;
10000         VALUE pval;
10001         ParameterRef *pref;
10002         UnionPar *up;
10003     Data_Get_Struct(self, Molecule, mol);
10004         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10005         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10006         
10007         /*  This is the special part of this method; it allows modification of the src field. */
10008         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10009         Data_Get_Struct(pval, ParameterRef, pref);
10010         up = ParameterRefGetPar(pref);
10011         up->bond.src = FIX2INT(sval);
10012         
10013         return vval;
10014 }
10015
10016 /*
10017  *  call-seq:
10018  *     parameter -> Parameter
10019  *
10020  *  Get the local parameter of this molecule. If not defined, returns nil.
10021  */
10022 static VALUE
10023 s_Molecule_Parameter(VALUE self)
10024 {
10025     Molecule *mol;
10026     Data_Get_Struct(self, Molecule, mol);
10027 /*      if (mol->par == NULL)
10028                 return Qnil; */
10029         return s_NewParameterValueFromValue(self);
10030 }
10031
10032 /*
10033  *  call-seq:
10034  *     selectedMO -> IntGroup
10035  *
10036  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10037  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10038  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10039  */
10040 static VALUE
10041 s_Molecule_SelectedMO(VALUE self)
10042 {
10043     Molecule *mol;
10044         IntGroup *ig;
10045         VALUE val;
10046     Data_Get_Struct(self, Molecule, mol);
10047         if (mol->mview == NULL)
10048                 return Qnil;
10049         ig = MainView_selectedMO(mol->mview);
10050         if (ig == NULL)
10051                 return Qnil;
10052         IntGroupOffset(ig, 1);
10053         val = ValueFromIntGroup(ig);
10054         IntGroupRelease(ig);
10055         return val;
10056 }
10057
10058 /*
10059  *  call-seq:
10060  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10061  *
10062  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10063  *  If the molecule does not contain a basis set information, then returns nil.
10064  */
10065 static VALUE
10066 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10067 {
10068     Molecule *mol;
10069         Vector o, dx, dy, dz;
10070         Int nx, ny, nz;
10071         VALUE nval;
10072         Int npoints = 80 * 80 * 80;
10073     Data_Get_Struct(self, Molecule, mol);
10074         if (mol->bset == NULL)
10075                 return Qnil;
10076         rb_scan_args(argc, argv, "01", &nval);
10077         if (nval != Qnil)
10078                 npoints = NUM2INT(rb_Integer(nval));
10079         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10080                 return Qnil;
10081         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));
10082 }
10083
10084 static int
10085 s_Cubegen_callback(double progress, void *ref)
10086 {
10087         MyAppCallback_setProgressValue(progress);
10088         if (MyAppCallback_checkInterrupt())
10089                 return 1;
10090         else return 0;
10091 }
10092
10093 /*
10094  *  call-seq:
10095  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10096  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10097  *
10098  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10099  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10100  *  second form, the cube dimension is explicitly given.
10101  *  Returns fname when successful, nil otherwise.
10102  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10103  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10104  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10105  */
10106 static VALUE
10107 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10108 {
10109         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10110     Molecule *mol;
10111         Int mono, nx, ny, nz, npoints;
10112         Vector o, dx, dy, dz;
10113         int index, n;
10114         char buf[1024];
10115     Data_Get_Struct(self, Molecule, mol);
10116         if (mol->bset == NULL)
10117                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10118         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10119         
10120         /*  Set up parameters  */
10121         mono = NUM2INT(rb_Integer(mval));
10122         if (mono <= 0 || mono > mol->bset->ncomps)
10123                 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
10124         if (RTEST(bval)) {
10125                 if (mol->bset->rflag != 0)
10126                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10127                 mono += mol->bset->ncomps;
10128         }
10129                 
10130         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10131                 /*  Automatic grid formation  */
10132                 if (oval != Qnil)
10133                         npoints = NUM2INT(rb_Integer(oval));
10134                 else npoints = 0;
10135                 if (npoints == 0)
10136                         npoints = 1000000;
10137                 else if (npoints < 8)
10138                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10139                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10140                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10141                 ival = dxval;
10142                 bval = dyval;
10143         } else {
10144                 VectorFromValue(oval, &o);
10145                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10146                         VectorFromValue(dxval, &dx);
10147                 else {
10148                         dx.x = NUM2DBL(rb_Float(dxval));
10149                         dx.y = dx.z = 0.0;
10150                 }
10151                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10152                         VectorFromValue(dyval, &dy);
10153                 else {
10154                         dy.y = NUM2DBL(rb_Float(dyval));
10155                         dy.x = dy.z = 0.0;
10156                 }
10157                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10158                         VectorFromValue(dzval, &dz);
10159                 else {
10160                         dz.z = NUM2DBL(rb_Float(dzval));
10161                         dz.x = dz.y = 0.0;
10162                 }
10163                 nx = NUM2INT(rb_Integer(nxval));
10164                 ny = NUM2INT(rb_Integer(nyval));
10165                 nz = NUM2INT(rb_Integer(nzval));
10166                 if (nx <= 0 || ny <= 0 || nz <= 0)
10167                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10168                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10169                         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);
10170         }
10171         
10172         /*  Calc MO  */
10173         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10174         if (index == -2)
10175                 rb_interrupt();
10176         else if (index < 0)
10177                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10178         
10179         /*  Output to file  */
10180         MoleculeCallback_displayName(mol, buf, sizeof buf);
10181         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10182         if (n != 0)
10183                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10184         
10185         /*  Discard the cube  */
10186         MoleculeClearCubeAtIndex(mol, index);
10187         return fval;
10188 }
10189
10190 /*
10191  *  call-seq:
10192  *     create_surface(mo, attr = nil)
10193  *
10194  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10195  *  then it denotes the beta orbital.
10196  *  If mo is nil, then the attributes of the current surface are modified.
10197  *  Attributes:
10198  *    :npoints : the approximate number of grid points
10199  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10200  *    :thres   : the threshold for the isovalue surface
10201  *  If the molecule does not contain MO information, raises exception.
10202  */
10203 static VALUE
10204 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10205 {
10206     Molecule *mol;
10207         Vector o, dx, dy, dz;
10208         Int nmo, nx, ny, nz, i;
10209         Int need_recalc = 0;
10210         VALUE nval, hval, aval;
10211         Int npoints;
10212         Double expand;
10213         Double thres;
10214         Double d[4];
10215     Data_Get_Struct(self, Molecule, mol);
10216         rb_scan_args(argc, argv, "11", &nval, &hval);
10217         if (mol->bset == NULL)
10218                 rb_raise(rb_eMolbyError, "No MO information is given");
10219         if (nval == Qnil) {
10220                 nmo = -1;
10221         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10222                 nmo = mol->bset->nmos + 1;
10223         } else {
10224                 nmo = NUM2INT(rb_Integer(nval));
10225                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10226                         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);
10227                 if (nmo < 0)
10228                         nmo = -nmo + mol->bset->ncomps;
10229         }
10230         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10231                 npoints = NUM2INT(rb_Integer(aval));
10232                 need_recalc = 1;
10233         } else if (mol->mcube != NULL) {
10234                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10235         } else npoints = 80 * 80 * 80;
10236         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10237                 expand = NUM2DBL(rb_Float(aval));
10238         } else if (mol->mcube != NULL) {
10239                 expand = mol->mcube->expand;
10240         } else expand = 1.0;
10241         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10242                 thres = NUM2DBL(rb_Float(aval));
10243         } else if (mol->mcube != NULL) {
10244                 thres = mol->mcube->thres;
10245         } else thres = 0.05;
10246         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10247                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10248                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10249                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10250                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10251         }
10252         for (nx = 0; nx < 2; nx++) {
10253                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10254                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10255                         aval = rb_ary_to_ary(aval);
10256                         if (RARRAY_LEN(aval) < 3) {
10257                         raise:
10258                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10259                         }
10260                         for (i = 0; i < 4; i++)
10261                                 d[i] = mol->mcube->c[nx].rgba[i];
10262                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10263                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10264                                 if (d[i] < 0.0 && d[i] > 1.0)
10265                                         goto raise;
10266                         }
10267                         for (i = 0; i < 4; i++)
10268                                 mol->mcube->c[nx].rgba[i] = d[i];
10269                 }
10270         }
10271         if (mol->mcube->expand != expand)
10272                 need_recalc = 1;
10273         mol->mcube->thres = thres;
10274         mol->mcube->expand = expand;
10275         if (nmo < 0) {
10276                 if (mol->mcube->idn < 0)
10277                         return self;  /*  Only set attributes for now  */
10278                 if (need_recalc)
10279                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10280         }
10281         if (MoleculeUpdateMCube(mol, nmo) != 0)
10282                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10283         return self;
10284 }
10285
10286 /*
10287  *  call-seq:
10288  *     set_surface_attr(attr = nil)
10289  *
10290  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10291  */
10292 static VALUE
10293 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10294 {
10295         VALUE args[2];
10296         args[0] = Qnil;
10297         args[1] = hval;
10298         return s_Molecule_CreateSurface(2, args, self);
10299 }
10300
10301 /*
10302  *  call-seq:
10303  *     nelpots
10304  *
10305  *  Get the number of electrostatic potential info.
10306  */
10307 static VALUE
10308 s_Molecule_NElpots(VALUE self)
10309 {
10310         Molecule *mol;
10311     Data_Get_Struct(self, Molecule, mol);
10312         return INT2NUM(mol->nelpots);
10313 }
10314
10315 /*
10316  *  call-seq:
10317  *     elpot(idx)
10318  *
10319  *  Get the electrostatic potential info at the given index. If present, then the
10320  *  return value is [Vector, Float] (position and potential). If not present, then
10321  *  returns nil.
10322  */
10323 static VALUE
10324 s_Molecule_Elpot(VALUE self, VALUE ival)
10325 {
10326         Molecule *mol;
10327         int idx;
10328     Data_Get_Struct(self, Molecule, mol);
10329         idx = NUM2INT(rb_Integer(ival));
10330         if (idx < 0 || idx >= mol->nelpots)
10331                 return Qnil;
10332         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10333 }
10334
10335 /*
10336  *  call-seq:
10337  *     clear_basis_set
10338  *
10339  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10340  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10341  */
10342 static VALUE
10343 s_Molecule_ClearBasisSet(VALUE self)
10344 {
10345         Molecule *mol;
10346     Data_Get_Struct(self, Molecule, mol);
10347         if (mol != NULL) {
10348                 if (mol->bset != NULL) {
10349                         BasisSetRelease(mol->bset);
10350                         mol->bset = NULL;
10351                 }
10352                 if (mol->mcube != NULL) {
10353                         MoleculeDeallocateMCube(mol->mcube);
10354                         mol->mcube = NULL;
10355                 }
10356         }
10357         return self;
10358 }
10359
10360 /*
10361  *  call-seq:
10362  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10363  *
10364  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10365  *  and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10366  *  -2, D5-type.
10367  */
10368 static VALUE
10369 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10370 {
10371         Molecule *mol;
10372         int sym, nprims, a_idx, n;
10373     Data_Get_Struct(self, Molecule, mol);
10374         a_idx = NUM2INT(rb_Integer(aval));
10375         sym = NUM2INT(rb_Integer(symval));
10376         nprims = NUM2INT(rb_Integer(npval));
10377         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10378         if (n == -1)
10379                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10380         else if (n == -2)
10381                 rb_raise(rb_eMolbyError, "Low memory");
10382         else if (n == -3)
10383                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10384         else if (n != 0)
10385                 rb_raise(rb_eMolbyError, "Unknown error");
10386         return self;
10387 }
10388
10389 /*
10390  *  call-seq:
10391  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10392  *
10393  *  To be used internally. Add a gaussian primitive coefficients.
10394  */
10395 static VALUE
10396 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10397 {
10398         Molecule *mol;
10399         Int n;
10400         Double exponent, contraction, contraction_sp;
10401     Data_Get_Struct(self, Molecule, mol);
10402         exponent = NUM2DBL(rb_Float(expval));
10403         contraction = NUM2DBL(rb_Float(cval));
10404         contraction_sp = NUM2DBL(rb_Float(cspval));
10405         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10406         if (n == -1)
10407                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10408         else if (n == -2)
10409                 rb_raise(rb_eMolbyError, "Low memory");
10410         else if (n != 0)
10411                 rb_raise(rb_eMolbyError, "Unknown error");
10412         return self;
10413 }
10414
10415 /*
10416  *  call-seq:
10417  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10418  *
10419  *  Get the Gaussian shell information for the given MO coefficient index.
10420  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10421  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10422  *  is the number of MO component belonging to this shell.
10423  */
10424 static VALUE
10425 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10426 {
10427         Molecule *mol;
10428         ShellInfo *sp;
10429         int s_idx, sym;
10430     Data_Get_Struct(self, Molecule, mol);
10431         if (mol->bset == NULL)
10432                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10433         s_idx = NUM2INT(rb_Integer(sval));
10434         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10435                 return Qnil;
10436         sp = mol->bset->shells + s_idx;
10437         sym = sp->sym;
10438         switch (sym) {
10439                 case kGTOType_S:  sym = 0;  break;
10440                 case kGTOType_SP: sym = -1; break;
10441                 case kGTOType_P:  sym = 1;  break;
10442                 case kGTOType_D:  sym = 2;  break;
10443                 case kGTOType_D5: sym = -2; break;
10444                 case kGTOType_F:  sym = 3;  break;
10445                 case kGTOType_F7: sym = -3; break;
10446                 case kGTOType_G:  sym = 4;  break;
10447                 case kGTOType_G9: sym = -4; break;
10448                 default:
10449                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10450         }
10451         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10452 }
10453
10454 /*
10455  *  call-seq:
10456  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10457  *
10458  *  Get the Gaussian primitive coefficients for the given MO component.
10459  */
10460 static VALUE
10461 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10462 {
10463         Molecule *mol;
10464         ShellInfo *sp;
10465         PrimInfo *pp;
10466         int s_idx, i;
10467         VALUE retval, aval;
10468     Data_Get_Struct(self, Molecule, mol);
10469         if (mol->bset == NULL)
10470                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10471         s_idx = NUM2INT(rb_Integer(sval));
10472         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10473                 return Qnil;
10474         sp = mol->bset->shells + s_idx;
10475         pp = mol->bset->priminfos + sp->p_idx;
10476         retval = rb_ary_new2(sp->nprim);
10477         for (i = 0; i < sp->nprim; i++) {
10478                 if (sp->sym == kGTOType_SP) {
10479                         /*  With P contraction coefficient  */
10480                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10481                 } else {
10482                         /*  Without P contraction coefficient  */
10483                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10484                 }
10485                 rb_ary_store(retval, i, aval);
10486         }
10487         return retval;
10488 }
10489
10490 /*
10491  *  call-seq:
10492  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10493  *
10494  *  Get the Gaussian shell information for the given MO coefficient index.
10495  */
10496 static VALUE
10497 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10498 {
10499         Molecule *mol;
10500         Int n, c, atom_idx, shell_idx;
10501         char label[32];
10502     Data_Get_Struct(self, Molecule, mol);
10503         if (mol->bset == NULL)
10504                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10505         c = NUM2INT(rb_Integer(cval));
10506         if (c < 0 || c >= mol->bset->ncomps)
10507                 return Qnil;
10508         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10509         if (n != 0)
10510                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10511         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10512 }
10513
10514 /*
10515  *  call-seq:
10516  *     clear_mo_coefficients
10517  *
10518  *  Clear the existing MO coefficients.
10519  */
10520 static VALUE
10521 s_Molecule_ClearMOCoefficients(VALUE self)
10522 {
10523         Molecule *mol;
10524         Data_Get_Struct(self, Molecule, mol);
10525         if (mol->bset != NULL) {
10526                 if (mol->bset->moenergies != NULL) {
10527                         free(mol->bset->moenergies);
10528                         mol->bset->moenergies = NULL;
10529                 }
10530                 if (mol->bset->mo != NULL) {
10531                         free(mol->bset->mo);
10532                         mol->bset->mo = NULL;
10533                 }
10534                 mol->bset->nmos = 0;
10535         }
10536         return self;
10537 }
10538
10539 /*
10540  *  call-seq:
10541  *     set_mo_coefficients(idx, energy, coefficients)
10542  *
10543  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10544  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10545  *  Energy is the MO energy, and coefficients is an array
10546  *  of MO coefficients.
10547  */
10548 static VALUE
10549 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10550 {
10551         Molecule *mol;
10552         Int idx, ncomps, i;
10553         Double energy;
10554         Double *coeffs;
10555     Data_Get_Struct(self, Molecule, mol);
10556         idx = NUM2INT(rb_Integer(ival));
10557         energy = NUM2DBL(rb_Float(eval));
10558         aval = rb_ary_to_ary(aval);
10559         ncomps = RARRAY_LEN(aval);
10560         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10561         if (coeffs == NULL) {
10562                 i = -2;
10563                 goto end;
10564         }
10565         for (i = 0; i < ncomps; i++)
10566                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10567         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10568 end:
10569         if (i == -1)
10570                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10571         else if (i == -2)
10572                 rb_raise(rb_eMolbyError, "Low memory");
10573         else if (i == -3)
10574                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10575         else if (i == -4)
10576                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10577         else if (i == -5)
10578                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10579         else if (i != 0)
10580                 rb_raise(rb_eMolbyError, "Unknown error");
10581         return self;
10582 }
10583
10584 /*
10585  *  call-seq:
10586  *     get_mo_coefficients(idx)
10587  *
10588  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10589  */
10590 static VALUE
10591 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10592 {
10593         Molecule *mol;
10594         Int idx, ncomps, n;
10595         Double energy;
10596         Double *coeffs;
10597         VALUE retval;
10598     Data_Get_Struct(self, Molecule, mol);
10599         idx = NUM2INT(rb_Integer(ival));
10600         ncomps = 0;
10601         coeffs = NULL;
10602         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10603         if (n == -1)
10604                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10605         else if (n == -2)
10606                 rb_raise(rb_eMolbyError, "No basis set information is present");
10607         else if (n == -3)
10608                 return Qnil;  /*  Silently returns nil  */
10609         retval = rb_ary_new2(ncomps);
10610         for (n = 0; n < ncomps; n++)
10611                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10612         free(coeffs);
10613         return retval;
10614 }
10615
10616 /*
10617  *  call-seq:
10618  *     get_mo_energy(idx)
10619  *
10620  *  To be used internally. Get the MO energy for the given MO index (1-based).
10621  */
10622 static VALUE
10623 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10624 {
10625         Molecule *mol;
10626         Int idx, n;
10627         Double energy;
10628     Data_Get_Struct(self, Molecule, mol);
10629         idx = NUM2INT(rb_Integer(ival));
10630         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10631         if (n == -1)
10632                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10633         else if (n == -2)
10634                 rb_raise(rb_eMolbyError, "No basis set information is present");
10635         else if (n == -3)
10636                 return Qnil;
10637         return rb_float_new(energy);
10638 }
10639
10640 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10641
10642 static inline void
10643 s_InitMOInfoKeys(void)
10644 {
10645         if (sTypeSym == 0) {
10646                 sTypeSym = ID2SYM(rb_intern("type"));
10647                 sAlphaSym = ID2SYM(rb_intern("alpha"));
10648                 sBetaSym = ID2SYM(rb_intern("beta"));
10649                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10650                 sNshellsSym = ID2SYM(rb_intern("nshells"));
10651         }
10652 }
10653
10654 /*
10655  *  call-seq:
10656  *     set_mo_info(hash)
10657  *
10658  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10659  *  :alpha=>integer, :beta=>integer
10660  */
10661 static VALUE
10662 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10663 {
10664         Molecule *mol;
10665         VALUE aval;
10666         Int rflag, na, nb, n;
10667         char *s;
10668     Data_Get_Struct(self, Molecule, mol);
10669         if (mol->bset != NULL) {
10670                 rflag = mol->bset->rflag;
10671                 na = mol->bset->ne_alpha;
10672                 nb = mol->bset->ne_beta;
10673         } else {
10674                 rflag = 1;
10675                 na = 0;
10676                 nb = 0;
10677         }
10678         if (hval != Qnil) {
10679                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10680                         s = StringValuePtr(aval);
10681                         if (strcasecmp(s, "RHF") == 0)
10682                                 rflag = 1;
10683                         else if (strcasecmp(s, "UHF") == 0)
10684                                 rflag = 0;
10685                         else if (strcasecmp(s, "ROHF") == 0)
10686                                 rflag = 2;
10687                 }
10688                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10689                         n = NUM2INT(rb_Integer(aval));
10690                         if (n >= 0)
10691                                 na = n;
10692                 }
10693                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10694                         n = NUM2INT(rb_Integer(aval));
10695                         if (n >= 0)
10696                                 nb = n;
10697                 }
10698                 MoleculeSetMOInfo(mol, rflag, na, nb);
10699         }
10700         return self;
10701 }
10702
10703 /*
10704  *  call-seq:
10705  *     get_mo_info(key)
10706  *
10707  *  Get the MO info. The key is as described in set_mo_info.
10708  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10709  */
10710 static VALUE
10711 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10712 {
10713         Molecule *mol;
10714     Data_Get_Struct(self, Molecule, mol);
10715         if (mol->bset == NULL)
10716                 return Qnil;
10717         if (kval == sTypeSym) {
10718                 switch (mol->bset->rflag) {
10719                         case 0: return rb_str_new2("UHF");
10720                         case 1: return rb_str_new2("RHF");
10721                         case 2: return rb_str_new2("ROHF");
10722                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10723                 }
10724         } else if (kval == sAlphaSym) {
10725                 return INT2NUM(mol->bset->ne_alpha);
10726         } else if (kval == sBetaSym) {
10727                 return INT2NUM(mol->bset->ne_beta);
10728         } else if (kval == sNcompsSym) {
10729                 return INT2NUM(mol->bset->ncomps);
10730         } else if (kval == sNshellsSym) {
10731                 return INT2NUM(mol->bset->nshells);
10732         } else {
10733                 kval = rb_inspect(kval);
10734                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10735                 return Qnil;  /*  Does not reach here  */
10736         }
10737 }
10738
10739 /*
10740  *  call-seq:
10741  *     mo_type
10742  *
10743  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10744  */
10745 static VALUE
10746 s_Molecule_MOType(VALUE self)
10747 {
10748         return s_Molecule_GetMOInfo(self, sTypeSym);
10749 }
10750
10751
10752 #if 0
10753 /*
10754  *  call-seq:
10755  *     allocate_basis_set_record(rflag, ne_alpha, ne_beta)
10756  *
10757  *  To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
10758  *  ne_alpha, ne_beta: number of alpha/beta electrons.
10759  */
10760 static VALUE
10761 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
10762 {
10763         Molecule *mol;
10764         Int rflag, na, nb, n;
10765     Data_Get_Struct(self, Molecule, mol);
10766         rflag = NUM2INT(rb_Integer(rval));
10767         na = NUM2INT(rb_Integer(naval));
10768         nb = NUM2INT(rb_Integer(nbval));
10769         n = MoleculeSetMOInfo(mol, rflag, na, nb);
10770         if (n == -1)
10771                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10772         else if (n == -2)
10773                 rb_raise(rb_eMolbyError, "Low memory");
10774         else if (n != 0)
10775                 rb_raise(rb_eMolbyError, "Unknown error");
10776         return self;
10777 }
10778 #endif
10779
10780 /*
10781  *  call-seq:
10782  *     search_equivalent_atoms(ig = nil)
10783  *
10784  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
10785  */
10786 static VALUE
10787 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10788 {
10789         Molecule *mol;
10790         Int *result, i;
10791         VALUE val;
10792         IntGroup *ig;
10793     Data_Get_Struct(self, Molecule, mol);
10794         if (mol->natoms == 0)
10795                 return Qnil;
10796         rb_scan_args(argc, argv, "01", &val);
10797         if (val != Qnil)
10798                 ig = IntGroupFromValue(val);
10799         else ig = NULL;
10800         result = MoleculeSearchEquivalentAtoms(mol, ig);
10801         if (result == NULL)
10802                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10803         if (ig != NULL)
10804                 IntGroupRelease(ig);
10805         val = rb_ary_new2(mol->natoms);
10806         for (i = 0; i < mol->natoms; i++)
10807                 rb_ary_push(val, INT2NUM(result[i]));
10808         free(result);
10809         return val;
10810 }
10811
10812 /*
10813  *  call-seq:
10814  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10815  *
10816  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10817  *  Name is the name of the new pi anchor, and group is the atoms that define
10818  *  the pi system. Type (a String) is an atom type for MM implementation.
10819  *  Weights represent the relative significance of the component atoms; if omitted, then
10820  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
10821  *  The weight values will be normalized so that the sum of the weights is 1.0.
10822  *  The weight values must be positive.
10823  *  Index is the atom index where the created pi-anchor is inserted in the 
10824  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
10825  *  having the largest index.
10826  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
10827  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10828  */
10829 static VALUE
10830 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10831 {
10832         Molecule *mol;
10833         VALUE nval, gval;
10834         IntGroup *ig;
10835         Int i, n, idx, last_component;
10836         Atom a, *ap;
10837         PiAnchor an;
10838         AtomRef *aref;
10839         if (argc < 2 || argc >= 6)
10840                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10841         nval = *argv++;
10842         gval = *argv++;
10843         argc -= 2;
10844     Data_Get_Struct(self, Molecule, mol);
10845         ig = IntGroupFromValue(gval);
10846         memset(&a, 0, sizeof(a));
10847         memset(&an, 0, sizeof(an));
10848         strncpy(a.aname, StringValuePtr(nval), 4);
10849         if (a.aname[0] == '_')
10850                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10851         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
10852         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10853                 if (n >= mol->natoms) {
10854                         AtomConnectResize(&an.connect, 0);
10855                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10856                 }
10857                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10858                 last_component = n;
10859         }
10860         if (an.connect.count == 0)
10861                 rb_raise(rb_eMolbyError, "no atoms are specified");
10862         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10863         for (i = 0; i < an.connect.count; i++) {
10864                 an.coeffs[i] = 1.0 / an.connect.count;
10865         }
10866         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10867                 /*  Atom type  */
10868                 if (argv[0] != Qnil)
10869                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10870                 argc--;
10871                 argv++;
10872         }
10873         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10874                 if (argv[0] != Qnil) {
10875                         VALUE aval = rb_ary_to_ary(argv[0]);
10876                         Double d, sum;
10877                         if (RARRAY_LEN(aval) != an.connect.count)
10878                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10879                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10880                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10881                                 if (d <= 0.0)
10882                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
10883                                 sum += d;
10884                                 an.coeffs[i] = d;
10885                         }
10886                         for (i = 0; i < an.connect.count; i++)
10887                                 an.coeffs[i] /= sum;
10888                 }
10889                 argc--;
10890                 argv++;
10891         }
10892         if (argc > 0 && argv[0] != Qnil) {
10893                 /*  Index  */
10894                 idx = NUM2INT(rb_Integer(argv[0]));
10895         } else idx = -1;
10896         if (idx < 0 || idx > mol->natoms) {
10897                 /*  Immediately after the last specified atom  */
10898                 idx = last_component + 1;
10899         }
10900         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10901         memmove(a.anchor, &an, sizeof(PiAnchor));
10902         /*  Use residue information of the last specified atom  */
10903         ap = ATOM_AT_INDEX(mol->atoms, last_component);
10904         a.resSeq = ap->resSeq;
10905         strncpy(a.resName, ap->resName, 4);
10906         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10907                 return Qnil;
10908         MoleculeCalculatePiAnchorPosition(mol, idx);
10909     aref = AtomRefNew(mol, idx);
10910     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10911 }
10912
10913 /*
10914  *  call-seq:
10915  *     set_property(name, value[, index]) -> value
10916  *     set_property(name, values, group) -> values
10917  *
10918  *  Set molecular property. A property is a floating-point number with a specified name,
10919  *  and can be set for each frame separately. The name of the property is given as a String.
10920  *  The value can be a single floating point number, which is set to the current frame.
10921  *  
10922  */
10923 static VALUE
10924 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
10925 {
10926         Molecule *mol;
10927         VALUE nval, vval, ival;
10928         char *name;
10929         IntGroup *ig;
10930         Int i, n, idx, fidx;
10931         Double *dp;
10932         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
10933     Data_Get_Struct(self, Molecule, mol);
10934         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
10935                 idx = NUM2INT(rb_Integer(nval));
10936                 if (idx < 0 || idx >= mol->nmolprops)
10937                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
10938         } else {
10939                 name = StringValuePtr(nval);
10940                 idx = MoleculeLookUpProperty(mol, name);
10941                 if (idx < 0) {
10942                         idx = MoleculeCreateProperty(mol, name);
10943                         if (idx < 0)
10944                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
10945                 }
10946         }
10947         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
10948                 if (ival == Qnil)
10949                         fidx = mol->cframe;
10950                 else {
10951                         fidx = NUM2INT(rb_Integer(ival));
10952                         n = MoleculeGetNumberOfFrames(mol);
10953                         if (fidx < 0 || fidx >= n)
10954                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
10955                 }
10956                 ig = IntGroupNewWithPoints(fidx, 1, -1);
10957                 dp = (Double *)malloc(sizeof(Double));
10958                 *dp = NUM2DBL(rb_Float(vval));
10959                 n = 1;
10960         } else {
10961                 vval = rb_ary_to_ary(vval);
10962                 ig = IntGroupFromValue(ival);
10963                 n = IntGroupGetCount(ig);
10964                 if (n == 0)
10965                         rb_raise(rb_eMolbyError, "No frames are specified");
10966                 if (RARRAY_LEN(vval) < n)
10967                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
10968                 dp = (Double *)calloc(sizeof(Double), n);
10969                 for (i = 0; i < n; i++)
10970                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
10971         }
10972         
10973         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
10974         free(dp);
10975         IntGroupRelease(ig);
10976         return self;
10977 }
10978
10979 /*
10980  *  call-seq:
10981  *     get_property(name[, index]) -> value
10982  *     get_property(name, group) -> values
10983  *
10984  *  Get molecular property. In the first form, a property value for a single frame is returned.
10985  *  (If index is omitted, then the value for the current frame is given)
10986  *  In the second form, an array of property values for the given frames is returned.
10987  *  If name is not one of known properties or a valid index integer, exception is raised.
10988  */
10989 static VALUE
10990 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
10991 {
10992         Molecule *mol;
10993         VALUE nval, ival;
10994         char *name;
10995         IntGroup *ig;
10996         Int i, n, idx, fidx;
10997         Double *dp;
10998         rb_scan_args(argc, argv, "11", &nval, &ival);
10999     Data_Get_Struct(self, Molecule, mol);
11000         if (mol->nmolprops == 0)
11001                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11002         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11003                 idx = NUM2INT(rb_Integer(nval));
11004                 if (idx < 0 || idx >= mol->nmolprops)
11005                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11006         } else {
11007                 name = StringValuePtr(nval);
11008                 idx = MoleculeLookUpProperty(mol, name);
11009                 if (idx < 0)
11010                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11011         }
11012         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11013                 if (ival == Qnil)
11014                         fidx = mol->cframe;
11015                 else {
11016                         fidx = NUM2INT(rb_Integer(ival));
11017                         n = MoleculeGetNumberOfFrames(mol);
11018                         if (fidx < 0 || fidx >= n)
11019                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11020                 }
11021                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11022                 ival = INT2FIX(fidx);
11023                 n = 1;
11024         } else {
11025                 ig = IntGroupFromValue(ival);
11026                 n = IntGroupGetCount(ig);
11027                 if (n == 0)
11028                         return rb_ary_new();
11029         }
11030         dp = (Double *)calloc(sizeof(Double), n);
11031         MoleculeGetProperty(mol, idx, ig, dp);  
11032         if (FIXNUM_P(ival))
11033                 ival = rb_float_new(dp[0]);
11034         else {
11035                 ival = rb_ary_new();
11036                 for (i = n - 1; i >= 0; i--) {
11037                         nval = rb_float_new(dp[i]);
11038                         rb_ary_store(ival, i, nval);
11039                 }
11040         }
11041         free(dp);
11042         IntGroupRelease(ig);
11043         return ival;
11044 }
11045
11046 /*
11047  *  call-seq:
11048  *     property_names -> Array
11049  *
11050  *  Get an array of property names.
11051  */
11052 static VALUE
11053 s_Molecule_PropertyNames(VALUE self)
11054 {
11055         Molecule *mol;
11056         VALUE rval, nval;
11057         int i;
11058     Data_Get_Struct(self, Molecule, mol);
11059         rval = rb_ary_new();
11060         for (i = mol->nmolprops - 1; i >= 0; i--) {
11061                 nval = rb_str_new2(mol->molprops[i].propname);
11062                 rb_ary_store(rval, i, nval);
11063         }
11064         return rval;
11065 }
11066
11067 /*
11068  *  call-seq:
11069  *     export_graphic(fname, scale = 1.0, bg_color = -1)
11070  *
11071  *  Export the current graphic to a PNG or TIF file (determined by the extension).
11072  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
11073  *  
11074  */
11075 static VALUE
11076 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
11077 {
11078         Molecule *mol;
11079         VALUE fval, sval, bval;
11080         char *fname;
11081         float scale;
11082         int bg_color;
11083     Data_Get_Struct(self, Molecule, mol);
11084         if (mol->mview == NULL)
11085                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
11086         rb_scan_args(argc, argv, "12", &fval, &sval, &bval);
11087         fname = FileStringValuePtr(fval);
11088         if (sval == Qnil)
11089                 scale = 1.0;
11090         else scale = NUM2DBL(rb_Float(sval));
11091         if (bval == Qnil)
11092                 bg_color = -1;
11093         else bg_color = NUM2INT(rb_Integer(bval));
11094         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color) == 0)
11095                 return fval;
11096         else return Qnil;
11097 }
11098
11099 /*
11100  *  call-seq:
11101  *     current       -> Molecule
11102  *
11103  *  Get the currently "active" molecule.
11104  */
11105 static VALUE
11106 s_Molecule_Current(VALUE klass)
11107 {
11108         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11109 }
11110
11111 /*
11112  *  call-seq:
11113  *     Molecule[]          -> Molecule
11114  *     Molecule[n]         -> Molecule
11115  *     Molecule[name]      -> Molecule
11116  *     Molecule[name, k]   -> Molecule
11117  *     Molecule[regex]     -> Molecule
11118  *     Molecule[regex, k]  -> Molecule
11119  *
11120  *  Molecule[] is equivalent to Molecule.current.
11121  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11122  *  Molecule[name] gives the first document (in the order of creation time) that has
11123  *  the given name. If a second argument (k) is given, the k-th document that has the
11124  *  given name is returned.
11125  *  Molecule[regex] gives the first document (in the order of creation time) that
11126  *  has a name matching the regular expression. If a second argument (k) is given, 
11127  *  the k-th document that has a name matching the re is returned.
11128  */
11129 static VALUE
11130 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11131 {
11132         VALUE val, kval;
11133         int idx, k;
11134         Molecule *mol;
11135         char buf[1024];
11136         rb_scan_args(argc, argv, "02", &val, &kval);
11137         if (val == Qnil)
11138                 return s_Molecule_Current(klass);
11139         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11140                 idx = NUM2INT(val);
11141                 mol = MoleculeCallback_moleculeAtIndex(idx);
11142         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11143                 char *p = StringValuePtr(val);
11144                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11145                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11146                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11147                         if (strcmp(buf, p) == 0 && --k == 0)
11148                                 break;
11149                 }
11150         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11151                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11152                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11153                         VALUE name;
11154                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11155                         name = rb_str_new2(buf);
11156                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11157                                 break;
11158                 }       
11159         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11160         
11161         if (mol == NULL)
11162                 return Qnil;
11163         else return ValueFromMolecule(mol);
11164 }
11165
11166 /*
11167  *  call-seq:
11168  *     list         -> array of Molecules
11169  *
11170  *  Get the list of molecules associated to the documents, in the order of creation
11171  *  time of the document. If no document is open, returns an empry array.
11172  */
11173 static VALUE
11174 s_Molecule_List(VALUE klass)
11175 {
11176         Molecule *mol;
11177         int i;
11178         VALUE ary;
11179         i = 0;
11180         ary = rb_ary_new();
11181         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11182                 rb_ary_push(ary, ValueFromMolecule(mol));
11183                 i++;
11184         }
11185         return ary;
11186 }
11187
11188 /*
11189  *  call-seq:
11190  *     ordered_list         -> array of Molecules
11191  *
11192  *  Get the list of molecules associated to the documents, in the order of front-to-back
11193  *  ordering of the associated window. If no document is open, returns an empry array.
11194  */
11195 static VALUE
11196 s_Molecule_OrderedList(VALUE klass)
11197 {
11198         Molecule *mol;
11199         int i;
11200         VALUE ary;
11201         i = 0;
11202         ary = rb_ary_new();
11203         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11204                 rb_ary_push(ary, ValueFromMolecule(mol));
11205                 i++;
11206         }
11207         return ary;
11208 }
11209
11210 /*
11211  *  call-seq:
11212  *     error_message       -> String
11213  *
11214  *  Get the error_message from the last load/save method. If no error, returns nil.
11215  */
11216 static VALUE
11217 s_Molecule_ErrorMessage(VALUE klass)
11218 {
11219         if (gLoadSaveErrorMessage == NULL)
11220                 return Qnil;
11221         else return rb_str_new2(gLoadSaveErrorMessage);
11222 }
11223
11224 /*
11225  *  call-seq:
11226  *     set_error_message(String)
11227  *     Molecule.error_message = String
11228  *
11229  *  Get the error_message from the last load/save method. If no error, returns nil.
11230  */
11231 static VALUE
11232 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
11233 {
11234         if (gLoadSaveErrorMessage != NULL) {
11235                 free(gLoadSaveErrorMessage);
11236                 gLoadSaveErrorMessage = NULL;
11237         }
11238         if (sval != Qnil) {
11239                 sval = rb_str_to_str(sval);
11240                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
11241         }
11242         return sval;
11243 }
11244
11245 /*
11246  *  call-seq:
11247  *     self == Molecule -> boolean
11248  *
11249  *  True if the two arguments point to the same molecule.
11250  */
11251 static VALUE
11252 s_Molecule_Equal(VALUE self, VALUE val)
11253 {
11254         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
11255                 Molecule *mol1, *mol2;
11256                 Data_Get_Struct(self, Molecule, mol1);
11257                 Data_Get_Struct(val, Molecule, mol2);
11258                 return (mol1 == mol2 ? Qtrue : Qfalse);
11259         } else return Qfalse;
11260 }
11261
11262 /*  The callback functions for call_subprocess_async  */
11263 static int
11264 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11265 {
11266         int ruby_status;
11267         VALUE procval, retval, args[2];
11268         args[0] = ValueFromMolecule(mol);
11269         args[1] = INT2NUM(status);
11270         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11271         if (procval != Qnil) {
11272                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11273                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11274                         return 1;
11275         }
11276         return 0;
11277 }
11278
11279 static int
11280 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11281 {
11282         int ruby_status;
11283         VALUE procval, retval, args[2];
11284         args[0] = ValueFromMolecule(mol);
11285         args[1] = INT2NUM(tcount);
11286         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11287         if (procval != Qnil) {
11288                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11289                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11290                         return 1;
11291         }
11292         return 0;
11293 }
11294
11295 /*
11296  *  call-seq:
11297  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11298  *
11299  *  Call subprocess asynchronically.
11300  *  If end_callback is given, it will be called (with two arguments self and termination status)
11301  *  when the subprocess terminated.
11302  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11303  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11304  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11305  *  filename begins with ">>", then the message will be appended to the file.
11306  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11307  *  If the argument is nil, then the message will be sent to the Ruby console.
11308  *  Returns the process ID as an integer.
11309  */
11310 static VALUE
11311 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11312 {
11313         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11314         Molecule *mol;
11315         char *sout, *serr;
11316         int n;
11317         FILE *fpout, *fperr;
11318         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11319         Data_Get_Struct(self, Molecule, mol);
11320
11321         if (stdout_val == Qnil) {
11322                 fpout = (FILE *)1;
11323         } else {
11324                 sout = StringValuePtr(stdout_val);
11325                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11326                         fpout = NULL;
11327                 else {
11328                         if (strncmp(sout, ">>", 2) == 0) {
11329                                 sout += 2;
11330                                 fpout = fopen(sout, "a");
11331                         } else {
11332                                 if (*sout == '>')
11333                                         sout++;
11334                                 fpout = fopen(sout, "w");
11335                         }
11336                         if (fpout == NULL)
11337                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11338                 }
11339         }
11340         if (stderr_val == Qnil) {
11341                 fperr = (FILE *)1;
11342         } else {
11343                 serr = StringValuePtr(stderr_val);
11344                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11345                         fperr = NULL;
11346                 else {
11347                         if (strncmp(serr, ">>", 2) == 0) {
11348                                 serr += 2;
11349                                 fpout = fopen(serr, "a");
11350                         } else {
11351                                 if (*serr == '>')
11352                                         serr++;
11353                                 fperr = fopen(serr, "w");
11354                         }
11355                         if (fperr == NULL)
11356                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11357                 }
11358         }
11359         
11360         /*  Register procs as instance variables  */
11361         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11362         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11363         n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11364         if (fpout != NULL && fpout != (FILE *)1)
11365                 fclose(fpout);
11366         if (fperr != NULL && fperr != (FILE *)1)
11367                 fclose(fperr);
11368         return INT2NUM(n);
11369 }
11370
11371 void
11372 Init_Molby(void)
11373 {
11374         int i;
11375         
11376         /*  Define module Molby  */
11377         rb_mMolby = rb_define_module("Molby");
11378         
11379         /*  Define Vector3D, Transform, IntGroup  */
11380         Init_MolbyTypes();
11381         
11382         /*  Define MDArena  */
11383         Init_MolbyMDTypes();
11384
11385         /*  class Molecule  */
11386         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11387         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11388     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11389     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11390     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11391     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11392     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11393     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11394     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11395     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11396     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11397     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11398     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11399     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11400     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11401     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11402         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11403     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11404     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11405     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11406     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11407 /*    rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
11408     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11409         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11410     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11411     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11412     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11413     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11414     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11415     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11416     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11417     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11418     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11419         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11420         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11421         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11422         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11423         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11424
11425         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
11426         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
11427         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
11428         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
11429         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
11430
11431         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11432         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11433         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11434         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11435         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11436         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11437         
11438         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11439         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11440         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11441         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11442         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11443         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11444         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11445         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11446         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11447         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11448         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11449         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11450         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11451         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11452         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11453         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11454         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11455         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11456         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11457         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11458         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11459         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11460         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11461         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11462         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11463     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11464         rb_define_alias(rb_cMolecule, "+", "add");
11465     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11466         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11467         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11468         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11469         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11470         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11471         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11472         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11473         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11474         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11475         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11476         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11477         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11478         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11479         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11480         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11481         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11482         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11483         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11484         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11485         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11486         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11487         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11488         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11489         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11490         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11491         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11492         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11493         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11494         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11495         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11496         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11497         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11498         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11499         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11500         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11501         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11502         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11503         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11504         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11505         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11506         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11507         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11508         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11509         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11510         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11511         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11512         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11513         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11514         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11515         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11516         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11517         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11518         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11519         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11520         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11521         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11522         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11523         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11524         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11525         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11526         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11527         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11528         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11529         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11530         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11531         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11532         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11533         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11534         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11535         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11536         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11537         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11538         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11539         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11540         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11541         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11542         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11543         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11544         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11545         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11546         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11547         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11548         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11549         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11550         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11551         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11552         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11553         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11554         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11555         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11556         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11557         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11558         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11559         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11560         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11561         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11562         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11563         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11564         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11565         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11566         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11567         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11568         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11569         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11570         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11571         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11572         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11573         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11574         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11575         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11576         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11577         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11578         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11579         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11580         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11581         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11582         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11583         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11584         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11585         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11586         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11587         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11588         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11589         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11590         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
11591         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11592         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11593         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11594         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11595         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11596         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11597         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11598         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11599         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11600         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11601         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11602         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11603         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11604         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11605         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11606         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11607         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11608         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11609         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11610         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11611         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11612         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11613         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11614         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11615         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11616         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11617         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11618 /*      rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3); */
11619         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11620         
11621         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11622         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11623         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11624         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11625         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11626                 
11627         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11628         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11629         
11630         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11631         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11632         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11633         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11634         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11635         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11636         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11637         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11638         
11639         /*  class MolEnumerable  */
11640         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11641     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11642         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11643         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11644     rb_define_alias(rb_cMolEnumerable, "size", "length");
11645         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11646         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11647
11648         /*  class AtomRef  */
11649         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11650         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11651                 char buf[64];
11652                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11653                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11654                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11655                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11656                 strcat(buf, "=");
11657                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11658         }
11659         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11660         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11661         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11662         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11663         s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11664         rb_global_variable(&s_SetAtomAttrString);
11665         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11666         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11667
11668         /*  class Parameter  */
11669         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11670         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11671         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11672         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11673         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11674         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11675         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11676         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11677         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11678         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11679         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11680         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11681         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11682         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11683         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11684         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11685         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11686         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11687         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11688         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11689         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11690         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11691         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11692         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11693         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11694         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11695         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11696         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11697         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11698         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11699         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11700         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11701         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11702         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11703         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11704         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11705         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11706         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11707         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11708         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11709         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11710         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11711         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11712         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11713         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11714         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11715         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11716         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11717         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11718         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11719         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11720         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11721         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11722         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11723
11724         /*  class ParEnumerable  */
11725         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11726     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11727         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11728         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11729         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11730         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11731         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11732         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11733         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11734         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11735         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11736         
11737         /*  class ParameterRef  */
11738         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11739         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11740                 char buf[64];
11741                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11742                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11743                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11744                 if (s_ParameterAttrDefTable[i].symref != NULL)
11745                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11746                 if (s_ParameterAttrDefTable[i].setter != NULL) {
11747                         strcat(buf, "=");
11748                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11749                 }
11750         }
11751         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11752         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11753         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11754         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11755         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11756         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11757         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11758         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11759
11760         /*  class MolbyError  */
11761         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11762
11763         /*  module Kernel  */
11764         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11765         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11766         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11767         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11768         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11769         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11770         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11771         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11772         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11773         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11774         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11775         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11776         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11777         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11778         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11779         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11780         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11781         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11782         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11783         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11784         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11785         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11786         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11787         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11788
11789         /*  class IO  */
11790         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11791         
11792         s_ID_equal = rb_intern("==");
11793         g_RubyID_call = rb_intern("call");
11794         
11795         s_InitMOInfoKeys();
11796 }
11797
11798 #pragma mark ====== External functions ======
11799
11800 static VALUE s_ruby_top_self = Qfalse;
11801 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11802 static VALUE s_ruby_export_local_variables = Qfalse;
11803
11804 static VALUE
11805 s_evalRubyScriptOnMoleculeSub(VALUE val)
11806 {
11807         void **ptr = (void **)val;
11808         Molecule *mol = (Molecule *)ptr[1];
11809         VALUE sval, fnval, lnval, retval;
11810         VALUE binding;
11811
11812         /*  Clear the error information (store in the history array if necessary)  */
11813         sval = rb_errinfo();
11814         if (sval != Qnil) {
11815                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11816                 rb_set_errinfo(Qnil);
11817         }
11818
11819         if (s_ruby_top_self == Qfalse) {
11820                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11821         }
11822         if (s_ruby_get_binding_for_molecule == Qfalse) {
11823                 const char *s1 =
11824                  "lambda { |_mol_, _bind_| \n"
11825                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11826                  "  _proc_.call(_mol_) } ";
11827                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11828                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11829         }
11830         if (s_ruby_export_local_variables == Qfalse) {
11831                 const char *s2 =
11832                 "lambda { |_bind_| \n"
11833                 "   # find local variables newly defined in _bind_ \n"
11834                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11835                 " _a_.each { |_vsym_| \n"
11836                 "   _vname_ = _vsym_.to_s \n"
11837                 "   _vval_ = _bind_.eval(_vname_) \n"
11838                 "   #  Define local variable \n"
11839                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11840                 "   #  Then set value  \n"
11841                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11842                 " } \n"
11843                 "}";
11844                 s_ruby_export_local_variables = rb_eval_string(s2);
11845                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11846         }
11847         if (ptr[2] == NULL) {
11848                 char *scr;
11849                 /*  String literal: we need to specify string encoding  */
11850 #if defined(__WXMSW__)
11851                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11852 #else
11853                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11854 #endif
11855                 sval = rb_str_new2(scr);
11856                 free(scr);
11857                 fnval = rb_str_new2("(eval)");
11858                 lnval = INT2FIX(0);
11859         } else {
11860                 sval = rb_str_new2((char *)ptr[0]);
11861                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11862                 lnval = INT2FIX(1);
11863         }
11864         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11865         if (mol != NULL) {
11866                 VALUE mval = ValueFromMolecule(mol);
11867                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11868         }
11869         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11870         if (mol != NULL) {
11871                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11872         }
11873         return retval;
11874 }
11875
11876 RubyValue
11877 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
11878 {
11879         RubyValue retval;
11880         void *args[3];
11881         VALUE save_interrupt_flag;
11882 /*      char *save_ruby_sourcefile;
11883         int save_ruby_sourceline; */
11884         if (gMolbyIsCheckingInterrupt) {
11885                 MolActionAlertRubyIsRunning();
11886                 *status = -1;
11887                 return (RubyValue)Qnil;
11888         }
11889         gMolbyRunLevel++;
11890         args[0] = (void *)script;
11891         args[1] = (void *)mol;
11892         args[2] = (void *)fname;
11893         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
11894 /*      save_ruby_sourcefile = ruby_sourcefile;
11895         save_ruby_sourceline = ruby_sourceline; */
11896         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
11897         if (*status != 0) {
11898                 /*  Is this 'exit' exception?  */
11899                 VALUE last_exception = rb_gv_get("$!");
11900                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
11901                         /*  Capture exit and return the status value  */
11902                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
11903                         *status = 0;
11904                         rb_set_errinfo(Qnil);
11905                 }
11906         }
11907         s_SetInterruptFlag(Qnil, save_interrupt_flag);
11908 /*      ruby_sourcefile = save_ruby_sourcefile;
11909         ruby_sourceline = save_ruby_sourceline; */
11910         gMolbyRunLevel--;
11911         return retval;
11912 }
11913
11914 int
11915 Molby_showRubyValue(RubyValue value, char **outValueString)
11916 {
11917         VALUE val = (VALUE)value;
11918         if (gMolbyIsCheckingInterrupt) {
11919                 MolActionAlertRubyIsRunning();
11920                 return 0;
11921         }
11922         if (val != Qnil) {
11923                 int status;
11924                 char *str;
11925                 gMolbyRunLevel++;
11926                 val = rb_protect(rb_inspect, val, &status);
11927                 gMolbyRunLevel--;
11928                 if (status != 0)
11929                         return status;
11930                 str = StringValuePtr(val);
11931                 if (outValueString != NULL)
11932                         *outValueString = strdup(str);
11933                 MyAppCallback_showScriptMessage("%s", str);
11934         } else {
11935                 if (outValueString != NULL)
11936                         *outValueString = NULL;
11937         }
11938         return 0;
11939 }
11940
11941 void
11942 Molby_showError(int status)
11943 {
11944         static const int tag_raise = 6;
11945         char *msg = NULL, *msg2;
11946         VALUE val, backtrace;
11947         int interrupted = 0;
11948         if (status == tag_raise) {
11949                 VALUE errinfo = rb_errinfo();
11950                 VALUE eclass = CLASS_OF(errinfo);
11951                 if (eclass == rb_eInterrupt) {
11952                         msg = "Interrupt";
11953                         interrupted = 1;
11954                 }
11955         }
11956         gMolbyRunLevel++;
11957         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
11958         if (msg == NULL) {
11959                 val = rb_eval_string_protect("$!.to_s", &status);
11960                 if (status == 0)
11961                         msg = RSTRING_PTR(val);
11962                 else msg = "(message not available)";
11963         }
11964         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
11965         MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
11966         free(msg2);
11967         gMolbyRunLevel--;
11968 }
11969
11970 char *
11971 Molby_getDescription(void)
11972 {
11973         extern const char *gVersionString, *gCopyrightString;
11974         extern int gRevisionNumber;
11975         extern char *gLastBuildString;
11976         char *s;
11977         char *revisionString;
11978         if (gRevisionNumber > 0) {
11979                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
11980         } else revisionString = "";
11981         asprintf(&s, 
11982                          "Molby %s%s\n%s\nLast compile: %s\n"
11983 #if !defined(__CMDMAC__)
11984                          "\nIncluding:\n"
11985                          "%s"
11986 #else
11987                          "Including "
11988 #endif
11989                          "ruby %s, http://www.ruby-lang.org/\n"
11990                          "%s\n"
11991                          "FFTW 3.3.2, http://www.fftw.org/\n"
11992                          "  Copyright (C) 2003, 2007-11 Matteo Frigo\n"
11993                          "  and Massachusetts Institute of Technology",
11994                          gVersionString, revisionString, gCopyrightString, gLastBuildString,
11995 #if !defined(__CMDMAC__)
11996                          MyAppCallback_getGUIDescriptionString(),
11997 #endif
11998                          gRubyVersion, gRubyCopyright);
11999         if (revisionString[0] != 0)
12000                 free(revisionString);
12001         return s;
12002 }
12003
12004 void
12005 Molby_startup(const char *script, const char *dir)
12006 {
12007         VALUE val;
12008         int status;
12009         char *libpath;
12010         char *respath, *p, *wbuf;
12011
12012         /*  Get version/copyright string from Ruby interpreter  */
12013         {
12014                 gRubyVersion = strdup(ruby_version);
12015                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12016 #if defined(__CMDMAC__)
12017                                  "",
12018 #else
12019                                  "  ",  /*  Indent for displaying in About dialog  */
12020 #endif
12021                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12022         }
12023         
12024         /*  Read build and revision information for Molby  */
12025 /*      {
12026                 char buf[200];
12027                 extern int gRevisionNumber;
12028                 extern char *gLastBuildString;
12029                 FILE *fp = fopen("../buildInfo.txt", "r");
12030                 gLastBuildString = "";
12031                 if (fp != NULL) {
12032                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12033                                 char *p1 = strchr(buf, '\"');
12034                                 char *p2 = strrchr(buf, '\"');
12035                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12036                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12037                                         buf[p2 - p1 - 1] = 0;
12038                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12039                                 }
12040                         }
12041                         fclose(fp);
12042                 }
12043                 fp = fopen("../revisionInfo.txt", "r");
12044                 gRevisionNumber = 0;
12045                 if (fp != NULL) {
12046                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12047                                 gRevisionNumber = strtol(buf, NULL, 0);
12048                         }
12049                         fclose(fp);
12050                 }
12051     } */
12052
12053 #if defined(__CMDMAC__)
12054         wbuf = Molby_getDescription();
12055         printf("%s\n", wbuf);
12056         free(wbuf);
12057 #endif
12058         
12059         /*  Read atom display parameters  */
12060         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12061 #if defined(__CMDMAC__)
12062                 fprintf(stderr, "%s\n", wbuf);
12063 #else
12064                 MyAppCallback_setConsoleColor(1);
12065                 MyAppCallback_showScriptMessage("%s", wbuf);
12066                 MyAppCallback_setConsoleColor(0);
12067 #endif
12068                 free(wbuf);
12069         }
12070         
12071         /*  Read default parameters  */
12072         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12073         if (wbuf != NULL) {
12074 #if defined(__CMDMAC__)
12075                 fprintf(stderr, "%s\n", wbuf);
12076 #else
12077                 MyAppCallback_setConsoleColor(1);
12078                 MyAppCallback_showScriptMessage("%s", wbuf);
12079                 MyAppCallback_setConsoleColor(0);
12080 #endif
12081                 free(wbuf);
12082         }
12083                 
12084         /*  Initialize Ruby interpreter  */
12085 #if __WXMSW__
12086         {
12087                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12088                     it causes rb_bug() (= fatal error) during ruby_init().
12089                     As a workaround, these standard streams are reopend as
12090                     NUL stream.  */
12091                 freopen("NUL", "r", stdin);
12092                 freopen("NUL", "w", stdout);
12093                 freopen("NUL", "w", stderr);
12094         }
12095 #endif
12096         ruby_init();
12097
12098         {
12099                 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12100                 Init_shift_jis();
12101                 Init_trans_japanese_sjis();
12102         }
12103         
12104         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12105         ruby_incpush(".");
12106         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12107         ruby_incpush(libpath);
12108         free(libpath);
12109         ruby_incpush(dir);
12110
12111         ruby_script("Molby");
12112         
12113         /*  Find the resource path (the parent directory of the given directory)  */
12114         respath = strdup(dir);
12115         p = strrchr(respath, '/');
12116         if (p == NULL && PATH_SEPARATOR != '/')
12117                 p = strrchr(respath, PATH_SEPARATOR);
12118         if (p != NULL)
12119                 *p = 0;
12120         val = Ruby_NewFileStringValue(respath);
12121         rb_define_global_const("MolbyResourcePath", val);
12122         free(respath);
12123
12124         /*  Define Molby classes  */
12125         Init_Molby();
12126         RubyDialogInitClass();
12127
12128         rb_define_const(rb_mMolby, "ResourcePath", val);
12129         val = Ruby_NewFileStringValue(dir);
12130         rb_define_const(rb_mMolby, "ScriptPath", val);
12131         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12132         val = Ruby_NewFileStringValue(p);
12133         rb_define_const(rb_mMolby, "MbsfPath", val);    
12134         free(p);
12135         
12136         p = MyAppCallback_getHomeDir();
12137         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12138         rb_define_const(rb_mMolby, "HomeDirectory", val);
12139         free(p);
12140         p = MyAppCallback_getDocumentHomeDir();
12141         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12142         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12143         free(p);
12144         
12145 #if defined(__CMDMAC__)
12146         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12147 #else
12148         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12149 #endif
12150
12151 #if !__CMDMAC__
12152         
12153         /*  Create objects for stdout and stderr  */
12154         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12155         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12156         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12157         rb_gv_set("$stdout", val);
12158         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12159         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12160         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12161         rb_gv_set("$stderr", val);
12162
12163         /*  Create objects for stdin  */
12164         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12165         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12166         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12167         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12168         rb_gv_set("$stdin", val);
12169         
12170 #endif
12171         
12172         /*  Global variable to hold error information  */
12173         rb_define_variable("$backtrace", &gMolbyBacktrace);
12174         rb_define_variable("$error_history", &gMolbyErrorHistory);
12175         gMolbyErrorHistory = rb_ary_new();
12176         
12177         /*  Global variables for script menus  */
12178         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12179         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12180         gScriptMenuCommands = rb_ary_new();
12181         gScriptMenuEnablers = rb_ary_new();
12182         
12183 #if !__CMDMAC__
12184         /*  Register interrupt check code  */
12185         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12186 #endif
12187         
12188 #if !__CMDMAC__
12189         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12190         s_SetIntervalTimer(0, 50);
12191 #endif
12192         
12193         /*  Read the startup script  */
12194         if (script != NULL && script[0] != 0) {
12195                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12196                 gMolbyRunLevel++;
12197                 rb_load_protect(rb_str_new2(script), 0, &status);
12198                 gMolbyRunLevel--;
12199                 if (status != 0)
12200                         Molby_showError(status);
12201                 else
12202                         MyAppCallback_showScriptMessage("Done.\n");
12203         }
12204 }
12205
12206 void
12207 Molby_buildARGV(int argc, const char **argv)
12208 {
12209         int i;
12210     rb_ary_clear(rb_argv);
12211     for (i = 0; i < argc; i++) {
12212                 VALUE arg = rb_tainted_str_new2(argv[i]);
12213                 OBJ_FREEZE(arg);
12214                 rb_ary_push(rb_argv, arg);
12215     }
12216 }