OSDN Git Service

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