OSDN Git Service

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