OSDN Git Service

Copyright and License description for JANPA are included
[molby/Molby.git] / MolLib / Ruby_bind / ruby_bind.c
1 /*
2  *  ruby_bind.c
3  *  Ruby binding
4  *
5  *  Created by Toshi Nagata on 07/11/09.
6  *  Copyright 2007-2008 Toshi Nagata. All rights reserved.
7  *
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation version 2 of the License.
11  
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16 */
17
18 #include "Molby.h"
19 #include "ruby_dialog.h"
20 #include "../MD/MDCore.h"
21
22 #include <stdio.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <limits.h>
27
28 #include "version.h"       /*  for Ruby version  */
29 #include "ruby/version.h"  /*  for RUBY_BIRTH_YEAR etc.  */
30 #include "ruby/encoding.h" /*  for rb_str_encode() etc. */
31 /*#include <node.h>     *//*  for rb_add_event_hook()  */
32
33 #if defined(__WXMAC__) || defined(__CMDMAC__)
34 #define USE_PTHREAD_FOR_TIMER 1
35 #endif
36
37 #if !__WXMSW__
38 #if USE_PTHREAD_FOR_TIMER
39 #include <unistd.h>   /*  for usleep()  */
40 #include <pthread.h>  /*  for pthread  */
41 #else
42 #include <signal.h>   /*  for sigaction()  */
43 #endif
44 #endif
45
46 #include "../Missing.h"
47
48 #pragma mark ====== Global Values ======
49
50 VALUE rb_eMolbyError;
51 VALUE rb_mMolby;
52 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
53 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
54
55 VALUE gMolbyBacktrace;
56 VALUE gMolbyErrorHistory;
57 VALUE gScriptMenuCommands;
58 VALUE gScriptMenuEnablers;
59
60 int gMolbyRunLevel = 0;
61 int gMolbyIsCheckingInterrupt = 0;
62
63 char *gRubyVersion, *gRubyCopyright;
64
65 /*  For convenience  */
66 static ID s_ID_equal;  /*  rb_intern("==")  */
67
68 int g_RubyID_call;
69
70 /*  Symbols for atom attributes  */
71 static VALUE
72         s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
73         s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
74         s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
75         s_RSym, s_XSym, s_YSym, s_ZSym,
76         s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
77         s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
78         s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
79         s_AnisoSym, s_AnisoEigvalSym, s_SymopSym, s_IntChargeSym,
80     s_FixForceSym, s_FixPosSym, s_ExclusionSym, s_MMExcludeSym,
81     s_PeriodicExcludeSym, s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
82
83 /*  Symbols for parameter attributes  */
84 static VALUE
85         s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
86         s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
87         /* s_ASym, s_BSym, */
88         s_ReqSym, s_EpsSym,
89         /* s_A14Sym, s_B14Sym, */
90         s_Req14Sym, s_Eps14Sym,
91         s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym, s_VdwRadiusSym,
92         s_CommentSym, s_SourceSym;
93
94 /*  Symbols for graphics  */
95 static VALUE
96         s_LineSym, s_PolySym, s_CylinderSym, s_ConeSym, s_EllipsoidSym;
97
98 /*
99  *  Utility function
100  *  Get ary[i] by calling "[]" method
101  */
102 VALUE
103 Ruby_ObjectAtIndex(VALUE ary, int idx)
104 {
105         static ID index_method = 0;
106         if (TYPE(ary) == T_ARRAY) {
107                 int len = RARRAY_LEN(ary);
108                 if (idx >= 0 && idx < len)
109                         return (RARRAY_PTR(ary))[idx];
110                 else return Qnil;
111         }
112         if (index_method == 0)
113                 index_method = rb_intern("[]");
114         return rb_funcall(ary, index_method, 1, INT2NUM(idx));
115 }
116
117 char *
118 Ruby_FileStringValuePtr(VALUE *valp)
119 {
120 #if __WXMSW__
121         char *p = strdup(StringValuePtr(*valp));
122         translate_char(p, '/', '\\');
123         *valp = Ruby_NewEncodedStringValue2(p);
124         free(p);
125         return StringValuePtr(*valp);
126 #else
127         return StringValuePtr(*valp);
128 #endif
129 }
130
131 VALUE
132 Ruby_NewFileStringValue(const char *fstr)
133 {
134 #if __WXMSW__
135         VALUE retval;
136         char *p = strdup(fstr);
137         translate_char(p, '\\', '/');
138         retval = Ruby_NewEncodedStringValue2(p);
139         free(p);
140         return retval;
141 #else
142         return Ruby_NewEncodedStringValue2(fstr);
143 #endif
144 }
145
146 char *
147 Ruby_EncodedStringValuePtr(VALUE *valp)
148 {
149         rb_string_value(valp);
150         *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
151         return RSTRING_PTR(*valp);
152 }
153
154 VALUE
155 Ruby_NewEncodedStringValue(const char *str, int len)
156 {
157         if (len <= 0)
158                 len = strlen(str);
159         return rb_enc_str_new(str, len, rb_default_external_encoding());
160 }
161
162 VALUE
163 Ruby_NewEncodedStringValue2(const char *str)
164 {
165     return Ruby_NewEncodedStringValue(str, -1);
166 }
167
168 VALUE
169 Ruby_ObjToStringObj(VALUE val)
170 {
171         switch (TYPE(val)) {
172                 case T_STRING:
173                         return val;
174                 case T_SYMBOL:
175                         return rb_str_new2(rb_id2name(SYM2ID(val)));
176                 default:
177                         return rb_str_to_str(val);
178         }
179 }
180
181 #pragma mark ====== Message input/output ======
182
183 /*
184  *  call-seq:
185  *     message_box(str, title, button = nil, icon = :info)
186  *
187  *  Show a message box.
188  *  Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
189  *  Icon: :info, :warning, :error
190  */
191 static VALUE
192 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
193 {
194         char *str, *title, *s;
195     int buttons, icon, retval;
196         VALUE sval, tval, bval, ival;
197         rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
198         str = StringValuePtr(sval);
199         title = StringValuePtr(tval);
200         if (bval != Qnil) {
201                 bval = Ruby_ObjToStringObj(bval);
202                 s = RSTRING_PTR(bval);
203                 if (strncmp(s, "ok", 2) == 0)
204                         buttons = 1;
205                 else if (strncmp(s, "cancel", 6) == 0)
206                         buttons = 2;
207                 else
208                         rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
209         } else buttons = 3;
210         if (ival != Qnil) {
211                 ival = Ruby_ObjToStringObj(ival);
212                 s = RSTRING_PTR(ival);
213                 if (strncmp(s, "info", 4) == 0)
214                         icon = 1;
215                 else if (strncmp(s, "warn", 4) == 0)
216                         icon = 2;
217                 else if (strncmp(s, "err", 3) == 0)
218                         icon = 3;
219                 else
220                         rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
221         } else icon = 1;
222         retval = MyAppCallback_messageBox(str, title, buttons, icon);
223     return (retval ? Qtrue : Qfalse);
224 }
225
226 /*
227  *  call-seq:
228  *     error_message_box(str)
229  *
230  *  Show an error message box.
231  */
232 static VALUE
233 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
234 {
235         char *str = StringValuePtr(sval);
236         MyAppCallback_errorMessageBox("%s", str);
237         return Qnil;
238 }
239
240 /*
241  *  call-seq:
242  *     ask(prompt, default = nil) -> string
243  *
244  *  Open a modal dialog and get a line of text.
245  */
246 static VALUE
247 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
248 {
249         volatile VALUE prompt, message;
250         char buf[1024];
251         int retval;
252         rb_scan_args(argc, argv, "11", &prompt, &message);
253         if (message != Qnil) {
254                 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
255                 buf[sizeof buf - 1] = 0;
256         } else buf[0] = 0;
257         retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
258         if (retval)
259                 return Ruby_NewEncodedStringValue2(buf);
260         else
261                 return Qnil;    
262 }
263
264 /*
265  *  call-seq:
266  *     show_console_window
267  *
268  *  Show the console window and bring to the front.
269  */
270 static VALUE
271 s_Kernel_ShowConsoleWindow(VALUE self)
272 {
273         MyAppCallback_showConsoleWindow();
274         return Qnil;
275 }
276
277 /*
278  *  call-seq:
279  *     hide_console_window
280  *
281  *  Hide the console window.
282  */
283 static VALUE
284 s_Kernel_HideConsoleWindow(VALUE self)
285 {
286         MyAppCallback_hideConsoleWindow();
287         return Qnil;
288 }
289
290 /*
291  *  call-seq:
292  *     bell
293  *
294  *  Ring the system bell.
295  */
296 static VALUE
297 s_Kernel_Bell(VALUE self)
298 {
299         MyAppCallback_bell();
300         return Qnil;
301 }
302
303 /*
304  *  call-seq:
305  *     play_sound(filename, flag = 0)
306  *
307  *  Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
308  *  1, play the sound asynchronously; 3, play the sound with loop asynchronously
309  */
310 static VALUE
311 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
312 {
313         VALUE fnval, flval;
314         int flag, retval;
315         char *fname;
316         rb_scan_args(argc, argv, "11", &fnval, &flval);
317         if (flval == Qnil)
318                 flag = 0;
319         else flag = NUM2INT(rb_Integer(flval));
320         fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
321         fname = StringValuePtr(fnval);
322         retval = MyAppCallback_playSound(fname, flag);
323         return (retval ? Qtrue : Qnil);
324 }
325
326 /*
327  *  call-seq:
328  *     stop_sound
329  *
330  *  Stop the sound if playing.
331  */
332 static VALUE
333 s_Kernel_StopSound(VALUE self)
334 {
335         MyAppCallback_stopSound();
336         return Qnil;
337 }
338
339 /*
340  *  call-seq:
341  *     export_to_clipboard(str)
342  *
343  *  Export the given string to clipboard.
344  */
345 static VALUE
346 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
347 {
348 #if !defined(__CMDMAC__)
349     const char *s;
350         char *ns;
351     if (!gUseGUI)
352         return Qnil;
353     s = StringValuePtr(sval);
354 #if __WXMSW__
355         /*  Convert the end-of-line characters  */
356         {       const char *p; int nc; char *np;
357                 nc = 0;
358                 for (p = s; *p != 0; p++) {
359                         if (*p == '\n')
360                                 nc++;
361                 }       
362                 ns = (char *)malloc(strlen(s) + nc + 1);
363                 for (np = ns, p = s; *p != 0; p++, np++) {
364                         if (*p == '\n')
365                                 *np++ = '\r';
366                         *np = *p;
367                 }
368                 *np = 0;
369         }
370 #else
371         ns = (char *)malloc(strlen(s) + 1);
372         strcpy(ns, s);
373 #if __WXMAC__
374         {       char *np;
375                 /*  wxMac still has Carbon code. Oops.  */
376                 for (np = ns; *np != 0; np++) {
377                         if (*np == '\n')
378                                 *np = '\r';
379                 }
380         }
381 #endif
382 #endif
383         if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
384                 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
385 #endif
386         return Qnil;
387 }
388
389 /*
390  *  call-seq:
391  *     hartree_to_kcal(val)
392  *
393  *  Convert hartree to kcal/mol
394  */
395 static VALUE
396 s_Kernel_HartreeToKcal(VALUE self, VALUE fval)
397 {
398     double d = NUM2DBL(rb_Float(fval));
399     return rb_float_new(d * 627.5094740630557);
400 }
401
402 /*
403  *  call-seq:
404  *     kcal_to_hartree(val)
405  *
406  *  Convert kcal/mol to hartree
407  */
408 static VALUE
409 s_Kernel_KcalToHartree(VALUE self, VALUE fval)
410 {
411     double d = NUM2DBL(rb_Float(fval));
412     return rb_float_new(d / 627.5094740630557);
413 }
414
415 /*
416  *  call-seq:
417  *     hartree_to_kj(val)
418  *
419  *  Convert hartree to kJ/mol
420  */
421 static VALUE
422 s_Kernel_HartreeToKJ(VALUE self, VALUE fval)
423 {
424     double d = NUM2DBL(rb_Float(fval));
425     return rb_float_new(d * 2625.4996394798253);
426 }
427
428 /*
429  *  call-seq:
430  *     kj_to_hartree(val)
431  *
432  *  Convert kJ/mol to hartree
433  */
434 static VALUE
435 s_Kernel_KJToHartree(VALUE self, VALUE fval)
436 {
437     double d = NUM2DBL(rb_Float(fval));
438     return rb_float_new(d / 2625.4996394798253);
439 }
440
441 /*
442  *  call-seq:
443  *     bohr_to_angstrom(val)
444  *
445  *  Convert bohr to angstrom
446  */
447 static VALUE
448 s_Kernel_BohrToAngstrom(VALUE self, VALUE fval)
449 {
450     double d = NUM2DBL(rb_Float(fval));
451     return rb_float_new(d * 0.529177210903);
452 }
453
454 /*
455  *  call-seq:
456  *     angstrom_to_bohr(val)
457  *
458  *  Convert angstrom to bohr
459  */
460 static VALUE
461 s_Kernel_AngstromToBohr(VALUE self, VALUE fval)
462 {
463     double d = NUM2DBL(rb_Float(fval));
464     return rb_float_new(d / 0.529177210903);
465 }
466
467 /*
468  *  call-seq:
469  *     stdout.write(str)
470  *
471  *  Put the message in the main text view in black color.
472  */
473 static VALUE
474 s_StandardOutput(VALUE self, VALUE str)
475 {
476         int n;
477         MyAppCallback_setConsoleColor(0);
478         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
479         return INT2NUM(n);
480 }
481
482 /*
483  *  call-seq:
484  *     stderr.write(str)
485  *
486  *  Put the message in the main text view in red color.
487  */
488 static VALUE
489 s_StandardErrorOutput(VALUE self, VALUE str)
490 {
491         int n;
492         MyAppCallback_setConsoleColor(1);
493         n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
494         MyAppCallback_setConsoleColor(0);
495         return INT2NUM(n);
496 }
497
498 /*
499  *  call-seq:
500  *     stdout.flush
501  *     stderr.flush
502  *
503  *  Flush the standard (error) output. Actually do nothing.
504  */
505 static VALUE
506 s_FlushConsoleOutput(VALUE self)
507 {
508         return self;
509 }
510
511 /*
512  *  call-seq:
513  *     stdin.gets(rs = $/)
514  *
515  *  Read one line message via dialog box.
516  */
517 static VALUE
518 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
519 {
520         VALUE pval, rval;
521         pval = Ruby_NewEncodedStringValue2("Enter a line:");
522         rval = s_Kernel_Ask(1, &pval, self);
523         if (rval == Qnil)
524                 rb_interrupt();
525         rb_str_cat2(rval, "\n");
526         return rval;
527 }
528
529 /*
530  *  call-seq:
531  *     stdin.method_missing(name, args, ...)
532  *
533  *  Throw an exception, noting only gets and readline are defined.
534  */
535 static VALUE
536 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
537 {
538         VALUE nval;
539         rb_scan_args(argc, argv, "10", &nval);
540         rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
541         return Qnil;  /*  Not reached  */
542 }
543
544 #pragma mark ====== Track key events ======
545
546 /*  User interrupt handling
547  *  User interrupt (command-period on Mac OS) is handled by periodic polling of
548  *  key events. This polling should only be enabled during "normal" execution
549  *  of scripts and must be disabled when the rest of the application (or Ruby
550  *  script itself) is handling GUI. This is ensured by appropriate calls to
551  *  enable_interrupt and disable_interrupt.  */
552
553 static VALUE s_interrupt_flag = Qfalse;
554
555 static VALUE
556 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
557 {
558         volatile VALUE message;
559         const char *p;
560         if (Ruby_GetInterruptFlag() == Qtrue) {
561                 rb_scan_args(argc, argv, "01", &message);
562                 if (message != Qnil)
563                         p = StringValuePtr(message);
564                 else
565                         p = NULL;
566                 MyAppCallback_showProgressPanel(p);
567         }
568         return Qnil;
569 }
570
571 static VALUE
572 s_HideProgressPanel(VALUE self)
573 {
574         MyAppCallback_hideProgressPanel();
575         return Qnil;
576 }
577
578 static VALUE
579 s_SetProgressValue(VALUE self, VALUE val)
580 {
581         double dval = NUM2DBL(rb_Float(val));
582         MyAppCallback_setProgressValue(dval);
583         return Qnil;
584 }
585
586 static VALUE
587 s_SetProgressMessage(VALUE self, VALUE msg)
588 {
589         const char *p;
590         if (msg == Qnil)
591                 p = NULL;
592         else p = StringValuePtr(msg);
593         MyAppCallback_setProgressMessage(p);
594         return Qnil;
595 }
596
597 static VALUE
598 s_SetInterruptFlag(VALUE self, VALUE val)
599 {
600         VALUE oldval;
601         if (val != Qundef) {
602                 if (val == Qfalse || val == Qnil)
603                         val = Qfalse;
604                 else val = Qtrue;
605         }
606         oldval = s_interrupt_flag;
607         if (val != Qundef) {
608                 s_interrupt_flag = val;
609         }
610         return oldval;
611 }
612
613 static VALUE
614 s_GetInterruptFlag(VALUE self)
615 {
616         return s_SetInterruptFlag(self, Qundef);
617 }
618
619 VALUE
620 Ruby_SetInterruptFlag(VALUE val)
621 {
622         return s_SetInterruptFlag(Qnil, val);
623 }
624
625 VALUE
626 Ruby_GetInterruptFlag(void)
627 {
628         return s_SetInterruptFlag(Qnil, Qundef);
629 }
630
631 /*
632  *  call-seq:
633  *     check_interrupt -> integer
634  *
635  *  Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
636  */
637 static VALUE
638 s_Kernel_CheckInterrupt(VALUE self)
639 {
640         if (Ruby_GetInterruptFlag() == Qfalse)
641                 return INT2NUM(-1);
642         else if (MyAppCallback_checkInterrupt())
643                 return INT2NUM(1);
644         else return INT2NUM(0);
645 }
646
647 static volatile unsigned long sITimerCount = 0;
648
649 #if __WXMSW__
650 static HANDLE sITimerEvent;
651 static HANDLE sITimerThread;
652 static int sITimerInterval;
653
654 static __stdcall unsigned
655 s_ITimerThreadFunc(void *p)
656 {
657         while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
658                 sITimerCount++;
659         }
660         return 0;
661 }
662
663 #elif USE_PTHREAD_FOR_TIMER
664
665 /*  Timer thread  */
666 static pthread_t sTimerThread;
667
668 /*  -1: uninitiated; 0: active, 1: inactive, -2: request to terminate  */
669 static volatile signed char sTimerFlag = -1;
670 static volatile int sTimerIntervalMicrosec = 0;
671
672 static void *
673 s_TimerThreadEntry(void *param)
674 {
675         while (1) {
676                 usleep(sTimerIntervalMicrosec);
677                 if (sTimerFlag == 0)
678                         sITimerCount++;
679                 else if (sTimerFlag == -2)
680                         break;
681         }
682         return NULL;    
683 }
684
685 #endif
686
687 static void
688 s_SignalAction(int n)
689 {
690         sITimerCount++;
691 }
692
693 static void
694 s_SetIntervalTimer(int n, int msec)
695 {
696 #if __WXMSW__
697         if (n == 0) {
698                 /*  Start interval timer  */
699                 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
700                 sITimerInterval = msec;
701                 if (sITimerEvent) {
702                         sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
703                 }
704         } else {
705                 /*  Stop interval timer  */
706                 if (sITimerEvent)
707                         SetEvent(sITimerEvent);   /*  Tell thread to terminate  */
708                 if (sITimerThread) {
709                         WaitForSingleObject(sITimerThread, 1000);
710                         CloseHandle(sITimerThread);
711                 }
712                 if (sITimerEvent)
713                         CloseHandle(sITimerEvent);
714                 sITimerEvent = NULL;
715                 sITimerThread = NULL;
716         }
717 #elif USE_PTHREAD_FOR_TIMER
718         if (n == 0) {
719                 if (sTimerFlag == -1) {
720                         int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
721                         if (status != 0) {
722                                 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
723                         }
724                 }
725                 sTimerFlag = 0;  /*  Active  */
726                 sTimerIntervalMicrosec = msec * 1000;
727         } else if (sTimerFlag != -1)
728                 sTimerFlag = 1;  /*  Inactive  */       
729 #else
730         static struct itimerval sOldValue;
731         static struct sigaction sOldAction;
732         struct itimerval val;
733         struct sigaction act;
734         if (n == 0) {
735                 sITimerCount = 0;
736                 act.sa_handler = s_SignalAction;
737                 act.sa_mask = 0;
738                 act.sa_flags = 0;
739                 sigaction(SIGALRM, &act, &sOldAction);
740                 val.it_value.tv_sec = 0;
741                 val.it_value.tv_usec = msec * 1000;
742                 val.it_interval.tv_sec = 0;
743                 val.it_interval.tv_usec = msec * 1000;
744                 setitimer(ITIMER_REAL, &val, &sOldValue);
745         } else {
746                 setitimer(ITIMER_REAL, &sOldValue, &val);
747                 sigaction(SIGALRM, &sOldAction, &act);
748         }
749 #endif
750 }
751
752 static unsigned long
753 s_GetTimerCount(void)
754 {
755         return sITimerCount;
756 }
757
758 static void
759 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
760 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
761 {
762         if (s_interrupt_flag != Qfalse) {
763                 static unsigned long sLastTime = 0;
764                 unsigned long currentTime;
765                 int flag;
766                 currentTime = s_GetTimerCount();
767                 if (currentTime != sLastTime) {
768                         sLastTime = currentTime;
769                         gMolbyIsCheckingInterrupt = 1;
770                         flag = MyAppCallback_checkInterrupt();
771                         gMolbyIsCheckingInterrupt = 0;
772                         if (flag) {
773                                 s_SetInterruptFlag(Qnil, Qfalse);
774                                 rb_interrupt();
775                         }
776                 }
777         }
778 }
779
780 #pragma mark ====== Menu handling ======
781
782 /*
783  *  call-seq:
784  *     register_menu(title, method, enable_proc = nil)
785  *
786  *  Register the method (specified as a symbol) in the script menu.
787  *  The method must be either an instance method of Molecule with no argument,
788  *  or a class method of Molecule with one argument (the current molecule),
789  *  or a proc object with one argument (the current molecule).
790  *  The menu associated with the class method can be invoked even when no document
791  *  is open (the argument is set to Qnil in this case). On the other hand, the
792  *  menu associated with the instance method can only be invoked when at least one 
793  *  document is active.
794  *  If enable_proc is non-nil, then it is called whenever the availability of
795  *  the menu command is tested. It is usually a proc object with one argument
796  *  (the current molecule or nil). As a special case, the following symbols can
797  *  be given; :mol (enabled when any molecule is open), :non_empty (enabled when
798  *  the top-level molecule has at least one atom), :selection (enabled when
799  *  the top-level molecule has one or more selected atoms).
800  */
801 static VALUE
802 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
803 {
804         int n, mtype = 0;
805         VALUE tval, mval, pval;
806         static VALUE sMolSym, sNonEmptySym, sSelectionSym;
807         static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
808         rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
809         tval = rb_str_to_str(tval);
810         n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
811         if (n < 0)
812                 return Qnil;
813         if (TYPE(mval) == T_SYMBOL) {
814                 /*  Create an appropriate proc object  */
815                 const char *name = rb_id2name(SYM2ID(mval));
816                 char *s;
817                 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
818                         /*  Defined as a Molecule method  */
819                         asprintf(&s, "lambda { |m| m.%s }", name);
820                         mtype = 1;
821                 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
822                         /*  Defined as a Molecule class method  */
823                         asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
824                         mtype = 2;
825                 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
826                 mval = rb_eval_string(s);
827                 free(s);
828         }
829         if (sMolSym == Qfalse) {
830                 sMolSym = ID2SYM(rb_intern("mol"));
831                 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
832                 sSelectionSym = ID2SYM(rb_intern("selection"));
833                 sMolProc = rb_eval_string("lambda { |m| m != nil }");
834         rb_define_variable("$is_a_molecule_proc", &sMolProc);
835                 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
836         rb_define_variable("$is_molecule_not_empty_proc", &sNonEmptyProc);
837                 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
838         rb_define_variable("$has_molecule_selection_proc", &sSelectionProc);
839                 sTrueProc = rb_eval_string("lambda { |m| true }");
840         rb_define_variable("$always_true_proc", &sTrueProc);
841         }
842         
843         if (pval == Qnil) {
844                 if (mtype == 1)
845                         pval = sMolProc;
846                 else
847                         pval = sTrueProc;
848         } else if (pval == sMolSym)
849                 pval = sMolProc;
850         else if (pval == sNonEmptySym)
851                 pval = sNonEmptyProc;
852         else if (pval == sSelectionSym)
853                 pval = sSelectionProc;
854         rb_ary_store(gScriptMenuCommands, n, mval);
855         rb_ary_store(gScriptMenuEnablers, n, pval);
856         return INT2NUM(n);
857 }
858
859 static VALUE
860 s_Kernel_LookupMenu(VALUE self, VALUE title)
861 {
862         int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
863         return INT2NUM(n);
864 }
865
866 static VALUE
867 s_Ruby_UpdateUI_handler(VALUE data)
868 {
869         void **p = (void **)data;
870         int index = (intptr_t)p[0];
871         Molecule *mol = (Molecule *)p[1];
872         int *outChecked = (int *)p[2];
873         char **outTitle = (char **)p[3];
874         VALUE mval = ValueFromMolecule(mol);
875         VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
876         static ID call_id = 0;
877         if (call_id == 0)
878                 call_id = rb_intern("call");
879         if (pval == Qnil)
880                 return Qnil;
881         pval = rb_funcall(pval, call_id, 1, mval);
882         if (rb_obj_is_kind_of(pval, rb_cArray)) {
883                 VALUE val;
884                 if (outChecked != NULL) {
885                         val = rb_ary_entry(pval, 1);  /*  Checked or not  */
886                         *outChecked = (RTEST(val) ? 1 : 0);
887                 }
888                 if (outTitle != NULL) {
889                         val = rb_ary_entry(pval, 2);  /*  Text  */
890                         if (TYPE(val) == T_STRING) {
891                                 *outTitle = strdup(StringValuePtr(val));
892                         }
893                 }
894                 pval = rb_ary_entry(pval, 0);
895         }
896         return pval;
897 }
898
899 int
900 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
901 {
902         int status;
903         void *p[4];
904         VALUE retval;
905         p[0] = (void *)(intptr_t)index;
906         p[1] = mol;
907         p[2] = outChecked;
908         p[3] = outTitle;
909         retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
910         return (RTEST(retval) ? 1 : 0);
911 }
912
913 /*
914 static VALUE
915 s_Ruby_methodType_sub(VALUE data)
916 {
917         const char **p = (const char **)data;
918         VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
919         ID mid = rb_intern(p[1]);
920         int ival;
921         if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
922                 ival = 1;
923         else if (rb_respond_to(klass, mid))
924                 ival = 2;
925         else ival = 0;
926         return INT2FIX(ival);
927 }
928 */      
929 /*  Returns 1 if the class defines the instance method with the given name, 2 if the class
930     has the singleton method (class method) with the given name, 0 otherwise.  */
931 /*int
932 Ruby_methodType(const char *className, const char *methodName)
933 {
934         int status;
935         VALUE retval;
936         const char *p[2];
937         p[0] = className;
938         p[1] = methodName;
939         retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
940         if (status == 0)
941                 return FIX2INT(retval);
942         else return 0;
943 }
944 */
945
946 /*
947  *  call-seq:
948  *     execute_script_file(fname)
949  *
950  *  Execute the script in the given file. If a molecule is active, then
951  *  the script is evaluated as Molecule.current.instance_eval(script).
952  *  Before entering the script, the current directory is set to the parent
953  *  directory of the script.
954  */
955 static VALUE
956 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
957 {
958         int status;
959         VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
960         if (retval == (VALUE)6 && status == -1)
961                 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
962         if (status != 0)
963                 rb_jump_tag(status);
964         return retval;
965 }
966
967 /*
968  *  call-seq:
969  *     document_home
970  *
971  *  Get the directory suitable for storing user documents. On Windows
972  *  it is the home directory + "My Documents". On other platforms
973  *  it is the home directory.
974  */
975 static VALUE
976 s_Kernel_DocumentHome(VALUE self)
977 {
978         char *s = MyAppCallback_getDocumentHomeDir();
979         VALUE retval = Ruby_NewFileStringValue(s);
980         free(s);
981         return retval;
982 }
983
984 /*  The callback function for call_subprocess  */
985 static int
986 s_Kernel_CallSubProcess_Callback(void *data)
987 {
988         int status;
989         VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
990         if (status != 0 || retval == Qnil || retval == Qfalse)
991                 return 1;
992         else return 0;
993 }
994
995 /*
996  *  call-seq:
997  *     call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
998  *
999  *  Call subprocess. A progress dialog window is displayed, with a message
1000  *  "Running #{process_name}...".
1001  *  cmd is either a single string of an array of string. If it is a single string, then
1002  *  it will be given to wxExecute as a single argument. In this case, the string can be
1003  *  split into arguments by whitespace. If this behavior is not intended, then use an array
1004  *  containing a single string.
1005  *  A callback proc can be given, which is called periodically during execution. If the proc returns
1006  *  nil or false, then the execution will be interrupted.
1007  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
1008  *  filename begins with ">>", then the message will be appended to the file.
1009  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
1010  *  If the argument is nil, then the message will be sent to the Ruby console.
1011  */
1012 static VALUE
1013 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
1014 {
1015         VALUE cmd, procname, cproc, stdout_val, stderr_val;
1016     VALUE save_interruptFlag;
1017         int n, exitstatus, pid;
1018         char *sout, *serr;
1019     const char *pnamestr, **cmdargv;
1020         FILE *fpout, *fperr;
1021
1022         rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
1023
1024         if (stdout_val == Qnil) {
1025                 fpout = (FILE *)1;
1026         } else {
1027                 sout = StringValuePtr(stdout_val);
1028                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
1029                         fpout = NULL;
1030                 else {
1031                         if (strncmp(sout, ">>", 2) == 0) {
1032                                 sout += 2;
1033                                 fpout = fopen(sout, "a");
1034                         } else {
1035                                 if (*sout == '>')
1036                                         sout++;
1037                                 fpout = fopen(sout, "w");
1038                         }
1039                         if (fpout == NULL)
1040                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
1041                 }
1042         }
1043         if (stderr_val == Qnil) {
1044                 fperr = (FILE *)1;
1045         } else {
1046                 serr = StringValuePtr(stderr_val);
1047                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
1048                         fperr = NULL;
1049                 else {
1050                         if (strncmp(serr, ">>", 2) == 0) {
1051                                 serr += 2;
1052                                 fpout = fopen(serr, "a");
1053                         } else {
1054                                 if (*serr == '>')
1055                                         serr++;
1056                                 fperr = fopen(serr, "w");
1057                         }
1058                         if (fperr == NULL)
1059                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
1060                 }
1061         }
1062     
1063     save_interruptFlag = s_SetInterruptFlag(self, Qnil);
1064     if (procname != Qnil)
1065         pnamestr = StringValuePtr(procname);
1066     else pnamestr = NULL;
1067     if (rb_obj_is_kind_of(cmd, rb_cString)) {
1068         cmdargv = calloc(sizeof(cmdargv[0]), 3);
1069         cmdargv[0] = StringValuePtr(cmd);
1070         cmdargv[1] = "";
1071         cmdargv[2] = NULL;
1072     } else {
1073         cmd = rb_ary_to_ary(cmd);
1074         cmdargv = calloc(sizeof(cmdargv[0]), RARRAY_LEN(cmd) + 1);
1075         for (n = 0; n < RARRAY_LEN(cmd); n++) {
1076             cmdargv[n] = StringValuePtr(RARRAY_PTR(cmd)[n]);
1077         }
1078         cmdargv[n] = NULL;
1079     }
1080         n = MyAppCallback_callSubProcess(cmdargv, pnamestr, (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
1081     s_SetInterruptFlag(self, save_interruptFlag);
1082     free(cmdargv);
1083
1084         if (fpout != NULL && fpout != (FILE *)1)
1085                 fclose(fpout);
1086         if (fperr != NULL && fperr != (FILE *)1)
1087                 fclose(fperr);
1088
1089         return INT2NUM(n);
1090
1091         
1092 }
1093
1094 /*
1095  *  call-seq:
1096  *     backquote(cmd)
1097  *
1098  *  Same as the builtin backquote, except that, under Windows, no console window gets opened.
1099  */
1100 static VALUE
1101 s_Kernel_Backquote(VALUE self, VALUE cmd)
1102 {
1103         char *buf;
1104         int n, exitstatus, pid;
1105         VALUE val;
1106     const char *cmdargv[3];
1107     cmdargv[0] = StringValuePtr(cmd);
1108     cmdargv[1] = "";
1109     cmdargv[2] = NULL;
1110         n = MyAppCallback_callSubProcess(cmdargv, NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1111 /*      fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1112         if (n >= 0 && buf != NULL) {
1113                 val = Ruby_NewEncodedStringValue(buf, 0);
1114                 free(buf);
1115         } else {
1116                 val = Ruby_NewEncodedStringValue("", 0);
1117         }
1118         rb_last_status_set(exitstatus, pid);
1119         return val;
1120 }
1121
1122 #pragma mark ====== User defaults ======
1123
1124 /*
1125  *  call-seq:
1126  *     get_global_settings(key)
1127  *
1128  *  Get a setting data for key from the application preferences.
1129  */
1130 static VALUE
1131 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1132 {
1133         char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1134         if (p != NULL) {
1135                 VALUE retval = rb_eval_string(p);
1136                 free(p);
1137                 return retval;
1138         } else return Qnil;
1139 }
1140
1141 /*
1142  *  call-seq:
1143  *     set_global_settings(key, value)
1144  *
1145  *  Set a setting data for key to the application preferences.
1146  */
1147 static VALUE
1148 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1149 {
1150         VALUE sval = rb_inspect(value);
1151         MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1152         return value;
1153 }
1154
1155 #pragma mark ====== IO extension ======
1156
1157 static VALUE
1158 s_Ruby_str_encode_protected(VALUE val)
1159 {
1160         return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1161                                   ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1162 }
1163
1164 /*
1165  *  call-seq:
1166  *     gets_any_eol
1167  *
1168  *  A gets variant that works for CR, LF, and CRLF end-of-line characters. 
1169  */
1170 static VALUE
1171 s_IO_gets_any_eol(VALUE self)
1172 {
1173         VALUE val, val2, cval;
1174         char buf[1024];
1175         int i, c, status;
1176         static ID id_getbyte = 0, id_ungetbyte;
1177         if (id_getbyte == 0) {
1178                 id_getbyte = rb_intern("getbyte");
1179                 id_ungetbyte = rb_intern("ungetbyte");
1180         }
1181         i = 0;
1182         val = Qnil;
1183         while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1184                 c = NUM2INT(rb_Integer(cval));
1185                 if (c == 0x0d) {
1186                         cval = rb_funcall(self, id_getbyte, 0);
1187                         if (cval != Qnil) {
1188                                 c = NUM2INT(rb_Integer(cval));
1189                                 if (c != 0x0a)
1190                                         rb_funcall(self, id_ungetbyte, 1, cval);
1191                         }
1192                         break;
1193                 } else if (c != 0x0a) {
1194                         buf[i++] = c;
1195                         if (i >= 1020) {
1196                                 buf[i] = 0;
1197                                 if (val == Qnil)
1198                                         val = rb_str_new(buf, i);
1199                                 else
1200                                         rb_str_append(val, rb_str_new(buf, i));
1201                                 i = 0;
1202                         }
1203                 } else break;
1204         }
1205         if (cval == Qnil && i == 0 && val == Qnil)
1206                 return Qnil;  /*  End of file  */
1207         buf[i] = 0;
1208         if (val == Qnil)
1209                 val = rb_str_new(buf, i);
1210         else if (i > 0)
1211                 rb_str_append(val, rb_str_new(buf, i));
1212         val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /*  Ignore exception  */
1213         if (status == 0)
1214                 val = val2;
1215         if (cval != Qnil) {
1216                 /*  Needs a end-of-line mark  */
1217                 cval = rb_gv_get("$/");
1218                 rb_str_append(val, cval);
1219         }
1220         rb_gv_set("$_", val);
1221         return val;
1222 }
1223
1224 #pragma mark ====== Utility functions (protected funcall) ======
1225
1226 struct Ruby_funcall2_record {
1227         VALUE recv;
1228         ID mid;
1229         int argc;
1230         VALUE *argv;
1231 };
1232
1233 static VALUE
1234 s_Ruby_funcall2_sub(VALUE data)
1235 {
1236         struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1237         return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1238 }
1239
1240 VALUE
1241 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1242 {
1243         struct Ruby_funcall2_record rec;
1244         rec.recv = recv;
1245         rec.mid = mid;
1246         rec.argc = argc;
1247         rec.argv = argv;
1248         return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1249 }
1250
1251 RubyValue
1252 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1253 {
1254         return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1255 }
1256
1257 #pragma mark ====== ParameterRef Class ======
1258
1259 static UnionPar *
1260 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1261 {
1262         UnionPar *up;
1263         ParameterRef *pref;
1264         Data_Get_Struct(self, ParameterRef, pref);
1265         if (typep != NULL)
1266                 *typep = pref->parType;
1267         if (pref->parType == kElementParType) {
1268                 up = (UnionPar *)&gElementParameters[pref->idx];
1269         } else {
1270                 up = ParameterRefGetPar(pref);
1271                 if (checkEditable) {
1272                         if (pref->idx < 0)
1273                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1274                         if (up->bond.src != 0 && up->bond.src != -1)
1275                                 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1276                 }
1277         }
1278         return up;
1279 }
1280
1281 static void
1282 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1283 {
1284         UnionPar *up;
1285         ParameterRef *pref;
1286         Data_Get_Struct(self, ParameterRef, pref);
1287         if (pref->mol == NULL)
1288                 return;
1289         up = ParameterRefGetPar(pref);
1290         if (key != s_SourceSym)
1291                 up->bond.src = 0;  /*  Becomes automatically molecule-local  */
1292         if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1293                 /*  Register undo  */
1294                 MolAction *act;
1295                 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1296                 MolActionCallback_registerUndo(pref->mol, act);
1297                 MoleculeCallback_notifyModification(pref->mol, 0);
1298                 pref->mol->needsMDRebuild = 1;
1299         }
1300 }
1301
1302 VALUE
1303 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1304 {
1305         ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1306         if (pref != NULL)
1307                 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1308         else
1309                 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1310 }
1311
1312 static int
1313 s_AtomTypeIndexFromValue(VALUE val)
1314 {
1315         if (rb_obj_is_kind_of(val, rb_cNumeric))
1316                 return NUM2INT(val);
1317         else
1318                 return AtomTypeEncodeToUInt(StringValuePtr(val));
1319 }
1320
1321 static const char *s_ParameterTypeNames[] = {
1322         "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1323 };
1324 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1325
1326 static int
1327 s_ParTypeFromValue(VALUE val)
1328 {
1329         int i, n;
1330         ID valid;
1331         n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1332         if (s_ParameterTypeIDs[0] == 0) {
1333                 for (i = 0; i < n; i++)
1334                         s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1335         }
1336         valid = rb_to_id(val);
1337         for (i = 0; i < n; i++) {
1338                 if (valid == s_ParameterTypeIDs[i]) {
1339                         if (i == 7)
1340                                 return kElementParType;
1341                         else return kFirstParType + i;
1342                 }
1343         }
1344         return kInvalidParType;
1345 }
1346
1347 /*
1348  *  call-seq:
1349  *     index -> Integer
1350  *
1351  *  Get the index in the parameter list.
1352  */
1353 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1354         ParameterRef *pref;
1355         Data_Get_Struct(self, ParameterRef, pref);
1356         return INT2NUM(pref->idx);
1357 }
1358
1359 /*
1360  *  call-seq:
1361  *     par_type -> String
1362  *
1363  *  Get the parameter type, like "bond", "angle", etc.
1364  */
1365 static VALUE s_ParameterRef_GetParType(VALUE self) {
1366         Int tp;
1367         s_UnionParFromValue(self, &tp, 0);
1368         if (tp == kElementParType)
1369                 return Ruby_NewEncodedStringValue2("element");
1370         tp -= kFirstParType;
1371         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1372                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1373         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1374 }
1375
1376 /*
1377  *  call-seq:
1378  *     atom_type -> String or Array of String
1379  *     atom_types -> String or Array of String
1380  *
1381  *  Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1382  *  is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1383  *  is returned. For a dihedral or improper parameter, an array of four strings is returned.
1384  *  The atom type may be "X", which is a wildcard that matches any atom type.
1385  */
1386 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1387         UnionPar *up;
1388         Int tp, i, n;
1389         UInt types[4];
1390         VALUE vals[4];
1391         up = s_UnionParFromValue(self, &tp, 0);
1392         n = ParameterGetAtomTypes(tp, up, types);
1393         if (n == 0)
1394                 rb_raise(rb_eMolbyError, "invalid member atom_types");
1395         for (i = 0; i < n; i++) {
1396                 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1397                         vals[i] = INT2NUM(types[i]);
1398                 else
1399                         vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1400         }
1401         if (n == 1)
1402                 return vals[0];
1403         else
1404                 return rb_ary_new4(n, vals);
1405 }
1406
1407 /*
1408  *  call-seq:
1409  *     k -> Float
1410  *
1411  *  Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1412  */
1413 static VALUE s_ParameterRef_GetK(VALUE self) {
1414         UnionPar *up;
1415         Int tp, i, n;
1416         VALUE vals[3];
1417         up = s_UnionParFromValue(self, &tp, 0);
1418         switch (tp) {
1419                 case kBondParType:
1420                         return rb_float_new(up->bond.k * INTERNAL2KCAL);
1421                 case kAngleParType:
1422                         return rb_float_new(up->angle.k * INTERNAL2KCAL);
1423                 case kDihedralParType:
1424                 case kImproperParType:
1425                         if (up->torsion.mult == 1)
1426                                 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1427                         n = up->torsion.mult;
1428                         if (n > 3)
1429                                 n = 3;
1430                         for (i = 0; i < n; i++)
1431                                 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1432                         return rb_ary_new4(n, vals);
1433                 default:
1434                         rb_raise(rb_eMolbyError, "invalid member k");
1435         }
1436 }
1437
1438 /*
1439  *  call-seq:
1440  *     r0 -> Float
1441  *
1442  *  Get the equilibrium bond length. Only available for bond parameters.
1443  */
1444 static VALUE s_ParameterRef_GetR0(VALUE self) {
1445         UnionPar *up;
1446         Int tp;
1447         up = s_UnionParFromValue(self, &tp, 0);
1448         if (tp == kBondParType)
1449                 return rb_float_new(up->bond.r0);
1450         else rb_raise(rb_eMolbyError, "invalid member r0");
1451 }
1452
1453 /*
1454  *  call-seq:
1455  *     a0 -> Float
1456  *
1457  *  Get the equilibrium angle (in degree). Only available for angle parameters.
1458  */
1459 static VALUE s_ParameterRef_GetA0(VALUE self) {
1460         UnionPar *up;
1461         Int tp;
1462         up = s_UnionParFromValue(self, &tp, 0);
1463         if (tp == kAngleParType)
1464                 return rb_float_new(up->angle.a0 * kRad2Deg);
1465         else rb_raise(rb_eMolbyError, "invalid member a0");
1466 }
1467
1468 /*
1469  *  call-seq:
1470  *     mult -> Float
1471  *
1472  *  Get the multiplicity. Only available for dihedral and improper parameters.
1473  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1474  */
1475 static VALUE s_ParameterRef_GetMult(VALUE self) {
1476         UnionPar *up;
1477         Int tp;
1478         up = s_UnionParFromValue(self, &tp, 0);
1479         if (tp == kDihedralParType || tp == kImproperParType)
1480                 return rb_float_new(up->torsion.mult);
1481         else rb_raise(rb_eMolbyError, "invalid member mult");
1482 }
1483
1484 /*
1485  *  call-seq:
1486  *     period -> Integer or Array of Integers
1487  *
1488  *  Get the periodicity. Only available for dihedral and improper parameters.
1489  *  If the multiplicity is larger than 1, then an array of integers is returned. 
1490  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1491  */
1492 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1493         UnionPar *up;
1494         Int tp, i, n;
1495         VALUE vals[3];
1496         up = s_UnionParFromValue(self, &tp, 0);
1497         if (tp == kDihedralParType || tp == kImproperParType) {
1498                 if (up->torsion.mult == 1)
1499                         return INT2NUM(up->torsion.period[0]);
1500                 n = up->torsion.mult;
1501                 if (n > 3)
1502                         n = 3;
1503                 for (i = 0; i < n; i++)
1504                         vals[i] = INT2NUM(up->torsion.period[i]);
1505                 return rb_ary_new4(n, vals);
1506         } else rb_raise(rb_eMolbyError, "invalid member period");
1507 }
1508
1509 /*
1510  *  call-seq:
1511  *     phi0 -> Float or Array of Floats
1512  *
1513  *  Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1514  *  If the multiplicity is larger than 1, then an array of floats is returned. 
1515  *  (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1516  */
1517 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1518         UnionPar *up;
1519         Int tp, i, n;
1520         VALUE vals[3];
1521         up = s_UnionParFromValue(self, &tp, 0);
1522         if (tp == kDihedralParType || tp == kImproperParType) {
1523                 if (up->torsion.mult == 1)
1524                         return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1525                 n = up->torsion.mult;
1526                 if (n > 3)
1527                         n = 3;
1528                 for (i = 0; i < n; i++)
1529                         vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1530                 return rb_ary_new4(n, vals);
1531         } else rb_raise(rb_eMolbyError, "invalid member phi0");
1532 }
1533
1534 /*
1535  *  call-seq:
1536  *     A -> Float
1537  *
1538  *  Get the "A" value for the van der Waals parameter.
1539  */
1540 /*
1541  static VALUE s_ParameterRef_GetA(VALUE self) {
1542         UnionPar *up;
1543         Int tp;
1544         up = s_UnionParFromValue(self, &tp, 0);
1545         if (tp == kVdwParType)
1546                 return rb_float_new(up->vdw.A);
1547         else if (tp == kVdwPairParType)
1548                 return rb_float_new(up->vdwp.A);
1549         else rb_raise(rb_eMolbyError, "invalid member A");
1550 }
1551 */
1552
1553 /*
1554  *  call-seq:
1555  *     B -> Float
1556  *
1557  *  Get the "B" value for the van der Waals parameter.
1558  */
1559 /*
1560 static VALUE s_ParameterRef_GetB(VALUE self) {
1561         UnionPar *up;
1562         Int tp;
1563         up = s_UnionParFromValue(self, &tp, 0);
1564         if (tp == kVdwParType)
1565                 return rb_float_new(up->vdw.B);
1566         else if (tp == kVdwPairParType)
1567                 return rb_float_new(up->vdwp.B);
1568         else rb_raise(rb_eMolbyError, "invalid member B");
1569 }
1570 */
1571
1572 /*
1573  *  call-seq:
1574  *     r_eq -> Float
1575  *
1576  *  Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1577  */
1578 static VALUE s_ParameterRef_GetReq(VALUE self) {
1579         UnionPar *up;
1580         Int tp;
1581 /*      Double a, b, r; */
1582         Double r;
1583         up = s_UnionParFromValue(self, &tp, 0);
1584         if (tp == kVdwParType) {
1585         /*      a = up->vdw.A;
1586                 b = up->vdw.B;  */
1587                 r = up->vdw.r_eq;
1588         } else if (tp == kVdwPairParType) {
1589         /*      a = up->vdwp.A;
1590                 b = up->vdwp.B;  */
1591                 r = up->vdwp.r_eq;
1592         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1593 /*      if (a == 0.0 || b == 0.0) */
1594         return rb_float_new(r);
1595 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1596 }
1597
1598 /*
1599  *  call-seq:
1600  *     eps -> Float
1601  *
1602  *  Get the minimum energy for the van der Waals parameter.
1603  */
1604 static VALUE s_ParameterRef_GetEps(VALUE self) {
1605         UnionPar *up;
1606         Int tp;
1607 /*      Double a, b; */
1608         Double eps;
1609         up = s_UnionParFromValue(self, &tp, 0);
1610         if (tp == kVdwParType) {
1611         /*      a = up->vdw.A;
1612                 b = up->vdw.B;  */
1613                 eps = up->vdw.eps;
1614         } else if (tp == kVdwPairParType) {
1615         /*      a = up->vdwp.A;
1616                 b = up->vdwp.B; */
1617                 eps = up->vdwp.eps;
1618         } else rb_raise(rb_eMolbyError, "invalid member eps");
1619 /*      if (a == 0.0 || b == 0.0)  */
1620                 return rb_float_new(eps * INTERNAL2KCAL);
1621 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1622 }
1623
1624 /*
1625  *  call-seq:
1626  *     A14 -> Float
1627  *
1628  *  Get the "A" value for the 1-4 van der Waals parameter.
1629  */
1630 /*
1631 static VALUE s_ParameterRef_GetA14(VALUE self) {
1632         UnionPar *up;
1633         Int tp;
1634         up = s_UnionParFromValue(self, &tp, 0);
1635         if (tp == kVdwParType)
1636                 return rb_float_new(up->vdw.A14);
1637         else if (tp == kVdwPairParType)
1638                 return rb_float_new(up->vdwp.A14);
1639         else rb_raise(rb_eMolbyError, "invalid member A14");
1640 }
1641 */
1642
1643 /*
1644  *  call-seq:
1645  *     B14 -> Float
1646  *
1647  *  Get the "B" value for the 1-4 van der Waals parameter.
1648  */
1649 /*
1650 static VALUE s_ParameterRef_GetB14(VALUE self) {
1651         UnionPar *up;
1652         Int tp;
1653         up = s_UnionParFromValue(self, &tp, 0);
1654         if (tp == kVdwParType)
1655                 return rb_float_new(up->vdw.B14);
1656         else if (tp == kVdwPairParType)
1657                 return rb_float_new(up->vdwp.B14);
1658         else rb_raise(rb_eMolbyError, "invalid member B14");
1659 }
1660 */
1661
1662 /*
1663  *  call-seq:
1664  *     r_eq14 -> Float
1665  *
1666  *  Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1667  */
1668 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1669         UnionPar *up;
1670         Int tp;
1671 /*      Double a, b, r; */
1672         Double r;
1673         up = s_UnionParFromValue(self, &tp, 0);
1674         if (tp == kVdwParType) {
1675         /*      a = up->vdw.A14;
1676                 b = up->vdw.B14; */
1677                 r = up->vdw.r_eq14;
1678         } else if (tp == kVdwPairParType) {
1679         /*      a = up->vdwp.A14;
1680                 b = up->vdwp.B14;  */
1681                 r = up->vdwp.r_eq14;
1682         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1683 /*      if (a == 0.0 || b == 0.0)  */
1684         return rb_float_new(r);
1685 /*      else return rb_float_new(pow(2*a/b, 1.0/6.0));  */
1686 }
1687
1688 /*
1689  *  call-seq:
1690  *     eps14 -> Float
1691  *
1692  *  Get the minimum energy for the 1-4 van der Waals parameter.
1693  */
1694 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1695         UnionPar *up;
1696         Int tp;
1697 /*      Double a, b;  */
1698         Double eps;
1699         up = s_UnionParFromValue(self, &tp, 0);
1700         if (tp == kVdwParType) {
1701         /*      a = up->vdw.A14;
1702                 b = up->vdw.B14;  */
1703                 eps = up->vdw.eps14;
1704         } else if (tp == kVdwPairParType) {
1705         /*      a = up->vdwp.A14;
1706                 b = up->vdwp.B14; */
1707                 eps = up->vdwp.eps14;
1708         } else rb_raise(rb_eMolbyError, "invalid member eps14");
1709 /*      if (a == 0.0 || b == 0.0) */
1710         return rb_float_new(eps * INTERNAL2KCAL);
1711 /*      else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL);  */
1712 }
1713
1714 /*
1715  *  call-seq:
1716  *     cutoff -> Float
1717  *
1718  *  Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1719  */
1720 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1721         UnionPar *up;
1722         Int tp;
1723         up = s_UnionParFromValue(self, &tp, 0);
1724         if (tp == kVdwCutoffParType)
1725                 return rb_float_new(up->vdwcutoff.cutoff);
1726         else rb_raise(rb_eMolbyError, "invalid member cutoff");
1727 }
1728
1729 /*
1730  *  call-seq:
1731  *     radius -> Float
1732  *
1733  *  Get the atomic (covalent) radius for the element parameter.
1734  */
1735 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1736         UnionPar *up;
1737         Int tp;
1738         up = s_UnionParFromValue(self, &tp, 0);
1739         if (tp == kElementParType)
1740                 return rb_float_new(up->atom.radius);
1741         else rb_raise(rb_eMolbyError, "invalid member radius");
1742 }
1743
1744 /*
1745  *  call-seq:
1746  *     vdw_radius -> Float
1747  *
1748  *  Get the van der Waals radius for the element parameter. (0 if not given)
1749  */
1750 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1751         UnionPar *up;
1752         Int tp;
1753         up = s_UnionParFromValue(self, &tp, 0);
1754         if (tp == kElementParType)
1755                 return rb_float_new(up->atom.vdw_radius);
1756         else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1757 }
1758
1759 /*
1760  *  call-seq:
1761  *     color -> [Float, Float, Float]
1762  *
1763  *  Get the rgb color for the element parameter.
1764  */
1765 static VALUE s_ParameterRef_GetColor(VALUE self) {
1766         UnionPar *up;
1767         Int tp;
1768         up = s_UnionParFromValue(self, &tp, 0);
1769         if (tp == kElementParType)
1770                 return rb_ary_new3(3, rb_float_new(up->atom.red / 65535.0), rb_float_new(up->atom.green / 65535.0), rb_float_new(up->atom.blue / 65535.0));
1771         else rb_raise(rb_eMolbyError, "invalid member color");
1772 }
1773
1774 /*
1775  *  call-seq:
1776  *     atomic_number -> Integer
1777  *
1778  *  Get the atomic number for the vdw or element parameter.
1779  */
1780 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1781         UnionPar *up;
1782         Int tp;
1783         up = s_UnionParFromValue(self, &tp, 0);
1784         if (tp == kElementParType)
1785                 return INT2NUM(up->atom.number);
1786         else if (tp == kVdwParType)
1787                 return INT2NUM(up->vdw.atomicNumber);
1788         else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1789 }
1790
1791 /*
1792  *  call-seq:
1793  *     name -> String
1794  *
1795  *  Get the name for the element parameter.
1796  */
1797 static VALUE s_ParameterRef_GetName(VALUE self) {
1798         UnionPar *up;
1799         Int tp;
1800         up = s_UnionParFromValue(self, &tp, 0);
1801         if (tp == kElementParType) {
1802                 char name[5];
1803                 strncpy(name, up->atom.name, 4);
1804                 name[4] = 0;
1805                 return Ruby_NewEncodedStringValue2(name);
1806         } else rb_raise(rb_eMolbyError, "invalid member name");
1807 }
1808
1809 /*
1810  *  call-seq:
1811  *     weight -> Float
1812  *
1813  *  Get the atomic weight for the element parameter.
1814  */
1815 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1816         UnionPar *up;
1817         Int tp;
1818         up = s_UnionParFromValue(self, &tp, 0);
1819         if (tp == kElementParType)
1820                 return rb_float_new(up->atom.weight);
1821         else if (tp == kVdwParType)
1822                 return rb_float_new(up->vdw.weight);
1823         else rb_raise(rb_eMolbyError, "invalid member weight");
1824 }
1825
1826 /*
1827  *  call-seq:
1828  *     fullname -> String
1829  *
1830  *  Get the full name for the element parameter.
1831  */
1832 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1833         UnionPar *up;
1834         Int tp;
1835         up = s_UnionParFromValue(self, &tp, 0);
1836         if (tp == kElementParType) {
1837                 char fullname[16];
1838                 strncpy(fullname, up->atom.fullname, 15);
1839                 fullname[15] = 0;
1840                 return Ruby_NewEncodedStringValue2(fullname);
1841         } else rb_raise(rb_eMolbyError, "invalid member fullname");
1842 }
1843
1844 /*
1845  *  call-seq:
1846  *     comment -> String
1847  *
1848  *  Get the comment for the parameter.
1849  */
1850 static VALUE s_ParameterRef_GetComment(VALUE self) {
1851         UnionPar *up;
1852         Int tp, com;
1853         up = s_UnionParFromValue(self, &tp, 0);
1854         com = up->bond.com;
1855         if (com == 0)
1856                 return Qnil;
1857         else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1858 }
1859
1860 /*
1861  *  call-seq:
1862  *     source -> String
1863  *
1864  *  Get the source string for the parameter. Returns false for undefined parameter,
1865  *  and nil for "local" parameter that is specific for the molecule.
1866  */
1867 static VALUE s_ParameterRef_GetSource(VALUE self) {
1868         UnionPar *up;
1869         Int tp, src;
1870         up = s_UnionParFromValue(self, &tp, 0);
1871         src = up->bond.src;
1872         if (src < 0)
1873                 return Qfalse;  /* undefined */
1874         else if (src == 0)
1875                 return Qnil;  /*  local  */
1876         else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1877 }
1878
1879 static void
1880 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1881 {
1882         VALUE *valp;
1883         int i;
1884         if (n == 1)
1885                 valp = &val;
1886         else {
1887                 if (rb_obj_is_kind_of(val, rb_cString)) {
1888                         char *s = StringValuePtr(val);
1889                         char *p;
1890                         for (i = 0; i < n; i++) {
1891                                 char buf[40];
1892                                 int len;
1893                                 /*  Skip leading separaters  */
1894                                 while (*s == '-' || *s == ' ' || *s == '\t')
1895                                         s++;
1896                                 for (p = s; *p != 0; p++) {
1897                                         if (*p == '-' || *p == ' ' || *p == '\t')
1898                                                 break;
1899                                 }
1900                                 len = p - s;
1901                                 if (len >= sizeof(buf))
1902                                         len = sizeof(buf) - 1;
1903                                 strncpy(buf, s, len);
1904                                 buf[len] = 0;
1905                                 /*  Skip trailing blanks  */
1906                                 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1907                                         buf[len] = 0;
1908                                 if (buf[0] == 0)
1909                                         rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1910                                 if (buf[0] >= '0' && buf[0] <= '9')
1911                                         types[i] = atoi(buf);
1912                                 else
1913                                         types[i] = AtomTypeEncodeToUInt(buf);
1914                                 if (p == NULL || *p == 0) {
1915                                         i++;
1916                                         break;
1917                                 } else s = p + 1;
1918                         }
1919                         if (i < n)
1920                                 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1921                         return;
1922                 }
1923                 val = rb_ary_to_ary(val);
1924                 if (RARRAY_LEN(val) != n)
1925                         rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1926                 valp = RARRAY_PTR(val);
1927         }
1928         for (i = 0; i < n; i++) {
1929                 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1930                         types[i] = NUM2INT(rb_Integer(valp[i]));
1931                 else {
1932                         VALUE sval = valp[i];
1933                         types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1934                 }
1935         }
1936 }
1937
1938 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1939         UnionPar *up;
1940         VALUE oldval;
1941         Int oldsrc, tp;
1942         UInt types[4];
1943         up = s_UnionParFromValue(self, &tp, 1);
1944         oldval = s_ParameterRef_GetAtomTypes(self);
1945         oldsrc = up->bond.src;
1946         switch (tp) {
1947                 case kBondParType:
1948                         s_ScanAtomTypes(val, 2, types);
1949                         up->bond.type1 = types[0];
1950                         up->bond.type2 = types[1];
1951                         break;
1952                 case kAngleParType:
1953                         s_ScanAtomTypes(val, 3, types);
1954                         up->angle.type1 = types[0];
1955                         up->angle.type2 = types[1];
1956                         up->angle.type3 = types[2];
1957                         break;
1958                 case kDihedralParType:
1959                 case kImproperParType:
1960                         s_ScanAtomTypes(val, 4, types);
1961                         up->torsion.type1 = types[0];
1962                         up->torsion.type2 = types[1];
1963                         up->torsion.type3 = types[2];
1964                         up->torsion.type4 = types[3];
1965                         break;
1966                 case kVdwParType:
1967                         s_ScanAtomTypes(val, 1, types);
1968                         up->vdw.type1 = types[0];
1969                         break;
1970                 case kVdwPairParType:
1971                         s_ScanAtomTypes(val, 2, types);
1972                         up->vdwp.type1 = types[0];
1973                         up->vdwp.type2 = types[1];
1974                         break;
1975                 case kVdwCutoffParType:
1976                         s_ScanAtomTypes(val, 2, types);
1977                         up->vdwcutoff.type1 = types[0];
1978                         up->vdwcutoff.type2 = types[1];
1979                         break;
1980                 default:
1981                         return Qnil;
1982         }
1983         s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1984         return val;
1985 }
1986
1987 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1988         UnionPar *up;
1989         Int tp, i, n, oldsrc;
1990         VALUE *valp, oldval;
1991         up = s_UnionParFromValue(self, &tp, 1);
1992         oldval = s_ParameterRef_GetK(self);
1993         oldsrc = up->bond.src;
1994         switch (tp) {
1995                 case kBondParType:
1996                         val = rb_Float(val);
1997                         up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1998                         break;
1999                 case kAngleParType:
2000                         val = rb_Float(val);
2001                         up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
2002                         break;
2003                 case kDihedralParType:
2004                 case kImproperParType:
2005                         if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2006                                 up->torsion.mult = 1;
2007                                 val = rb_Float(val);
2008                                 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
2009                                 break;
2010                         }
2011                         n = up->torsion.mult;
2012                         if (n > 3)
2013                                 n = 3;
2014                         val = rb_ary_to_ary(val);
2015                         if (RARRAY_LEN(val) != n)
2016                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2017                         valp = RARRAY_PTR(val);
2018                         for (i = 0; i < n; i++) {
2019                                 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
2020                         }
2021                         break;
2022                 default:
2023                         rb_raise(rb_eMolbyError, "invalid member k");
2024         }
2025         s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
2026         return val;
2027 }
2028
2029 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
2030         UnionPar *up;
2031         Int tp, oldsrc;
2032         VALUE oldval;
2033         up = s_UnionParFromValue(self, &tp, 1);
2034         oldval = s_ParameterRef_GetR0(self);
2035         oldsrc = up->bond.src;
2036         if (tp == kBondParType) {
2037                 val = rb_Float(val);
2038                 up->bond.r0 = NUM2DBL(val);
2039         } else rb_raise(rb_eMolbyError, "invalid member r0");
2040         s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
2041         return val;
2042 }
2043
2044 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
2045         UnionPar *up;
2046         Int tp, oldsrc;
2047         VALUE oldval;
2048         up = s_UnionParFromValue(self, &tp, 1);
2049         oldval = s_ParameterRef_GetA0(self);
2050         oldsrc = up->bond.src;
2051         if (tp == kAngleParType) {
2052                 val = rb_Float(val);
2053                 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
2054         } else rb_raise(rb_eMolbyError, "invalid member a0");
2055         s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
2056         return val;
2057 }
2058
2059 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
2060         UnionPar *up;
2061         Int tp, oldsrc;
2062         VALUE oldval;
2063         up = s_UnionParFromValue(self, &tp, 1);
2064         oldval = s_ParameterRef_GetMult(self);
2065         oldsrc = up->bond.src;
2066         if (tp == kDihedralParType || tp == kImproperParType) {
2067                 int i;
2068                 val = rb_Integer(val);
2069                 i = NUM2INT(val);
2070                 if (i < 0 || i > 3)
2071                         rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
2072                 up->torsion.mult = i;
2073         } else rb_raise(rb_eMolbyError, "invalid member mult");
2074         s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
2075         return val;
2076 }
2077
2078 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
2079         UnionPar *up;
2080         Int tp, i, n, oldsrc;
2081         VALUE *valp, oldval;
2082         up = s_UnionParFromValue(self, &tp, 1);
2083         oldval = s_ParameterRef_GetPeriod(self);
2084         oldsrc = up->bond.src;
2085         if (tp == kDihedralParType || tp == kImproperParType) {
2086                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2087                         up->torsion.mult = 1;
2088                         val = rb_Integer(val);
2089                         up->torsion.period[0] = NUM2INT(val);
2090                 } else {
2091                         n = up->torsion.mult;
2092                         if (n > 3)
2093                                 n = 3;
2094                         val = rb_ary_to_ary(val);
2095                         if (RARRAY_LEN(val) != n)
2096                                 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
2097                         valp = RARRAY_PTR(val);
2098                         for (i = 0; i < n; i++) {
2099                                 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
2100                         }
2101                 }
2102         } else rb_raise(rb_eMolbyError, "invalid member period");
2103         s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2104         return val;
2105 }
2106
2107 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2108         UnionPar *up;
2109         Int tp, i, n, oldsrc;
2110         VALUE *valp, oldval;
2111         up = s_UnionParFromValue(self, &tp, 1);
2112         oldval = s_ParameterRef_GetPhi0(self);
2113         oldsrc = up->bond.src;
2114         if (tp == kDihedralParType || tp == kImproperParType) {
2115                 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2116                         up->torsion.mult = 1;
2117                         val = rb_Float(val);
2118                         up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2119                 } else {
2120                         n = up->torsion.mult;
2121                         if (n > 3)
2122                                 n = 3;
2123                         val = rb_ary_to_ary(val);
2124                         if (RARRAY_LEN(val) != n)
2125                                 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2126                         valp = RARRAY_PTR(val);
2127                         for (i = 0; i < n; i++)
2128                                 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2129                 }
2130         } else rb_raise(rb_eMolbyError, "invalid member phi0");
2131         s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2132         return val;
2133 }
2134
2135 /*
2136 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2137         UnionPar *up;
2138         Int tp, oldsrc;
2139         double d;
2140         VALUE oldval;
2141         up = s_UnionParFromValue(self, &tp, 1);
2142         oldval = s_ParameterRef_GetA(self);
2143         oldsrc = up->bond.src;
2144         val = rb_Float(val);
2145         d = NUM2DBL(val);
2146         if (tp == kVdwParType)
2147                 up->vdw.A = d;
2148         else if (tp == kVdwPairParType)
2149                 up->vdwp.A = d;
2150         else rb_raise(rb_eMolbyError, "invalid member A");
2151         s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2152         return val;
2153 }
2154
2155 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2156         UnionPar *up;
2157         Int tp, oldsrc;
2158         double d;
2159         VALUE oldval;
2160         up = s_UnionParFromValue(self, &tp, 1);
2161         oldval = s_ParameterRef_GetB(self);
2162         oldsrc = up->bond.src;
2163         val = rb_Float(val);
2164         d = NUM2DBL(val);
2165         if (tp == kVdwParType)
2166                 up->vdw.B = d;
2167         else if (tp == kVdwPairParType)
2168                 up->vdwp.B = d;
2169         else rb_raise(rb_eMolbyError, "invalid member B");
2170         s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2171         return val;
2172 }
2173 */
2174
2175 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2176         UnionPar *up;
2177         Int tp, oldsrc;
2178         Double r;
2179         VALUE oldval;
2180         up = s_UnionParFromValue(self, &tp, 1);
2181         oldval = s_ParameterRef_GetReq(self);
2182         oldsrc = up->bond.src;
2183         val = rb_Float(val);
2184         r = NUM2DBL(val);
2185         if (tp == kVdwParType) {
2186                 up->vdw.r_eq = r;
2187                 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2188                 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2189         } else if (tp == kVdwPairParType) {
2190                 up->vdwp.r_eq = r;
2191                 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2192                 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2193         } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2194         s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2195         return val;
2196 }
2197
2198 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2199         UnionPar *up;
2200         Int tp, oldsrc;
2201         Double e;
2202         VALUE oldval;
2203         up = s_UnionParFromValue(self, &tp, 1);
2204         oldval = s_ParameterRef_GetEps(self);
2205         oldsrc = up->bond.src;
2206         val = rb_Float(val);
2207         e = NUM2DBL(val) * KCAL2INTERNAL;
2208         if (tp == kVdwParType) {
2209                 up->vdw.eps = e;
2210                 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2211                 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2212         } else if (tp == kVdwPairParType) {
2213                 up->vdwp.eps = e;
2214                 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2215                 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2216         } else rb_raise(rb_eMolbyError, "invalid member eps");
2217         s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2218         return val;
2219 }
2220
2221 /*
2222 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2223         UnionPar *up;
2224         Int tp, oldsrc;
2225         double d;
2226         VALUE oldval;
2227         up = s_UnionParFromValue(self, &tp, 1);
2228         oldval = s_ParameterRef_GetA14(self);
2229         oldsrc = up->bond.src;
2230         val = rb_Float(val);
2231         d = NUM2DBL(val);
2232         if (tp == kVdwParType)
2233                 up->vdw.A14 = d;
2234         else if (tp == kVdwPairParType)
2235                 up->vdwp.A14 = d;
2236         else rb_raise(rb_eMolbyError, "invalid member A14");
2237         s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);      
2238         return val;
2239 }
2240
2241 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2242         UnionPar *up;
2243         Int tp, oldsrc;
2244         double d;
2245         VALUE oldval;
2246         up = s_UnionParFromValue(self, &tp, 1);
2247         oldval = s_ParameterRef_GetB14(self);
2248         oldsrc = up->bond.src;
2249         val = rb_Float(val);
2250         d = NUM2DBL(val);
2251         if (tp == kVdwParType)
2252                 up->vdw.B14 = d;
2253         else if (tp == kVdwPairParType)
2254                 up->vdwp.B14 = d;
2255         else rb_raise(rb_eMolbyError, "invalid member B14");
2256         s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);      
2257         return val;
2258 }
2259 */
2260
2261 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2262         UnionPar *up;
2263         Int tp, oldsrc;
2264         Double r;
2265         VALUE oldval;
2266         up = s_UnionParFromValue(self, &tp, 1);
2267         oldval = s_ParameterRef_GetReq14(self);
2268         oldsrc = up->bond.src;
2269         val = rb_Float(val);
2270         r = NUM2DBL(val);
2271         if (tp == kVdwParType) {
2272                 up->vdw.r_eq14 = r;
2273                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2274                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2275         } else if (tp == kVdwPairParType) {
2276                 up->vdwp.r_eq14 = r;
2277                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2278                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2279         } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2280         s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);    
2281         return val;
2282 }
2283
2284 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2285         UnionPar *up;
2286         Int tp, oldsrc;
2287         Double e;
2288         VALUE oldval;
2289         up = s_UnionParFromValue(self, &tp, 1);
2290         oldval = s_ParameterRef_GetEps14(self);
2291         oldsrc = up->bond.src;
2292         val = rb_Float(val);
2293         e = NUM2DBL(val) * KCAL2INTERNAL;
2294         if (tp == kVdwParType) {
2295                 up->vdw.eps14 = e;
2296                 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2297                 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2298         } else if (tp == kVdwPairParType) {
2299                 up->vdwp.eps14 = e;
2300                 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2301                 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2302         } else rb_raise(rb_eMolbyError, "invalid member eps14");
2303         s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);    
2304         return val;
2305 }
2306
2307 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2308         UnionPar *up;
2309         Int tp, oldsrc;
2310         VALUE oldval;
2311         oldval = s_ParameterRef_GetCutoff(self);
2312         oldsrc = up->bond.src;
2313         up = s_UnionParFromValue(self, &tp, 1);
2314         val = rb_Float(val);
2315         if (tp == kVdwCutoffParType) {
2316                 up->vdwcutoff.cutoff = NUM2DBL(val);
2317         } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2318         s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);   
2319         return val;
2320 }
2321
2322 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2323         UnionPar *up;
2324         Int tp, oldsrc;
2325         VALUE oldval;
2326         up = s_UnionParFromValue(self, &tp, 1);
2327         oldval = s_ParameterRef_GetRadius(self);
2328         oldsrc = up->bond.src;
2329         val = rb_Float(val);
2330         if (tp == kElementParType) {
2331                 up->atom.radius = NUM2DBL(val);
2332         } else rb_raise(rb_eMolbyError, "invalid member radius");
2333         s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);   
2334         return val;
2335 }
2336
2337 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2338         UnionPar *up;
2339         Int tp, oldsrc;
2340         VALUE oldval;
2341         up = s_UnionParFromValue(self, &tp, 1);
2342         oldval = s_ParameterRef_GetVdwRadius(self);
2343         oldsrc = up->bond.src;
2344         val = rb_Float(val);
2345         if (tp == kElementParType) {
2346                 up->atom.vdw_radius = NUM2DBL(val);
2347         } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2348         s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);        
2349         return val;
2350 }
2351
2352 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2353         UnionPar *up;
2354         Int tp, oldsrc;
2355         VALUE *valp, oldval;
2356         up = s_UnionParFromValue(self, &tp, 1);
2357         oldval = s_ParameterRef_GetColor(self);
2358         oldsrc = up->bond.src;
2359         val = rb_ary_to_ary(val);
2360         if (RARRAY_LEN(val) != 3)
2361                 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2362         valp = RARRAY_PTR(val);
2363         if (tp == kElementParType) {
2364                 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2365                 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2366                 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2367         } else rb_raise(rb_eMolbyError, "invalid member color");
2368         s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);    
2369         return val;
2370 }
2371
2372 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2373         UnionPar *up;
2374         Int tp, oldsrc;
2375         VALUE oldval;
2376         up = s_UnionParFromValue(self, &tp, 1);
2377         oldval = s_ParameterRef_GetAtomicNumber(self);
2378         oldsrc = up->bond.src;
2379         val = rb_Integer(val);
2380         if (tp == kElementParType)
2381                 up->atom.number = NUM2INT(val);
2382         else if (tp == kVdwParType) {
2383                 up->vdw.atomicNumber = NUM2INT(val);
2384                 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2385         } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2386         s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);     
2387         return val;
2388 }
2389
2390 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2391         UnionPar *up;
2392         Int tp, oldsrc;
2393         VALUE oldval;
2394         up = s_UnionParFromValue(self, &tp, 1);
2395         oldval = s_ParameterRef_GetName(self);
2396         oldsrc = up->bond.src;
2397         if (tp == kElementParType) {
2398                 strncpy(up->atom.name, StringValuePtr(val), 4);
2399         } else rb_raise(rb_eMolbyError, "invalid member name");
2400         s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);     
2401         return val;
2402 }
2403
2404 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2405         UnionPar *up;
2406         Int tp, oldsrc;
2407         VALUE oldval;
2408         val = rb_Float(val);
2409         oldval = s_ParameterRef_GetWeight(self);
2410         up = s_UnionParFromValue(self, &tp, 1);
2411         oldsrc = up->bond.src;
2412         if (tp == kElementParType)
2413                 up->atom.weight = NUM2DBL(val);
2414         else if (tp == kVdwParType)
2415                 up->vdw.weight = NUM2DBL(val);
2416         else rb_raise(rb_eMolbyError, "invalid member weight");
2417         s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);   
2418         return val;
2419 }
2420
2421 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2422         UnionPar *up;
2423         Int tp, oldsrc;
2424         VALUE oldval;
2425         up = s_UnionParFromValue(self, &tp, 1);
2426         oldval = s_ParameterRef_GetFullName(self);
2427         oldsrc = up->bond.src;
2428         if (tp == kElementParType) {
2429                 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2430                 up->atom.fullname[15] = 0;
2431         } else rb_raise(rb_eMolbyError, "invalid member fullname");
2432         s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc); 
2433         return val;
2434 }
2435
2436 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2437         UnionPar *up;
2438         Int tp, com, oldsrc;
2439         VALUE oldval;
2440         up = s_UnionParFromValue(self, &tp, 1);
2441         oldval = s_ParameterRef_GetComment(self);
2442         oldsrc = up->bond.src;
2443         if (val == Qnil)
2444                 up->bond.com = 0;
2445         else {
2446                 com = ParameterCommentIndex(StringValuePtr(val));
2447                 up->bond.com = com;
2448         }
2449         s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);  
2450         return val;     
2451 }
2452
2453 /*  Only false (undefined) and nil (local) can be set  */
2454 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2455         UnionPar *up;
2456         Int tp, oldsrc;
2457         VALUE oldval;
2458         up = s_UnionParFromValue(self, &tp, 1);
2459         if (val != Qfalse && val != Qnil)
2460                 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2461         oldval = s_ParameterRef_GetSource(self);
2462         oldsrc = up->bond.src;
2463         if (oldsrc != 0 && oldsrc != -1)
2464                 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2465         up->bond.src = (val == Qfalse ? -1 : 0);
2466         s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);   
2467         return val;     
2468 }
2469
2470 static struct s_ParameterAttrDef {
2471         char *name;
2472         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
2473         ID id;                  /*  Will be set within InitMolby()  */
2474         VALUE (*getter)(VALUE);
2475         VALUE (*setter)(VALUE, VALUE);
2476 } s_ParameterAttrDefTable[] = {
2477         {"index",        &s_IndexSym,        0, s_ParameterRef_GetIndex,        NULL},
2478         {"par_type",     &s_ParTypeSym,      0, s_ParameterRef_GetParType,      NULL},
2479         {"atom_types",   &s_AtomTypesSym,    0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2480         {"atom_type",    &s_AtomTypeSym,     0, s_ParameterRef_GetAtomTypes,    s_ParameterRef_SetAtomTypes},
2481         {"k",            &s_KSym,            0, s_ParameterRef_GetK,            s_ParameterRef_SetK},
2482         {"r0",           &s_R0Sym,           0, s_ParameterRef_GetR0,           s_ParameterRef_SetR0},
2483         {"a0",           &s_A0Sym,           0, s_ParameterRef_GetA0,           s_ParameterRef_SetA0},
2484         {"mult",         &s_MultSym,         0, s_ParameterRef_GetMult,         s_ParameterRef_SetMult},
2485         {"period",       &s_PeriodSym,       0, s_ParameterRef_GetPeriod,       s_ParameterRef_SetPeriod},
2486         {"phi0",         &s_Phi0Sym,         0, s_ParameterRef_GetPhi0,         s_ParameterRef_SetPhi0},
2487 /*      {"A",            &s_ASym,            0, s_ParameterRef_GetA,            NULL},
2488         {"B",            &s_BSym,            0, s_ParameterRef_GetB,            NULL}, */
2489         {"r_eq",         &s_ReqSym,          0, s_ParameterRef_GetReq,          s_ParameterRef_SetReq},
2490         {"eps",          &s_EpsSym,          0, s_ParameterRef_GetEps,          s_ParameterRef_SetEps},
2491 /*      {"A14",          &s_A14Sym,          0, s_ParameterRef_GetA14,          NULL},
2492         {"B14",          &s_B14Sym,          0, s_ParameterRef_GetB14,          NULL}, */
2493         {"r_eq14",       &s_Req14Sym,        0, s_ParameterRef_GetReq14,        s_ParameterRef_SetReq14},
2494         {"eps14",        &s_Eps14Sym,        0, s_ParameterRef_GetEps14,        s_ParameterRef_SetEps14},
2495         {"cutoff",       &s_CutoffSym,       0, s_ParameterRef_GetCutoff,       s_ParameterRef_SetCutoff},
2496         {"radius",       &s_RadiusSym,       0, s_ParameterRef_GetRadius,       s_ParameterRef_SetRadius},
2497         {"vdw_radius",   &s_VdwRadiusSym,    0, s_ParameterRef_GetVdwRadius,    s_ParameterRef_SetVdwRadius},
2498         {"color",        &s_ColorSym,        0, s_ParameterRef_GetColor,        s_ParameterRef_SetColor},
2499         {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2500         {"name",         &s_NameSym,         0, s_ParameterRef_GetName,         s_ParameterRef_SetName},
2501         {"weight",       &s_WeightSym,       0, s_ParameterRef_GetWeight,       s_ParameterRef_SetWeight},
2502         {"fullname",     &s_FullNameSym,     0, s_ParameterRef_GetFullName,     s_ParameterRef_SetFullName},
2503         {"comment",      &s_CommentSym,      0, s_ParameterRef_GetComment,      s_ParameterRef_SetComment},
2504         {"source",       &s_SourceSym,       0, s_ParameterRef_GetSource,       s_ParameterRef_SetSource},
2505         {NULL} /* Sentinel */
2506 };
2507
2508 static VALUE
2509 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2510 {
2511         int i;
2512         ID kid;
2513         if (TYPE(key) != T_SYMBOL) {
2514                 kid = rb_intern(StringValuePtr(key));
2515                 key = ID2SYM(kid);
2516         } else kid = SYM2ID(key);
2517         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2518                 if (s_ParameterAttrDefTable[i].id == kid) {
2519                         if (value == Qundef)
2520                                 return (*(s_ParameterAttrDefTable[i].getter))(self);
2521                         else if (s_ParameterAttrDefTable[i].setter == NULL)
2522                                 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2523                         else
2524                                 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2525                 }
2526         }
2527         rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2528         return Qnil; /* not reached */
2529 }
2530
2531 static VALUE
2532 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2533 {
2534         return s_ParameterRef_SetAttr(self, key, Qundef);
2535 }
2536
2537 /*
2538  *  call-seq:
2539  *     keys(idx)          -> array of valid parameter attributes
2540  *  
2541  *  Returns an array of valid parameter attributes (as Symbols).
2542  */
2543 static VALUE
2544 s_ParameterRef_Keys(VALUE self)
2545 {
2546         ParameterRef *pref;
2547         Data_Get_Struct(self, ParameterRef, pref);
2548         switch (pref->parType) {
2549                 case kBondParType:
2550                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2551                 case kAngleParType:
2552                         return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2553                 case kDihedralParType:
2554                 case kImproperParType:
2555                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2556                 case kVdwParType:
2557                         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);
2558                 case kVdwPairParType:
2559                         return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2560                 case kVdwCutoffParType:
2561                         return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2562                 case kElementParType:
2563                         return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_VdwRadiusSym, s_CommentSym, s_SourceSym);
2564                 default:
2565                         rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2566         }
2567         return Qnil;  /*  Not reached  */
2568 }
2569
2570 /*
2571  *  call-seq:
2572  *     to_hash(idx)          -> Hash
2573  *  
2574  *  Returns a hash containing valid parameter names and values
2575  */
2576 static VALUE
2577 s_ParameterRef_ToHash(VALUE self)
2578 {
2579         VALUE keys = s_ParameterRef_Keys(self);
2580         VALUE retval;
2581         int i;
2582         if (keys == Qnil)
2583                 return Qnil;
2584         retval = rb_hash_new();
2585         for (i = 0; i < RARRAY_LEN(keys); i++) {
2586                 VALUE key = RARRAY_PTR(keys)[i];
2587                 VALUE val = s_ParameterRef_GetAttr(self, key);
2588                 rb_hash_aset(retval, key, val);
2589         }
2590         return retval;
2591 }
2592
2593 /*
2594  *  call-seq:
2595  *     parameter.to_s(idx)          -> String
2596  *  
2597  *  Returns a string representation of the given parameter
2598  */
2599 static VALUE
2600 s_ParameterRef_ToString(VALUE self)
2601 {
2602         Int tp, i, n;
2603         char buf[1024], types[4][8];
2604         UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2605         switch (tp) {
2606                 case kBondParType:
2607                         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);
2608                         break;
2609                 case kAngleParType:
2610                         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);
2611                         break;
2612                 case kDihedralParType:
2613                 case kImproperParType:
2614                         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]));
2615                         n = strlen(buf);
2616                         for (i = 0; i < up->torsion.mult; i++) {
2617                                 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);
2618                                 n = strlen(buf);
2619                         }
2620                         break;
2621                 case kVdwParType:
2622                         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);
2623                         break;
2624                 case kVdwPairParType:
2625                         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);
2626                         break;
2627                 case kVdwCutoffParType:
2628                         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);
2629                         break;
2630                 case kElementParType:
2631                         snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s %6.3f", up->atom.name, up->atom.number, up->atom.radius, up->atom.red / 65535.0, up->atom.green / 65535.0, up->atom.blue / 65535.0, up->atom.weight, up->atom.fullname, up->atom.vdw_radius);
2632                         break;
2633         }
2634         return Ruby_NewEncodedStringValue2(buf);
2635 }
2636
2637 /*
2638  *  call-seq:
2639  *     self == parameterRef -> boolean
2640  *  
2641  *  True if the parameters point to the same parameter record.
2642  */
2643 static VALUE
2644 s_ParameterRef_Equal(VALUE self, VALUE val)
2645 {
2646         Int tp1, tp2;
2647         if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2648                 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2649         } else return Qfalse;
2650 }
2651         
2652 #pragma mark ====== Parameter Class ======
2653
2654 /*  The Parameter class actually encapsulate Molecule record. If the record pointer
2655  *  is NULL, then the global parameters are looked for.  */
2656
2657 /*  Rebuild the MD parameter record if necessary: may throw an exception  */
2658 /*  The second parameter is passed to md_arena.prepare; if true, then check only  */
2659 static void
2660 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2661 {
2662         Molecule *mol;
2663         Data_Get_Struct(val, Molecule, mol);
2664         if (mol == NULL)
2665                 rb_raise(rb_eMolbyError, "the molecule is empty");
2666         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2667                 /*  Do self.md_arena.prepare  */
2668                 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2669                 if (val2 != Qnil)
2670                         val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2671         }
2672 }
2673
2674 static VALUE
2675 s_NewParameterValueFromValue(VALUE val)
2676 {
2677         Molecule *mol;
2678         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2679                 Data_Get_Struct(val, Molecule, mol);
2680                 s_RebuildMDParameterIfNecessary(val, Qtrue);
2681                 MoleculeRetain(mol);
2682                 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2683         } else {
2684                 mol = NULL;
2685                 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2686         }
2687 }
2688
2689 static Molecule *
2690 s_MoleculeFromParameterValue(VALUE val)
2691 {
2692         Molecule *mol;
2693         Data_Get_Struct(val, Molecule, mol);
2694         return mol;
2695 }
2696
2697 static Parameter *
2698 s_ParameterFromParameterValue(VALUE val)
2699 {
2700         Molecule *mol;
2701         Data_Get_Struct(val, Molecule, mol);
2702         if (mol != NULL)
2703                 return mol->par;
2704         return gBuiltinParameters;
2705 }
2706
2707 /*  Forward declarations  */
2708 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2709 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2710
2711 static Molecule *
2712 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2713 {
2714         if (val == rb_cParameter) {
2715                 return NULL;  /*  Parameter class method: builtin parameters  */
2716         } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2717                 return s_MoleculeFromParameterValue(val);
2718         } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2719                 return s_MoleculeFromParEnumerableValue(val);
2720         } else return NULL;
2721 }
2722
2723 /*
2724  *  call-seq:
2725  *     builtin    -> Parameter
2726  *  
2727  *  Returns a parameter value that points to the global (builtin) parameters.
2728  *  Equivalent to Parameter::Builtin (constant).
2729  */
2730 static VALUE
2731 s_Parameter_Builtin(VALUE self)
2732 {
2733         static ID s_builtin_id = 0;
2734         if (s_builtin_id == 0)
2735                 s_builtin_id = rb_intern("Builtin");
2736         return rb_const_get(rb_cParameter, s_builtin_id);
2737 }
2738
2739 /*
2740  *  call-seq:
2741  *     bond(idx)          -> ParameterRef
2742  *  
2743  *  The index-th bond parameter record is returned.
2744  */
2745 static VALUE
2746 s_Parameter_Bond(VALUE self, VALUE ival)
2747 {
2748         Molecule *mol;
2749         int idx, n;
2750         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2751         idx = NUM2INT(rb_Integer(ival));
2752         if (mol == NULL)
2753                 n = gBuiltinParameters->nbondPars;
2754         else if (mol->par != NULL)
2755                 n = mol->par->nbondPars;
2756         else n = 0;
2757         if (idx < -n || idx >= n)
2758                 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2759         if (idx < 0)
2760                 idx += n;
2761         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2762 }
2763
2764 /*
2765  *  call-seq:
2766  *     angle(idx)          -> ParameterRef
2767  *  
2768  *  The index-th angle parameter record is returned.
2769  */
2770 static VALUE
2771 s_Parameter_Angle(VALUE self, VALUE ival)
2772 {
2773         Molecule *mol;
2774         int idx, n;
2775         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2776         idx = NUM2INT(rb_Integer(ival));
2777         if (mol == NULL)
2778                 n = gBuiltinParameters->nanglePars;
2779         else if (mol->par != NULL)
2780                 n = mol->par->nanglePars;
2781         else n = 0;
2782         if (idx < -n || idx >= n)
2783                 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2784         if (idx < 0)
2785                 idx += n;
2786         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2787 }
2788
2789 /*
2790  *  call-seq:
2791  *     dihedral(idx)          -> ParameterRef
2792  *  
2793  *  The index-th dihedral parameter record is returned.
2794  */
2795 static VALUE
2796 s_Parameter_Dihedral(VALUE self, VALUE ival)
2797 {
2798         Molecule *mol;
2799         int idx, n;
2800         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2801         idx = NUM2INT(rb_Integer(ival));
2802         if (mol == NULL)
2803                 n = gBuiltinParameters->ndihedralPars;
2804         else if (mol->par != NULL)
2805                 n = mol->par->ndihedralPars;
2806         else n = 0;
2807         if (idx < -n || idx >= n)
2808                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2809         if (idx < 0)
2810                 idx += n;
2811         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2812 }
2813
2814 /*
2815  *  call-seq:
2816  *     improper(idx)          -> ParameterRef
2817  *  
2818  *  The index-th improper parameter record is returned.
2819  */
2820 static VALUE
2821 s_Parameter_Improper(VALUE self, VALUE ival)
2822 {
2823         Molecule *mol;
2824         int idx, n;
2825         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2826         idx = NUM2INT(rb_Integer(ival));
2827         if (mol == NULL)
2828                 n = gBuiltinParameters->nimproperPars;
2829         else if (mol->par != NULL)
2830                 n = mol->par->nimproperPars;
2831         else n = 0;
2832         if (idx < -n || idx >= n)
2833                 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2834         if (idx < 0)
2835                 idx += n;
2836         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2837 }
2838
2839 /*
2840  *  call-seq:
2841  *     vdw(idx)          -> ParameterRef
2842  *  
2843  *  The index-th vdw parameter record is returned.
2844  */
2845 static VALUE
2846 s_Parameter_Vdw(VALUE self, VALUE ival)
2847 {
2848         Molecule *mol;
2849         int idx, n;
2850         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2851         idx = NUM2INT(rb_Integer(ival));
2852         if (mol == NULL)
2853                 n = gBuiltinParameters->nvdwPars;
2854         else if (mol->par != NULL)
2855                 n = mol->par->nvdwPars;
2856         else n = 0;
2857         if (idx < -n || idx >= n)
2858                 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2859         if (idx < 0)
2860                 idx += n;
2861         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2862 }
2863
2864 /*
2865  *  call-seq:
2866  *     vdw_pair(idx)          -> ParameterRef
2867  *  
2868  *  The index-th vdw pair parameter record is returned.
2869  */
2870 static VALUE
2871 s_Parameter_VdwPair(VALUE self, VALUE ival)
2872 {
2873         Molecule *mol;
2874         int idx, n;
2875         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2876         idx = NUM2INT(rb_Integer(ival));
2877         if (mol == NULL)
2878                 n = gBuiltinParameters->nvdwpPars;
2879         else if (mol->par != NULL)
2880                 n = mol->par->nvdwpPars;
2881         else n = 0;
2882         if (idx < -n || idx >= n)
2883                 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2884         if (idx < 0)
2885                 idx += n;
2886         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2887 }
2888
2889 /*
2890  *  call-seq:
2891  *     vdw_cutoff(idx)          -> ParameterRef
2892  *  
2893  *  The index-th vdw cutoff parameter record is returned.
2894  */
2895 static VALUE
2896 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2897 {
2898         Molecule *mol;
2899         int idx, n;
2900         mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2901         idx = NUM2INT(rb_Integer(ival));
2902         if (mol == NULL)
2903                 n = gBuiltinParameters->nvdwCutoffPars;
2904         else if (mol->par != NULL)
2905                 n = mol->par->nvdwCutoffPars;
2906         else n = 0;
2907         if (idx < -n || idx >= n)
2908                 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2909         if (idx < 0)
2910                 idx += n;
2911         return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2912 }
2913
2914 /*
2915  *  call-seq:
2916  *     element(idx)            -> ParameterRef
2917  *     element(t1)             -> ParameterRef
2918  *  
2919  *  In the first form, the index-th element parameter record is returned. In the second
2920  *  form, the element parameter for t1 is looked up (the last index first). t1
2921  *  is the element name string (up to 4 characters).
2922  *  Unlike other Parameter methods, this is used only for the global parameter.
2923  */
2924 static VALUE
2925 s_Parameter_Element(VALUE self, VALUE ival)
2926 {
2927         Int idx1;
2928         if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2929                 int n = gCountElementParameters;
2930                 idx1 = NUM2INT(rb_Integer(ival));
2931                 if (idx1 < -n || idx1 >= n)
2932                         rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2933                 if (idx1 < 0)
2934                         idx1 += n;
2935                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2936         } else {
2937                 ElementPar *ep;
2938                 char name[6];
2939                 int i;
2940                 strncpy(name, StringValuePtr(ival), 4);
2941                 name[4] = 0;
2942                 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2943                         if (strncmp(ep->name, name, 4) == 0)
2944                                 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2945                 }
2946                 return Qnil;
2947         }
2948 }
2949
2950 /*
2951  *  call-seq:
2952  *     nbonds          -> Integer
2953  *  
2954  *  Returns the number of bond parameters.
2955  */
2956 static VALUE
2957 s_Parameter_Nbonds(VALUE self)
2958 {
2959         Int n;
2960         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2961         if (mol == NULL)
2962                 n = gBuiltinParameters->nbondPars;
2963         else if (mol->par != NULL)
2964                 n = mol->par->nbondPars;
2965         else n = 0;
2966         return INT2NUM(n);
2967 }
2968
2969 /*
2970  *  call-seq:
2971  *     nangles          -> Integer
2972  *  
2973  *  Returns the number of angle parameters.
2974  */
2975 static VALUE
2976 s_Parameter_Nangles(VALUE self)
2977 {
2978         Int n;
2979         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2980         if (mol == NULL)
2981                 n = gBuiltinParameters->nanglePars;
2982         else if (mol->par != NULL)
2983                 n = mol->par->nanglePars;
2984         else n = 0;
2985         return INT2NUM(n);
2986 }
2987
2988 /*
2989  *  call-seq:
2990  *     ndihedrals          -> Integer
2991  *  
2992  *  Returns the number of dihedral parameters.
2993  */
2994 static VALUE
2995 s_Parameter_Ndihedrals(VALUE self)
2996 {
2997         Int n;
2998         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2999         if (mol == NULL)
3000                 n = gBuiltinParameters->ndihedralPars;
3001         else if (mol->par != NULL)
3002                 n = mol->par->ndihedralPars;
3003         else n = 0;
3004         return INT2NUM(n);
3005 }
3006
3007 /*
3008  *  call-seq:
3009  *     nimpropers          -> Integer
3010  *  
3011  *  Returns the number of improper parameters.
3012  */
3013 static VALUE
3014 s_Parameter_Nimpropers(VALUE self)
3015 {
3016         Int n;
3017         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3018         if (mol == NULL)
3019                 n = gBuiltinParameters->nimproperPars;
3020         else if (mol->par != NULL)
3021                 n = mol->par->nimproperPars;
3022         else n = 0;
3023         return INT2NUM(n);
3024 }
3025
3026 /*
3027  *  call-seq:
3028  *     nvdws          -> Integer
3029  *  
3030  *  Returns the number of vdw parameters.
3031  */
3032 static VALUE
3033 s_Parameter_Nvdws(VALUE self)
3034 {
3035         Int n;
3036         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3037         if (mol == NULL)
3038                 n = gBuiltinParameters->nvdwPars;
3039         else if (mol->par != NULL)
3040                 n = mol->par->nvdwPars;
3041         else n = 0;
3042         return INT2NUM(n);
3043 }
3044
3045 /*
3046  *  call-seq:
3047  *     nvdw_pairs          -> Integer
3048  *  
3049  *  Returns the number of vdw pair parameters.
3050  */
3051 static VALUE
3052 s_Parameter_NvdwPairs(VALUE self)
3053 {
3054         Int n;
3055         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3056         if (mol == NULL)
3057                 n = gBuiltinParameters->nvdwpPars;
3058         else if (mol->par != NULL)
3059                 n = mol->par->nvdwpPars;
3060         else n = 0;
3061         return INT2NUM(n);
3062 }
3063
3064 /*
3065  *  call-seq:
3066  *     nvdw_cutoffs          -> Integer
3067  *  
3068  *  Returns the number of vdw cutoff parameters.
3069  */
3070 static VALUE
3071 s_Parameter_NvdwCutoffs(VALUE self)
3072 {
3073         Int n;
3074         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3075         if (mol == NULL)
3076                 n = gBuiltinParameters->nvdwCutoffPars;
3077         else if (mol->par != NULL)
3078                 n = mol->par->nvdwCutoffPars;
3079         else n = 0;
3080         return INT2NUM(n);
3081 }
3082
3083 /*
3084  *  call-seq:
3085  *     nelements          -> Integer
3086  *  
3087  *  Returns the number of element parameters.
3088  */
3089 static VALUE
3090 s_Parameter_Nelements(VALUE self)
3091 {
3092         return INT2NUM(gCountElementParameters);
3093 }
3094
3095 /*
3096  *  call-seq:
3097  *     bonds          -> ParEnumerable
3098  *  
3099  *  Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
3100  *  Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
3101  *  useful when all accessible parameters should be examined by use of 'each' method.
3102  */
3103 static VALUE
3104 s_Parameter_Bonds(VALUE self)
3105 {
3106         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3107         return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3108 }
3109
3110 /*
3111  *  call-seq:
3112  *     angles          -> ParEnumerable
3113  *  
3114  *  Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3115  *  Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3116  *  useful when all accessible parameters should be examined by use of 'each' method.
3117  */
3118 static VALUE
3119 s_Parameter_Angles(VALUE self)
3120 {
3121         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3122         return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3123 }
3124
3125 /*
3126  *  call-seq:
3127  *     dihedrals          -> ParEnumerable
3128  *  
3129  *  Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3130  *  Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3131  *  useful when all accessible parameters should be examined by use of 'each' method.
3132  */
3133 static VALUE
3134 s_Parameter_Dihedrals(VALUE self)
3135 {
3136         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3137         return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3138 }
3139
3140 /*
3141  *  call-seq:
3142  *     impropers          -> ParEnumerable
3143  *  
3144  *  Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3145  *  Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3146  *  useful when all accessible parameters should be examined by use of 'each' method.
3147  */
3148 static VALUE
3149 s_Parameter_Impropers(VALUE self)
3150 {
3151         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3152         return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3153 }
3154
3155 /*
3156  *  call-seq:
3157  *     vdws          -> ParEnumerable
3158  *  
3159  *  Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3160  *  Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3161  *  useful when all accessible parameters should be examined by use of 'each' method.
3162  */
3163 static VALUE
3164 s_Parameter_Vdws(VALUE self)
3165 {
3166         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3167         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3168 }
3169
3170 /*
3171  *  call-seq:
3172  *     vdw_pairs          -> ParEnumerable
3173  *  
3174  *  Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3175  *  Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3176  *  useful when all accessible parameters should be examined by use of 'each' method.
3177  */
3178 static VALUE
3179 s_Parameter_VdwPairs(VALUE self)
3180 {
3181         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3182         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3183 }
3184
3185 /*
3186  *  call-seq:
3187  *     vdw_cutoffs          -> ParEnumerable
3188  *  
3189  *  Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3190  *  Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3191  *  useful when all accessible parameters should be examined by use of 'each' method.
3192  */
3193 static VALUE
3194 s_Parameter_VdwCutoffs(VALUE self)
3195 {
3196         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3197         return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3198 }
3199
3200 /*
3201  *  call-seq:
3202  *     elements          -> ParEnumerable
3203  *  
3204  *  Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3205  *  Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3206  *  useful when all accessible parameters should be examined by use of 'each' method.
3207  */
3208 static VALUE
3209 s_Parameter_Elements(VALUE self)
3210 {
3211         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3212         return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3213 }
3214
3215 static VALUE
3216 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3217 {
3218         VALUE atval, optval;
3219         UInt t[4];
3220         Int ii[4];
3221         int i, n, idx, flags, is_global;
3222
3223         rb_scan_args(argc, argv, "1*", &atval, &optval);
3224         
3225         /*  Get the atom types  */
3226         switch (parType) {
3227                 case kBondParType: n = 2; break;
3228                 case kAngleParType: n = 3; break;
3229                 case kDihedralParType: n = 4; break;
3230                 case kImproperParType: n = 4; break;
3231                 case kVdwParType: n = 1; break;
3232                 case kVdwPairParType: n = 2; break;
3233                 default: return Qnil;
3234         }
3235         s_ScanAtomTypes(atval, n, t);
3236         for (i = 0; i < n; i++) {
3237                 if (t[i] < kAtomTypeMinimum) {
3238                         /*  Explicit atom index  */
3239                         if (mol == NULL)
3240                                 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3241                         if (t[i] >= mol->natoms)
3242                                 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3243                         ii[i] = t[i];
3244                         t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3245                 } else ii[i] = -1;
3246         }
3247         
3248         /*  Analyze options  */
3249         flags = 0;
3250         n = RARRAY_LEN(optval);
3251         for (i = 0; i < n; i++) {
3252                 VALUE oval = RARRAY_PTR(optval)[i];
3253                 if (oval == ID2SYM(rb_intern("global")))
3254                         flags |= kParameterLookupGlobal;
3255                 else if (oval == ID2SYM(rb_intern("local")))
3256                         flags |= kParameterLookupLocal;
3257                 else if (oval == ID2SYM(rb_intern("missing")))
3258                         flags |= kParameterLookupMissing;
3259                 else if (oval == ID2SYM(rb_intern("nowildcard")))
3260                         flags |= kParameterLookupNoWildcard;
3261                 else if (oval == ID2SYM(rb_intern("nobasetype")))
3262                         flags |= kParameterLookupNoBaseAtomType;
3263                 else if (oval == ID2SYM(rb_intern("create")))
3264                         flags |= 256;
3265         }
3266         if (flags == 0)
3267                 flags = kParameterLookupGlobal | kParameterLookupLocal;
3268         
3269         idx = -1;
3270         is_global = 0;
3271         switch (parType) {
3272                 case kBondParType: {
3273                         BondPar *bp;
3274                         if (mol != NULL) {
3275                                 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3276                                 if (bp != NULL) {
3277                                         idx = bp - mol->par->bondPars;
3278                                         break;
3279                                 }
3280                         }
3281                         bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3282                         if (bp != NULL) {
3283                                 idx = bp - gBuiltinParameters->bondPars;
3284                                 is_global = 1;
3285                         }
3286                         break;
3287                 }
3288                 case kAngleParType: {
3289                         AnglePar *ap;
3290                         if (mol != NULL) {
3291                                 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3292                                 if (ap != NULL) {
3293                                         idx = ap - mol->par->anglePars;
3294                                         break;
3295                                 }
3296                         }
3297                         ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3298                         if (ap != NULL) {
3299                                 idx = ap - gBuiltinParameters->anglePars;
3300                                 is_global = 1;
3301                         }
3302                         break;
3303                 }
3304                 case kDihedralParType: {
3305                         TorsionPar *tp;
3306                         if (mol != NULL) {
3307                                 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3308                                 if (tp != NULL) {
3309                                         idx = tp - mol->par->dihedralPars;
3310                                         break;
3311                                 }
3312                         }
3313                         tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3314                         if (tp != NULL) {
3315                                 idx = tp - gBuiltinParameters->dihedralPars;
3316                                 is_global = 1;
3317                         }
3318                         break;
3319                 }
3320                 case kImproperParType: {
3321                         TorsionPar *tp;
3322                         if (mol != NULL) {
3323                                 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3324                                 if (tp != NULL) {
3325                                         idx = tp - mol->par->improperPars;
3326                                         break;
3327                                 }
3328                         }
3329                         tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3330                         if (tp != NULL) {
3331                                 idx = tp - gBuiltinParameters->improperPars;
3332                                 is_global = 1;
3333                         }
3334                         break;
3335                 }       
3336                 case kVdwParType: {
3337                         VdwPar *vp;
3338                         if (mol != NULL) {
3339                                 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3340                                 if (vp != NULL) {
3341                                         idx = vp - mol->par->vdwPars;
3342                                         break;
3343                                 }
3344                         }
3345                         vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3346                         if (vp != NULL) {
3347                                 idx = vp - gBuiltinParameters->vdwPars;
3348                                 is_global = 1;
3349                         }
3350                         break;
3351                 }       
3352                 case kVdwPairParType: {
3353                         VdwPairPar *vp;
3354                         if (mol != NULL) {
3355                                 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3356                                 if (vp != NULL) {
3357                                         idx = vp - mol->par->vdwpPars;
3358                                         break;
3359                                 }
3360                         }
3361                         vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3362                         if (vp != NULL) {
3363                                 idx = vp - gBuiltinParameters->vdwpPars;
3364                                 is_global = 1;
3365                         }
3366                         break;
3367                 }
3368                 default:
3369                         return Qnil;
3370         }
3371         if (idx < 0) {
3372                 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3373                         return Qnil;            
3374                 else {
3375                         /*  Insert a new parameter record  */
3376                         UnionPar *up;
3377                         Int count = ParameterGetCountForType(mol->par, parType);
3378                         IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3379                         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3380                         IntGroupRelease(ig);
3381                         is_global = 0;
3382                         idx = count;
3383                         /*  Set atom types  */
3384                         up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3385                         if (up == NULL)
3386                                 return Qnil;
3387                         switch (parType) {
3388                                 case kBondParType:
3389                                         up->bond.type1 = t[0];
3390                                         up->bond.type2 = t[1];
3391                                         break;
3392                                 case kAngleParType:
3393                                         up->angle.type1 = t[0];
3394                                         up->angle.type2 = t[1];
3395                                         up->angle.type3 = t[2];
3396                                         break;
3397                                 case kDihedralParType:
3398                                 case kImproperParType:
3399                                         up->torsion.type1 = t[0];
3400                                         up->torsion.type2 = t[1];
3401                                         up->torsion.type3 = t[2];
3402                                         up->torsion.type4 = t[3];
3403                                         break;
3404                                 case kVdwParType:
3405                                         up->vdw.type1 = t[0];
3406                                         break;
3407                                 case kVdwPairParType:
3408                                         up->vdwp.type1 = t[0];
3409                                         up->vdwp.type2 = t[1];
3410                                         break;
3411                                 default:
3412                                         return Qnil;
3413                         }
3414                 }
3415         }
3416         return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3417 }
3418
3419 /*
3420  *  call-seq:
3421  *     lookup(par_type, atom_types, options, ...) -> ParameterRef
3422  *     lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3423  *
3424  *  Find the parameter record that matches the given atom types. The atom types are given
3425  *  either as an array of string, or a single string delimited by whitespaces or hyphens.
3426  *  Options are given as symbols. Valid values are :global (look for global parameters), :local
3427  *  (look for local parameters), :missing (look for missing parameters), :nowildcard (do not 
3428  *  allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3429  */
3430 static VALUE
3431 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3432 {
3433         int parType;
3434         Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3435         if (argc == 0)
3436                 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3437         parType = s_ParTypeFromValue(argv[0]);
3438         return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3439 }
3440
3441 /*
3442  *  call-seq:
3443  *     self == parameter -> boolean
3444  *  
3445  *  True if the parameters point to the same parameter table.
3446  */
3447 static VALUE
3448 s_Parameter_Equal(VALUE self, VALUE val)
3449 {
3450         if (rb_obj_is_kind_of(val, rb_cParameter)) {
3451                 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3452         } else return Qfalse;
3453 }
3454
3455 #pragma mark ====== ParEnumerable Class ======
3456
3457 /*  The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3458  and the parameter type. If the Molecule is NULL, then it refers to the
3459  global (built-in) parameters. Note that, even when the Molecule is not NULL,
3460  the global parameters are always accessible. */
3461
3462 typedef struct ParEnumerable {
3463         Molecule *mol;
3464         Int parType;   /*  Same as parType in ParameterRef  */
3465 } ParEnumerable;
3466
3467 static ParEnumerable *
3468 s_ParEnumerableNew(Molecule *mol, Int parType)
3469 {
3470         ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3471         if (pen != NULL) {
3472                 pen->mol = mol;
3473                 if (mol != NULL)
3474                         MoleculeRetain(mol);
3475                 pen->parType = parType;
3476         }
3477         return pen;
3478 }
3479
3480 static void
3481 s_ParEnumerableRelease(ParEnumerable *pen)
3482 {
3483         if (pen != NULL) {
3484                 if (pen->mol != NULL)
3485                         MoleculeRelease(pen->mol);
3486                 free(pen);
3487         }
3488 }
3489
3490 static Molecule *
3491 s_MoleculeFromParEnumerableValue(VALUE val)
3492 {
3493         ParEnumerable *pen;
3494     Data_Get_Struct(val, ParEnumerable, pen);
3495         return pen->mol;
3496 }
3497
3498 static VALUE
3499 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3500 {
3501         ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3502         if (pen == NULL)
3503                 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3504         return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3505 }
3506
3507 /*
3508  *  call-seq:
3509  *     par_type -> String
3510  *
3511  *  Get the parameter type, like "bond", "angle", etc.
3512  */
3513 static VALUE
3514 s_ParEnumerable_ParType(VALUE self) {
3515         ParEnumerable *pen;
3516         Int tp;
3517     Data_Get_Struct(self, ParEnumerable, pen);
3518         tp = pen->parType;
3519         if (tp == kElementParType)
3520                 return Ruby_NewEncodedStringValue2("element");
3521         tp -= kFirstParType;
3522         if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3523                 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3524         else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3525 }
3526
3527 /*
3528  *  call-seq:
3529  *     self[idx]          -> ParameterRef
3530  *  
3531  *  Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3532  *  Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3533  *  parent Parameter object of self.
3534  *
3535  *  <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper, 
3536  *  Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3537  */
3538 static VALUE
3539 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3540 {
3541         ParEnumerable *pen;
3542     Data_Get_Struct(self, ParEnumerable, pen);
3543         switch (pen->parType) {
3544                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3545                 case kBondParType:      return s_Parameter_Bond(self, ival);
3546                 case kAngleParType:     return s_Parameter_Angle(self, ival);
3547                 case kDihedralParType:  return s_Parameter_Dihedral(self, ival);
3548                 case kImproperParType:  return s_Parameter_Improper(self, ival);
3549                 case kVdwParType:       return s_Parameter_Vdw(self, ival);
3550                 case kVdwPairParType:   return s_Parameter_VdwPair(self, ival);
3551                 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3552                 case kElementParType:   return s_Parameter_Element(self, ival);
3553                 default:
3554                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3555         }
3556         return Qnil;  /*  Not reached  */
3557 }
3558
3559 /*
3560  *  call-seq:
3561  *     length          -> Integer
3562  *  
3563  *  Returns the number of parameters included in this enumerable.
3564  */
3565 static VALUE
3566 s_ParEnumerable_Length(VALUE self)
3567 {
3568         ParEnumerable *pen;
3569     Data_Get_Struct(self, ParEnumerable, pen);
3570         switch (pen->parType) {
3571                         /*  s_Parameter_XXXX() also accepts ParEnumerable argument  */
3572                 case kBondParType:      return s_Parameter_Nbonds(self);
3573                 case kAngleParType:     return s_Parameter_Nangles(self); 
3574                 case kDihedralParType:  return s_Parameter_Ndihedrals(self);
3575                 case kImproperParType:  return s_Parameter_Nimpropers(self);
3576                 case kVdwParType:       return s_Parameter_Nvdws(self);
3577                 case kVdwPairParType:   return s_Parameter_NvdwPairs(self);
3578                 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3579                 case kElementParType:   return s_Parameter_Nelements(self);
3580                 default:
3581                         rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3582         }
3583         return Qnil;  /*  Not reached  */
3584 }
3585
3586 /*
3587  *  call-seq:
3588  *     each {|pref| ...}
3589  *  
3590  *  Call the block for each parameter, passing a ParameterRef object as a block argument.
3591  */
3592 VALUE
3593 s_ParEnumerable_Each(VALUE self)
3594 {
3595         VALUE aval;
3596         ParEnumerable *pen;
3597         ParameterRef *pref;
3598         int i, ofs, n;
3599     Data_Get_Struct(self, ParEnumerable, pen);
3600         if (pen->parType == kElementParType)
3601                 n = gCountElementParameters;
3602         else {
3603                 switch (pen->parType) {
3604                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3605                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3606                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3607                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3608                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3609                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3610                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3611                         default:
3612                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3613                 }
3614                 if (pen->mol == NULL)
3615                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3616                 else if (pen->mol->par != NULL)
3617                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3618                 else return self;
3619         }               
3620         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3621         Data_Get_Struct(aval, ParameterRef, pref);
3622         for (i = 0; i < n; i++) {
3623                 pref->idx = i;
3624                 rb_yield(aval);
3625         }
3626     return self;
3627 }
3628
3629 /*
3630  *  call-seq:
3631  *     reverse_each {|pref| ...}
3632  *  
3633  *  Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3634  */
3635 VALUE
3636 s_ParEnumerable_ReverseEach(VALUE self)
3637 {
3638         VALUE aval;
3639         ParEnumerable *pen;
3640         ParameterRef *pref;
3641         int i, ofs, n;
3642     Data_Get_Struct(self, ParEnumerable, pen);
3643         if (pen->parType == kElementParType)
3644                 n = gCountElementParameters;
3645         else {
3646                 switch (pen->parType) {
3647                         case kBondParType:      ofs = offsetof(Parameter, nbondPars); break;
3648                         case kAngleParType:     ofs = offsetof(Parameter, nanglePars); break;
3649                         case kDihedralParType:  ofs = offsetof(Parameter, ndihedralPars); break;
3650                         case kImproperParType:  ofs = offsetof(Parameter, nimproperPars); break;
3651                         case kVdwParType:       ofs = offsetof(Parameter, nvdwPars); break;
3652                         case kVdwPairParType:   ofs = offsetof(Parameter, nvdwpPars); break;
3653                         case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3654                         default:
3655                                 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3656                 }
3657                 if (pen->mol == NULL)
3658                         n = *((Int *)((char *)gBuiltinParameters + ofs));
3659                 else if (pen->mol->par != NULL)
3660                         n = *((Int *)((char *)(pen->mol->par) + ofs));
3661                 else return self;
3662         }               
3663         aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3664         Data_Get_Struct(aval, ParameterRef, pref);
3665         for (i = n - 1; i >= 0; i--) {
3666                 pref->idx = i;
3667                 rb_yield(aval);
3668         }
3669     return self;
3670 }
3671
3672 /*
3673  *  call-seq:
3674  *     insert(idx = nil, pref = nil)       -> ParameterRef
3675  *  
3676  *  Insert a new parameter at the specified position (if idx is nil, then at the end).
3677  *  If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3678  *  and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3679  *  parameter is left undefined.
3680  *  Throws an exception if ParEnumerable points to the global parameter.
3681  */
3682 static VALUE
3683 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3684 {
3685         VALUE ival, pval;
3686         ParEnumerable *pen;
3687         int i, n;
3688         IntGroup *ig;
3689         UnionPar u;
3690         MolAction *act;
3691     Data_Get_Struct(self, ParEnumerable, pen);
3692         if (pen->mol == NULL)
3693                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3694         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3695         rb_scan_args(argc, argv, "02", &ival, &pval);
3696         if (ival != Qnil) {
3697                 i = NUM2INT(rb_Integer(ival));
3698                 if (i < 0 || i > n)
3699                         rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3700                 n = i;
3701         }
3702         if (pval != Qnil) {
3703                 Int type;
3704                 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3705                 if (up == NULL || type != pen->parType)
3706                         rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3707                 ParameterCopyOneWithType(&u, up, pen->parType);
3708                 u.bond.src = 0;
3709         } else {
3710                 memset(&u, 0, sizeof(u));
3711                 u.bond.src = 0;
3712         }
3713         ig = IntGroupNewWithPoints(n, 1, -1);
3714         ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3715
3716         act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3717         MolActionCallback_registerUndo(pen->mol, act);
3718         MolActionRelease(act);
3719         
3720         IntGroupRelease(ig);
3721         return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3722 }
3723
3724 /*
3725  *  call-seq:
3726  *     delete(Integer)
3727  *     delete(IntGroup)
3728  *  
3729  *  Delete the parameter(s) specified by the argument.
3730  */
3731 static VALUE
3732 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3733 {
3734         ParEnumerable *pen;
3735         int i, n;
3736         IntGroup *ig;
3737     Data_Get_Struct(self, ParEnumerable, pen);
3738         if (pen->mol == NULL)
3739                 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3740         n = ParameterGetCountForType(pen->mol->par, pen->parType);
3741         if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3742                 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3743                 i = 1;
3744         } else {
3745                 ig = IntGroupFromValue(ival);
3746                 if ((i = IntGroupGetCount(ig)) == 0) {
3747                         IntGroupRelease(ig);
3748                         return Qnil;
3749                 }
3750         }
3751         if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3752                 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3753         n = i;
3754
3755         MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3756         IntGroupRelease(ig);
3757         return ival;
3758 }
3759
3760 /*
3761  *  call-seq:
3762  *     lookup(atom_types, options, ...) -> ParameterRef
3763  *     lookup(atom_type_string, options, ...) -> ParameterRef
3764  *
3765  *  Find the parameter record that matches the given atom types. The arguments are
3766  *  the same as Parameter#lookup, except for the parameter type which is implicitly
3767  *  specified.
3768  */
3769 static VALUE
3770 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3771 {
3772         ParEnumerable *pen;
3773     Data_Get_Struct(self, ParEnumerable, pen);
3774         return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3775 }
3776
3777 /*
3778  *  call-seq:
3779  *     self == parEnumerable -> boolean
3780  *  
3781  *  True if the arguments point to the same parameter table and type.
3782  */
3783 static VALUE
3784 s_ParEnumerable_Equal(VALUE self, VALUE val)
3785 {
3786         if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3787                 ParEnumerable *pen1, *pen2;
3788                 Data_Get_Struct(self, ParEnumerable, pen1);
3789                 Data_Get_Struct(val, ParEnumerable, pen2);
3790                 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3791         } else return Qfalse;
3792 }
3793
3794 #pragma mark ====== AtomRef Class ======
3795
3796 /*  Forward declaration for register undo  */
3797 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3798
3799 /*  Ruby string "set_atom_attr"  */
3800 static VALUE s_SetAtomAttrString;
3801
3802 static int
3803 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3804 {
3805         AtomRef *aref;
3806         int idx;
3807         Data_Get_Struct(self, AtomRef, aref);
3808         idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3809         if (idx < 0 || idx >= aref->mol->natoms)
3810                 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3811         if (app != NULL)
3812                 *app = aref->mol->atoms + idx;
3813         if (mpp != NULL)
3814                 *mpp = aref->mol;
3815         return idx;
3816 }
3817
3818 static Atom *
3819 s_AtomFromValue(VALUE self)
3820 {
3821         Atom *ap;
3822         s_AtomIndexFromValue(self, &ap, NULL);
3823         return ap;
3824 }
3825
3826 static Atom *
3827 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3828 {
3829         Atom *ap;
3830         s_AtomIndexFromValue(self, &ap, mpp);
3831         return ap;
3832 }
3833
3834 static void
3835 s_NotifyModificationForAtomRef(VALUE self)
3836 {
3837         AtomRef *aref;
3838         Data_Get_Struct(self, AtomRef, aref);
3839         MoleculeIncrementModifyCount(aref->mol);
3840 }
3841
3842 static void
3843 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3844 {
3845         AtomRef *aref;
3846         Data_Get_Struct(self, AtomRef, aref);
3847         if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3848                 /*  Register undo  */
3849                 MolAction *act;
3850                 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3851                 MolActionCallback_registerUndo(aref->mol, act);
3852                 MoleculeCallback_notifyModification(aref->mol, 0);
3853                 /*  Request MD rebuilt if necessary  */
3854                 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3855                         aref->mol->needsMDRebuild = 1;
3856         }
3857 }
3858
3859 VALUE
3860 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3861 {
3862         AtomRef *aref;
3863         aref = AtomRefNew(mol, idx);
3864         return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3865 }
3866
3867 static VALUE
3868 s_AtomRef_GetMolecule(VALUE self)
3869 {
3870         Molecule *mpp;
3871         s_AtomIndexFromValue(self, NULL, &mpp);
3872         return ValueFromMolecule(mpp);
3873 }
3874
3875 static VALUE s_AtomRef_GetIndex(VALUE self) {
3876         return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3877 }
3878
3879 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3880         return INT2NUM(s_AtomFromValue(self)->segSeq);
3881 }
3882
3883 static VALUE s_AtomRef_GetSegName(VALUE self) {
3884         char *p = s_AtomFromValue(self)->segName;
3885         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3886 }
3887
3888 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3889         return INT2NUM(s_AtomFromValue(self)->resSeq);
3890 }
3891
3892 static VALUE s_AtomRef_GetResName(VALUE self) {
3893         char *p = s_AtomFromValue(self)->resName;
3894         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3895 }
3896
3897 static VALUE s_AtomRef_GetName(VALUE self) {
3898         char *p = s_AtomFromValue(self)->aname;
3899         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3900 }
3901
3902 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3903         int type = s_AtomFromValue(self)->type;
3904         char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3905         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3906 }
3907
3908 static VALUE s_AtomRef_GetCharge(VALUE self) {
3909         return rb_float_new(s_AtomFromValue(self)->charge);
3910 }
3911
3912 static VALUE s_AtomRef_GetWeight(VALUE self) {
3913         return rb_float_new(s_AtomFromValue(self)->weight);
3914 }
3915
3916 static VALUE s_AtomRef_GetElement(VALUE self) {
3917         char *p = s_AtomFromValue(self)->element;
3918         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3919 }
3920
3921 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3922         return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3923 }
3924
3925 static VALUE s_AtomRef_GetConnects(VALUE self) {
3926         VALUE retval;
3927         Int i, *cp;
3928         Atom *ap = s_AtomFromValue(self);
3929         retval = rb_ary_new();
3930         cp = AtomConnectData(&ap->connect);
3931         for (i = 0; i < ap->connect.count; i++)
3932                 rb_ary_push(retval, INT2NUM(cp[i]));
3933         return retval;
3934 }
3935
3936 static VALUE s_AtomRef_GetR(VALUE self) {
3937         return ValueFromVector(&(s_AtomFromValue(self)->r));
3938 }
3939
3940 static VALUE s_AtomRef_GetX(VALUE self) {
3941         return rb_float_new(s_AtomFromValue(self)->r.x);
3942 }
3943
3944 static VALUE s_AtomRef_GetY(VALUE self) {
3945         return rb_float_new(s_AtomFromValue(self)->r.y);
3946 }
3947
3948 static VALUE s_AtomRef_GetZ(VALUE self) {
3949         return rb_float_new(s_AtomFromValue(self)->r.z);
3950 }
3951
3952 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3953         Atom *ap;
3954         Molecule *mp;
3955         Vector r1;
3956         s_AtomIndexFromValue(self, &ap, &mp);
3957         r1 = ap->r;
3958         if (mp->cell != NULL)
3959                 TransformVec(&r1, mp->cell->rtr, &r1);
3960         return r1;
3961 }
3962
3963 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3964         Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3965         return ValueFromVector(&r1);
3966 }
3967
3968 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3969         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3970 }
3971
3972 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3973         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3974 }
3975
3976 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3977         return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3978 }
3979
3980 static VALUE s_AtomRef_GetSigma(VALUE self) {
3981         return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3982 }
3983
3984 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3985         return rb_float_new(s_AtomFromValue(self)->sigma.x);
3986 }
3987
3988 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3989         return rb_float_new(s_AtomFromValue(self)->sigma.y);
3990 }
3991
3992 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3993         return rb_float_new(s_AtomFromValue(self)->sigma.z);
3994 }
3995
3996 static VALUE s_AtomRef_GetV(VALUE self) {
3997         return ValueFromVector(&(s_AtomFromValue(self)->v));
3998 }
3999
4000 static VALUE s_AtomRef_GetF(VALUE self) {
4001         Vector v = s_AtomFromValue(self)->f;
4002         VecScaleSelf(v, INTERNAL2KCAL);
4003         return ValueFromVector(&v);
4004 }
4005
4006 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
4007         return rb_float_new(s_AtomFromValue(self)->occupancy);
4008 }
4009
4010 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
4011         return rb_float_new(s_AtomFromValue(self)->tempFactor);
4012 }
4013
4014 static VALUE s_AtomRef_GetAniso(VALUE self) {
4015         VALUE retval;
4016         int i;
4017         Atom *ap = s_AtomFromValue(self);
4018         if (ap->aniso == NULL)
4019                 return Qnil;
4020         retval = rb_ary_new();
4021         for (i = 0; i < 6; i++)
4022                 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
4023         if (ap->aniso->has_bsig) {
4024                 rb_ary_push(retval, INT2NUM(0));
4025                 for (i = 0; i < 6; i++)
4026                         rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
4027         }
4028         return retval;
4029 }
4030
4031 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
4032     VALUE retval;
4033     int i;
4034     Atom *ap = s_AtomFromValue(self);
4035     if (ap->aniso == NULL)
4036         return Qnil;
4037     retval = rb_ary_new();
4038     for (i = 0; i < 3; i++)
4039         rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
4040     return retval;
4041 }
4042
4043 static VALUE s_AtomRef_GetSymop(VALUE self) {
4044         VALUE retval;
4045         Atom *ap = s_AtomFromValue(self);
4046         if (!ap->symop.alive)
4047                 return Qnil;
4048         retval = rb_ary_new();
4049         rb_ary_push(retval, INT2NUM(ap->symop.sym));
4050         rb_ary_push(retval, INT2NUM(ap->symop.dx));
4051         rb_ary_push(retval, INT2NUM(ap->symop.dy));
4052         rb_ary_push(retval, INT2NUM(ap->symop.dz));
4053         rb_ary_push(retval, INT2NUM(ap->symbase));
4054         return retval;
4055 }
4056
4057 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
4058         return INT2NUM(s_AtomFromValue(self)->intCharge);
4059 }
4060
4061 static VALUE s_AtomRef_GetFixForce(VALUE self) {
4062         return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
4063 }
4064
4065 static VALUE s_AtomRef_GetFixPos(VALUE self) {
4066         return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
4067 }
4068
4069 static VALUE s_AtomRef_GetExclusion(VALUE self) {
4070         Molecule *mol;
4071         Atom *ap;
4072         int idx, i;
4073         MDExclusion *exinfo;
4074         Int *exlist;
4075         VALUE retval, aval;
4076         idx = s_AtomIndexFromValue(self, &ap, &mol);
4077         if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
4078                 VALUE mval = ValueFromMolecule(mol);
4079                 s_RebuildMDParameterIfNecessary(mval, Qnil);
4080         }
4081         if (mol->arena->exinfo == NULL)
4082                 return Qnil;
4083         exinfo = mol->arena->exinfo + idx;
4084         exlist = mol->arena->exlist;
4085         retval = rb_ary_new();
4086         aval = rb_ary_new();
4087         for (i = exinfo->index1; i < exinfo->index2; i++)  /* 1-2 exclusion  */
4088                 rb_ary_push(aval, INT2NUM(exlist[i]));
4089         rb_ary_push(retval, aval);
4090         aval = rb_ary_new();
4091         for (i = exinfo->index2; i < exinfo->index3; i++)  /* 1-3 exclusion  */
4092                 rb_ary_push(aval, INT2NUM(exlist[i]));
4093         rb_ary_push(retval, aval);
4094         aval = rb_ary_new();
4095         for (i = exinfo->index3; i < (exinfo + 1)->index0; i++)  /* 1-4 exclusion  */
4096                 rb_ary_push(aval, INT2NUM(exlist[i]));
4097         rb_ary_push(retval, aval);
4098         return retval;
4099 }
4100
4101 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
4102         return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4103 }
4104
4105 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4106         return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4107 }
4108
4109 static VALUE s_AtomRef_GetHidden(VALUE self) {
4110         return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4111 }
4112
4113 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4114         VALUE retval;
4115         Int i, count, *cp;
4116         Atom *ap = s_AtomFromValue(self);
4117         if (ap->anchor == NULL)
4118                 return Qnil;
4119         count = ap->anchor->connect.count;
4120         retval = rb_ary_new2(count * 2);
4121         cp = AtomConnectData(&ap->anchor->connect);
4122         for (i = 0; i < count; i++) {
4123                 rb_ary_store(retval, i, INT2NUM(cp[i]));
4124                 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4125         }
4126         return retval;
4127 }
4128
4129 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4130         char *p = s_AtomFromValue(self)->uff_type;
4131         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4132 }
4133
4134 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4135         rb_raise(rb_eMolbyError, "index cannot be directly set");
4136         return Qnil;
4137 }
4138
4139 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4140         VALUE oval = s_AtomRef_GetSegSeq(self);
4141         val = rb_Integer(val);
4142         s_AtomFromValue(self)->segSeq = NUM2INT(val);
4143         s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4144         return val;
4145 }
4146
4147 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4148         char *p = StringValuePtr(val);
4149         VALUE oval = s_AtomRef_GetSegName(self);
4150         strncpy(s_AtomFromValue(self)->segName, p, 4);
4151         s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4152         return val;
4153 }
4154
4155 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4156         rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4157         return val; /* Not reached */
4158 }
4159
4160 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4161         Atom *ap = s_AtomFromValue(self);
4162         char *p = StringValuePtr(val);
4163         VALUE oval = s_AtomRef_GetName(self);
4164         if (ap->anchor != NULL && p[0] == '_')
4165                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4166         strncpy(ap->aname, p, 4);
4167         s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4168         return val;
4169 }
4170
4171 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4172         Molecule *mp;
4173         char *p = StringValuePtr(val);
4174         VALUE oval = s_AtomRef_GetAtomType(self);
4175         int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4176         if (type != 0 && type < kAtomTypeMinimum)
4177                 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4178         s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4179         mp->needsMDRebuild = 1;
4180         s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4181         return val;
4182 }
4183
4184 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4185         Molecule *mp;
4186         VALUE oval = s_AtomRef_GetCharge(self);
4187         val = rb_Float(val);
4188         s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4189         mp->needsMDRebuild = 1;
4190         s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4191         return val;
4192 }
4193
4194 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4195         Molecule *mp;
4196         VALUE oval = s_AtomRef_GetWeight(self);
4197         val = rb_Float(val);
4198         s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4199         mp->needsMDRebuild = 1;
4200         s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4201         return val;
4202 }
4203
4204 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4205         Double w;
4206         Molecule *mp;
4207         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4208         char *p = StringValuePtr(val);
4209         VALUE oval = s_AtomRef_GetElement(self);
4210         ap->atomicNumber = ElementToInt(p);
4211         ElementToString(ap->atomicNumber, ap->element);
4212         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4213                 ap->weight = w;
4214         s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4215         mp->needsMDRebuild = 1;
4216         return val;
4217 }
4218
4219 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4220         Double w;
4221         Molecule *mp;
4222         Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4223         VALUE oval = s_AtomRef_GetAtomicNumber(self);
4224         val = rb_Integer(val);
4225         ap->atomicNumber = NUM2INT(val);
4226         ElementToString(ap->atomicNumber, ap->element);
4227         if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4228                 ap->weight = w;
4229         s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4230         mp->needsMDRebuild = 1;
4231         return val;
4232 }
4233
4234 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4235         rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4236         return val; /* Not reached */
4237 }
4238
4239 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4240         Vector v;
4241         Molecule *mp;
4242         VALUE oval = s_AtomRef_GetR(self);
4243         VectorFromValue(val, &v);
4244         s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4245         s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4246         mp->needsMDCopyCoordinates = 1;
4247         return val;
4248 }
4249
4250 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4251         Double f;
4252         Molecule *mp;
4253         VALUE oval = s_AtomRef_GetX(self);
4254         val = rb_Float(val);
4255         f = NUM2DBL(val);
4256         s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4257         s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4258         mp->needsMDCopyCoordinates = 1;
4259         return val;
4260 }
4261
4262 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4263         Double f;
4264         Molecule *mp;
4265         VALUE oval = s_AtomRef_GetY(self);
4266         val = rb_Float(val);
4267         f = NUM2DBL(val);
4268         s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4269         s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4270         mp->needsMDCopyCoordinates = 1;
4271         return val;
4272 }
4273
4274 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4275         Double f;
4276         Molecule *mp;
4277         VALUE oval = s_AtomRef_GetZ(self);
4278         val = rb_Float(val);
4279         f = NUM2DBL(val);
4280         s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4281         s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4282         mp->needsMDCopyCoordinates = 1;
4283         return val;
4284 }
4285
4286 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4287         Vector v, ov;
4288         Atom *ap;
4289         Molecule *mp;
4290         s_AtomIndexFromValue(self, &ap, &mp);
4291         ov = ap->r;
4292         VectorFromValue(val, &v);
4293         if (mp->cell != NULL)
4294                 TransformVec(&v, mp->cell->tr, &v);
4295         ap->r = v;
4296         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4297         mp->needsMDCopyCoordinates = 1;
4298         return val;
4299 }
4300
4301 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4302         double f;
4303         Vector v, ov;
4304         Atom *ap;
4305         Molecule *mp;
4306         s_AtomIndexFromValue(self, &ap, &mp);
4307         ov = v = ap->r;
4308         val = rb_Float(val);
4309         f = NUM2DBL(val);
4310         if (mp->cell != NULL) {
4311                 TransformVec(&v, mp->cell->rtr, &v);
4312                 v.x = f;
4313                 TransformVec(&v, mp->cell->tr, &v);
4314         } else v.x = f;
4315         ap->r = v;
4316         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4317         mp->needsMDCopyCoordinates = 1;
4318         return val;
4319 }
4320
4321 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4322         double f;
4323         Vector v, ov;
4324         Atom *ap;
4325         Molecule *mp;
4326         s_AtomIndexFromValue(self, &ap, &mp);
4327         ov = v = ap->r;
4328         val = rb_Float(val);
4329         f = NUM2DBL(val);
4330         if (mp->cell != NULL) {
4331                 TransformVec(&v, mp->cell->rtr, &v);
4332                 v.y = f;
4333                 TransformVec(&v, mp->cell->tr, &v);
4334         } else v.y = f;
4335         ap->r = v;
4336         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4337         mp->needsMDCopyCoordinates = 1;
4338         return val;
4339 }
4340
4341 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4342         double f;
4343         Vector v, ov;
4344         Atom *ap;
4345         Molecule *mp;
4346         s_AtomIndexFromValue(self, &ap, &mp);
4347         ov = v = ap->r;
4348         val = rb_Float(val);
4349         f = NUM2DBL(val);
4350         if (mp->cell != NULL) {
4351                 TransformVec(&v, mp->cell->rtr, &v);
4352                 v.z = f;
4353                 TransformVec(&v, mp->cell->tr, &v);
4354         } else v.z = f;
4355         ap->r = v;
4356         s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4357         mp->needsMDCopyCoordinates = 1;
4358         return val;
4359 }
4360
4361 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4362         Vector v;
4363         Molecule *mp;
4364         VALUE oval = s_AtomRef_GetSigma(self);
4365         VectorFromValue(val, &v);
4366         s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4367         s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4368         mp->needsMDCopyCoordinates = 1;
4369         return val;
4370 }
4371
4372 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4373         Double f;
4374         Molecule *mp;
4375         VALUE oval = s_AtomRef_GetSigmaX(self);
4376         val = rb_Float(val);
4377         f = NUM2DBL(val);
4378         s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4379         s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4380         mp->needsMDCopyCoordinates = 1;
4381         return val;
4382 }
4383
4384 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4385         Double f;
4386         Molecule *mp;
4387         VALUE oval = s_AtomRef_GetSigmaY(self);
4388         val = rb_Float(val);
4389         f = NUM2DBL(val);
4390         s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4391         s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4392         mp->needsMDCopyCoordinates = 1;
4393         return val;
4394 }
4395
4396 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4397         Double f;
4398         Molecule *mp;
4399         VALUE oval = s_AtomRef_GetSigmaZ(self);
4400         val = rb_Float(val);
4401         f = NUM2DBL(val);
4402         s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4403         s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4404         mp->needsMDCopyCoordinates = 1;
4405         return val;
4406 }
4407
4408 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4409         Vector v;
4410         Atom *ap;
4411         Molecule *mp;
4412         VALUE oval = s_AtomRef_GetV(self);
4413         VectorFromValue(val, &v);
4414         s_AtomIndexFromValue(self, &ap, &mp);
4415         ap->v = v;
4416         s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4417         mp->needsMDCopyCoordinates = 1;
4418         return val;
4419 }
4420
4421 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4422         Vector v;
4423         Molecule *mp;
4424         VALUE oval = s_AtomRef_GetF(self);
4425         VectorFromValue(val, &v);
4426         VecScaleSelf(v, KCAL2INTERNAL);
4427         s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4428         s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4429         mp->needsMDCopyCoordinates = 1;
4430         return val;
4431 }
4432
4433 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4434         VALUE oval = s_AtomRef_GetOccupancy(self);
4435         Molecule *mp;
4436         val = rb_Float(val);
4437         s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4438         s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4439         mp->needsMDCopyCoordinates = 1;  /*  Occupancy can be used to exclude particular atoms from MM/MD  */
4440         return val;
4441 }
4442
4443 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4444         VALUE oval = s_AtomRef_GetTempFactor(self);
4445         val = rb_Float(val);
4446         s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4447         s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4448         return val;
4449 }
4450
4451 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4452         AtomRef *aref;
4453         int i, n, type;
4454         VALUE *valp;
4455         Double f[12];
4456         VALUE oval = s_AtomRef_GetAniso(self);
4457         Data_Get_Struct(self, AtomRef, aref);
4458         val = rb_funcall(val, rb_intern("to_a"), 0);
4459         n = RARRAY_LEN(val);
4460         valp = RARRAY_PTR(val);
4461         for (i = 0; i < 6; i++) {
4462                 if (i < n)
4463                         f[i] = NUM2DBL(rb_Float(valp[i]));
4464                 else f[i] = 0.0;
4465         }
4466         if (n >= 7)
4467                 type = NUM2INT(rb_Integer(valp[6]));
4468         else type = 0;
4469         if (n >= 13) {
4470                 for (i = 0; i < 6; i++)
4471                         f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4472         } else {
4473                 for (i = 0; i < 6; i++)
4474                         f[i + 6] = 0.0;
4475         }
4476         i = s_AtomIndexFromValue(self, NULL, NULL);
4477         MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4478         s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4479         return val;
4480 }
4481
4482 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4483     rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4484     return val; /* Not reached */
4485 }
4486
4487 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4488         Molecule *mol;
4489         Atom *ap;
4490         int i, n;
4491         VALUE *valp;
4492         Int ival[5];
4493         VALUE oval = s_AtomRef_GetSymop(self);
4494         i = s_AtomIndexFromValue(self, &ap, &mol);
4495         if (val == Qnil) {
4496                 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4497         } else {
4498                 val = rb_funcall(val, rb_intern("to_a"), 0);
4499                 n = RARRAY_LEN(val);
4500                 valp = RARRAY_PTR(val);
4501                 for (i = 0; i < 5; i++) {
4502                         if (i < n) {
4503                                 if (valp[i] == Qnil)
4504                                         ival[i] = -100000;
4505                                 else 
4506                                         ival[i] = NUM2INT(rb_Integer(valp[i]));
4507                         } else ival[i] = -100000;
4508                 }
4509         }
4510         if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4511                 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));
4512         if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4513                 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4514         if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4515                 return val;  /*  No need to change  */
4516         if (ival[0] != -100000)
4517                 ap->symop.sym = ival[0];
4518         if (ival[1] != -100000)
4519                 ap->symop.dx = ival[1];
4520         if (ival[2] != -100000)
4521                 ap->symop.dy = ival[2];
4522         if (ival[3] != -100000)
4523                 ap->symop.dz = ival[3];
4524         ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4525         if (ival[4] != -100000)
4526                 ap->symbase = ival[4];
4527         if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4528                 /*  The anisotropic parameters should be recalculated  */
4529                 VALUE oaval = s_AtomRef_GetAniso(self);
4530                 MoleculeSetAnisoBySymop(mol, i);
4531                 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4532         }
4533         s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4534         return val;
4535 }
4536
4537 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4538         VALUE oval = s_AtomRef_GetIntCharge(self);
4539         val = rb_Integer(val);
4540         s_AtomFromValue(self)->intCharge = NUM2INT(val);
4541         s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4542         return val;
4543 }
4544
4545 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4546         Molecule *mp;
4547         VALUE oval = s_AtomRef_GetFixForce(self);
4548         val = rb_Float(val);
4549         s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4550         s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4551         mp->needsMDRebuild = 1;
4552         return val;
4553 }
4554
4555 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4556         Vector v;
4557         Molecule *mp;
4558         VALUE oval = s_AtomRef_GetFixPos(self);
4559         VectorFromValue(val, &v);
4560         s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4561         s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4562         mp->needsMDRebuild = 1;
4563         return val;
4564 }
4565
4566 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4567         rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4568         return val; /* Not reached */
4569 }
4570
4571 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4572         VALUE oval = s_AtomRef_GetIntCharge(self);
4573         val = rb_Integer(val);
4574         s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4575         s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4576         return val;
4577 }
4578
4579 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4580         VALUE oval = s_AtomRef_GetIntCharge(self);
4581         val = rb_Integer(val);
4582         s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4583         s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4584         return val;
4585 }
4586
4587 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4588         Atom *ap = s_AtomFromValue(self);
4589         VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4590         if (RTEST(val)) {
4591                 ap->exflags |= kAtomHiddenFlag;
4592         } else {
4593                 ap->exflags &= ~kAtomHiddenFlag;
4594         }
4595         s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4596         return val;
4597 }
4598
4599 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4600         Int idx, i, j, k, n, *ip;
4601         Double *dp;
4602         Atom *ap;
4603         Molecule *mol;
4604         VALUE oval, v;
4605         AtomConnect ac;
4606         Int nUndoActions;
4607         MolAction **undoActions;
4608         memset(&ac, 0, sizeof(ac));
4609         idx = s_AtomIndexFromValue(self, &ap, &mol);
4610         oval = s_AtomRef_GetAnchorList(self);
4611         if (val != Qnil) {
4612                 val = rb_ary_to_ary(val);
4613                 n = RARRAY_LEN(val);
4614         } else n = 0;
4615         if (n == 0) {
4616                 if (ap->anchor != NULL) {
4617                         AtomConnectResize(&ap->anchor->connect, 0);
4618                         free(ap->anchor->coeffs);
4619                         free(ap->anchor);
4620                         ap->anchor = NULL;
4621                         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4622                 }
4623                 return val;
4624         }
4625         if (n < 2)
4626                 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4627         if (ap->aname[0] == '_')
4628                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4629         ip = (Int *)malloc(sizeof(Int) * n);
4630         dp = NULL;
4631         for (i = 0; i < n; i++) {
4632                 v = RARRAY_PTR(val)[i];
4633                 if (rb_obj_is_kind_of(v, rb_cFloat))
4634                         break;
4635                 j = NUM2INT(rb_Integer(v));
4636                 if (j < 0 || j >= mol->natoms)
4637                         rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4638                 for (k = 0; k < i; k++) {
4639                         if (ip[k] == j)
4640                                 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4641                 }
4642                 ip[i] = j;
4643         }
4644         if (i < n) {
4645                 if (i < 2)
4646                         rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4647                 else if (i * 2 != n)
4648                         rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4649                 dp = (Double *)malloc(sizeof(Double) * n / 2);
4650                 for (i = 0; i < n / 2; i++) {
4651                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4652                         if (dp[i] <= 0.0)
4653                                 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4654                 }
4655                 n /= 2;
4656         }
4657         nUndoActions = 0;
4658         undoActions = NULL;
4659         i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4660         free(dp);
4661         free(ip);
4662         if (i != 0)
4663                 rb_raise(rb_eMolbyError, "invalid argument");
4664         if (nUndoActions > 0) {
4665                 for (i = 0; i < nUndoActions; i++) {
4666                         MolActionCallback_registerUndo(mol, undoActions[i]);
4667                         MolActionRelease(undoActions[i]);
4668                 }
4669                 free(undoActions);
4670         }
4671         s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4672         return val;
4673 }
4674
4675 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4676         Atom *ap = s_AtomFromValue(self);
4677         char *p = StringValuePtr(val);
4678         VALUE oval = s_AtomRef_GetUFFType(self);
4679         strncpy(ap->uff_type, p, 5);
4680         ap->uff_type[5] = 0;
4681         s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4682         return val;
4683 }
4684
4685 static struct s_AtomAttrDef {
4686         char *name;
4687         VALUE *symref;  /*  Address of s_IndexSymbol etc. */
4688         ID id;                  /*  Will be set within InitMolby()  */
4689         VALUE (*getter)(VALUE);
4690         VALUE (*setter)(VALUE, VALUE);
4691 } s_AtomAttrDefTable[] = {
4692         {"index",        &s_IndexSym,        0, s_AtomRef_GetIndex,        s_AtomRef_SetIndex},
4693         {"seg_seq",       &s_SegSeqSym,      0, s_AtomRef_GetSegSeq,       s_AtomRef_SetSegSeq},
4694         {"seg_name",      &s_SegNameSym,     0, s_AtomRef_GetSegName,      s_AtomRef_SetSegName},
4695         {"res_seq",       &s_ResSeqSym,      0, s_AtomRef_GetResSeq,       s_AtomRef_SetResSeqOrResName},
4696         {"res_name",      &s_ResNameSym,     0, s_AtomRef_GetResName,      s_AtomRef_SetResSeqOrResName},
4697         {"name",         &s_NameSym,         0, s_AtomRef_GetName,         s_AtomRef_SetName},
4698         {"atom_type",     &s_AtomTypeSym,    0, s_AtomRef_GetAtomType,     s_AtomRef_SetAtomType},
4699         {"charge",       &s_ChargeSym,       0, s_AtomRef_GetCharge,       s_AtomRef_SetCharge},
4700         {"weight",       &s_WeightSym,       0, s_AtomRef_GetWeight,       s_AtomRef_SetWeight},
4701         {"element",      &s_ElementSym,      0, s_AtomRef_GetElement,      s_AtomRef_SetElement},
4702         {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4703         {"connects",     &s_ConnectsSym,     0, s_AtomRef_GetConnects,     s_AtomRef_SetConnects},
4704         {"r",            &s_RSym,            0, s_AtomRef_GetR,            s_AtomRef_SetR},
4705         {"x",            &s_XSym,            0, s_AtomRef_GetX,            s_AtomRef_SetX},
4706         {"y",            &s_YSym,            0, s_AtomRef_GetY,            s_AtomRef_SetY},
4707     {"z",            &s_ZSym,            0, s_AtomRef_GetZ,            s_AtomRef_SetZ},
4708         {"fract_r",      &s_FractRSym,       0, s_AtomRef_GetFractionalR,  s_AtomRef_SetFractionalR},
4709         {"fract_x",      &s_FractXSym,       0, s_AtomRef_GetFractionalX,  s_AtomRef_SetFractionalX},
4710         {"fract_y",      &s_FractYSym,       0, s_AtomRef_GetFractionalY,  s_AtomRef_SetFractionalY},
4711         {"fract_z",      &s_FractZSym,       0, s_AtomRef_GetFractionalZ,  s_AtomRef_SetFractionalZ},
4712         {"sigma",        &s_SigmaSym,        0, s_AtomRef_GetSigma,        s_AtomRef_SetSigma},
4713         {"sigma_x",      &s_SigmaXSym,       0, s_AtomRef_GetSigmaX,       s_AtomRef_SetSigmaX},
4714         {"sigma_y",      &s_SigmaYSym,       0, s_AtomRef_GetSigmaY,       s_AtomRef_SetSigmaY},
4715         {"sigma_z",      &s_SigmaZSym,       0, s_AtomRef_GetSigmaZ,       s_AtomRef_SetSigmaZ},
4716         {"v",            &s_VSym,            0, s_AtomRef_GetV,            s_AtomRef_SetV},
4717         {"f",            &s_FSym,            0, s_AtomRef_GetF,            s_AtomRef_SetF},
4718         {"occupancy",    &s_OccupancySym,    0, s_AtomRef_GetOccupancy,    s_AtomRef_SetOccupancy},
4719         {"temp_factor",  &s_TempFactorSym,   0, s_AtomRef_GetTempFactor,   s_AtomRef_SetTempFactor},
4720         {"aniso",        &s_AnisoSym,        0, s_AtomRef_GetAniso,        s_AtomRef_SetAniso},
4721     {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues,        s_AtomRef_SetAnisoEigenValues},
4722         {"symop",        &s_SymopSym,        0, s_AtomRef_GetSymop,        s_AtomRef_SetSymop},
4723         {"int_charge",   &s_IntChargeSym,    0, s_AtomRef_GetIntCharge,    s_AtomRef_SetIntCharge},
4724         {"fix_force",    &s_FixForceSym,     0, s_AtomRef_GetFixForce,     s_AtomRef_SetFixForce},
4725         {"fix_pos",      &s_FixPosSym,       0, s_AtomRef_GetFixPos,       s_AtomRef_SetFixPos},
4726         {"exclusion",    &s_ExclusionSym,    0, s_AtomRef_GetExclusion,    s_AtomRef_SetExclusion},
4727         {"mm_exclude",   &s_MMExcludeSym,    0, s_AtomRef_GetMMExclude,    s_AtomRef_SetMMExclude},
4728         {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4729         {"hidden",       &s_HiddenSym,       0, s_AtomRef_GetHidden,       s_AtomRef_SetHidden},
4730         {"anchor_list",  &s_AnchorListSym,   0, s_AtomRef_GetAnchorList,   s_AtomRef_SetAnchorList},
4731         {"uff_type",     &s_UFFTypeSym,      0, s_AtomRef_GetUFFType,      s_AtomRef_SetUFFType},
4732         {NULL} /* Sentinel */
4733 };
4734
4735 static VALUE
4736 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4737 {
4738         int i;
4739         ID kid;
4740         if (TYPE(key) != T_SYMBOL) {
4741                 kid = rb_intern(StringValuePtr(key));
4742                 key = ID2SYM(kid);
4743         } else kid = SYM2ID(key);
4744         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4745                 if (s_AtomAttrDefTable[i].id == kid) {
4746                         if (value == Qundef)
4747                                 return (*(s_AtomAttrDefTable[i].getter))(self);
4748                         else
4749                                 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4750                 }
4751         }
4752         rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4753         return Qnil; /* not reached */
4754 }
4755
4756 static VALUE
4757 s_AtomRef_GetAttr(VALUE self, VALUE key)
4758 {
4759         return s_AtomRef_SetAttr(self, key, Qundef);
4760 }
4761
4762 /*
4763  *  call-seq:
4764  *     self == atomRef -> boolean
4765  *
4766  *  True if the two references point to the same atom.
4767  */
4768 static VALUE
4769 s_AtomRef_Equal(VALUE self, VALUE val)
4770 {
4771         if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4772                 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4773         } else return Qfalse;
4774 }
4775
4776 #pragma mark ====== MolEnumerable Class ======
4777
4778 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4779
4780 /*
4781  *  call-seq:
4782  *     self[idx] -> AtomRef or Array of Integers
4783  *  
4784  *  Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4785  *  derived from. For the atom, the return value is AtomRef. For the residue, the return
4786  *  value is a String. Otherwise, the return value is an Array of Integers.
4787  */
4788 static VALUE
4789 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4790 {
4791         MolEnumerable *mseq;
4792         Molecule *mol;
4793         int idx1, idx2;
4794     Data_Get_Struct(self, MolEnumerable, mseq);
4795         mol = mseq->mol;
4796         if (mseq->kind == kAtomKind) {
4797                 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4798         }
4799         idx1 = NUM2INT(arg1);
4800         switch (mseq->kind) {
4801                 case kBondKind: {
4802                         idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4803                         if (idx2 < 0 || idx2 >= mol->nbonds)
4804                                 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4805                         return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4806                 }
4807                 case kAngleKind: {
4808                         idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4809                         if (idx2 < 0 || idx2 >= mol->nangles)
4810                                 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4811                         return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4812                 }
4813                 case kDihedralKind: {
4814                         idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4815                         if (idx2 < 0 || idx2 >= mol->ndihedrals)
4816                                 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4817                         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]));
4818                 }
4819                 case kImproperKind: {
4820                         idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4821                         if (idx2 < 0 || idx2 >= mol->nimpropers)
4822                                 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4823                         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]));
4824                 }
4825                 case kResidueKind: {
4826                         char *p;
4827                         idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4828                         if (idx2 < 0 || idx2 >= mol->nresidues)
4829                                 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4830                         p = mol->residues[idx2];
4831                         return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4832                 }
4833         }
4834         return Qnil;
4835 }
4836
4837 /*
4838  *  call-seq:
4839  *     length          -> Integer
4840  *  
4841  *  Returns the number of objects included in this enumerable.
4842  */
4843 static VALUE
4844 s_MolEnumerable_Length(VALUE self)
4845 {
4846         MolEnumerable *mseq;
4847     Data_Get_Struct(self, MolEnumerable, mseq);
4848         switch (mseq->kind) {
4849                 case kAtomKind:
4850                         return INT2NUM(mseq->mol->natoms);
4851                 case kBondKind:
4852                         return INT2NUM(mseq->mol->nbonds);
4853                 case kAngleKind:
4854                         return INT2NUM(mseq->mol->nangles);
4855                 case kDihedralKind:
4856                         return INT2NUM(mseq->mol->ndihedrals);
4857                 case kImproperKind:
4858                         return INT2NUM(mseq->mol->nimpropers);
4859                 case kResidueKind:
4860                         return INT2NUM(mseq->mol->nresidues);
4861         }
4862         return INT2NUM(-1);
4863 }
4864
4865 /*
4866  *  call-seq:
4867  *     each {|obj| ...}
4868  *  
4869  *  Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4870  *  an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4871  *  For the atoms, a same AtomRef object is passed (with different internal information)
4872  *  for each invocation of block. Otherwise, a new Ruby object will be created and passed
4873  *  for each iteration.
4874  */
4875 VALUE
4876 s_MolEnumerable_Each(VALUE self)
4877 {
4878         MolEnumerable *mseq;
4879         int i;
4880         int len = NUM2INT(s_MolEnumerable_Length(self));
4881     Data_Get_Struct(self, MolEnumerable, mseq);
4882         if (mseq->kind == kAtomKind) {
4883                 /*  The same AtomRef object will be used during the loop  */
4884                 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4885                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4886                 for (i = 0; i < len; i++) {
4887                         aref->idx = i;
4888                         rb_yield(arval);
4889                 }
4890     } else {
4891                 /*  A new ruby object will be created at each iteration (not very efficient)  */
4892                 for (i = 0; i < len; i++) {
4893                         rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4894                 }
4895         }
4896     return self;
4897 }
4898
4899 /*
4900  *  call-seq:
4901  *     self == molEnumerable -> boolean
4902  *
4903  *  True if the two arguments point to the same molecule and enumerable type.
4904  */
4905 static VALUE
4906 s_MolEnumerable_Equal(VALUE self, VALUE val)
4907 {
4908         if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4909                 MolEnumerable *mseq1, *mseq2;
4910                 Data_Get_Struct(self, MolEnumerable, mseq1);
4911                 Data_Get_Struct(val, MolEnumerable, mseq2);
4912                 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4913         } else return Qfalse;
4914 }
4915
4916
4917 #pragma mark ====== Molecule Class ======
4918
4919 #pragma mark ------ Allocate/Release/Accessor ------
4920
4921 /*  An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method.  */
4922 /*  Accessible from Ruby as Molecule#error_message and Molecule#error_message=.  */
4923 char *gLoadSaveErrorMessage = NULL;
4924
4925 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4926
4927 Molecule *
4928 MoleculeFromValue(VALUE val)
4929 {
4930         Molecule *mol;
4931         if (!rb_obj_is_kind_of(val, rb_cMolecule))
4932                 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4933     Data_Get_Struct(val, Molecule, mol);
4934         return mol;
4935 }
4936
4937 static VALUE sMoleculeRetainArray = Qnil;
4938
4939 /*  The function is called from MoleculeRelease()  */
4940 /*  The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4941 /*  GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4942 /*  object is always returned for the same Molecule.  */
4943 /*  When the reference count of the Molecule becomes 1, then the Ruby object is */
4944 /*  removed from sMoleculeRetainArray. In this situation, the Molecule is retained  */
4945 /*  only by the currently alive Ruby containers.  When the Ruby Molecule object is */
4946 /*  removed from all alive Ruby containers, the Ruby object will be collected by */
4947 /*  the next GC invocation, and at that time the Molecule structure is properly released. */
4948
4949 /*  Register/unregister the exmolobj Ruby object  */
4950 void
4951 MoleculeReleaseExternalObj(Molecule *mol)
4952 {
4953         if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4954                 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4955                 mol->exmolobjProtected = 0;
4956         }
4957 }
4958
4959 void
4960 MoleculeRetainExternalObj(Molecule *mol)
4961 {
4962         if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4963                 if (sMoleculeRetainArray == Qnil) {
4964                         rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4965                         sMoleculeRetainArray = rb_ary_new();
4966                 }
4967                 
4968                 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4969                 mol->exmolobjProtected = 1;
4970         }
4971 }
4972
4973 /*  Release hook function for Ruby  */
4974 void
4975 MoleculeReleaseHook(Molecule *mol)
4976 {
4977         if (mol->exmolobj != NULL) {
4978                 /*  No need to remove from sMoleculeRetainArray  */
4979                 mol->exmolobj = NULL;
4980                 mol->exmolobjProtected = 0;
4981         }
4982         MoleculeRelease(mol);
4983 }
4984
4985 VALUE
4986 ValueFromMolecule(Molecule *mol)
4987 {
4988         if (mol == NULL)
4989                 return Qnil;
4990         if (mol->exmolobj != NULL)
4991                 return (VALUE)mol->exmolobj;
4992         mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4993         MoleculeRetain(mol);  /*  MoleculeRetainExternalObj() is automatically called  */
4994         return (VALUE)mol->exmolobj;
4995 }
4996
4997 /*  Allocator  */
4998 static VALUE
4999 s_Molecule_Alloc(VALUE klass)
5000 {
5001         VALUE val;
5002         Molecule *mol = MoleculeNew();
5003         val = ValueFromMolecule(mol);
5004         MoleculeRelease(mol); /*  Only the returned Ruby object retains this molecule  */
5005         return val;
5006 }
5007
5008 static int
5009 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
5010 {
5011         int n;
5012         char *p = "";
5013         if (FIXNUM_P(val)) {
5014                 n = FIX2INT(val);
5015                 if (n >= 0 && n < mol->natoms)
5016                         return n;
5017                 n = -1; /*  No such atom  */
5018                 val = rb_inspect(val);
5019         } else {
5020                 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
5021         }
5022         if (n >= 0 && n < mol->natoms)
5023                 return n;
5024         p = StringValuePtr(val);
5025         if (n == -1)
5026                 rb_raise(rb_eMolbyError, "no such atom: %s", p);
5027         else if (n == -2)
5028                 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
5029         else
5030                 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
5031         return 0; /* Not reached */
5032 }
5033
5034 static IntGroup *
5035 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
5036 {
5037         IntGroup *ig;
5038     Molecule *mp1;
5039     Data_Get_Struct(self, Molecule, mp1);
5040         val = rb_funcall(self, rb_intern("atom_group"), 1, val);
5041         if (!rb_obj_is_kind_of(val, rb_cIntGroup))
5042                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5043         Data_Get_Struct(val, IntGroup, ig);
5044     IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
5045         IntGroupRetain(ig);
5046         return ig;
5047 }
5048
5049 /*
5050  *  call-seq:
5051  *     dup          -> Molecule
5052  *
5053  *  Duplicate a molecule. All entries are deep copied, so modifying the newly
5054  *  created object does not affect the old object in any sense.
5055  */
5056 static VALUE
5057 s_Molecule_InitCopy(VALUE self, VALUE arg)
5058 {
5059         Molecule *mp1, *mp2;
5060         Data_Get_Struct(self, Molecule, mp1);
5061         mp2 = MoleculeFromValue(arg);
5062         if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
5063                 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
5064         return self;
5065 }
5066
5067 /*
5068  *  call-seq:
5069  *     atom_index(val)       -> Integer
5070  *
5071  *  Returns the atom index represented by val. val can be either a non-negative integer
5072  *  (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5073  *  a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name", 
5074  *  where resname, resid, name are the residue name, residue id, and atom name respectively.
5075  *  If val is a string and multiple atoms match the description, the atom with the lowest index
5076  *  is returned.
5077  */
5078 static VALUE
5079 s_Molecule_AtomIndex(VALUE self, VALUE val)
5080 {
5081     Molecule *mol;
5082     Data_Get_Struct(self, Molecule, mol);
5083         return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5084 }
5085
5086 /*
5087  *  call-seq:
5088  *     self == Molecule -> boolean
5089  *
5090  *  True if the two arguments point to the same molecule.
5091  */
5092 static VALUE
5093 s_Molecule_Equal(VALUE self, VALUE val)
5094 {
5095         if (rb_obj_is_kind_of(val, rb_cMolecule)) {
5096                 Molecule *mol1, *mol2;
5097                 Data_Get_Struct(self, Molecule, mol1);
5098                 Data_Get_Struct(val, Molecule, mol2);
5099                 return (mol1 == mol2 ? Qtrue : Qfalse);
5100         } else return Qfalse;
5101 }
5102
5103 #pragma mark ------ Load/Save ------
5104
5105 static void
5106 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5107 {
5108         if (gLoadSaveErrorMessage != NULL) {
5109                 MyAppCallback_setConsoleColor(1);
5110                 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5111                 MyAppCallback_setConsoleColor(0);
5112         }
5113         if (status != 0)
5114                 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5115 }
5116
5117 /*
5118  *  call-seq:
5119  *     loadmbsf(file)       -> bool
5120  *
5121  *  Read a structure from a mbsf file.
5122  *  Return true if successful.
5123  */
5124 static VALUE
5125 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5126 {
5127         VALUE fname;
5128         char *fstr;
5129         Molecule *mol;
5130         int retval;
5131         MoleculeClearLoadSaveErrorMessage();
5132         Data_Get_Struct(self, Molecule, mol);
5133         rb_scan_args(argc, argv, "1", &fname);
5134         fstr = FileStringValuePtr(fname);
5135         retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5136         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5137         return Qtrue;   
5138 }
5139
5140 /*
5141  *  call-seq:
5142  *     loadpsf(file, pdbfile = nil)       -> bool
5143  *
5144  *  Read a structure from a psf file. molecule must be empty. The psf may be
5145  *  an "extended" version, which also contains coordinates. If pdbfile 
5146  *  is given, then atomic coordinates are read from that file.
5147  *  Return true if successful.
5148  */
5149 static VALUE
5150 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5151 {
5152         VALUE fname, pdbname;
5153         char *fstr, *pdbstr;
5154         Molecule *mol;
5155         int retval;
5156         Data_Get_Struct(self, Molecule, mol);
5157         if (mol->natoms > 0)
5158                 return Qnil;  /*  Must be a new molecule  */
5159         MoleculeClearLoadSaveErrorMessage();
5160         rb_scan_args(argc, argv, "11", &fname, &pdbname);
5161         fstr = FileStringValuePtr(fname);
5162         retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5163         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5164         pdbstr = NULL;
5165         if (!NIL_P(pdbname)) {
5166                 pdbstr = strdup(FileStringValuePtr(pdbname));
5167                 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5168                 free(pdbstr);
5169                 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5170         }
5171         return Qtrue;
5172 }
5173
5174 /*
5175  *  call-seq:
5176  *     loadpdb(file)       -> bool
5177  *
5178  *  Read coordinates from a pdb file. If molecule is empty, then structure is build
5179  *  by use of CONECT instructions. Otherwise, only the coordinates are read in.
5180  *  Return true if successful.
5181  */
5182 static VALUE
5183 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5184 {
5185         VALUE fname;
5186         char *fstr;
5187         Molecule *mol;
5188         int retval;
5189         Data_Get_Struct(self, Molecule, mol);
5190         rb_scan_args(argc, argv, "1", &fname);
5191         MoleculeClearLoadSaveErrorMessage();
5192         fstr = FileStringValuePtr(fname);
5193         retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5194         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5195         return Qtrue;   
5196 }
5197
5198 /*
5199  *  call-seq:
5200  *     loaddcd(file)       -> bool
5201  *
5202  *  Read coordinates from a dcd file. The molecule should not empty.
5203  *  Return true if successful.
5204  */
5205 static VALUE
5206 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5207 {
5208         VALUE fname;
5209         char *fstr;
5210         Molecule *mol;
5211         int retval;
5212         Data_Get_Struct(self, Molecule, mol);
5213         rb_scan_args(argc, argv, "1", &fname);
5214         MoleculeClearLoadSaveErrorMessage();
5215         fstr = FileStringValuePtr(fname);
5216         retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5217         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5218         return Qtrue;   
5219 }
5220
5221 /*
5222  *  call-seq:
5223  *     loadtep(file)       -> bool
5224  *
5225  *  Read coordinates from an ortep .tep file.
5226  *  Return true if successful.
5227  */
5228 static VALUE
5229 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5230 {
5231         VALUE fname;
5232         char *fstr;
5233         Molecule *mol;
5234         int retval;
5235         Data_Get_Struct(self, Molecule, mol);
5236         rb_scan_args(argc, argv, "1", &fname);
5237         MoleculeClearLoadSaveErrorMessage();
5238         fstr = FileStringValuePtr(fname);
5239         retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5240         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5241         return Qtrue;   
5242 }
5243
5244 /*
5245  *  call-seq:
5246  *     loadres(file)       -> bool
5247  *
5248  *  Read coordinates from a shelx .res file.
5249  *  Return true if successful.
5250  */
5251 static VALUE
5252 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5253 {
5254         VALUE fname;
5255         char *fstr;
5256         Molecule *mol;
5257         int retval;
5258         Data_Get_Struct(self, Molecule, mol);
5259         rb_scan_args(argc, argv, "1", &fname);
5260         MoleculeClearLoadSaveErrorMessage();
5261         fstr = FileStringValuePtr(fname);
5262         retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5263         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5264         return Qtrue;   
5265 }
5266
5267 /*
5268  *  call-seq:
5269  *     loadfchk(file)       -> bool
5270  *
5271  *  Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this) 
5272  *  Return true if successful.
5273  */
5274 static VALUE
5275 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5276 {
5277         VALUE fname;
5278         char *fstr;
5279         Molecule *mol;
5280         int retval;
5281         Data_Get_Struct(self, Molecule, mol);
5282         rb_scan_args(argc, argv, "1", &fname);
5283         MoleculeClearLoadSaveErrorMessage();
5284         fstr = FileStringValuePtr(fname);
5285         retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5286         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5287         return Qtrue;   
5288 }
5289
5290 /*
5291  *  call-seq:
5292  *     loaddat(file)       -> bool
5293  *
5294  *  Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well) 
5295  *  Return true if successful.
5296  */
5297 static VALUE
5298 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5299 {
5300         VALUE fname;
5301         char *fstr;
5302         Molecule *mol;
5303         int retval;
5304         Data_Get_Struct(self, Molecule, mol);
5305         rb_scan_args(argc, argv, "1", &fname);
5306         MoleculeClearLoadSaveErrorMessage();
5307         fstr = FileStringValuePtr(fname);
5308         MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5309         retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5310         MyAppCallback_hideProgressPanel();
5311         s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5312         return Qtrue;   
5313 }
5314
5315 /*
5316  *  call-seq:
5317  *     savembsf(file)       -> bool
5318  *
5319  *  Write structure as a mbsf file. Returns true if successful.
5320  */
5321 static VALUE
5322 s_Molecule_Savembsf(VALUE self, VALUE fname)
5323 {
5324         char *fstr;
5325     Molecule *mol;
5326         int retval;
5327     Data_Get_Struct(self, Molecule, mol);
5328         MoleculeClearLoadSaveErrorMessage();
5329         fstr = FileStringValuePtr(fname);
5330         retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5331         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5332         return Qtrue;
5333 }
5334
5335 /*
5336  *  call-seq:
5337  *     savepsf(file)       -> bool
5338  *
5339  *  Write structure as a psf file. Returns true if successful.
5340  */
5341 static VALUE
5342 s_Molecule_Savepsf(VALUE self, VALUE fname)
5343 {
5344         char *fstr;
5345     Molecule *mol;
5346         int retval;
5347     Data_Get_Struct(self, Molecule, mol);
5348         MoleculeClearLoadSaveErrorMessage();
5349         fstr = FileStringValuePtr(fname);
5350         retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5351         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5352         return Qtrue;
5353 }
5354
5355 /*
5356  *  call-seq:
5357  *     savepdb(file)       -> bool
5358  *
5359  *  Write coordinates as a pdb file. Returns true if successful.
5360  */
5361 static VALUE
5362 s_Molecule_Savepdb(VALUE self, VALUE fname)
5363 {
5364         char *fstr;
5365     Molecule *mol;
5366         int retval;
5367     Data_Get_Struct(self, Molecule, mol);
5368         MoleculeClearLoadSaveErrorMessage();
5369         fstr = FileStringValuePtr(fname);
5370         retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5371         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5372         return Qtrue;
5373 }
5374
5375 /*
5376  *  call-seq:
5377  *     savedcd(file)       -> bool
5378  *
5379  *  Write coordinates as a dcd file. Returns true if successful.
5380  */
5381 static VALUE
5382 s_Molecule_Savedcd(VALUE self, VALUE fname)
5383 {
5384         char *fstr;
5385     Molecule *mol;
5386         int retval;
5387     Data_Get_Struct(self, Molecule, mol);
5388         MoleculeClearLoadSaveErrorMessage();
5389         fstr = FileStringValuePtr(fname);
5390         retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5391         s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5392         return Qtrue;
5393 }
5394
5395 /*  load([ftype, ] fname, ...)  */
5396 static VALUE
5397 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5398 {
5399     VALUE rval, argv0;
5400         char *argstr, *methname, *p, *type = "";
5401         ID mid = 0;
5402         int i;
5403         const char *ls = (loadFlag ? "load" : "save");
5404         int lslen = strlen(ls);
5405
5406         if (argc == 0)
5407                 return Qnil;
5408
5409     argv0 = argv[0]; /* Keep as a local variable to avoid GC  */
5410         if (argc == 0 || (argstr = StringValuePtr(argv0)) == NULL)
5411                 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5412         if (argstr[0] == ':') {
5413                 /*  Call "loadXXX" (or "saveXXX") for type ":XXX"  */
5414                 methname = ALLOC_N(char, lslen + strlen(argstr));
5415                 strcpy(methname, ls);
5416                 strcat(methname, argstr + 1);
5417                 type = argstr + 1;
5418                 for (i = lslen; methname[i] != 0; i++)
5419                         methname[i] = tolower(methname[i]);
5420                 mid = rb_intern(methname);
5421                 xfree(methname);
5422                 argc--;
5423                 argv++;
5424                 rval = rb_funcall2(self, mid, argc, argv);
5425                 if (rval == Qnil)
5426                         goto failure;
5427                 else
5428                         goto success;
5429         }
5430         /*  Guess file type from extension  */
5431         p = strrchr(argstr, '.');
5432         if (p != NULL) {
5433                 p++;
5434                 type = p;
5435                 for (methname = p; *methname != 0; methname++) {
5436                         if (!isalpha(*methname))
5437                                 break;
5438                 }
5439                 if (*methname == 0) {
5440                         methname = ALLOC_N(char, lslen + strlen(p) + 1);
5441                         if (methname == NULL)
5442                                 rb_raise(rb_eMolbyError, "Low memory");
5443                         strcpy(methname, ls);
5444                         strcat(methname, p);
5445                         for (i = lslen; methname[i] != 0; i++)
5446                                 methname[i] = tolower(methname[i]);
5447                         mid = rb_intern(methname);
5448                         xfree(methname);
5449                         if (loadFlag) {
5450                                 if (rb_respond_to(self, mid)) {
5451                                         /*  Load: try to call the load procedure only if it is available  */
5452                                         rval = rb_funcall2(self, mid, argc, argv);
5453                                         if (rval != Qnil)
5454                                                 goto success;
5455                                 }
5456                         } else {
5457                                 /*  Save: call the save procedure, and if not found then call 'method_missing'  */
5458                                 rval = rb_funcall2(self, mid, argc, argv);
5459                                 if (rval != Qnil)
5460                                         goto success;
5461                         }
5462                 }
5463         }
5464 failure:
5465         rval = rb_str_to_str(argv0);
5466         asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5467         s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5468         return Qnil;  /*  Does not reach here  */
5469
5470 success:
5471         {
5472                 /*  Register the path  */
5473                 Molecule *mol;
5474         /*      Atom *ap; */
5475                 Data_Get_Struct(self, Molecule, mol);
5476                 MoleculeSetPath(mol, StringValuePtr(argv0));
5477                 
5478                 /*  Check if all occupancy factors are zero; if that is the case, then all set to 1.0  */
5479         /*      for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5480                         if (ap->occupancy != 0.0)
5481                                 break;
5482                 }
5483                 if (i == mol->natoms) {
5484                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5485                                 ap->occupancy = 1.0;
5486                         }
5487                 } */
5488         }
5489         return rval;
5490 }
5491
5492 /*
5493  *  call-seq:
5494  *     molload(file, *args)       -> bool
5495  *
5496  *  Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5497  *  file type given by the extension). If this method fails, then all defined (public)
5498  *  "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5499  */
5500 static VALUE
5501 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5502 {
5503         return s_Molecule_LoadSave(argc, argv, self, 1);
5504 }
5505
5506 /*
5507  *  call-seq:
5508  *     molsave(file, *args)       -> bool
5509  *
5510  *  Write a structure/coordinate to the given file by calling the public method "saveXXX"
5511  *  (XXX is the file type given by the extension).
5512  */
5513 static VALUE
5514 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5515 {
5516         return s_Molecule_LoadSave(argc, argv, self, 0);
5517 }
5518
5519 /*
5520  *  call-seq:
5521  *     open        -> Molecule
5522  *     open(file)  -> Molecule
5523  *
5524  *  Create a new molecule from file as a document. If file is not given, an untitled document is created.
5525  */
5526 static VALUE
5527 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5528 {
5529         VALUE fname;
5530         const char *p;
5531         Molecule *mp;
5532         VALUE iflag;
5533     
5534     if (!gUseGUI) {
5535         rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5536     }
5537     
5538         rb_scan_args(argc, argv, "01", &fname);
5539         if (NIL_P(fname))
5540                 p = NULL;
5541         else
5542                 p = FileStringValuePtr(fname);
5543         iflag = Ruby_SetInterruptFlag(Qfalse);
5544         mp = MoleculeCallback_openNewMolecule(p);
5545         Ruby_SetInterruptFlag(iflag);
5546         if (mp == NULL) {
5547                 if (p == NULL)
5548                         rb_raise(rb_eMolbyError, "Cannot create untitled document");
5549                 else
5550                         rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5551         }
5552         return ValueFromMolecule(mp);
5553 }
5554
5555 /*
5556  *  call-seq:
5557  *     new  -> Molecule
5558  *     new(file, *args)  -> Molecule
5559  *
5560  *  Create a new molecule and call "load" method with the same arguments.
5561  */
5562 static VALUE
5563 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5564 {
5565         if (argc > 0)
5566                 return s_Molecule_Load(argc, argv, self);
5567         else return Qnil;  /*  An empty molecule (which is prepared in s_Molecule_Alloc()) is returned  */
5568 }
5569
5570 /*
5571  *  call-seq:
5572  *     error_message       -> String
5573  *
5574  *  Get the error_message from the last load/save method. If no error, returns nil.
5575  */
5576 static VALUE
5577 s_Molecule_ErrorMessage(VALUE klass)
5578 {
5579         if (gLoadSaveErrorMessage == NULL)
5580                 return Qnil;
5581         else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5582 }
5583
5584 /*
5585  *  call-seq:
5586  *     set_error_message(String)
5587  *     Molecule.error_message = String
5588  *
5589  *  Set the error_message for the present load/save method.
5590  */
5591 static VALUE
5592 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5593 {
5594         if (gLoadSaveErrorMessage != NULL) {
5595                 free(gLoadSaveErrorMessage);
5596                 gLoadSaveErrorMessage = NULL;
5597         }
5598         if (sval != Qnil) {
5599                 sval = rb_str_to_str(sval);
5600                 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5601         }
5602         return sval;
5603 }
5604
5605 /*
5606  *  call-seq:
5607  *     set_molecule(Molecule)
5608  *
5609  *  Duplicate the given molecule and set to self. The present molecule must be empty.
5610  *  This method is exclusively used for associating a new document with an existing molecule.
5611  */
5612 static VALUE
5613 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5614 {
5615         Molecule *mp1, *mp2;
5616         Data_Get_Struct(self, Molecule, mp1);
5617         mp2 = MoleculeFromValue(mval);
5618         MoleculeInitWithMolecule(mp1, mp2);
5619         MoleculeCallback_notifyModification(mp1, 1);
5620         return self;
5621 }
5622
5623 #pragma mark ------ Name attributes ------
5624
5625 /*
5626  *  call-seq:
5627  *     name       -> String
5628  *
5629  *  Returns the display name of the molecule. If the molecule has no associated
5630  *  document, then returns nil.
5631  */
5632 static VALUE
5633 s_Molecule_Name(VALUE self)
5634 {
5635     Molecule *mol;
5636         char buf[1024];
5637     Data_Get_Struct(self, Molecule, mol);
5638         MoleculeCallback_displayName(mol, buf, sizeof buf);
5639         if (buf[0] == 0)
5640                 return Qnil;
5641         else
5642                 return Ruby_NewEncodedStringValue2(buf);
5643 }
5644
5645 /*
5646  *  call-seq:
5647  *     set_name(string) -> self
5648  *
5649  *  Set the name of an untitled molecule. If the molecule is not associated with window
5650  *  or it already has an associated file, then exception is thrown.
5651  */
5652 static VALUE
5653 s_Molecule_SetName(VALUE self, VALUE nval)
5654 {
5655     Molecule *mol;
5656     Data_Get_Struct(self, Molecule, mol);
5657         if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5658                 rb_raise(rb_eMolbyError, "Cannot change the window title");
5659         return self;
5660 }
5661
5662
5663 /*
5664  *  call-seq:
5665  *     path       -> String
5666  *
5667  *  Returns the full path name of the molecule, if it is associated with a file.
5668  *  If the molecule has no associated file, then returns nil.
5669  */
5670 static VALUE
5671 s_Molecule_Path(VALUE self)
5672 {
5673     Molecule *mol;
5674         char buf[1024];
5675     Data_Get_Struct(self, Molecule, mol);
5676         MoleculeCallback_pathName(mol, buf, sizeof buf);
5677         if (buf[0] == 0)
5678                 return Qnil;
5679         else
5680                 return Ruby_NewFileStringValue(buf);
5681 }
5682
5683 /*
5684  *  call-seq:
5685  *     dir       -> String
5686  *
5687  *  Returns the full path name of the directory in which the file associated with the
5688  *  molecule is located. If the molecule has no associated file, then returns nil.
5689  */
5690 static VALUE
5691 s_Molecule_Dir(VALUE self)
5692 {
5693     Molecule *mol;
5694         char buf[1024], *p;
5695     Data_Get_Struct(self, Molecule, mol);
5696         MoleculeCallback_pathName(mol, buf, sizeof buf);
5697 #if __WXMSW__
5698         translate_char(buf, '\\', '/');
5699 #endif
5700         if (buf[0] == 0)
5701                 return Qnil;
5702         else {
5703                 p = strrchr(buf, '/');
5704                 if (p != NULL)
5705                         *p = 0;
5706                 return Ruby_NewEncodedStringValue2(buf);
5707         }
5708 }
5709
5710 /*
5711  *  call-seq:
5712  *     inspect       -> String
5713  *
5714  *  Returns a string in the form "Molecule[name]" if the molecule has the associated
5715  *  document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5716  *  the Molecule structure) is returned.
5717  */
5718 static VALUE
5719 s_Molecule_Inspect(VALUE self)
5720 {
5721     Molecule *mol;
5722         char buf[256];
5723     Data_Get_Struct(self, Molecule, mol);
5724         MoleculeCallback_displayName(mol, buf, sizeof buf);
5725         if (buf[0] == 0) {
5726                 /*  No associated document  */
5727                 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5728                 return Ruby_NewEncodedStringValue2(buf);
5729         } else {
5730                 /*  Check whether the document name is duplicate  */
5731                 char buf2[256];
5732                 int idx, k, k2;
5733                 Molecule *mol2;
5734                 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5735                         MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5736                         if (strcmp(buf, buf2) == 0) {
5737                                 k++;
5738                                 if (mol == mol2)
5739                                         k2 = k;
5740                         }
5741                 }
5742                 if (k > 1) {
5743                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5744                 } else {
5745                         snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5746                 }
5747                 return Ruby_NewEncodedStringValue2(buf2);
5748         }
5749 }
5750
5751 #pragma mark ------ MolEnumerables ------
5752
5753 static VALUE
5754 s_Molecule_MolEnumerable(VALUE self, int kind)
5755 {
5756     Molecule *mol;
5757         MolEnumerable *mseq;
5758     Data_Get_Struct(self, Molecule, mol);
5759         mseq = MolEnumerableNew(mol, kind);
5760         return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5761 }
5762
5763 /*
5764  *  call-seq:
5765  *     atoms       -> MolEnumerable
5766  *
5767  *  Returns a MolEnumerable object representing the array of atoms.
5768  */
5769 static VALUE
5770 s_Molecule_Atoms(VALUE self)
5771 {
5772         return s_Molecule_MolEnumerable(self, kAtomKind);
5773 }
5774
5775 /*
5776  *  call-seq:
5777  *     bonds       -> MolEnumerable
5778  *
5779  *  Returns a MolEnumerable object representing the array of bonds. A bond is represented
5780  *  by an array of two atom indices.
5781  */
5782 static VALUE
5783 s_Molecule_Bonds(VALUE self)
5784 {
5785         return s_Molecule_MolEnumerable(self, kBondKind);
5786 }
5787
5788 /*
5789  *  call-seq:
5790  *     angles       -> MolEnumerable
5791  *
5792  *  Returns a MolEnumerable object representing the array of angles. An angle is represented
5793  *  by an array of three atom indices.
5794  */
5795 static VALUE
5796 s_Molecule_Angles(VALUE self)
5797 {
5798         return s_Molecule_MolEnumerable(self, kAngleKind);
5799 }
5800
5801 /*
5802  *  call-seq:
5803  *     dihedrals       -> MolEnumerable
5804  *
5805  *  Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5806  *  by an array of four atom indices.
5807  */
5808 static VALUE
5809 s_Molecule_Dihedrals(VALUE self)
5810 {
5811         return s_Molecule_MolEnumerable(self, kDihedralKind);
5812 }
5813
5814 /*
5815  *  call-seq:
5816  *     impropers       -> MolEnumerable
5817  *
5818  *  Returns a MolEnumerable object representing the array of impropers. An improper is represented
5819  *  by an array of four atom indices.
5820  */
5821 static VALUE
5822 s_Molecule_Impropers(VALUE self)
5823 {
5824         return s_Molecule_MolEnumerable(self, kImproperKind);
5825 }
5826
5827 /*
5828  *  call-seq:
5829  *     residues       -> MolEnumerable
5830  *
5831  *  Returns a MolEnumerable object representing the array of residue names.
5832  */
5833 static VALUE
5834 s_Molecule_Residues(VALUE self)
5835 {
5836         return s_Molecule_MolEnumerable(self, kResidueKind);
5837 }
5838
5839 /*
5840  *  call-seq:
5841  *     natoms       -> Integer
5842  *
5843  *  Returns the number of atoms.
5844  */
5845 static VALUE
5846 s_Molecule_Natoms(VALUE self)
5847 {
5848     Molecule *mol;
5849     Data_Get_Struct(self, Molecule, mol);
5850         return INT2NUM(mol->natoms);
5851 }
5852
5853 /*
5854  *  call-seq:
5855  *     nbonds       -> Integer
5856  *
5857  *  Returns the number of bonds.
5858  */
5859 static VALUE
5860 s_Molecule_Nbonds(VALUE self)
5861 {
5862     Molecule *mol;
5863     Data_Get_Struct(self, Molecule, mol);
5864         return INT2NUM(mol->nbonds);
5865 }
5866
5867 /*
5868  *  call-seq:
5869  *     nangles       -> Integer
5870  *
5871  *  Returns the number of angles.
5872  */
5873 static VALUE
5874 s_Molecule_Nangles(VALUE self)
5875 {
5876     Molecule *mol;
5877     Data_Get_Struct(self, Molecule, mol);
5878         return INT2NUM(mol->nangles);
5879 }
5880
5881 /*
5882  *  call-seq:
5883  *     ndihedrals       -> Integer
5884  *
5885  *  Returns the number of dihedrals.
5886  */
5887 static VALUE
5888 s_Molecule_Ndihedrals(VALUE self)
5889 {
5890     Molecule *mol;
5891     Data_Get_Struct(self, Molecule, mol);
5892         return INT2NUM(mol->ndihedrals);
5893 }
5894
5895 /*
5896  *  call-seq:
5897  *     nimpropers       -> Integer
5898  *
5899  *  Returns the number of impropers.
5900  */
5901 static VALUE
5902 s_Molecule_Nimpropers(VALUE self)
5903 {
5904     Molecule *mol;
5905     Data_Get_Struct(self, Molecule, mol);
5906         return INT2NUM(mol->nimpropers);
5907 }
5908
5909 /*
5910  *  call-seq:
5911  *     nresidues       -> Integer
5912  *
5913  *  Returns the number of residues.
5914  */
5915 static VALUE
5916 s_Molecule_Nresidues(VALUE self)
5917 {
5918     Molecule *mol;
5919     Data_Get_Struct(self, Molecule, mol);
5920         return INT2NUM(mol->nresidues);
5921 }
5922
5923 /*
5924  *  call-seq:
5925  *     nresidues = Integer
5926  *
5927  *  Change the number of residues.
5928  */
5929 static VALUE
5930 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5931 {
5932     Molecule *mol;
5933         int ival = NUM2INT(val);
5934     Data_Get_Struct(self, Molecule, mol);
5935         MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5936         if (ival != mol->nresidues)
5937                 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5938         return val;
5939 }
5940
5941 /*
5942  *  call-seq:
5943  *     max_residue_number(atom_group = nil)     -> Integer
5944  *
5945  *  Returns the maximum residue number actually used. If an atom group is given, only
5946  *  these atoms are examined. If no atom is present, nil is returned.
5947  */
5948 static VALUE
5949 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5950 {
5951     Molecule *mol;
5952         VALUE gval;
5953         int maxSeq;
5954         IntGroup *ig;
5955     Data_Get_Struct(self, Molecule, mol);
5956         rb_scan_args(argc, argv, "01", &gval);
5957         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5958         maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5959         return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5960 }
5961
5962 /*
5963  *  call-seq:
5964  *     min_residue_number(atom_group = nil)     -> Integer
5965  *
5966  *  Returns the minimum residue number actually used. If an atom group is given, only
5967  *  these atoms are examined. If no atom is present, nil is returned.
5968  */
5969 static VALUE
5970 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5971 {
5972     Molecule *mol;
5973         VALUE gval;
5974         int minSeq;
5975         IntGroup *ig;
5976     Data_Get_Struct(self, Molecule, mol);
5977         rb_scan_args(argc, argv, "01", &gval);
5978         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5979         minSeq = MoleculeMinimumResidueNumber(mol, ig);
5980         return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5981 }
5982
5983 /*
5984  *  call-seq:
5985  *     each_atom(atom_group = nil) {|aref| ...}
5986  *
5987  *  Execute the block, with the AtomRef object for each atom as the argument. If an atom
5988  *  group is given, only these atoms are processed.
5989  *  If atom_group is nil, this is equivalent to self.atoms.each, except that the return value 
5990  *  is self (a Molecule object).
5991  */
5992 static VALUE
5993 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5994 {
5995         int i;
5996     Molecule *mol;
5997         AtomRef *aref;
5998         VALUE arval;
5999         VALUE gval;
6000         IntGroup *ig;
6001     Data_Get_Struct(self, Molecule, mol);
6002         rb_scan_args(argc, argv, "01", &gval);
6003         ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6004         arval = ValueFromMoleculeAndIndex(mol, 0);
6005         Data_Get_Struct(arval, AtomRef, aref);
6006         for (i = 0; i < mol->natoms; i++) {
6007                 aref->idx = i;
6008                 if (ig == NULL || IntGroupLookup(ig, i, NULL))
6009                         rb_yield(arval);
6010         }
6011         if (ig != NULL)
6012                 IntGroupRelease(ig);
6013     return self;
6014 }
6015
6016 #pragma mark ------ Atom Group ------
6017
6018 static VALUE
6019 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6020 {
6021         Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6022         IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6023         int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6024         IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6025         return Qnil;
6026 }
6027
6028 /*
6029  *  call-seq:
6030  *     atom_group
6031  *     atom_group {|aref| ...}
6032  *     atom_group(arg1, arg2, ...)
6033  *     atom_group(arg1, arg2, ...) {|aref| ...}
6034  *
6035  *  Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6036  *  If arguments are given, then the atoms reprensented by the arguments are added to the
6037  *  group. For a conversion of a string to an atom index, see the description
6038  *  of Molecule#atom_index.
6039  *  If a block is given, it is evaluated with an AtomRef (not atom index integers)
6040  *  representing each atom, and the atoms are removed from the result if the block returns false.
6041  *
6042  */
6043 static VALUE
6044 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6045 {
6046         IntGroup *ig1, *ig2;
6047     Molecule *mol;
6048         Int i, startPt, interval;
6049         VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6050         Data_Get_Struct(retval, IntGroup, ig1);
6051     Data_Get_Struct(self, Molecule, mol);
6052         if (argc == 0) {
6053                 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6054         } else {
6055                 while (argc > 0) {
6056                         if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6057                                 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6058                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6059                         } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6060                                 ig2 = IntGroupFromValue(*argv);
6061                                 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6062                                         interval = IntGroupGetInterval(ig2, i);
6063                                         IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6064                                 }
6065                                 IntGroupRelease(ig2);
6066                         } else if (rb_respond_to(*argv, rb_intern("each"))) {
6067                                 VALUE values[2];
6068                                 values[0] = (VALUE)mol;
6069                                 values[1] = (VALUE)ig1;
6070                                 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6071                         } else
6072                                 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6073                         argc--;
6074                         argv++;
6075                 }
6076         }
6077         if (rb_block_given_p()) {
6078                 /*  Evaluate the given block with an AtomRef as the argument, and delete
6079                         the index if the block returns false  */
6080                 AtomRef *aref = AtomRefNew(mol, 0);
6081                 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6082                 ig2 = IntGroupNew();
6083                 IntGroupCopy(ig2, ig1);
6084                 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6085                         VALUE resval;
6086                         if (startPt >= mol->natoms)
6087                                 break;
6088                         aref->idx = startPt;
6089                         resval = rb_yield(arval);
6090                         if (!RTEST(resval))
6091                                 IntGroupRemove(ig1, startPt, 1);
6092                 }
6093                 IntGroupRelease(ig2);
6094         }
6095         
6096         /*  Remove points that are out of bounds */
6097         IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6098
6099         return retval;                  
6100 }
6101
6102 /*
6103  *  call-seq:
6104  *     selection       -> IntGroup
6105  *
6106  *  Returns the current selection.
6107  */
6108 static VALUE
6109 s_Molecule_Selection(VALUE self)
6110 {
6111     Molecule *mol;
6112         IntGroup *ig;
6113         VALUE val;
6114     Data_Get_Struct(self, Molecule, mol);
6115         if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6116                 ig = IntGroupNewFromIntGroup(ig);  /*  Duplicate, so that the change from GUI does not affect the value  */
6117                 val = ValueFromIntGroup(ig);
6118                 IntGroupRelease(ig);
6119         } else {
6120                 val = IntGroup_Alloc(rb_cIntGroup);
6121         }
6122         return val;
6123 }
6124
6125 static VALUE
6126 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6127 {
6128     Molecule *mol;
6129         IntGroup *ig;
6130     Data_Get_Struct(self, Molecule, mol);
6131         if (val == Qnil)
6132                 ig = NULL;
6133         else
6134                 ig = s_Molecule_AtomGroupFromValue(self, val);
6135         if (undoable)
6136                 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6137         else
6138                 MoleculeSetSelection(mol, ig);
6139         if (ig != NULL)
6140                 IntGroupRelease(ig);
6141         return val;
6142 }
6143
6144 /*
6145  *  call-seq:
6146  *     selection = IntGroup
6147  *
6148  *  Set the current selection. The right-hand operand may be nil.
6149  *  This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6150  */
6151 static VALUE
6152 s_Molecule_SetSelection(VALUE self, VALUE val)
6153 {
6154         return s_Molecule_SetSelectionSub(self, val, 0);
6155 }
6156
6157 /*
6158  *  call-seq:
6159  *     set_undoable_selection(IntGroup)
6160  *
6161  *  Set the current selection with undo registration. The right-hand operand may be nil.
6162  *  This operation is undoable.
6163  */
6164 static VALUE
6165 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6166 {
6167         return s_Molecule_SetSelectionSub(self, val, 1);
6168 }
6169
6170 #pragma mark ------ Editing ------
6171
6172 /*
6173  *  call-seq:
6174  *     extract(group, dummy_flag = nil)       -> Molecule
6175  *
6176  *  Extract the atoms given by group and return as a new molecule object.
6177  *  If dummy_flag is true, then the atoms that are not included in the group but are connected
6178  *  to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and 
6179  *  names beginning with an underscore) and included in the new molecule object.
6180  */
6181 static VALUE
6182 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6183 {
6184     Molecule *mol1, *mol2;
6185         IntGroup *ig;
6186         VALUE group, dummy_flag, retval;
6187     Data_Get_Struct(self, Molecule, mol1);
6188         rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6189         ig = s_Molecule_AtomGroupFromValue(self, group);
6190         if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6191                 retval = Qnil;
6192         } else {
6193                 retval = ValueFromMolecule(mol2);
6194         }
6195         IntGroupRelease(ig);
6196         return retval;
6197 }
6198
6199 /*
6200  *  call-seq:
6201  *     add(molecule2)       -> self
6202  *
6203  *  Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6204     conflicts.
6205     This operation is undoable.
6206  */
6207 static VALUE
6208 s_Molecule_Add(VALUE self, VALUE val)
6209 {
6210     Molecule *mol1, *mol2;
6211     Data_Get_Struct(self, Molecule, mol1);
6212         mol2 = MoleculeFromValue(val);
6213         MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6214         return self; 
6215 }
6216
6217 /*
6218  *  call-seq:
6219  *     remove(group)       -> Molecule
6220  *
6221  *  The atoms designated by the given group are removed from the molecule.
6222  *  This operation is undoable.
6223  */
6224 static VALUE
6225 s_Molecule_Remove(VALUE self, VALUE group)
6226 {
6227     Molecule *mol1;
6228         IntGroup *ig, *bg;
6229         Int i;
6230         IntGroupIterator iter;
6231
6232     ig = s_Molecule_AtomGroupFromValue(self, group);
6233 /*    Data_Get_Struct(self, Molecule, mol1);
6234         group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6235         if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6236                 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6237         Data_Get_Struct(group, IntGroup, ig); */
6238     Data_Get_Struct(self, Molecule, mol1);
6239     
6240         /*  Remove the bonds between the two fragments  */
6241         /*  (This is necessary for undo to work correctly)  */
6242         IntGroupIteratorInit(ig, &iter);
6243         bg = NULL;
6244         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6245                 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6246                 Int j, *cp;
6247                 cp = AtomConnectData(&ap->connect);
6248                 for (j = 0; j < ap->connect.count; j++) {
6249                         int n = cp[j];
6250                         if (!IntGroupLookup(ig, n, NULL)) {
6251                                 /*  bond i-n, i is in ig and n is not  */
6252                                 int k = MoleculeLookupBond(mol1, i, n);
6253                                 if (k >= 0) {
6254                                         if (bg == NULL)
6255                                                 bg = IntGroupNew();
6256                                         IntGroupAdd(bg, k, 1);
6257                                 }
6258                         }
6259                 }
6260         }
6261         IntGroupIteratorRelease(&iter);
6262         if (bg != NULL) {
6263                 /*  Remove bonds  */
6264                 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6265                 IntGroupRelease(bg);
6266         }
6267         /*  Remove atoms  */
6268         if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6269                 return Qnil;
6270         return self;
6271 }
6272
6273 /*
6274  *  call-seq:
6275  *     create_atom(name, pos = -1)  -> AtomRef
6276  *
6277  *  Create a new atom with the specified name (may contain residue 
6278  *  information) and position (if position is out of range, the atom is appended at
6279  *  the end). Returns the reference to the new atom.
6280  *  This operation is undoable.
6281  */
6282 static VALUE
6283 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6284 {
6285     Molecule *mol;
6286     Int i, pos;
6287         VALUE name, ival;
6288     Atom arec;
6289     AtomRef *aref;
6290         char *p, resName[6], atomName[6];
6291         int resSeq;
6292     Data_Get_Struct(self, Molecule, mol);
6293         rb_scan_args(argc, argv, "02", &name, &ival);
6294         if (ival != Qnil)
6295                 pos = NUM2INT(rb_Integer(ival));
6296         else pos = -1;
6297         if (name != Qnil) {
6298                 p = StringValuePtr(name);
6299                 if (p[0] != 0) {
6300                         i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6301                         if (atomName[0] == 0)
6302                           rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6303                 }
6304         } else p = NULL;
6305         if (p == NULL || p[0] == 0) {
6306                 memset(atomName, 0, 4);
6307                 resSeq = -1;
6308         }
6309     memset(&arec, 0, sizeof(arec));
6310     strncpy(arec.aname, atomName, 4);
6311     if (resSeq >= 0) {
6312       strncpy(arec.resName, resName, 4);
6313       arec.resSeq = resSeq;
6314     }
6315         arec.occupancy = 1.0;
6316 //    i = MoleculeCreateAnAtom(mol, &arec);
6317         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6318                 return Qnil;
6319     aref = AtomRefNew(mol, pos);
6320     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6321 }
6322
6323 /*
6324  *  call-seq:
6325  *     duplicate_atom(atomref, pos = -1)  -> AtomRef
6326  *
6327  *  Create a new atom with the same attributes (but no bonding information)
6328  *  with the specified atom. Returns the reference to the new atom.
6329  *  This operation is undoable.
6330  */
6331 static VALUE
6332 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6333 {
6334     Molecule *mol;
6335         const Atom *apsrc;
6336     Atom arec;
6337         AtomRef *aref;
6338         VALUE retval, aval, ival;
6339         Int pos;
6340     Data_Get_Struct(self, Molecule, mol);
6341         rb_scan_args(argc, argv, "11", &aval, &ival);
6342         if (FIXNUM_P(aval)) {
6343                 int idx = NUM2INT(aval);
6344                 if (idx < 0 || idx >= mol->natoms)
6345                         rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6346                 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6347         } else {
6348                 apsrc = s_AtomFromValue(aval);
6349         }
6350         if (apsrc == NULL)
6351                 rb_raise(rb_eMolbyError, "bad atom specification");
6352         if (ival != Qnil)
6353                 pos = NUM2INT(rb_Integer(ival));
6354         else pos = -1;
6355         AtomDuplicate(&arec, apsrc);
6356         arec.connect.count = 0;
6357         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6358                 retval = Qnil;
6359         else {
6360                 aref = AtomRefNew(mol, pos);
6361                 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6362         }
6363         AtomClean(&arec);
6364         return retval;
6365 }
6366
6367 /*
6368  *  call-seq:
6369  *     create_bond(n1, n2, ...)       -> Integer
6370  *
6371  *  Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6372  *  do nothing for that pair. Returns the number of bonds actually created.
6373  *  This operation is undoable.
6374  */
6375 static VALUE
6376 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6377 {
6378     Molecule *mol;
6379         Int i, j, k, *ip, old_nbonds;
6380         if (argc == 0)
6381                 rb_raise(rb_eMolbyError, "missing arguments");
6382         if (argc % 2 != 0)
6383                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6384     Data_Get_Struct(self, Molecule, mol);
6385         ip = ALLOC_N(Int, argc + 1);
6386         for (i = j = 0; i < argc; i++, j++) {
6387                 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6388                 if (i % 2 == 1) {
6389                         if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6390                                 j -= 2;  /*  This bond is already present: skip it  */
6391                         else {
6392                                 for (k = 0; k < j - 1; k += 2) {
6393                                         if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6394                                                 j -= 2;   /*  The same entry is already in the argument  */
6395                                                 break;
6396                                         }
6397                                 }
6398                         }
6399                 }
6400         }
6401         old_nbonds = mol->nbonds;
6402         if (j > 0) {
6403                 ip[j] = kInvalidIndex;
6404                 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6405         } else i = 0;
6406         xfree(ip);
6407         if (i == -1)
6408                 rb_raise(rb_eMolbyError, "atom index out of range");
6409         else if (i == -2)
6410                 rb_raise(rb_eMolbyError, "too many bonds");
6411         else if (i == -3)
6412                 rb_raise(rb_eMolbyError, "duplicate bonds");
6413         else if (i == -5)
6414                 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6415         else if (i != 0)
6416                 rb_raise(rb_eMolbyError, "error in creating bonds");
6417         return INT2NUM(mol->nbonds - old_nbonds);
6418 }
6419
6420 /*
6421  *  call-seq:
6422  *     molecule.remove_bonds(n1, n2, ...)       -> Integer
6423  *
6424  *  Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6425  *  a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6426  *  This operation is undoable.
6427  */
6428 static VALUE
6429 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6430 {
6431     Molecule *mol;
6432         Int i, j, n[2];
6433         IntGroup *bg;
6434         if (argc == 0)
6435                 rb_raise(rb_eMolbyError, "missing arguments");
6436         if (argc % 2 != 0)
6437                 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6438     Data_Get_Struct(self, Molecule, mol);
6439         bg = NULL;
6440         for (i = j = 0; i < argc; i++, j = 1 - j) {
6441                 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6442                 if (j == 1) {
6443                         Int k = MoleculeLookupBond(mol, n[0], n[1]);
6444                         if (k >= 0) {
6445                                 if (bg == NULL)
6446                                         bg = IntGroupNew();
6447                                 IntGroupAdd(bg, k, 1);
6448                         }
6449                 }
6450         }
6451         if (bg != NULL) {
6452                 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6453                 i = IntGroupGetCount(bg);
6454                 IntGroupRelease(bg);
6455         } else i = 0;
6456         return INT2NUM(i);
6457 }
6458
6459 /*
6460  *  call-seq:
6461  *     assign_bond_order(idx, d1)
6462  *     assign_bond_orders(group, [d1, d2, ...])
6463  *
6464  *  Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6465  *  In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6466  *  At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6467  *  (This may change in the future)
6468  *  This operation is undoable.
6469  */
6470 static VALUE
6471 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6472 {
6473     Molecule *mol;
6474         IntGroup *ig;
6475     Data_Get_Struct(self, Molecule, mol);
6476         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6477                 /*  The first form  */
6478                 Int idx = NUM2INT(rb_Integer(idxval));
6479                 Double d1 = NUM2DBL(rb_Float(dval));
6480                 if (idx < 0 || idx >= mol->nbonds)
6481                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6482                 ig = IntGroupNewWithPoints(idx, 1, -1);
6483                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6484                 IntGroupRelease(ig);
6485         } else {
6486                 Int i, n;
6487                 Double *dp;
6488                 ig = IntGroupFromValue(idxval);
6489                 n = IntGroupGetCount(ig);
6490                 if (n == 0)
6491                         rb_raise(rb_eMolbyError, "the bond index is empty");
6492                 dval = rb_ary_to_ary(dval);
6493                 dp = (Double *)calloc(sizeof(Double), n);
6494                 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6495                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6496                 }
6497                 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6498                 free(dp);
6499                 IntGroupRelease(ig);
6500         }
6501         return self;
6502 }
6503
6504 /*
6505  *  call-seq:
6506  *     get_bond_order(idx) -> Float
6507  *     get_bond_orders(group) -> Array
6508  *
6509  *  Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6510  *  In the second form, the bond orders at the indices in the group are returned as an array.
6511  *  If no bond order information have been assigned, returns nil (the first form)
6512  *  or an empty array (the second form).
6513  */
6514 static VALUE
6515 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6516 {
6517     Molecule *mol;
6518         IntGroup *ig;
6519         Double *dp;
6520         VALUE retval;
6521         Int i, n, numericArg;
6522     Data_Get_Struct(self, Molecule, mol);
6523         if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6524                 /*  The first form  */
6525                 Int idx = NUM2INT(rb_Integer(idxval));
6526                 if (idx < 0 || idx >= mol->nbonds)
6527                         rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6528                 if (mol->bondOrders == NULL)
6529                         return Qnil;
6530                 ig = IntGroupNewWithPoints(idx, 1, -1);
6531                 n = 1;
6532                 numericArg = 1;
6533         } else {
6534                 if (mol->bondOrders == NULL)
6535                         return rb_ary_new();
6536                 ig = IntGroupFromValue(idxval);
6537                 n = IntGroupGetCount(ig);
6538                 if (n == 0)
6539                         rb_raise(rb_eMolbyError, "the bond index is empty");
6540                 numericArg = 0;
6541         }
6542         dp = (Double *)calloc(sizeof(Double), n);
6543         MoleculeGetBondOrders(mol, dp, ig);
6544         if (numericArg)
6545                 retval = rb_float_new(dp[0]);
6546         else {
6547                 retval = rb_ary_new();
6548                 for (i = 0; i < n; i++)
6549                         rb_ary_push(retval, rb_float_new(dp[i]));
6550         }
6551         free(dp);
6552         IntGroupRelease(ig);
6553         return retval;
6554 }
6555
6556 /*
6557  *  call-seq:
6558  *     bond_exist?(idx1, idx2) -> bool
6559  *
6560  *  Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6561  *  Imaginary bonds between a pi-anchor and member atoms are not considered.
6562  */
6563 static VALUE
6564 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6565 {
6566         Molecule *mol;
6567         Int idx1, idx2, i;
6568         Atom *ap;
6569         Int *cp;
6570     Data_Get_Struct(self, Molecule, mol);
6571         idx1 = NUM2INT(rb_Integer(ival1));
6572         idx2 = NUM2INT(rb_Integer(ival2));
6573         if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6574                 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6575         ap = ATOM_AT_INDEX(mol->atoms, idx1);
6576         cp = AtomConnectData(&ap->connect);
6577         for (i = 0; i < ap->connect.count; i++) {
6578                 if (cp[i] == idx2)
6579                         return Qtrue;
6580         }
6581         return Qfalse;
6582 }
6583
6584 /*
6585  *  call-seq:
6586  *     add_angle(n1, n2, n3)       -> Molecule
6587  *
6588  *  Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6589  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6590  *  This operation is undoable.
6591  */
6592 static VALUE
6593 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6594 {
6595         Int n[4];
6596     Molecule *mol;
6597     Data_Get_Struct(self, Molecule, mol);
6598         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6599         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6600         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6601         if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6602                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6603         n[3] = kInvalidIndex;
6604         MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6605         return self;
6606 }
6607
6608 /*
6609  *  call-seq:
6610  *     remove_angle(n1, n2, n3)       -> Molecule
6611  *
6612  *  Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6613  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6614  *  This operation is undoable.
6615  */
6616 static VALUE
6617 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6618 {
6619         Int n[4];
6620     Molecule *mol;
6621         IntGroup *ig;
6622     Data_Get_Struct(self, Molecule, mol);
6623         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6624         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6625         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6626         if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6627                 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6628         ig = IntGroupNewWithPoints(n[3], 1, -1);
6629         MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6630         IntGroupRelease(ig);
6631         return self;
6632 }
6633
6634 /*
6635  *  call-seq:
6636  *     add_dihedral(n1, n2, n3, n4)       -> Molecule
6637  *
6638  *  Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6639  *  when a bond is created, so it is rarely necessary to use this method explicitly.
6640  *  This operation is undoable.
6641  */
6642 static VALUE
6643 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6644 {
6645         Int n[5];
6646     Molecule *mol;
6647     Data_Get_Struct(self, Molecule, mol);
6648         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6649         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6650         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6651         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6652         if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6653                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6654         n[4] = kInvalidIndex;
6655         MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6656         return self;
6657 }
6658
6659 /*
6660  *  call-seq:
6661  *     remove_dihedral(n1, n2, n3, n4)       -> Molecule
6662  *
6663  *  Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6664  *  when a bond is removed, so it is rarely necessary to use this method explicitly.
6665  *  This operation is undoable.
6666  */
6667 static VALUE
6668 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6669 {
6670         Int n[5];
6671     Molecule *mol;
6672         IntGroup *ig;
6673     Data_Get_Struct(self, Molecule, mol);
6674         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6675         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6676         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6677         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6678         if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6679                 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6680         ig = IntGroupNewWithPoints(n[4], 1, -1);
6681         MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6682         IntGroupRelease(ig);
6683         return self;
6684 }
6685
6686 /*
6687  *  call-seq:
6688  *     add_improper(n1, n2, n3, n4)       -> Molecule
6689  *
6690  *  Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6691  *  not automatically added when a new bond is created, so this method is more useful than
6692  *  the angle/dihedral counterpart.
6693  *  This operation is undoable.
6694  */
6695 static VALUE
6696 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6697 {
6698         Int n[5];
6699     Molecule *mol;
6700     Data_Get_Struct(self, Molecule, mol);
6701         n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6702         n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6703         n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6704         n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6705         if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6706                 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6707         n[4] = kInvalidIndex;
6708         MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6709         return self;
6710 }
6711
6712 /*
6713  *  call-seq:
6714  *     remove_improper(n1, n2, n3, n4)       -> Molecule
6715  *     remove_improper(intgroup)             -> Molecule
6716  *
6717  *  Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6718  *  Returns self. Unlike angles and dihedrals, impropers are
6719  *  not automatically added when a new bond is created, so this method is more useful than
6720  *  the angle/dihedral counterpart.
6721  *  This operation is undoable.
6722  */
6723 static VALUE
6724 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6725 {
6726         Int n[5];
6727         VALUE v1, v2, v3, v4;
6728     Molecule *mol;
6729         IntGroup *ig;
6730     Data_Get_Struct(self, Molecule, mol);
6731         if (argc == 1) {
6732                 ig = IntGroupFromValue(argv[0]);
6733         } else {
6734                 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6735                 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6736                 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6737                 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6738                 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6739                 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6740                         rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6741                 ig = IntGroupNewWithPoints(n[4], 1, -1);
6742         }
6743         MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6744         IntGroupRelease(ig);
6745         return self;
6746 }
6747
6748 /*
6749  *  call-seq:
6750  *     assign_residue(group, res)       -> Molecule
6751  *
6752  *  Assign the specified atoms as the given residue. res can either be an integer, "resname"
6753  *  or "resname.resno". When the residue number is not specified, the residue number of
6754  *  the first atom in the group is used.
6755  *  This operation is undoable.
6756  */
6757 static VALUE
6758 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6759 {
6760     Molecule *mol;
6761         IntGroup *ig;
6762         char *p, *pp, buf[16];
6763         Int resid, n;
6764         Atom *ap;
6765     Data_Get_Struct(self, Molecule, mol);
6766         
6767         /*  Parse the argument res  */
6768         if (FIXNUM_P(res)) {
6769                 /*  We can assume Fixnum here because Bignum is non-realistic as residue numbers  */
6770                 resid = NUM2INT(res);
6771                 buf[0] = 0;
6772         } else {
6773                 p = StringValuePtr(res);
6774                 pp = strchr(p, '.');
6775                 if (pp != NULL) {
6776                         resid = atoi(pp + 1);
6777                         n = pp - p;
6778                 } else {
6779                         resid = -1;
6780                         n = strlen(p);
6781                 }
6782                 if (n > sizeof buf - 1)
6783                         n = sizeof buf - 1;
6784                 strncpy(buf, p, n);
6785                 buf[n] = 0;
6786         }
6787         ig = s_Molecule_AtomGroupFromValue(self, range);
6788         if (ig == NULL || IntGroupGetCount(ig) == 0)
6789                 return Qnil;
6790
6791         if (resid < 0) {
6792                 /*  Use the residue number of the first specified atom  */
6793                 n = IntGroupGetNthPoint(ig, 0);
6794                 if (n >= mol->natoms)
6795                         rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6796                 ap = ATOM_AT_INDEX(mol->atoms, n);
6797                 resid = ap->resSeq;
6798         }
6799         /*  Change the residue number  */
6800         MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6801         /*  Change the residue name if necessary  */
6802         if (buf[0] != 0) {
6803         /*      Int seqs[2];
6804                 seqs[0] = resid;
6805                 seqs[1] = kInvalidIndex; */
6806                 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6807         }
6808         IntGroupRelease(ig);
6809         return self;
6810 }
6811
6812 /*
6813  *  call-seq:
6814  *     offset_residue(group, offset)       -> Molecule
6815  *
6816  *  Offset the residue number of the specified atoms. If any of the residue number gets
6817  *  negative, then exception is thrown.
6818  *  This operation is undoable.
6819  */
6820 static VALUE
6821 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6822 {
6823     Molecule *mol;
6824         IntGroup *ig;
6825         int ofs, result;
6826     Data_Get_Struct(self, Molecule, mol);
6827         ig = s_Molecule_AtomGroupFromValue(self, range);
6828         ofs = NUM2INT(offset);
6829         result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6830         if (result > 0)
6831                 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6832         IntGroupRelease(ig);
6833         return self;
6834 }
6835
6836 /*
6837  *  call-seq:
6838  *     renumber_atoms(array)       -> IntGroup
6839  *
6840  *  Change the order of atoms so that the atoms specified in the array argument appear
6841  *  in this order from the top of the molecule. The atoms that are not included in array
6842  *  are placed after these atoms, and these atoms are returned as an intGroup.
6843  *  This operation is undoable.
6844  */
6845 static VALUE
6846 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6847 {
6848     Molecule *mol;
6849         Int *new2old;
6850         IntGroup *ig;
6851         int i, n;
6852         VALUE *valp, retval;
6853     Data_Get_Struct(self, Molecule, mol);
6854         if (TYPE(array) != T_ARRAY)
6855                 array = rb_funcall(array, rb_intern("to_a"), 0);
6856         n = RARRAY_LEN(array);
6857         valp = RARRAY_PTR(array);
6858         new2old = ALLOC_N(Int, n + 1);
6859         for (i = 0; i < n; i++)
6860                 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6861         new2old[i] = kInvalidIndex;
6862         i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6863         if (i == 1)
6864                 rb_raise(rb_eMolbyError, "Atom index out of range");
6865         else if (i == 2)
6866                 rb_raise(rb_eMolbyError, "Duplicate entry");
6867         else if (i == 3)
6868                 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6869         retval = IntGroup_Alloc(rb_cIntGroup);
6870         Data_Get_Struct(retval, IntGroup, ig);
6871         if (mol->natoms > n)
6872                 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6873         xfree(new2old);
6874         return retval;
6875 }
6876
6877 /*
6878  *  call-seq:
6879  *     set_atom_attr(index, key, value)
6880  *
6881  *  Set the atom attribute for the specified atom.
6882  *  This operation is undoable.
6883  */
6884 static VALUE
6885 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6886 {
6887         Molecule *mol;
6888         VALUE aref, oldval;
6889     Data_Get_Struct(self, Molecule, mol);
6890         aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6891         oldval = s_AtomRef_GetAttr(aref, key);
6892         if (val == Qundef)
6893                 return oldval;
6894         s_AtomRef_SetAttr(aref, key, val);
6895         return val;
6896 }
6897
6898 /*
6899  *  call-seq:
6900  *     get_atom_attr(index, key)
6901  *
6902  *  Get the atom attribute for the specified atom.
6903  */
6904 static VALUE
6905 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6906 {
6907         return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6908 }
6909
6910 #pragma mark ------ Undo Support ------
6911
6912 /*
6913  *  call-seq:
6914  *     register_undo(script, *args)
6915  *
6916  *  Register an undo operation with the current molecule.
6917  */
6918 static VALUE
6919 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6920 {
6921         Molecule *mol;
6922         VALUE script, args;
6923         MolAction *act;
6924     Data_Get_Struct(self, Molecule, mol);
6925         rb_scan_args(argc, argv, "1*", &script, &args);
6926         act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6927         MolActionCallback_registerUndo(mol, act);
6928         return script;
6929 }
6930
6931 /*
6932  *  call-seq:
6933  *     undo_enabled? -> bool
6934  *
6935  *  Returns true if undo is enabled for this molecule; otherwise no.
6936  */
6937 static VALUE
6938 s_Molecule_UndoEnabled(VALUE self)
6939 {
6940     Molecule *mol;
6941     Data_Get_Struct(self, Molecule, mol);
6942         if (MolActionCallback_isUndoRegistrationEnabled(mol))
6943                 return Qtrue;
6944         else return Qfalse;
6945 }
6946
6947 /*
6948  *  call-seq:
6949  *     undo_enabled = bool
6950  *
6951  *  Enable or disable undo.
6952  */
6953 static VALUE
6954 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6955 {
6956     Molecule *mol;
6957     Data_Get_Struct(self, Molecule, mol);
6958         MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6959         return val;
6960 }
6961
6962 #pragma mark ------ Measure ------
6963
6964 static void
6965 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6966 {
6967         switch (MoleculeCenterOfMass(mol, outv, ig)) {
6968                 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6969                 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6970                 case 0: break;
6971                 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6972         }
6973 }
6974
6975 /*
6976  *  call-seq:
6977  *     center_of_mass(group = nil)       -> Vector3D
6978  *
6979  *  Calculate the center of mass for the given set of atoms. The argument
6980  *  group is null, then all atoms are considered.
6981  */
6982 static VALUE
6983 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6984 {
6985     Molecule *mol;
6986         VALUE group;
6987         IntGroup *ig;
6988         Vector v;
6989     Data_Get_Struct(self, Molecule, mol);
6990         rb_scan_args(argc, argv, "01", &group);
6991         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6992         s_Molecule_DoCenterOfMass(mol, &v, ig);
6993         if (ig != NULL)
6994                 IntGroupRelease(ig);
6995         return ValueFromVector(&v);
6996 }
6997
6998 /*
6999  *  call-seq:
7000  *     centralize(group = nil)       -> self
7001  *
7002  *  Translate the molecule so that the center of mass of the given group is located
7003  *  at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
7004  */
7005 static VALUE
7006 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
7007 {
7008     Molecule *mol;
7009         VALUE group;
7010         IntGroup *ig;
7011         Vector v;
7012     Data_Get_Struct(self, Molecule, mol);
7013         rb_scan_args(argc, argv, "01", &group);
7014         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7015         s_Molecule_DoCenterOfMass(mol, &v, ig);
7016         if (ig != NULL)
7017                 IntGroupRelease(ig);
7018         v.x = -v.x;
7019         v.y = -v.y;
7020         v.z = -v.z;
7021         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7022         return self;
7023 }
7024
7025 /*
7026  *  call-seq:
7027  *     bounds(group = nil)       -> [min, max]
7028  *
7029  *  Calculate the boundary. The return value is an array of two Vector3D objects.
7030  */
7031 static VALUE
7032 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7033 {
7034     Molecule *mol;
7035         VALUE group;
7036         IntGroup *ig;
7037         Vector vmin, vmax;
7038         int n;
7039         Atom *ap;
7040     Data_Get_Struct(self, Molecule, mol);
7041         rb_scan_args(argc, argv, "01", &group);
7042         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7043         if (ig != NULL && IntGroupGetCount(ig) == 0)
7044                 rb_raise(rb_eMolbyError, "atom group is empty");
7045         vmin.x = vmin.y = vmin.z = 1e30;
7046         vmax.x = vmax.y = vmax.z = -1e30;
7047         for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7048                 Vector r;
7049                 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7050                         continue;
7051                 r = ap->r;
7052                 if (r.x < vmin.x)
7053                         vmin.x = r.x;
7054                 if (r.y < vmin.y)
7055                         vmin.y = r.y;
7056                 if (r.z < vmin.z)
7057                         vmin.z = r.z;
7058                 if (r.x > vmax.x)
7059                         vmax.x = r.x;
7060                 if (r.y > vmax.y)
7061                         vmax.y = r.y;
7062                 if (r.z > vmax.z)
7063                         vmax.z = r.z;
7064         }
7065         return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7066 }
7067
7068 /*  Get atom position or a vector  */
7069 static void
7070 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7071 {
7072         if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7073                 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7074                 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7075         } else {
7076                 VectorFromValue(val, vp);
7077         }
7078 }
7079
7080 /*
7081  *  call-seq:
7082  *     measure_bond(n1, n2)       -> Float
7083  *
7084  *  Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation, 
7085  *  or Vector3D values.
7086  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7087  */
7088 static VALUE
7089 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7090 {
7091     Molecule *mol;
7092         Vector v1, v2;
7093     Data_Get_Struct(self, Molecule, mol);
7094         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7095         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7096         return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7097 }
7098
7099 /*
7100  *  call-seq:
7101  *     measure_angle(n1, n2, n3)       -> Float
7102  *
7103  *  Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation, 
7104  *  or Vector3D values. The return value is in degree.
7105  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7106  */
7107 static VALUE
7108 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7109 {
7110     Molecule *mol;
7111         Vector v1, v2, v3;
7112         Double d;
7113     Data_Get_Struct(self, Molecule, mol);
7114         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7115         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7116         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7117         d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7118         if (isnan(d))
7119                 return Qnil;  /*  Cannot define  */
7120         else return rb_float_new(d);
7121 }
7122
7123 /*
7124  *  call-seq:
7125  *     measure_dihedral(n1, n2, n3, n4)       -> Float
7126  *
7127  *  Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation, 
7128  *  or Vector3D values. The return value is in degree.
7129  *  If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7130  */
7131 static VALUE
7132 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7133 {
7134     Molecule *mol;
7135         Vector v1, v2, v3, v4;
7136         Double d;
7137     Data_Get_Struct(self, Molecule, mol);
7138         s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7139         s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7140         s_Molecule_GetVectorFromArg(mol, nval3, &v3);   
7141         s_Molecule_GetVectorFromArg(mol, nval4, &v4);   
7142         d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7143         if (isnan(d))
7144                 return Qnil;  /*  Cannot define  */
7145         else return rb_float_new(d);
7146 }
7147
7148 /*
7149  *  call-seq:
7150  *     find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7151  *
7152  *  Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7153  *  first and second atom in the pair should belong to group1 and group2, respectively.
7154  *  If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7155  */
7156 static VALUE
7157 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7158 {
7159     Molecule *mol;
7160         VALUE limval, gval1, gval2, rval, igval;
7161         IntGroup *ig1, *ig2;
7162         IntGroupIterator iter1, iter2;
7163         Int npairs, *pairs;
7164         Int n[2], i;
7165         Double lim;
7166         Vector r1;
7167         Atom *ap1, *ap2;
7168         MDExclusion *exinfo;
7169         Int *exlist;
7170         
7171     Data_Get_Struct(self, Molecule, mol);
7172         rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7173         lim = NUM2DBL(rb_Float(limval));
7174         if (lim <= 0.0)
7175                 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7176         if (gval1 != Qnil)
7177                 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7178         else
7179                 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7180         if (gval2 != Qnil)
7181                 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7182         else
7183                 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7184         
7185         if (!RTEST(igval)) {
7186                 /*  Use the exclusion table in MDArena  */
7187                 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7188                         VALUE mval = ValueFromMolecule(mol);
7189                         s_RebuildMDParameterIfNecessary(mval, Qnil);
7190                 }
7191                 exinfo = mol->arena->exinfo;  /*  May be NULL  */
7192                 exlist = mol->arena->exlist;    
7193         } else {
7194                 exinfo = NULL;
7195                 exlist = NULL;
7196         }
7197         IntGroupIteratorInit(ig1, &iter1);
7198         IntGroupIteratorInit(ig2, &iter2);
7199         npairs = 0;
7200         pairs = NULL;
7201         while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7202                 Int exn1, exn2;
7203                 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7204                 r1 = ap1->r;
7205                 if (exinfo != NULL) {
7206                         exn1 = exinfo[n[0]].index1;
7207                         exn2 = exinfo[n[0] + 1].index1;
7208                 } else exn1 = exn2 = -1;
7209                 IntGroupIteratorReset(&iter2);
7210                 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7211                         ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7212                         if (n[0] == n[1])
7213                                 continue;  /*  Same atom  */
7214                         if (exinfo != NULL) {
7215                                 /*  Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs  */
7216                                 for (i = exn1; i < exn2; i++) {
7217                                         if (exlist[i] == n[1])
7218                                                 break;
7219                                 }
7220                                 if (i < exn2)
7221                                         continue;  /*  Should be excluded  */
7222                         }
7223                         if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7224                                 /*  Is this pair already registered?  */
7225                                 Int *ip;
7226                                 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7227                                         if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7228                                                 break;
7229                                 }
7230                                 if (i >= npairs) {
7231                                         /*  Not registered yet  */
7232                                         AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7233                                 }
7234                         }
7235                 }
7236         }
7237         IntGroupIteratorRelease(&iter2);
7238         IntGroupIteratorRelease(&iter1);
7239         IntGroupRelease(ig2);
7240         IntGroupRelease(ig1);
7241         rval = rb_ary_new2(npairs);
7242         if (pairs != NULL) {
7243                 for (i = 0; i < npairs; i++) {
7244                         rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7245                 }
7246                 free(pairs);
7247         }
7248         return rval;
7249 }
7250
7251 /*
7252  *  call-seq:
7253  *     find_close_atoms(atom, limit = 1.2, radius = 0.77)   -> array of Integers (atom indices)
7254  *
7255  *  Find atoms that are within the threshold distance from the given atom.
7256  *  (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.)
7257  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7258  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7259  *  If limit is not given, a default value of 1.2 is used.
7260  *  An array of atom indices is returned. If no atoms are found, an empty array is returned.
7261  */
7262 static VALUE
7263 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7264 {
7265     Molecule *mol;
7266         VALUE aval, limval, radval;
7267         double limit, radius;
7268         Int n1, nbonds, *bonds, an;
7269         Vector v;
7270     Data_Get_Struct(self, Molecule, mol);
7271         rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7272         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)) {
7273                 VectorFromValue(aval, &v);
7274                 if (radval == Qnil)
7275                         radius = gElementParameters[6].radius;
7276                 else
7277                         radius = NUM2DBL(rb_Float(radval));
7278                 n1 = mol->natoms;
7279         } else {
7280                 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7281                 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7282                 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7283                 if (an >= 0 && an < gCountElementParameters)
7284                         radius = gElementParameters[an].radius;
7285                 else radius = gElementParameters[6].radius;
7286         }
7287         if (limval == Qnil)
7288                 limit = 1.2;
7289         else
7290                 limit = NUM2DBL(rb_Float(limval));
7291         nbonds = 0;  /*  This initialization is necessary: see comments in MoleculeFindCloseAtoms()  */
7292         bonds = NULL;
7293         MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7294         aval = rb_ary_new();
7295         if (nbonds > 0) {
7296                 for (n1 = 0; n1 < nbonds; n1++)
7297                         rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7298                 free(bonds);
7299         }
7300         return aval;
7301 }
7302
7303 /*
7304  *  call-seq:
7305  *     guess_bonds(limit = 1.2)       -> Integer
7306  *
7307  *  Create bonds between atoms that are within the threshold distance.
7308  *  If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7309  *  If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7310  *  If limit is not given, a default value of 1.2 is used.
7311  *  The number of the newly created bonds is returned.
7312  *  This operation is undoable.
7313  */
7314 static VALUE
7315 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7316 {
7317     Molecule *mol;
7318         VALUE limval;
7319         double limit;
7320         Int nbonds, *bonds;
7321     Data_Get_Struct(self, Molecule, mol);
7322         rb_scan_args(argc, argv, "01", &limval);
7323         if (limval == Qnil)
7324                 limit = 1.2;
7325         else
7326                 limit = NUM2DBL(rb_Float(limval));
7327         MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7328         if (nbonds > 0) {
7329                 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7330                 free(bonds);
7331         }
7332         return INT2NUM(nbonds);
7333 }
7334
7335 #pragma mark ------ Cell and Symmetry ------
7336
7337 /*
7338  *  call-seq:
7339  *     cell     -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7340  *
7341  *  Returns the unit cell parameters. If cell is not set, returns nil.
7342  */
7343 static VALUE
7344 s_Molecule_Cell(VALUE self)
7345 {
7346     Molecule *mol;
7347         int i;
7348         VALUE val;
7349     Data_Get_Struct(self, Molecule, mol);
7350         if (mol->cell == NULL)
7351                 return Qnil;
7352         val = rb_ary_new2(6);
7353         for (i = 0; i < 6; i++)
7354                 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7355         if (mol->cell->has_sigma) {
7356                 for (i = 0; i < 6; i++) {
7357                         rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7358                 }
7359         }
7360         return val;
7361 }
7362
7363 /*
7364  *  call-seq:
7365  *     cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7366  *     set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7367  *
7368  *  Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7369  If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7370  This operation is undoable.
7371  Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7372  */
7373 static VALUE
7374 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7375 {
7376     Molecule *mol;
7377         VALUE val, cval;
7378         int i, convert_coord, n;
7379         double d[12];
7380     Data_Get_Struct(self, Molecule, mol);
7381         rb_scan_args(argc, argv, "11", &val, &cval);
7382         if (val == Qnil) {
7383                 n = 0;
7384         } else {
7385                 int len;
7386                 val = rb_ary_to_ary(val);
7387                 len = RARRAY_LEN(val);
7388                 if (len >= 12) {
7389                         n = 12;
7390                 } else if (len >= 6) {
7391                         n = 6;
7392                 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7393                 for (i = 0; i < n; i++)
7394                         d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7395         }
7396         convert_coord = (RTEST(cval) ? 1 : 0);
7397         MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7398         return val;
7399 }
7400
7401 /*
7402  *  call-seq:
7403  *     box -> [avec, bvec, cvec, origin, flags]
7404  *
7405  *  Get the unit cell information in the form of a periodic bounding box.
7406  *  Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of 
7407  *  Integers which define whether the system is periodic along the axis.
7408  *  If no unit cell is defined, nil is returned.
7409  */
7410 static VALUE
7411 s_Molecule_Box(VALUE self)
7412 {
7413     Molecule *mol;
7414         VALUE v[5], val;
7415     Data_Get_Struct(self, Molecule, mol);
7416         if (mol == NULL || mol->cell == NULL)
7417                 return Qnil;
7418         v[0] = ValueFromVector(&(mol->cell->axes[0]));
7419         v[1] = ValueFromVector(&(mol->cell->axes[1]));
7420         v[2] = ValueFromVector(&(mol->cell->axes[2]));
7421         v[3] = ValueFromVector(&(mol->cell->origin));
7422         v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7423         val = rb_ary_new4(5, v);
7424         return val;
7425 }
7426
7427 /*
7428  *  call-seq:
7429  *     set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7430  *     set_box(d, origin = [0, 0, 0])
7431  *     set_box
7432  *
7433  *  Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7434  If it is a number, the x/y/z axis vector is multiplied with the given number and used
7435  as the box vector.
7436  Flags, if present, is a 3-member array of Integers defining whether the system is
7437  periodic along the axis.
7438  If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7439  In the second form, an isotropic box with cell-length d is set.
7440  In the third form, the existing box is cleared.
7441  Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7442  */
7443 static VALUE
7444 s_Molecule_SetBox(VALUE self, VALUE aval)
7445 {
7446     Molecule *mol;
7447         VALUE v[6];
7448         static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7449         Vector vv[3];
7450         Vector origin = {0, 0, 0};
7451         char flags[3];
7452         Double d;
7453         int i, convertCoordinates = 0;
7454     Data_Get_Struct(self, Molecule, mol);
7455         if (aval == Qnil) {
7456                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7457                 return self;
7458         }
7459         aval = rb_ary_to_ary(aval);
7460         for (i = 0; i < 6; i++) {
7461                 if (i < RARRAY_LEN(aval))
7462                         v[i] = (RARRAY_PTR(aval))[i];
7463                 else v[i] = Qnil;
7464         }
7465         if (v[0] == Qnil) {
7466                 MolActionCreateAndPerform(mol, gMolActionClearBox);
7467                 return self;
7468         }
7469         if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7470                 d = NUM2DBL(rb_Float(v[0]));
7471                 for (i = 0; i < 3; i++)
7472                         VecScale(vv[i], ax[i], d);
7473                 if (v[1] != Qnil)
7474                         VectorFromValue(v[1], &origin);
7475                 flags[0] = flags[1] = flags[2] = 1;
7476         } else {
7477                 for (i = 0; i < 3; i++) {
7478                         if (v[i] == Qnil) {
7479                                 VecZero(vv[i]);
7480                         } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7481                                 d = NUM2DBL(rb_Float(v[i]));
7482                                 VecScale(vv[i], ax[i], d);
7483                         } else {
7484                                 VectorFromValue(v[i], &vv[i]);
7485                         }
7486                         flags[i] = (VecLength2(vv[i]) > 0.0);
7487                 }
7488                 if (v[3] != Qnil)
7489                         VectorFromValue(v[3], &origin);
7490                 if (v[4] != Qnil) {
7491                         for (i = 0; i < 3; i++) {
7492                                 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7493                                 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7494                         }
7495                 }
7496                 if (RTEST(v[5]))
7497                         convertCoordinates = 1;
7498         }
7499         MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7500         return self;
7501 }
7502
7503 /*
7504  *  call-seq:
7505  *     cell_periodicity -> [n1, n2, n3]
7506  *
7507  *  Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7508  *  nil is returned.
7509  */
7510 static VALUE
7511 s_Molecule_CellPeriodicity(VALUE self)
7512 {
7513     Molecule *mol;
7514     Data_Get_Struct(self, Molecule, mol);
7515         if (mol->cell == NULL)
7516                 return Qnil;
7517         return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7518 }
7519
7520 /*
7521  *  call-seq:
7522  *     self.cell_periodicity = [n1, n2, n3] or Integer or nil
7523  *     set_cell_periodicity = [n1, n2, n3] or Integer or nil
7524  *
7525  *  Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7526  *  its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7527  *  If cell is not defined, exception is raised.
7528  *  This operation is undoable.
7529  */
7530 static VALUE
7531 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7532 {
7533     Molecule *mol;
7534         Int flag;
7535     Data_Get_Struct(self, Molecule, mol);
7536         if (mol->cell == NULL)
7537                 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7538         if (arg == Qnil)
7539                 flag = 0;
7540         else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7541                 flag = NUM2INT(rb_Integer(arg));
7542         else {
7543                 Int i;
7544                 VALUE arg0;
7545                 arg = rb_ary_to_ary(arg);
7546                 flag = 0;
7547                 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7548                         arg0 = RARRAY_PTR(arg)[i];
7549                         if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7550                                 flag |= (1 << (2 - i));
7551                 }
7552         }
7553         MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7554         return arg;
7555 }
7556
7557 /*
7558  *  call-seq:
7559  *     cell_flexibility -> bool
7560  *
7561  *  Returns the unit cell is flexible or not
7562  */
7563 static VALUE
7564 s_Molecule_CellFlexibility(VALUE self)
7565 {
7566         rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7567         return Qtrue;
7568         /*    Molecule *mol;
7569          Data_Get_Struct(self, Molecule, mol);
7570          if (mol->cell == NULL)
7571          return Qfalse;
7572          if (mol->useFlexibleCell)
7573          return Qtrue;
7574          else return Qfalse; */
7575 }
7576
7577 /*
7578  *  call-seq:
7579  *     self.cell_flexibility = bool
7580  *     set_cell_flexibility(bool)
7581  *
7582  *  Change the unit cell is flexible or not
7583  */
7584 static VALUE
7585 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7586 {
7587         rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7588         return self;
7589         /*    Molecule *mol;
7590          Data_Get_Struct(self, Molecule, mol);
7591          MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7592          return self; */
7593 }
7594
7595 /*
7596  *  call-seq:
7597  *     cell_transform -> Transform
7598  *
7599  *  Get the transform matrix that converts internal coordinates to cartesian coordinates.
7600  *  If cell is not defined, nil is returned.
7601  */
7602 static VALUE
7603 s_Molecule_CellTransform(VALUE self)
7604 {
7605     Molecule *mol;
7606     Data_Get_Struct(self, Molecule, mol);
7607         if (mol == NULL || mol->cell == NULL)
7608                 return Qnil;
7609         return ValueFromTransform(&(mol->cell->tr));
7610 }
7611
7612 /*
7613  *  call-seq:
7614  *     symmetry -> Array of Transforms
7615  *     symmetries -> Array of Transforms
7616  *
7617  *  Get the currently defined symmetry operations. If no symmetry operation is defined,
7618  *  returns an empty array.
7619  */
7620 static VALUE
7621 s_Molecule_Symmetry(VALUE self)
7622 {
7623     Molecule *mol;
7624         VALUE val;
7625         int i;
7626     Data_Get_Struct(self, Molecule, mol);
7627         if (mol->nsyms <= 0)
7628                 return rb_ary_new();
7629         val = rb_ary_new2(mol->nsyms);
7630         for (i = 0; i < mol->nsyms; i++) {
7631                 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7632         }
7633         return val;
7634 }
7635
7636 /*
7637  *  call-seq:
7638  *     nsymmetries -> Integer
7639  *
7640  *  Get the number of currently defined symmetry operations.
7641  */
7642 static VALUE
7643 s_Molecule_Nsymmetries(VALUE self)
7644 {
7645     Molecule *mol;
7646     Data_Get_Struct(self, Molecule, mol);
7647         return INT2NUM(mol->nsyms);
7648 }
7649
7650 /*
7651  *  call-seq:
7652  *     add_symmetry(Transform) -> Integer
7653  *
7654  *  Add a new symmetry operation. If no symmetry operation is defined and the
7655  *  given argument is not an identity transform, then also add an identity
7656  *  transform at the index 0.
7657  *  Returns the total number of symmetries after operation.
7658  */
7659 static VALUE
7660 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7661 {
7662     Molecule *mol;
7663         Transform tr;
7664     Data_Get_Struct(self, Molecule, mol);
7665         TransformFromValue(trans, &tr);
7666         MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7667         return INT2NUM(mol->nsyms);
7668 }
7669
7670 /*
7671  *  call-seq:
7672  *     remove_symmetry(count = nil) -> Integer
7673  *     remove_symmetries(count = nil) -> Integer
7674  *
7675  *  Remove the specified number of symmetry operations. The last added ones are removed
7676  *  first. If count is nil, then all symmetry operations are removed. Returns the
7677  *  number of leftover symmetries.
7678  */
7679 static VALUE
7680 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7681 {
7682     Molecule *mol;
7683         VALUE cval;
7684         int i, n;
7685     Data_Get_Struct(self, Molecule, mol);
7686         rb_scan_args(argc, argv, "01", &cval);
7687         if (cval == Qnil)
7688                 n = mol->nsyms - 1;
7689         else {
7690                 n = NUM2INT(rb_Integer(cval));
7691                 if (n < 0 || n > mol->nsyms)
7692                         rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7693                 if (n == mol->nsyms)
7694                         n = mol->nsyms - 1;
7695         }
7696         for (i = 0; i < n; i++)
7697                 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7698         return INT2NUM(mol->nsyms);
7699 }
7700
7701 /*
7702  *  call-seq:
7703  *     wrap_unit_cell(group) -> Vector3D
7704  *
7705  *  Move the specified group so that the center of mass of the group is within the
7706  *  unit cell. The offset vector is returned. If no periodic box is defined, 
7707  *  exception is raised.
7708  */
7709 static VALUE
7710 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7711 {
7712     Molecule *mol;
7713         IntGroup *ig;
7714         Vector v, cv, dv;
7715     Data_Get_Struct(self, Molecule, mol);
7716         if (mol->cell == NULL)
7717                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7718         ig = s_Molecule_AtomGroupFromValue(self, gval);
7719         s_Molecule_DoCenterOfMass(mol, &cv, ig);
7720         TransformVec(&v, mol->cell->rtr, &cv);
7721         if (mol->cell->flags[0])
7722                 v.x -= floor(v.x);
7723         if (mol->cell->flags[1])
7724                 v.y -= floor(v.y);
7725         if (mol->cell->flags[2])
7726                 v.z -= floor(v.z);
7727         TransformVec(&dv, mol->cell->tr, &v);
7728         VecDec(dv, cv);
7729         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7730         IntGroupRelease(ig);
7731         return ValueFromVector(&dv);
7732 }
7733
7734 /*
7735  *  call-seq:
7736  *     expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7737  *
7738  *  Expand the specified part of the molecule by the given symmetry operation.
7739  *  Returns the array of atom indices corresponding to the expanded atoms.
7740  *  If allow_overlap is true, then new atoms are created even when the
7741  *  coordinates coincide with the some other atom (special position) of the
7742  *  same element; otherwise, such atom will not be created and the index of the
7743  *  existing atom is given in the returned array.
7744  */
7745 static VALUE
7746 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7747 {
7748     Molecule *mol;
7749         VALUE gval, sval, xval, yval, zval, rval, oval;
7750         IntGroup *ig;
7751         Int n[4], allow_overlap;
7752         Int natoms;
7753         Int nidx, *idx;
7754         
7755     Data_Get_Struct(self, Molecule, mol);
7756         rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7757         n[0] = NUM2INT(rb_Integer(sval));
7758         n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7759         n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7760         n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7761         allow_overlap = (RTEST(oval) ? 1 : 0);
7762         ig = s_Molecule_AtomGroupFromValue(self, gval);
7763         if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7764                 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7765         natoms = mol->natoms;
7766         
7767         MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7768         
7769         rval = rb_ary_new2(nidx);
7770         while (--nidx >= 0) {
7771                 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7772         }
7773         /*      if (natoms == mol->natoms)
7774          rval = Qnil;
7775          else {
7776          rval = IntGroup_Alloc(rb_cIntGroup);
7777          Data_Get_Struct(rval, IntGroup, ig);
7778          IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7779          } */
7780         return rval;
7781 }
7782
7783 /*
7784  *  call-seq:
7785  *     amend_by_symmetry(group = nil) -> IntGroup
7786  *
7787  *  Expand the specified part of the molecule by the given symmetry operation.
7788  *  Returns an IntGroup containing the added atoms.
7789  */
7790 static VALUE
7791 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7792 {
7793     Molecule *mol;
7794         IntGroup *ig, *ig2;
7795         VALUE rval, gval;
7796     Data_Get_Struct(self, Molecule, mol);
7797         rb_scan_args(argc, argv, "01", &gval);
7798         if (gval != Qnil)
7799                 ig = s_Molecule_AtomGroupFromValue(self, gval);
7800         else ig = NULL;
7801         MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7802         rval = ValueFromIntGroup(ig2);
7803         IntGroupRelease(ig2);
7804         return rval;
7805 }
7806
7807 #pragma mark ------ Transforms ------
7808
7809 /*
7810  *  call-seq:
7811  *     translate(vec, group = nil)       -> Molecule
7812  *
7813  *  Translate the molecule by vec. If group is given, only atoms in the group are moved.
7814  *  This operation is undoable.
7815  */
7816 static VALUE
7817 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7818 {
7819     Molecule *mol;
7820         VALUE vec, group;
7821         Vector v;
7822         IntGroup *ig;
7823     Data_Get_Struct(self, Molecule, mol);
7824         rb_scan_args(argc, argv, "11", &vec, &group);
7825         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7826         VectorFromValue(vec, &v);
7827         //      MoleculeTranslate(mol, &v, ig);
7828         MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7829         if (ig != NULL)
7830                 IntGroupRelease(ig);
7831         return self;
7832 }
7833
7834 /*
7835  *  call-seq:
7836  *     rotate(axis, angle, center = [0,0,0], group = nil)       -> Molecule
7837  *
7838  *  Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7839  *  If group is given, only atoms in the group are moved.
7840  *  This operation is undoable.
7841  */
7842 static VALUE
7843 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7844 {
7845     Molecule *mol;
7846         volatile VALUE aval, anval, cval, gval;
7847         Double angle;
7848         Vector av, cv;
7849         Transform tr;
7850         IntGroup *ig;
7851     Data_Get_Struct(self, Molecule, mol);
7852         rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7853         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7854         angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7855         VectorFromValue(aval, &av);
7856         if (NIL_P(cval))
7857                 cv.x = cv.y = cv.z = 0.0;
7858         else
7859                 VectorFromValue(cval, &cv);
7860         if (TransformForRotation(tr, &av, angle, &cv))
7861                 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7862         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7863         if (ig != NULL)
7864                 IntGroupRelease(ig);
7865         return self;
7866 }
7867
7868 /*
7869  *  call-seq:
7870  *     reflect(axis, center = [0,0,0], group = nil)       -> Molecule
7871  *
7872  *  Reflect the molecule by the plane which is perpendicular to axis and including center. 
7873  *  axis must not be a zero vector.
7874  *  If group is given, only atoms in the group are moved.
7875  *  This operation is undoable.
7876  */
7877 static VALUE
7878 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7879 {
7880     Molecule *mol;
7881         volatile VALUE aval, cval, gval;
7882         Vector av, cv;
7883         Transform tr;
7884         IntGroup *ig;
7885     Data_Get_Struct(self, Molecule, mol);
7886         rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7887         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7888         VectorFromValue(aval, &av);
7889         if (NIL_P(cval))
7890                 cv.x = cv.y = cv.z = 0.0;
7891         else
7892                 VectorFromValue(cval, &cv);
7893         if (TransformForReflection(tr, &av, &cv))
7894                 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7895         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7896         if (ig != NULL)
7897                 IntGroupRelease(ig);
7898         return self;
7899 }
7900
7901 /*
7902  *  call-seq:
7903  *     invert(center = [0,0,0], group = nil)       -> Molecule
7904  *
7905  *  Invert the molecule with the given center.
7906  *  If group is given, only atoms in the group are moved.
7907  *  This operation is undoable.
7908  */
7909 static VALUE
7910 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7911 {
7912         Molecule *mol;
7913         volatile VALUE cval, gval;
7914         Vector cv;
7915         Transform tr;
7916         IntGroup *ig;
7917     Data_Get_Struct(self, Molecule, mol);
7918         rb_scan_args(argc, argv, "02", &cval, &gval);
7919         ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7920         if (NIL_P(cval))
7921                 cv.x = cv.y = cv.z = 0.0;
7922         else
7923                 VectorFromValue(cval, &cv);
7924         TransformForInversion(tr, &cv);
7925         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7926         if (ig != NULL)
7927                 IntGroupRelease(ig);
7928         return self;
7929 }
7930
7931 /*
7932  *  call-seq:
7933  *     transform(transform, group = nil)       -> Molecule
7934  *
7935  *  Transform the molecule by the given Transform object.
7936  *  If group is given, only atoms in the group are moved.
7937  *  This operation is undoable.
7938  */
7939 static VALUE
7940 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7941 {
7942     Molecule *mol;
7943         VALUE trans, group;
7944         Transform tr;
7945         IntGroup *ig;
7946     Data_Get_Struct(self, Molecule, mol);
7947         rb_scan_args(argc, argv, "11", &trans, &group);
7948         ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7949         TransformFromValue(trans, &tr);
7950         /*      MoleculeTransform(mol, tr, ig); */
7951         MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7952         if (ig != NULL)
7953                 IntGroupRelease(ig);
7954         return self;
7955 }
7956
7957 /*
7958  *  call-seq:
7959  *     transform_for_symop(symop, is_cartesian = nil) -> Transform
7960  *
7961  *  Get the transform corresponding to the symmetry operation. The symop can either be
7962  *  an integer (index of symmetry operation) or [sym, dx, dy, dz].
7963  *  If is_cartesian is true, the returned transform is for cartesian coordinates.
7964  *  Otherwise, the returned transform is for fractional coordinates.
7965  *  Raises exception when no cell or no transform are defined.
7966  */
7967 static VALUE
7968 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7969 {
7970     Molecule *mol;
7971         VALUE sval, fval;
7972         Symop symop;
7973         Transform tr;
7974     Data_Get_Struct(self, Molecule, mol);
7975         if (mol->cell == NULL)
7976                 rb_raise(rb_eMolbyError, "no unit cell is defined");
7977         if (mol->nsyms == 0)
7978                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7979         rb_scan_args(argc, argv, "11", &sval, &fval);
7980         if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7981                 symop.sym = NUM2INT(rb_Integer(sval));
7982                 symop.dx = symop.dy = symop.dz = 0;
7983         } else {
7984                 sval = rb_ary_to_ary(sval);
7985                 if (RARRAY_LEN(sval) < 4)
7986                         rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7987                 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7988                 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7989                 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7990                 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7991         }
7992         if (symop.sym >= mol->nsyms)
7993                 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7994         MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7995         return ValueFromTransform(&tr);
7996 }
7997
7998 /*
7999  *  call-seq:
8000  *     symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8001  *
8002  *  Get the symmetry operation corresponding to the given transform.
8003  *  If is_cartesian is true, the given transform is for cartesian coordinates.
8004  *  Otherwise, the given transform is for fractional coordinates.
8005  *  Raises exception when no cell or no transform are defined.
8006  */
8007 static VALUE
8008 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8009 {
8010     Molecule *mol;
8011         VALUE tval, fval;
8012         Symop symop;
8013         Transform tr;
8014         int n;
8015     Data_Get_Struct(self, Molecule, mol);
8016         if (mol->cell == NULL)
8017                 rb_raise(rb_eMolbyError, "no unit cell is defined");
8018         if (mol->nsyms == 0)
8019                 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8020         rb_scan_args(argc, argv, "11", &tval, &fval);
8021         TransformFromValue(tval, &tr);
8022         n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8023         if (n == 0) {
8024                 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8025         } else {
8026                 return Qnil;  /*  Not found  */
8027         }
8028 }
8029
8030 #pragma mark ------ Frames ------
8031
8032 /*
8033  *  call-seq:
8034  *     select_frame(index)
8035  *     frame = index
8036  *
8037  *  Select the specified frame. If successful, returns true, otherwise returns false.
8038  */
8039 static VALUE
8040 s_Molecule_SelectFrame(VALUE self, VALUE val)
8041 {
8042     Molecule *mol;
8043         int ival = NUM2INT(val);
8044     Data_Get_Struct(self, Molecule, mol);
8045         ival = MoleculeSelectFrame(mol, ival, 1);
8046         if (ival >= 0)
8047                 return Qtrue;
8048         else return Qfalse;
8049 }
8050
8051 /*
8052  *  call-seq:
8053  *     frame -> Integer
8054  *
8055  *  Get the current frame.
8056  */
8057 static VALUE
8058 s_Molecule_Frame(VALUE self)
8059 {
8060     Molecule *mol;
8061     Data_Get_Struct(self, Molecule, mol);
8062         return INT2NUM(mol->cframe);
8063 }
8064
8065 /*
8066  *  call-seq:
8067  *     nframes -> Integer
8068  *
8069  *  Get the number of frames.
8070  */
8071 static VALUE
8072 s_Molecule_Nframes(VALUE self)
8073 {
8074     Molecule *mol;
8075     Data_Get_Struct(self, Molecule, mol);
8076         return INT2NUM(MoleculeGetNumberOfFrames(mol));
8077 }
8078
8079 /*
8080  *  call-seq:
8081  *     insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8082  *     insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8083  *
8084  *  Insert new frames at the indices specified by the intGroup. If the first argument is
8085  *  an integer, a single new frame is inserted at that index. If the first argument is 
8086  *  nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8087  *  should be an array of arrays of Vector3Ds, then those coordinates are set 
8088  *  to the new frame. Otherwise, the coordinates of current molecule are copied 
8089  *  to the new frame.
8090  *  Returns an intGroup representing the inserted frames if successful, nil if not.
8091  */
8092 static VALUE
8093 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8094 {
8095         VALUE val, coords, cells;
8096     Molecule *mol;
8097         IntGroup *ig;
8098         int count, ival, i, j, len, len_c, len2, nframes;
8099         VALUE *ptr, *ptr2;
8100         Vector *vp, *vp2;
8101     Data_Get_Struct(self, Molecule, mol);
8102         rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8103         if (coords != Qnil) {
8104                 if (TYPE(coords) != T_ARRAY)
8105                         rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8106                 len = RARRAY_LEN(coords);
8107         } else len = 0;
8108         if (cells != Qnil) {
8109                 if (mol->cell == NULL)
8110                         rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8111                 if (TYPE(cells) != T_ARRAY)
8112                         rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8113                 len_c = RARRAY_LEN(cells);
8114         } else len_c = 0;
8115         count = (len > len_c ? len : len_c);  /*  May be zero; will be updated later  */
8116         nframes = MoleculeGetNumberOfFrames(mol);
8117         if (val == Qnil) {
8118                 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8119                 val = ValueFromIntGroup(ig);
8120         } else {
8121                 ig = IntGroupFromValue(val);
8122         }
8123         count = IntGroupGetCount(ig);  /*  Count is updated here  */
8124         vp = ALLOC_N(Vector, mol->natoms * count);
8125         if (cells != Qnil)
8126                 vp2 = ALLOC_N(Vector, 4 * count);
8127         else vp2 = NULL;
8128         if (len > 0) {
8129                 if (len < count)
8130                         rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8131                 ptr = RARRAY_PTR(coords);
8132                 for (i = 0; i < count; i++) {
8133                         if (TYPE(ptr[i]) != T_ARRAY)
8134                                 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8135                         len2 = RARRAY_LEN(ptr[i]);
8136                         if (len2 < mol->natoms)
8137                                 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8138                         ptr2 = RARRAY_PTR(ptr[i]);
8139                         for (j = 0; j < mol->natoms; j++)
8140                                 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8141                 }
8142         } else {
8143                 Atom *ap;
8144                 for (i = 0; i < count; i++) {
8145                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8146                                 vp[i * mol->natoms + j] = ap->r;
8147                         }
8148                 }
8149         }
8150         if (len_c > 0) {
8151                 if (len_c < count)
8152                         rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8153                 ptr = RARRAY_PTR(cells);
8154                 for (i = 0; i < count; i++) {
8155                         if (TYPE(ptr[i]) != T_ARRAY)
8156                                 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8157                         len2 = RARRAY_LEN(ptr[i]);
8158                         if (len2 < 4)
8159                                 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8160                         ptr2 = RARRAY_PTR(ptr[i]);
8161                         for (j = 0; j < 4; j++)
8162                                 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8163                 }
8164         }
8165         ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8166         IntGroupRelease(ig);
8167         xfree(vp);
8168         if (vp2 != NULL)
8169                 xfree(vp2);
8170         return (ival >= 0 ? val : Qnil);
8171 }
8172
8173 /*
8174  *  call-seq:
8175  *     create_frame(coordinates = nil) -> Integer
8176  *     create_frames(coordinates = nil) -> Integer
8177  *
8178  *  Same as molecule.insert_frames(nil, coordinates).
8179  */
8180 static VALUE
8181 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8182 {
8183         VALUE vals[3];
8184         rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8185         vals[0] = Qnil;
8186         return s_Molecule_InsertFrames(3, vals, self);
8187 }
8188
8189 /*
8190  *  call-seq:
8191  *     remove_frames(IntGroup, wantCoordinates = false)
8192  *
8193  *  Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8194  *  and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8195  *  removed frames is returned if operation is successful.
8196  */
8197 static VALUE
8198 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8199 {
8200         VALUE val, flag;
8201         VALUE retval;
8202     Molecule *mol;
8203         IntGroup *ig;
8204         int count;
8205     Data_Get_Struct(self, Molecule, mol);
8206         rb_scan_args(argc, argv, "11", &val, &flag);
8207         ig = IntGroupFromValue(val);
8208         count = IntGroupGetCount(ig);
8209         if (RTEST(flag)) {
8210                 /*  Create return value before removing frames  */
8211                 VALUE coords;
8212                 int i, j, n;
8213                 Atom *ap;
8214                 Vector v;
8215                 retval = rb_ary_new2(count);
8216                 for (i = 0; i < count; i++) {
8217                         n = IntGroupGetNthPoint(ig, i);
8218                         coords = rb_ary_new2(mol->natoms);
8219                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8220                                 if (n < ap->nframes && n != mol->cframe)
8221                                         v = ap->frames[n];
8222                                 else v = ap->r;
8223                                 rb_ary_push(coords, ValueFromVector(&v));
8224                         }
8225                         rb_ary_push(retval, coords);
8226                 }
8227         } else retval = Qtrue;
8228         if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8229                 return retval;
8230         else return Qnil;
8231 }
8232
8233 /*
8234  *  call-seq:
8235  *     each_frame {|n| ...}
8236  *
8237  *  Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8238  *  the frame number. After completion, the original frame number is restored.
8239  */
8240 static VALUE
8241 s_Molecule_EachFrame(VALUE self)
8242 {
8243         int i, cframe, nframes;
8244     Molecule *mol;
8245     Data_Get_Struct(self, Molecule, mol);
8246         cframe = mol->cframe;
8247         nframes = MoleculeGetNumberOfFrames(mol);
8248         if (nframes > 0) {
8249                 for (i = 0; i < nframes; i++) {
8250                         MoleculeSelectFrame(mol, i, 1);
8251                         rb_yield(INT2NUM(i));
8252                 }
8253                 MoleculeSelectFrame(mol, cframe, 1);
8254         }
8255     return self;
8256 }
8257
8258 /*
8259  *  call-seq:
8260  *     get_coord_from_frame(index, group = nil)
8261  *
8262  *  Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8263  *  are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8264  *  copied; now they are always copied)
8265  */
8266 static VALUE
8267 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8268 {
8269         Molecule *mol;
8270         VALUE ival, gval, cval;
8271         Int index, i, j, n, nn;
8272         IntGroup *ig;
8273         IntGroupIterator iter;
8274         Atom *ap;
8275         Vector *vp;
8276     Data_Get_Struct(self, Molecule, mol);
8277         rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8278         if (argc == 3)
8279                 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8280         index = NUM2INT(rb_Integer(ival));
8281         if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8282                 if (n == 0)
8283                         rb_raise(rb_eMolbyError, "No frame is present");
8284                 else
8285                         rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8286         }
8287         if (gval == Qnil) {
8288                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8289         } else {
8290                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8291         }
8292         n = IntGroupGetCount(ig);
8293         if (n > 0) {
8294                 vp = (Vector *)calloc(sizeof(Vector), n);
8295                 IntGroupIteratorInit(ig, &iter);
8296                 j = 0;
8297                 nn = 0;
8298                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8299                         ap = ATOM_AT_INDEX(mol->atoms, i);
8300                         if (index < ap->nframes) {
8301                                 vp[j] = ap->frames[index];
8302                                 nn++;
8303                         } else {
8304                                 vp[j] = ap->r;
8305                         }
8306                         j++;
8307                 }
8308                 if (nn > 0)
8309                         MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8310                 free(vp);
8311                 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8312                         vp = mol->frame_cells + index * 4;
8313                         MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8314                 }
8315                 IntGroupIteratorRelease(&iter);
8316         }
8317         /*  Copy the extra properties  */
8318         IntGroupRelease(ig);
8319         for (i = 0; i < mol->nmolprops; i++) {
8320                 Double *dp = (Double *)malloc(sizeof(Double));
8321                 ig = IntGroupNew();
8322                 IntGroupAdd(ig, mol->cframe, 1);
8323                 *dp = mol->molprops[i].propvals[index];
8324                 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8325                 free(dp);
8326                 IntGroupRelease(ig);
8327         }
8328         
8329         return self;
8330 }
8331
8332 /*
8333  *  call-seq:
8334  *     reorder_frames(old_indices)
8335  *
8336  *  Reorder the frames. The argument is an array of integers that specify the 'old' 
8337  *  frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8338  *  same as the old frames 2/0/1, respectively.
8339  *  The argument must have the same number of integers as the number of frames.
8340  */
8341 static VALUE
8342 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8343 {
8344         Molecule *mol;
8345         Int *ip, *ip2, i, n, nframes;
8346     Data_Get_Struct(self, Molecule, mol);
8347         aval = rb_ary_to_ary(aval);
8348         nframes = MoleculeGetNumberOfFrames(mol);
8349         if (RARRAY_LEN(aval) != nframes)
8350                 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8351         ip2 = (Int *)calloc(sizeof(Int), nframes);
8352         ip = (Int *)calloc(sizeof(Int), nframes);
8353         for (i = 0; i < nframes; i++) {
8354                 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8355                 if (n < 0 || n >= nframes || ip2[n] != 0) {
8356                         free(ip2);
8357                         free(ip);
8358                         if (n < 0 || n >= nframes)
8359                                 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8360                         else
8361                                 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8362                 }
8363                 ip2[n] = 1;
8364                 ip[i] = n;
8365         }
8366         free(ip2);
8367         MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8368         free(ip);
8369         return self;
8370 }
8371
8372 #pragma mark ------ Fragments ------
8373
8374 /*
8375  *  call-seq:
8376  *     fragment(n1, *exatoms)  -> IntGroup
8377  *     fragment(group, *exatoms)  -> IntGroup
8378  *
8379  *  Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8380  *  those atoms will not be counted during the search.
8381  */
8382 static VALUE
8383 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8384 {
8385     Molecule *mol;
8386         IntGroup *baseg, *ig, *exatoms;
8387         int n;
8388         volatile VALUE nval, exval;
8389     Data_Get_Struct(self, Molecule, mol);
8390         rb_scan_args(argc, argv, "1*", &nval, &exval);
8391         if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8392                 baseg = NULL;
8393                 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8394         } else {
8395                 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8396         }
8397         if (RARRAY_LEN(exval) == 0) {
8398                 exatoms = NULL;
8399         } else {
8400                 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8401                 Data_Get_Struct(exval, IntGroup, exatoms);
8402         }
8403         if (baseg == NULL) {
8404                 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8405         } else {
8406                 IntGroupIterator iter;
8407                 IntGroupIteratorInit(baseg, &iter);
8408                 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8409                         ig = IntGroupNew();
8410                 } else {
8411                         ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8412                         if (ig != NULL) {
8413                                 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8414                                         IntGroup *subg;
8415                                         subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8416                                         if (subg != NULL) {
8417                                                 IntGroupAddIntGroup(ig, subg);
8418                                                 IntGroupRelease(subg);
8419                                         }
8420                                 }
8421                         }
8422                 }
8423                 IntGroupIteratorRelease(&iter);
8424                 IntGroupRelease(baseg);
8425         }
8426         if (ig == NULL)
8427                 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8428         nval = ValueFromIntGroup(ig);
8429         IntGroupRelease(ig);
8430         return nval;
8431 }
8432
8433 /*
8434  *  call-seq:
8435  *     fragments(exclude = nil)
8436  *
8437  *  Returns the fragments as an array of IntGroups. 
8438  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8439  *  in defining the fragment.
8440  */
8441 static VALUE
8442 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8443 {
8444     Molecule *mol;
8445         IntGroup *ag, *fg, *eg;
8446         VALUE gval, exval, retval;
8447     Data_Get_Struct(self, Molecule, mol);
8448         if (mol == NULL)
8449                 return Qnil;
8450         if (mol->natoms == 0)
8451                 return rb_ary_new();
8452         rb_scan_args(argc, argv, "01", &exval);
8453         if (exval == Qnil)
8454                 eg = NULL;
8455         else
8456                 eg = IntGroupFromValue(exval);
8457         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8458         if (eg != NULL)
8459                 IntGroupRemoveIntGroup(ag, eg);
8460         retval = rb_ary_new();
8461         while (IntGroupGetCount(ag) > 0) {
8462                 int n = IntGroupGetNthPoint(ag, 0);
8463                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8464                 if (fg == NULL)
8465                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8466                 gval = ValueFromIntGroup(fg);
8467                 rb_ary_push(retval, gval);
8468                 IntGroupRemoveIntGroup(ag, fg);
8469                 IntGroupRelease(fg);
8470         }
8471         IntGroupRelease(ag);
8472         if (eg != NULL)
8473                 IntGroupRelease(eg);
8474         return retval;
8475 }
8476
8477 /*
8478  *  call-seq:
8479  *     each_fragment(exclude = nil) {|group| ...}
8480  *
8481  *  Execute the block, with the IntGroup object for each fragment as the argument.
8482  *  Atoms or bonds should not be added or removed during the execution of the block.
8483  *  If exclude is given (as an array or an IntGroup), then those atoms are excluded
8484  *  in defining the fragment.
8485  */
8486 static VALUE
8487 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8488 {
8489     Molecule *mol;
8490         IntGroup *ag, *fg, *eg;
8491         VALUE gval, exval;
8492     Data_Get_Struct(self, Molecule, mol);
8493         if (mol == NULL || mol->natoms == 0)
8494                 return self;
8495         rb_scan_args(argc, argv, "01", &exval);
8496         if (exval == Qnil)
8497                 eg = NULL;
8498         else
8499                 eg = IntGroupFromValue(exval);
8500         ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8501         if (eg != NULL)
8502                 IntGroupRemoveIntGroup(ag, eg);
8503         while (IntGroupGetCount(ag) > 0) {
8504                 int n = IntGroupGetNthPoint(ag, 0);
8505                 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8506                 if (fg == NULL)
8507                         rb_raise(rb_eMolbyError, "internal error during each_fragment");
8508                 gval = ValueFromIntGroup(fg);
8509                 rb_yield(gval);
8510                 IntGroupRemoveIntGroup(ag, fg);
8511                 IntGroupRelease(fg);
8512         }
8513         IntGroupRelease(ag);
8514         if (eg != NULL)
8515                 IntGroupRelease(eg);
8516         return self;
8517 }
8518
8519 /*
8520  *  call-seq:
8521  *     detachable?(group)  -> [n1, n2]
8522  *
8523  *  Check whether the group is 'detachable', i.e. the group is bound to the rest 
8524  *  of the molecule via only one bond. If it is, then the indices of the atoms
8525  *  belonging to the bond is returned, the first element being the atom included
8526  *  in the fragment. Otherwise, Qnil is returned.
8527  */
8528 static VALUE
8529 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8530 {
8531         Molecule *mol;
8532         IntGroup *ig;
8533         int n1, n2;
8534         VALUE retval;
8535     Data_Get_Struct(self, Molecule, mol);
8536         ig = s_Molecule_AtomGroupFromValue(self, gval);
8537         if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8538                 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8539         } else retval = Qnil;
8540         IntGroupRelease(ig);
8541         return retval;
8542 }
8543
8544 /*
8545  *  call-seq:
8546  *     bonds_on_border(group = selection)  -> Array of Array of two Integers
8547  *
8548  *  Returns an array of bonds that connect an atom in the group and an atom out
8549  *  of the group. The first atom in the bond always belongs to the group. If no
8550  *  such bonds are present, an empty array is returned.
8551  */
8552 static VALUE
8553 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8554 {
8555         Molecule *mol;
8556         IntGroup *ig, *bg;
8557         VALUE gval, retval;
8558     Data_Get_Struct(self, Molecule, mol);
8559         rb_scan_args(argc, argv, "01", &gval);
8560         if (gval == Qnil) {
8561                 ig = MoleculeGetSelection(mol);
8562                 if (ig != NULL)
8563                         IntGroupRetain(ig);
8564         } else {
8565                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8566         }
8567         retval = rb_ary_new();
8568         if (ig == NULL)
8569                 return retval;
8570         bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8571         if (bg != NULL) {
8572                 IntGroupIterator iter;
8573                 Int i;
8574                 IntGroupIteratorInit(bg, &iter);
8575                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8576                         /*  The atoms at the border  */
8577                         Int n1, n2;
8578                         n1 = mol->bonds[i * 2];
8579                         n2 = mol->bonds[i * 2 + 1];
8580                         if (IntGroupLookupPoint(ig, n1) < 0) {
8581                                 int w = n1;
8582                                 n1 = n2;
8583                                 n2 = w;
8584                                 if (IntGroupLookupPoint(ig, n1) < 0)
8585                                         continue;  /*  Actually this is an internal error  */
8586                         }
8587                         rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8588                 }
8589                 IntGroupIteratorRelease(&iter);
8590         }
8591         IntGroupRelease(bg);
8592         IntGroupRelease(ig);
8593         return retval;
8594 }
8595
8596 /*  Calculate the transform that moves the current coordinates to the reference
8597  coordinates with least displacements.   */
8598 static Double
8599 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8600 {
8601         Atom *ap, *ap1;
8602         Int natoms, nn;
8603         Vector org1, org2;
8604         Int i, in, j, k;
8605         Double w, w1;
8606         Mat33 r, q, u;
8607         Double eigen_val[3];
8608         Vector eigen_vec[3];
8609         Vector s[3];
8610         IntGroupIterator iter;
8611
8612         natoms = mol->natoms;
8613         ap = mol->atoms;
8614         IntGroupIteratorInit(ig, &iter);
8615         
8616         /*  Calculate the weighted center  */
8617         VecZero(org1);
8618         VecZero(org2);
8619         w = 0.0;
8620         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8621                 ap1 = ATOM_AT_INDEX(ap, in);
8622                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8623                 VecScaleInc(org1, ap1->r, w1);
8624                 VecScaleInc(org2, ref[i], w1);
8625                 w += w1;
8626         }
8627         w = 1.0 / w;
8628         VecScaleSelf(org1, w);
8629         VecScaleSelf(org2, w);
8630
8631     /*  R = sum(weight[n]^2 * x[n] * t(y[n]));  */
8632     /*  Matrix to diagonalize = R * tR    */
8633         memset(r, 0, sizeof(Mat33));
8634         memset(q, 0, sizeof(Mat33));
8635         memset(u, 0, sizeof(Mat33));
8636         nn = 0;
8637         IntGroupIteratorReset(&iter);
8638         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8639                 Vector v1, v2;
8640                 ap1 = ATOM_AT_INDEX(ap, in);
8641                 w1 = (weights != NULL ? weights[i] : ap1->weight);
8642                 w1 *= w1;
8643                 VecSub(v1, ap1->r, org1);
8644                 VecSub(v2, ref[i], org2);
8645                 r[0] += w1 * v1.x * v2.x;
8646                 r[1] += w1 * v1.y * v2.x;
8647                 r[2] += w1 * v1.z * v2.x;
8648                 r[3] += w1 * v1.x * v2.y;
8649                 r[4] += w1 * v1.y * v2.y;
8650                 r[5] += w1 * v1.z * v2.y;
8651                 r[6] += w1 * v1.x * v2.z;
8652                 r[7] += w1 * v1.y * v2.z;
8653                 r[8] += w1 * v1.z * v2.z;
8654                 nn++;
8655         }
8656         for (i = 0; i < 9; i++)
8657                 r[i] /= (nn * nn);
8658         for (i = 0; i < 3; i++) {
8659                 for (j = 0; j < 3; j++) {
8660                         for (k = 0; k < 3; k++) {
8661                                 q[i+j*3] += r[i+k*3] * r[j+k*3];
8662                         }
8663                 }
8664         }
8665         
8666         if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8667                 IntGroupIteratorRelease(&iter);
8668                 return -1.0;  /*  Cannot determine the eigenvector  */
8669         }
8670
8671     /*  s[i] = tR * v[i] / sqrt(eigenval[i])  */
8672     /*  U = s0*t(v0) + s1*t(v1) + s2*t(v2)  */
8673         MatrixTranspose(r, r);
8674         for (i = 0; i < 3; i++) {
8675                 MatrixVec(&s[i], r, &eigen_vec[i]);
8676                 w1 = 1.0 / sqrt(eigen_val[i]);
8677                 VecScaleSelf(s[i], w1);
8678         }
8679         for (k = 0; k < 3; k++) {
8680                 u[0] += s[k].x * eigen_vec[k].x;
8681                 u[1] += s[k].y * eigen_vec[k].x;
8682                 u[2] += s[k].z * eigen_vec[k].x;
8683                 u[3] += s[k].x * eigen_vec[k].y;
8684                 u[4] += s[k].y * eigen_vec[k].y;
8685                 u[5] += s[k].z * eigen_vec[k].y;
8686                 u[6] += s[k].x * eigen_vec[k].z;
8687                 u[7] += s[k].y * eigen_vec[k].z;
8688                 u[8] += s[k].z * eigen_vec[k].z;
8689         }
8690         
8691         /*  y = U*(x - org1) + org2 = U*x + (org2 - U*org1)  */
8692         MatrixVec(&org1, u, &org1);
8693         VecDec(org2, org1);
8694         for (i = 0; i < 9; i++)
8695                 trans[i] = u[i];
8696         trans[9] = org2.x;
8697         trans[10] = org2.y;
8698         trans[11] = org2.z;
8699         
8700         /*  Calculate rmsd  */
8701         IntGroupIteratorReset(&iter);
8702         w = 0.0;
8703         for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8704                 Vector tv;
8705                 ap1 = ATOM_AT_INDEX(ap, in);
8706                 TransformVec(&tv, trans, &ap1->r);
8707                 VecDec(tv, ref[i]);
8708                 w += VecLength2(tv);
8709         }
8710         w = sqrt(w / nn);
8711         IntGroupIteratorRelease(&iter);
8712         return w;
8713 }
8714
8715 /*
8716  *  call-seq:
8717  *     fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8718  *
8719  *  Calculate the transform to fit the given group to the set of reference coordinates.
8720  *  The reference coordinates ref is given as either a frame number, an array of
8721  *  Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8722  *  of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8723  *  Return values are the transform (that converts the present coordinates to the
8724  *  target coordinates) and root mean square deviation (without weight).
8725  */
8726 static VALUE
8727 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8728 {
8729         Molecule *mol;
8730         Atom *ap;
8731         VALUE gval, rval, wval;
8732         IntGroup *ig;
8733         IntGroupIterator iter;
8734         int nn, errnum, i, j, in, status;
8735         Vector *ref;
8736         Double *weights, dval[3];
8737         Transform tr;
8738
8739         Data_Get_Struct(self, Molecule, mol);
8740         rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8741         if (gval == Qnil)
8742                 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8743         else
8744                 ig = s_Molecule_AtomGroupFromValue(self, gval);
8745         if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8746                 IntGroupRelease(ig);
8747                 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8748         }
8749         ref = (Vector *)calloc(sizeof(Vector), nn);
8750         weights = (Double *)calloc(sizeof(Double), nn);
8751         IntGroupIteratorInit(ig, &iter);
8752         if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8753                 int fn = NUM2INT(rb_Integer(rval));
8754                 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8755                         errnum = 1;
8756                         status = fn;
8757                         goto err;
8758                 }
8759                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8760                         ap = ATOM_AT_INDEX(mol->atoms, in);
8761                         if (fn < ap->nframes)
8762                                 ref[i] = ap->frames[fn];
8763                         else ref[i] = ap->r;
8764                 }
8765         } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8766                 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8767                 if (m->row * m->column < nn * 3) {
8768                         errnum = 2;
8769                         goto err;
8770                 }
8771                 for (i = 0; i < nn; i++) {
8772                         ref[i].x = m->data[i * 3];
8773                         ref[i].y = m->data[i * 3 + 1];
8774                         ref[i].z = m->data[i * 3 + 2];
8775                 }
8776         } else {
8777                 VALUE aval;
8778                 rval = rb_protect(rb_ary_to_ary, rval, &status);
8779                 if (status != 0) {
8780                         errnum = 3;
8781                         goto err;
8782                 }
8783                 if (RARRAY_LEN(rval) < nn) {
8784                         errnum = 2;
8785                         goto err;
8786                 }
8787                 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8788                         /*  Array of 3*nn numbers  */
8789                         if (RARRAY_LEN(rval) < nn * 3) {
8790                                 errnum = 2;
8791                                 goto err;
8792                         }
8793                         for (i = 0; i < nn; i++) {
8794                                 for (j = 0; j < 3; j++) {
8795                                         aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8796                                         if (status != 0) {
8797                                                 errnum = 3;
8798                                                 goto err;
8799                                         }
8800                                         dval[j] = NUM2DBL(aval);
8801                                 }
8802                                 ref[i].x = dval[0];
8803                                 ref[i].y = dval[1];
8804                                 ref[i].z = dval[2];
8805                         }
8806                 } else {
8807                         /*  Array of nn Vector3Ds or Arrays  */
8808                         for (i = 0; i < nn; i++) {
8809                                 aval = (RARRAY_PTR(rval))[i];
8810                                 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8811                                         VectorFromValue(aval, &ref[i]);
8812                                 } else {
8813                                         aval = rb_protect(rb_ary_to_ary, aval, &status);
8814                                         if (status != 0) {
8815                                                 errnum = 3;
8816                                                 goto err;
8817                                         }
8818                                         if (RARRAY_LEN(aval) < 3) {
8819                                                 errnum = 4;
8820                                                 status = i;
8821                                                 goto err;
8822                                         }
8823                                         for (j = 0; j < 3; j++) {
8824                                                 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8825                                                 if (status != 0) {
8826                                                         errnum = 3;
8827                                                         goto err;
8828                                                 }
8829                                                 dval[j] = NUM2DBL(aaval);
8830                                         }
8831                                         ref[i].x = dval[0];
8832                                         ref[i].y = dval[1];
8833                                         ref[i].z = dval[2];
8834                                 }
8835                         }
8836                 }
8837         }
8838         if (wval == Qnil) {
8839                 /*  Use atomic weights  */
8840                 IntGroupIteratorReset(&iter);
8841                 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8842                         ap = ATOM_AT_INDEX(mol->atoms, in);
8843                         weights[i] = ap->weight;
8844                 }
8845         } else {
8846                 wval = rb_protect(rb_ary_to_ary, wval, &status);
8847                 if (status != 0) {
8848                         errnum = 3;
8849                         goto err;
8850                 }
8851                 if (RARRAY_LEN(wval) < nn) {
8852                         errnum = 5;
8853                         goto err;
8854                 }
8855                 for (i = 0; i < nn; i++) {
8856                         VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8857                         if (status != 0) {
8858                                 errnum = 3;
8859                                 goto err;
8860                         }
8861                         weights[i] = NUM2DBL(wwval);
8862                 }
8863         }
8864         dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8865         if (dval[0] < 0) {
8866                 errnum = 6;
8867                 goto err;
8868         }
8869         errnum = 0;
8870 err:
8871         IntGroupIteratorRelease(&iter);
8872         free(ref);
8873         free(weights);
8874         if (errnum == 0) {
8875                 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8876         } else if (errnum == 1) {
8877                 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8878         } else if (errnum == 2) {
8879                 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8880         } else if (errnum == 3) {
8881                 rb_jump_tag(status);
8882         } else if (errnum == 4) {
8883                 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8884         } else if (errnum == 5) {
8885                 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8886         } else if (errnum == 6) {
8887                 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8888         }
8889         return Qnil;  /*  Not reached  */
8890 }
8891
8892 #pragma mark ------ Screen Display ------
8893
8894 /*
8895  *  call-seq:
8896  *     display
8897  *
8898  *  Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8899  */
8900 static VALUE
8901 s_Molecule_Display(VALUE self)
8902 {
8903     Molecule *mol;
8904     Data_Get_Struct(self, Molecule, mol);
8905         if (mol->mview != NULL)
8906                 MainViewCallback_display(mol->mview);
8907         return Qnil;
8908 }
8909
8910 /*
8911  *  call-seq:
8912  *     make_front
8913  *
8914  *  Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8915  */
8916 static VALUE
8917 s_Molecule_MakeFront(VALUE self)
8918 {
8919     Molecule *mol;
8920     Data_Get_Struct(self, Molecule, mol);
8921         if (mol->mview != NULL)
8922                 MainViewCallback_makeFront(mol->mview);
8923         return Qnil;
8924 }
8925
8926 /*
8927  *  call-seq:
8928  *     update_enabled? -> bool
8929  *
8930  *  Returns true if screen update is enabled; otherwise no.
8931  */
8932 static VALUE
8933 s_Molecule_UpdateEnabled(VALUE self)
8934 {
8935     Molecule *mol;
8936     Data_Get_Struct(self, Molecule, mol);
8937         if (mol->mview != NULL && !mol->mview->freezeScreen)
8938                 return Qtrue;
8939         else return Qfalse;
8940 }
8941
8942 /*
8943  *  call-seq:
8944  *     update_enabled = bool
8945  *
8946  *  Enable or disable screen update. This is effective for automatic update on modification.
8947  *  Explicit call to molecule.display() always updates the screen.
8948  */
8949 static VALUE
8950 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8951 {
8952     Molecule *mol;
8953     Data_Get_Struct(self, Molecule, mol);
8954         val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8955         if (mol->mview != NULL)
8956                 mol->mview->freezeScreen = (val == Qfalse);
8957         else val = Qfalse;
8958         return val;
8959 }
8960
8961 /*
8962  *  call-seq:
8963  *     show_unitcell
8964  *     show_unitcell(bool)
8965  *     show_unitcell = bool
8966  *
8967  *  Set the flag whether to show the unit cell. If no argument is given, the
8968  *  current flag is returned.
8969  */
8970 static VALUE
8971 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8972 {
8973     Molecule *mol;
8974     Data_Get_Struct(self, Molecule, mol);
8975         if (mol->mview == NULL)
8976                 return Qnil;
8977         if (argc > 0) {
8978                 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8979                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8980         }
8981         return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8982 }
8983
8984 /*
8985  *  call-seq:
8986  *     show_hydrogens
8987  *     show_hydrogens(bool)
8988  *     show_hydrogens = bool
8989  *
8990  *  Set the flag whether to show the hydrogen atoms. If no argument is given, the
8991  *  current flag is returned.
8992  */
8993 static VALUE
8994 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8995 {
8996     Molecule *mol;
8997     Data_Get_Struct(self, Molecule, mol);
8998         if (mol->mview == NULL)
8999                 return Qnil;
9000         if (argc > 0) {
9001                 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
9002                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9003         }
9004         return (mol->mview->showHydrogens ? Qtrue : Qfalse);
9005 }
9006
9007 /*
9008  *  call-seq:
9009  *     show_dummy_atoms
9010  *     show_dummy_atoms(bool)
9011  *     show_dummy_atoms = bool
9012  *
9013  *  Set the flag whether to show the dummy atoms. If no argument is given, the
9014  *  current flag is returned.
9015  */
9016 static VALUE
9017 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
9018 {
9019     Molecule *mol;
9020     Data_Get_Struct(self, Molecule, mol);
9021         if (mol->mview == NULL)
9022                 return Qnil;
9023         if (argc > 0) {
9024                 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9025                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9026         }
9027         return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9028 }
9029
9030 /*
9031  *  call-seq:
9032  *     show_expanded
9033  *     show_expanded(bool)
9034  *     show_expanded = bool
9035  *
9036  *  Set the flag whether to show the expanded atoms. If no argument is given, the
9037  *  current flag is returned.
9038  */
9039 static VALUE
9040 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9041 {
9042     Molecule *mol;
9043     Data_Get_Struct(self, Molecule, mol);
9044         if (mol->mview == NULL)
9045                 return Qnil;
9046         if (argc > 0) {
9047                 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9048                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9049         }
9050         return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9051 }
9052
9053 /*
9054  *  call-seq:
9055  *     show_ellipsoids
9056  *     show_ellipsoids(bool)
9057  *     show_ellipsoids = bool
9058  *
9059  *  Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9060  *  current flag is returned.
9061  */
9062 static VALUE
9063 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9064 {
9065     Molecule *mol;
9066     Data_Get_Struct(self, Molecule, mol);
9067         if (mol->mview == NULL)
9068                 return Qnil;
9069         if (argc > 0) {
9070                 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9071                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9072         }
9073         return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9074 }
9075
9076 /*
9077  *  call-seq:
9078  *     is_atom_visible(index)  -> Boolean
9079  *
9080  *  Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9081  *  as well as the molecule attributes (showHydrogens, etc.)
9082  */
9083 static VALUE
9084 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9085 {
9086         Molecule *mol;
9087         Int idx;
9088         Atom *ap;
9089     Data_Get_Struct(self, Molecule, mol);
9090         idx = s_Molecule_AtomIndexFromValue(mol, ival);
9091         if (idx < 0 || idx >= mol->natoms)
9092                 return Qnil;
9093         ap = ATOM_AT_INDEX(mol->atoms, idx);
9094         if (mol->mview != NULL) {
9095                 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9096                         return Qfalse;
9097                 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9098                         return Qfalse;
9099                 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9100                         return Qfalse;
9101         }
9102         return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9103 }
9104
9105 /*
9106  *  call-seq:
9107  *     hidden_atoms       -> IntGroup
9108  *
9109  *  Returns the currently hidden atoms.
9110  */
9111 static VALUE
9112 s_Molecule_HiddenAtoms(VALUE self)
9113 {
9114         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9115         return Qnil;  /*  Not reached  */
9116 }
9117
9118 /*
9119  *  call-seq:
9120  *     set_hidden_atoms(IntGroup)
9121  *     self.hidden_atoms = IntGroup
9122  *
9123  *  Hide the specified atoms. This operation is _not_ undoable.
9124  */
9125 static VALUE
9126 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9127 {
9128         rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9129         return Qnil;  /*  Not reached  */
9130 }
9131
9132 /*
9133  *  call-seq:
9134  *     show_graphite -> Integer
9135  *     show_graphite = Integer
9136  *     show_graphite = boolean
9137  *
9138  *  Set whether to show the graphite plane. If the argument is positive, it also indicates the
9139  *  number of rings to display for each direction.
9140  *  If the argument is boolean, only the show/hide flag is set.
9141  */
9142 static VALUE
9143 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9144 {
9145     Molecule *mol;
9146     Data_Get_Struct(self, Molecule, mol);
9147         if (mol->mview == NULL)
9148                 return Qnil;
9149         if (argc > 0) {
9150                 if (argv[0] == Qnil || argv[0] == Qfalse)
9151                         mol->mview->showGraphiteFlag = 0;
9152                 else if (argv[0] == Qtrue)
9153                         mol->mview->showGraphiteFlag = 1;
9154                 else {
9155                         int n = NUM2INT(rb_Integer(argv[0]));
9156                         if (n < 0)
9157                                 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9158                         mol->mview->showGraphite = n;
9159                 }
9160                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9161         }
9162         return INT2NUM(mol->mview->showGraphite);
9163 }
9164
9165 /*
9166  *  call-seq:
9167  *     show_graphite? -> boolean
9168  *
9169  *  Return whether the graphite is set visible or not.
9170 */
9171 static VALUE
9172 s_Molecule_ShowGraphiteFlag(VALUE self)
9173 {
9174     Molecule *mol;
9175     Data_Get_Struct(self, Molecule, mol);
9176         if (mol->mview == NULL)
9177                 return Qnil;
9178         return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9179 }
9180         
9181 /*
9182  *  call-seq:
9183  *     show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9184  *     show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9185  *     show_periodic_image = boolean
9186  *
9187  *  Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9188  *  set but no visual effects are observed.
9189  *  If the argument is boolean, only the show/hide flag is modified.
9190  */
9191 static VALUE
9192 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9193 {
9194     Molecule *mol;
9195         VALUE val;
9196         int ival[6];
9197         int i;
9198     Data_Get_Struct(self, Molecule, mol);
9199         if (mol->mview == NULL)
9200                 return Qnil;
9201         rb_scan_args(argc, argv, "01", &val);
9202         if (argc > 0) {
9203                 /*  Change current settings  */
9204                 if (val == Qnil || val == Qfalse)
9205                         mol->mview->showPeriodicImageFlag = 0;
9206                 else if (val == Qtrue)
9207                         mol->mview->showPeriodicImageFlag = 1;
9208                 else {
9209                         val = rb_ary_to_ary(val);
9210                         for (i = 0; i < 6; i++) {
9211                                 if (i < RARRAY_LEN(val))
9212                                         ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9213                         }
9214                         if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9215                                 rb_raise(rb_eMolbyError, "bad arguments");
9216                         for (i = 0; i < 6; i++)
9217                                 mol->mview->showPeriodicImage[i] = ival[i];
9218                 }
9219                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9220         }
9221         val = rb_ary_new();
9222         for (i = 0; i < 6; i++)
9223                 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9224         return val;
9225 }
9226
9227 /*
9228  *  call-seq:
9229  *     show_periodic_image? -> boolean
9230  *
9231  *  Return whether the periodic images are set to visible or not. This flag is
9232  *  independent from the show_periodic_image settings.
9233  */
9234 static VALUE
9235 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9236 {
9237     Molecule *mol;
9238     Data_Get_Struct(self, Molecule, mol);
9239         if (mol->mview == NULL)
9240                 return Qnil;
9241         return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9242 }
9243
9244 /*
9245  *  call-seq:
9246  *     show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9247  *     show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9248  *     show_rotation_center = boolean
9249  *
9250  *  Set to show the rotation center of the screen.
9251  *  If the argument is boolean, only the show/hide flag is modified.
9252  */
9253 static VALUE
9254 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9255 {
9256     Molecule *mol;
9257     Data_Get_Struct(self, Molecule, mol);
9258         if (mol->mview == NULL)
9259                 return Qnil;
9260         if (argc > 0) {
9261                 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9262                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9263         }
9264         return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9265 }
9266
9267 /*
9268  *  call-seq:
9269  *     line_mode
9270  *     line_mode(bool)
9271  *     line_mode = bool
9272  *
9273  *  Set the flag whether to draw the model in line mode. If no argument is given, the
9274  *  current flag is returned.
9275  */
9276 static VALUE
9277 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9278 {
9279     Molecule *mol;
9280     Data_Get_Struct(self, Molecule, mol);
9281         if (mol->mview == NULL)
9282                 return Qnil;
9283         if (argc > 0) {
9284                 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9285                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9286         }
9287         return (mol->mview->lineMode ? Qtrue : Qfalse);
9288 }
9289
9290 /*
9291  *  call-seq:
9292  *     atom_radius = float
9293  *     atom_radius
9294  *
9295  *  Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9296  *  (Default = 0.4)
9297  *  If no argument is given, the current value is returned.
9298  */
9299 static VALUE
9300 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9301 {
9302     Molecule *mol;
9303     Data_Get_Struct(self, Molecule, mol);
9304         if (mol->mview == NULL)
9305                 return Qnil;
9306         if (argc > 0) {
9307                 double rad = NUM2DBL(rb_Float(argv[0]));
9308                 if (rad > 0.0) {
9309                         mol->mview->atomRadius = rad;
9310                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9311                 }
9312                 return argv[0];
9313         }
9314         return rb_float_new(mol->mview->atomRadius);
9315 }
9316
9317 /*
9318  *  call-seq:
9319  *     bond_radius = float
9320  *     bond_radius
9321  *
9322  *  Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9323  *  If no argument is given, the current value is returned.
9324  */
9325 static VALUE
9326 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9327 {
9328     Molecule *mol;
9329     Data_Get_Struct(self, Molecule, mol);
9330         if (mol->mview == NULL)
9331                 return Qnil;
9332         if (argc > 0) {
9333                 double rad = NUM2DBL(rb_Float(argv[0]));
9334                 if (rad > 0.0) {
9335                         mol->mview->bondRadius = rad;
9336                         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9337                 }
9338                 return argv[0];
9339         }
9340         return rb_float_new(mol->mview->bondRadius);
9341 }
9342
9343 /*
9344  *  call-seq:
9345  *     atom_resolution = integer
9346  *     atom_resolution
9347  *
9348  *  Set the atom resolution used in drawing the model in normal (non-line) mode.
9349  *  (Default = 12; minimum = 6)
9350  *  If no argument is given, the current value is returned.
9351  */
9352 static VALUE
9353 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9354 {
9355     Molecule *mol;
9356     Data_Get_Struct(self, Molecule, mol);
9357         if (mol->mview == NULL)
9358                 return Qnil;
9359         if (argc > 0) {
9360                 int res = NUM2INT(rb_Integer(argv[0]));
9361                 if (res < 6)
9362                         rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9363                 mol->mview->atomResolution = res;
9364                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9365                 return INT2NUM(res);
9366         }
9367         return INT2NUM(mol->mview->atomResolution);
9368 }
9369
9370 /*
9371  *  call-seq:
9372  *     bond_resolution = integer
9373  *     bond_resolution
9374  *
9375  *  Set the bond resolution used in drawing the model in normal (non-line) mode.
9376  *  (Default = 8; minimum = 4)
9377  *  If no argument is given, the current value is returned.
9378  */
9379 static VALUE
9380 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9381 {
9382     Molecule *mol;
9383     Data_Get_Struct(self, Molecule, mol);
9384         if (mol->mview == NULL)
9385                 return Qnil;
9386         if (argc > 0) {
9387                 int res = NUM2INT(rb_Integer(argv[0]));
9388                 if (res < 4)
9389                         rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9390                 mol->mview->bondResolution = res;
9391                 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9392                 return INT2NUM(res);
9393         }
9394         return INT2NUM(mol->mview->bondResolution);
9395 }
9396
9397 /*
9398  *  call-seq:
9399  *     resize_to_fit
9400  *
9401  *  Resize the model drawing to fit in the window.
9402  */
9403 static VALUE
9404 s_Molecule_ResizeToFit(VALUE self)
9405 {
9406     Molecule *mol;
9407     Data_Get_Struct(self, Molecule, mol);
9408         if (mol->mview != NULL)
9409                 MainView_resizeToFit(mol->mview);
9410         return self;    
9411 }
9412
9413 /*
9414  *  call-seq:
9415  *     get_view_rotation -> [[ax, ay, az], angle]
9416  *
9417  *  Get the current rotation for the view. Angle is in degree, not radian.
9418  */
9419 static VALUE
9420 s_Molecule_GetViewRotation(VALUE self)
9421 {
9422     Molecule *mol;
9423         double f[4];
9424         Vector v;
9425     Data_Get_Struct(self, Molecule, mol);
9426         if (mol->mview == NULL)
9427                 return Qnil;
9428         TrackballGetRotate(mol->mview->track, f);
9429         f[0] = -f[0];  /*  Convert to left-handed screw (to be consistent with Transform)  */
9430         v.x = f[1];
9431         v.y = f[2];
9432         v.z = f[3];
9433         return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9434 }
9435
9436 /*
9437  *  call-seq:
9438  *     get_view_scale -> float
9439  *
9440  *  Get the current scale for the view.
9441  */
9442 static VALUE
9443 s_Molecule_GetViewScale(VALUE self)
9444 {
9445     Molecule *mol;
9446     Data_Get_Struct(self, Molecule, mol);
9447         if (mol->mview == NULL)
9448                 return Qnil;
9449         return rb_float_new(TrackballGetScale(mol->mview->track));
9450 }
9451
9452 /*
9453  *  call-seq:
9454  *     get_view_center -> Vector
9455  *
9456  *  Get the current center point of the view.
9457  */
9458 static VALUE
9459 s_Molecule_GetViewCenter(VALUE self)
9460 {
9461     Molecule *mol;
9462         double f[4];
9463         Vector v;
9464     Data_Get_Struct(self, Molecule, mol);
9465         if (mol->mview == NULL)
9466                 return Qnil;
9467         TrackballGetTranslate(mol->mview->track, f);
9468         v.x = -f[0] * mol->mview->dimension;
9469         v.y = -f[1] * mol->mview->dimension;
9470         v.z = -f[2] * mol->mview->dimension;
9471         return ValueFromVector(&v);
9472 }
9473
9474 /*
9475  *  call-seq:
9476  *     set_view_rotation([ax, ay, az], angle) -> self
9477  *
9478  *  Set the current rotation for the view. Angle is in degree, not radian.
9479  */
9480 static VALUE
9481 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9482 {
9483     Molecule *mol;
9484         double f[4];
9485         Vector v;
9486     Data_Get_Struct(self, Molecule, mol);
9487         if (mol->mview == NULL)
9488                 return Qnil;
9489         VectorFromValue(aval, &v);
9490         if (NormalizeVec(&v, &v))
9491                 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9492         f[1] = v.x;
9493         f[2] = v.y;
9494         f[3] = v.z;
9495         f[0] = -NUM2DBL(rb_Float(angval));
9496         TrackballSetRotate(mol->mview->track, f);
9497         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9498         return self;
9499 }
9500
9501 /*
9502  *  call-seq:
9503  *     set_view_scale(scale) -> self
9504  *
9505  *  Set the current scale for the view.
9506  */
9507 static VALUE
9508 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9509 {
9510     Molecule *mol;
9511     Data_Get_Struct(self, Molecule, mol);
9512         if (mol->mview == NULL)
9513                 return Qnil;
9514         TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9515         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9516         return self;
9517 }
9518
9519 /*
9520  *  call-seq:
9521  *     set_view_center(vec) -> self
9522  *
9523  *  Set the current center point of the view.
9524  */
9525 static VALUE
9526 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9527 {
9528     Molecule *mol;
9529         Vector v;
9530         double f[4];
9531     Data_Get_Struct(self, Molecule, mol);
9532         if (mol->mview == NULL)
9533                 return Qnil;
9534         VectorFromValue(aval, &v);
9535         f[0] = -v.x / mol->mview->dimension;
9536         f[1] = -v.y / mol->mview->dimension;
9537         f[2] = -v.z / mol->mview->dimension;
9538         TrackballSetTranslate(mol->mview->track, f);
9539         MainViewCallback_setNeedsDisplay(mol->mview, 1);
9540         return self;
9541 }
9542
9543 /*
9544  *  call-seq:
9545  *     set_background_color(red, green, blue)
9546  *
9547  *  Set the background color of the model window.
9548  */
9549 static VALUE
9550 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9551 {
9552     Molecule *mol;
9553     Data_Get_Struct(self, Molecule, mol);
9554         if (mol->mview != NULL) {
9555                 VALUE rval, gval, bval;
9556                 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9557                 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9558         }
9559         return self;    
9560 }
9561
9562 /*
9563  *  call-seq:
9564  *     export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9565  *
9566  *  Export the current graphic to a PNG or TIF file (determined by the extension).
9567  *  bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9568  *  If either width or height is not specified, then the screen width/height is used instead.
9569  */
9570 static VALUE
9571 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9572 {
9573         Molecule *mol;
9574         VALUE fval, sval, bval, wval, hval;
9575         char *fname;
9576         float scale;
9577         int bg_color, width, height;
9578     Data_Get_Struct(self, Molecule, mol);
9579         if (mol->mview == NULL)
9580                 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9581         rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9582         fname = FileStringValuePtr(fval);
9583         if (sval == Qnil)
9584                 scale = 1.0;
9585         else scale = NUM2DBL(rb_Float(sval));
9586         if (bval == Qnil)
9587                 bg_color = -1;
9588         else bg_color = NUM2INT(rb_Integer(bval));
9589         if (wval == Qnil)
9590                 width = 0;
9591         else width = NUM2INT(rb_Integer(wval));
9592         if (hval == Qnil)
9593                 height = 0;
9594         else height = NUM2INT(rb_Integer(hval));
9595         if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9596                 return fval;
9597         else return Qnil;
9598 }
9599
9600 #pragma mark ------ Graphics ------
9601
9602 static void
9603 s_CalculateGraphicNormals(MainViewGraphic *gp)
9604 {
9605         int i;
9606         Vector v1, v2, v3;
9607         if (gp == NULL || gp->npoints < 3)
9608                 return;
9609         AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9610         v1.x = gp->points[3] - gp->points[0];
9611         v1.y = gp->points[4] - gp->points[1];
9612         v1.z = gp->points[5] - gp->points[2];
9613         /*  nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1)  */
9614         for (i = 2; i < gp->npoints; i++) {
9615                 v2.x = gp->points[i * 3] - gp->points[0];
9616                 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9617                 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9618                 VecCross(v3, v1, v2);
9619                 NormalizeVec(&v3, &v3);
9620                 gp->normals[i * 3] = v3.x;
9621                 gp->normals[i * 3 + 1] = v3.y;
9622                 gp->normals[i * 3 + 2] = v3.z;
9623                 v1 = v2;
9624         }
9625         /*  normals[0] = average of all nv[i] (i=2..n-1)  */
9626         VecZero(v1);
9627         for (i = 2; i < gp->npoints; i++) {
9628                 v1.x += gp->normals[i * 3];
9629                 v1.y += gp->normals[i * 3 + 1];
9630                 v1.z += gp->normals[i * 3 + 2];
9631         }
9632         NormalizeVec(&v1, &v1);
9633         gp->normals[0] = v1.x;
9634         gp->normals[1] = v1.y;
9635         gp->normals[2] = v1.z;
9636         /*  normals[1] = nv[2].normalize  */
9637         v2.x = gp->normals[6];
9638         v2.y = gp->normals[7];
9639         v2.z = gp->normals[8];
9640         NormalizeVec(&v1, &v2);
9641         gp->normals[3] = v1.x;
9642         gp->normals[4] = v1.y;
9643         gp->normals[5] = v1.z;
9644         /*  normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2)  */
9645         for (i = 2; i < gp->npoints; i++) {
9646                 if (i == gp->npoints - 1)
9647                         VecZero(v3);
9648                 else {
9649                         v3.x = gp->normals[i * 3 + 3];
9650                         v3.y = gp->normals[i * 3 + 4];
9651                         v3.z = gp->normals[i * 3 + 5];
9652                 }
9653                 VecInc(v2, v3);
9654                 NormalizeVec(&v1, &v2);
9655                 gp->normals[i * 3] = v1.x;
9656                 gp->normals[i * 3 + 1] = v1.y;
9657                 gp->normals[i * 3 + 2] = v1.z;
9658                 v2 = v3;
9659         }
9660 }
9661
9662 /*
9663  *  call-seq:
9664  *     insert_graphic(index, kind, color, points, fill = nil) -> integer
9665  *
9666  *  Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9667  *   kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9668  *   color: an array of 3 (rgb) or 4 (rgba) floating numbers
9669  *   points: an array of Vectors
9670  *   
9671  */
9672 static VALUE
9673 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9674 {
9675     Molecule *mol;
9676         MainViewGraphic g;
9677         int i, n, ni, idx;
9678         const char *p;
9679         VALUE kval, cval, pval, fval, ival;
9680     Data_Get_Struct(self, Molecule, mol);
9681         if (mol->mview == NULL)
9682                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9683         rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9684         idx = NUM2INT(rb_Integer(ival));
9685         if (idx == -1)
9686                 idx = mol->mview->ngraphics;
9687         else if (idx < 0 || idx > mol->mview->ngraphics)
9688                 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9689         memset(&g, 0, sizeof(g));
9690         g.visible = 1;
9691         if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9692                 g.kind = NUM2INT(kval);  /*  Direct assign (for undo registration)  */
9693         } else {
9694                 kval = rb_obj_as_string(kval);
9695                 p = StringValuePtr(kval);
9696                 if (strcmp(p, "line") == 0)
9697                         g.kind = kMainViewGraphicLine;
9698                 else if (strcmp(p, "poly") == 0)
9699                         g.kind = kMainViewGraphicPoly;
9700                 else if (strcmp(p, "cylinder") == 0)
9701                         g.kind = kMainViewGraphicCylinder;
9702                 else if (strcmp(p, "cone") == 0)
9703                         g.kind = kMainViewGraphicCone;
9704                 else if (strcmp(p, "ellipsoid") == 0)
9705                         g.kind = kMainViewGraphicEllipsoid;
9706                 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9707         }
9708         g.closed = (RTEST(fval) ? 1 : 0);
9709         cval = rb_ary_to_ary(cval);
9710         n = RARRAY_LEN(cval);
9711         if (n < 3 || n >= 5)
9712                 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9713         if (n == 3)
9714                 g.rgba[3] = 1.0;
9715         for (i = 0; i < n; i++)
9716                 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9717         pval = rb_ary_to_ary(pval);
9718         n = RARRAY_LEN(pval);
9719         ni = -1;  /*  If this is non-negative, then ni-th control point is [number, 0, 0] */
9720         if (n <= 0)
9721                 rb_raise(rb_eArgError, "no control points are given");
9722         switch (g.kind) {
9723                 case kMainViewGraphicLine:
9724                         if (n < 2)
9725                                 rb_raise(rb_eArgError, "the line object must have at least two control points");
9726                         break;
9727                 case kMainViewGraphicPoly:
9728                         if (n < 3)
9729                                 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9730                         break;
9731                 case kMainViewGraphicCylinder:
9732                 case kMainViewGraphicCone:
9733                         if (n != 3)
9734                                 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9735                         ni = 2;
9736                         break;
9737                 case kMainViewGraphicEllipsoid:
9738                         if (n == 2) {
9739                                 ni = 1;
9740                         } else if (n != 4)
9741                                 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9742                         break;
9743         }
9744         NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9745         for (i = 0; i < n; i++) {
9746                 Vector v;
9747                 VALUE rval = RARRAY_PTR(pval)[i];
9748                 if (i == ni) {
9749                         if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9750                                 /*  The float argument can also be given as a vector (for simplify undo registration)  */
9751                                 VectorFromValue(rval, &v);
9752                         } else {
9753                                 v.x = NUM2DBL(rb_Float(rval));
9754                         }
9755                         v.y = v.z = 0;
9756                 } else {
9757                         VectorFromValue(rval, &v);
9758                 }
9759                 g.points[i * 3] = v.x;
9760                 g.points[i * 3 + 1] = v.y;
9761                 g.points[i * 3 + 2] = v.z;
9762         }
9763         if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9764                 /*  Sphere  */
9765                 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9766                 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9767                 g.points[7] = g.points[11] = g.points[3];
9768         }
9769         if (g.kind == kMainViewGraphicPoly) {
9770                 /*  Calculate normals  */
9771                 s_CalculateGraphicNormals(&g);
9772         }
9773         MainView_insertGraphic(mol->mview, idx, &g);
9774         
9775         {
9776                 /*  Register undo  */
9777                 MolAction *act;
9778                 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9779                 MolActionCallback_registerUndo(mol, act);
9780                 MolActionRelease(act);
9781         }
9782
9783         return INT2NUM(idx);    
9784 }
9785
9786 /*
9787  *  call-seq:
9788  *     create_graphic(kind, color, points, fill = nil) -> integer
9789  *
9790  *  Create a new graphic object. The arguments are similar as insert_graphic.
9791  */
9792 static VALUE
9793 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9794 {
9795         VALUE args[5];
9796         rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9797         args[0] = INT2NUM(-1);
9798         return s_Molecule_InsertGraphic(argc + 1, args, self);
9799 }
9800
9801 /*
9802  *  call-seq:
9803  *     remove_graphic(index) -> integer
9804  *
9805  *  Remove a graphic object.
9806  */
9807 static VALUE
9808 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9809 {
9810     Molecule *mol;
9811         int i;
9812     Data_Get_Struct(self, Molecule, mol);
9813         if (mol->mview == NULL)
9814                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9815         i = NUM2INT(rb_Integer(ival));
9816         if (i < 0 || i >= mol->mview->ngraphics)
9817                 rb_raise(rb_eArgError, "graphic index is out of range");
9818         {
9819                 /*  Prepare data for undo  */
9820                 MainViewGraphic *gp;
9821                 Vector *vp;
9822                 MolAction *act;
9823                 double col[4];
9824                 int n;
9825                 gp = mol->mview->graphics + i;
9826                 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9827                 for (n = 0; n < gp->npoints; n++) {
9828                         vp[n].x = gp->points[n * 3];
9829                         vp[n].y = gp->points[n * 3 + 1];
9830                         vp[n].z = gp->points[n * 3 + 2];
9831                 }
9832                 col[0] = gp->rgba[0];
9833                 col[1] = gp->rgba[1];
9834                 col[2] = gp->rgba[2];
9835                 col[3] = gp->rgba[3];
9836                 if (gp->visible == 0) {
9837                         act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9838                         MolActionCallback_registerUndo(mol, act);
9839                         MolActionRelease(act);
9840                 }
9841                 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9842                 MolActionCallback_registerUndo(mol, act);
9843                 free(vp);
9844                 MolActionRelease(act);
9845         }
9846         MainView_removeGraphic(mol->mview, i);
9847         return ival;
9848 }
9849
9850 /*
9851  *  call-seq:
9852  *     ngraphics -> integer
9853  *
9854  *  Get the number of graphic objects.
9855  */
9856 static VALUE
9857 s_Molecule_NGraphics(VALUE self)
9858 {
9859     Molecule *mol;
9860     Data_Get_Struct(self, Molecule, mol);
9861         if (mol->mview == NULL)
9862                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9863         return INT2NUM(mol->mview->ngraphics);
9864 }
9865
9866 /*
9867  *  call-seq:
9868  *     get_graphic_point(graphic_index, point_index) -> value
9869  *     get_graphic_points(graphic_index) -> values
9870  *
9871  *  Get the point_index-th control point of graphic_index-th graphic object.
9872  *  Get an array of all control points with the given values.
9873  *   
9874  */
9875 static VALUE
9876 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9877 {
9878         MainViewGraphic *gp;
9879     Molecule *mol;
9880         int index, pindex;
9881         Vector v;
9882         VALUE gval, pval;
9883     Data_Get_Struct(self, Molecule, mol);
9884         if (mol->mview == NULL)
9885                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9886         rb_scan_args(argc, argv, "11", &gval, &pval);
9887         index = NUM2INT(rb_Integer(gval));
9888         if (index < 0 || index >= mol->mview->ngraphics)
9889                 rb_raise(rb_eArgError, "the graphic index is out of range");
9890         gp = mol->mview->graphics + index;
9891         if (pval != Qnil) {
9892                 pindex = NUM2INT(rb_Integer(pval));
9893                 if (pindex < 0 || pindex >= gp->npoints)
9894                         rb_raise(rb_eArgError, "the point index is out of range");
9895                 v.x = gp->points[pindex * 3];
9896                 v.y = gp->points[pindex * 3 + 1];
9897                 v.z = gp->points[pindex * 3 + 2];
9898                 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9899                         return rb_float_new(v.x);
9900                 } else {
9901                         return ValueFromVector(&v);
9902                 }
9903         } else {
9904                 pval = rb_ary_new();
9905                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9906                         v.x = gp->points[pindex * 3];
9907                         v.y = gp->points[pindex * 3 + 1];
9908                         v.z = gp->points[pindex * 3 + 2];
9909                         rb_ary_push(pval, ValueFromVector(&v));
9910                 }
9911                 return pval;
9912         }
9913 }
9914
9915 /*
9916  *  call-seq:
9917  *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
9918  *     set_graphic_points(graphic_index, new_values) -> new_values
9919  *
9920  *  Change the point_index-th control point of graphic_index-th graphic object.
9921  *  Replace the control points with the given values.
9922  *   
9923  */
9924 static VALUE
9925 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9926 {
9927         MainViewGraphic *gp;
9928     Molecule *mol;
9929         int index, pindex;
9930         Vector v, v0;
9931         VALUE gval, pval, nval;
9932         MolAction *act;
9933     Data_Get_Struct(self, Molecule, mol);
9934         if (mol->mview == NULL)
9935                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9936         rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9937         index = NUM2INT(rb_Integer(gval));
9938         if (index < 0 || index >= mol->mview->ngraphics)
9939                 rb_raise(rb_eArgError, "the graphic index is out of range");
9940         gp = mol->mview->graphics + index;
9941         if (nval != Qnil) {
9942                 pindex = NUM2INT(rb_Integer(pval));
9943                 if (pindex < 0 || pindex >= gp->npoints)
9944                         rb_raise(rb_eArgError, "the point index is out of range");
9945                 v0.x = gp->points[pindex * 3];
9946                 v0.y = gp->points[pindex * 3 + 1];
9947                 v0.z = gp->points[pindex * 3 + 2];
9948                 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9949                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9950                                 v.x = NUM2DBL(rb_Float(nval));
9951                                 v.y = v.z = 0;
9952                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9953                                 v.x = NUM2DBL(rb_Float(nval));
9954                                 v.y = v.z = 0;
9955                                 gp->points[7] = gp->points[11] = v.x;
9956                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9957                         } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9958                 } else {
9959                         if (nval == Qnil) {
9960                                 v.x = kInvalidFloat;
9961                                 v.y = v.z = 0.0;
9962                         } else VectorFromValue(nval, &v);
9963                 }
9964                 gp->points[pindex * 3] = v.x;
9965                 gp->points[pindex * 3 + 1] = v.y;
9966                 gp->points[pindex * 3 + 2] = v.z;
9967                 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9968         } else {
9969                 VALUE aval;
9970                 int len;
9971                 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9972                 for (pindex = 0; pindex < gp->npoints; pindex++) {
9973                         vp[pindex].x = gp->points[pindex * 3];
9974                         vp[pindex].y = gp->points[pindex * 3 + 1];
9975                         vp[pindex].z = gp->points[pindex * 3 + 2];
9976                 }
9977                 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9978                 free(vp);
9979                 pval = rb_ary_to_ary(pval);
9980                 len = RARRAY_LEN(pval);
9981                 if (gp->npoints < len) {
9982                         gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9983                         gp->npoints = len;
9984                 } else if (gp->npoints > len) {
9985                         int len2 = 3;
9986                         switch (gp->kind) {
9987                                 case kMainViewGraphicLine: len2 = 2; break;
9988                                 case kMainViewGraphicPoly: len2 = 3; break;
9989                                 case kMainViewGraphicCylinder: len2 = 3; break;
9990                                 case kMainViewGraphicCone: len2 = 3; break;
9991                                 case kMainViewGraphicEllipsoid: len2 = 4; break;
9992                         }
9993                         if (len2 < len)
9994                                 len2 = len;
9995                         gp->npoints = len2;
9996                 }
9997                 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9998                         aval = RARRAY_PTR(pval)[pindex];
9999                         if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
10000                                 v.x = NUM2DBL(rb_Float(aval));
10001                                 v.y = v.z = 0;
10002                         } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
10003                                 v.x = NUM2DBL(rb_Float(aval));
10004                                 v.y = v.z = 0;
10005                                 gp->points[7] = gp->points[11] = v.x;
10006                                 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
10007                                 break;
10008                         } else VectorFromValue(aval, &v);
10009                         gp->points[pindex * 3] = v.x;
10010                         gp->points[pindex * 3 + 1] = v.y;
10011                         gp->points[pindex * 3 + 2] = v.z;
10012                 }
10013         }
10014         if (gp->kind == kMainViewGraphicPoly) {
10015                 /*  Calculate normals  */
10016                 s_CalculateGraphicNormals(gp);
10017         }
10018         MolActionCallback_registerUndo(mol, act);
10019         MolActionRelease(act);          
10020         MoleculeCallback_notifyModification(mol, 0);
10021         return nval;
10022 }
10023
10024 /*
10025  *  call-seq:
10026  *     get_graphic_color(graphic_index) -> value
10027  *
10028  *  Get the color of graphic_index-th graphic object
10029  */
10030 static VALUE
10031 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10032 {
10033         MainViewGraphic *gp;
10034     Molecule *mol;
10035         int index;
10036     Data_Get_Struct(self, Molecule, mol);
10037         if (mol->mview == NULL)
10038                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10039         index = NUM2INT(rb_Integer(gval));
10040         if (index < 0 || index >= mol->mview->ngraphics)
10041                 rb_raise(rb_eArgError, "the graphic index is out of range");
10042         gp = mol->mview->graphics + index;
10043         return rb_ary_new3(4, rb_float_new(gp->rgba[0]), rb_float_new(gp->rgba[1]), rb_float_new(gp->rgba[2]), rb_float_new(gp->rgba[3]));
10044 }
10045
10046 /*
10047  *  call-seq:
10048  *     set_graphic_color(graphic_index, new_value) -> new_value
10049  *
10050  *  Change the color of graphic_index-th graphic object
10051  *   
10052  */
10053 static VALUE
10054 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10055 {
10056         MainViewGraphic *gp;
10057     Molecule *mol;
10058         MolAction *act;
10059         double c[4];
10060         int index, i, n;
10061     Data_Get_Struct(self, Molecule, mol);
10062         if (mol->mview == NULL)
10063                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10064         index = NUM2INT(rb_Integer(gval));
10065         if (index < 0 || index >= mol->mview->ngraphics)
10066                 rb_raise(rb_eArgError, "the graphic index is out of range");
10067         gp = mol->mview->graphics + index;
10068         for (i = 0; i < 4; i++)
10069                 c[i] = gp->rgba[i];
10070         cval = rb_ary_to_ary(cval);
10071         n = RARRAY_LEN(cval);
10072         if (n != 3 && n != 4)
10073                 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10074
10075         for (i = 0; i < n; i++) {
10076                 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10077         }
10078         if (n == 3)
10079                 gp->rgba[3] = 1.0;
10080         act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10081         MolActionCallback_registerUndo(mol, act);
10082         MolActionRelease(act);          
10083         MoleculeCallback_notifyModification(mol, 0);
10084         return cval;
10085 }
10086
10087 /*
10088  *  call-seq:
10089  *     show_graphic(graphic_index) -> self
10090  *
10091  *  Enable the visible flag of the graphic_index-th graphic object
10092  *   
10093  */
10094 static VALUE
10095 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10096 {
10097         MainViewGraphic *gp;
10098     Molecule *mol;
10099         int index;
10100     Data_Get_Struct(self, Molecule, mol);
10101         if (mol->mview == NULL)
10102                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10103         index = NUM2INT(rb_Integer(gval));
10104         if (index < 0 || index >= mol->mview->ngraphics)
10105                 rb_raise(rb_eArgError, "the graphic index is out of range");
10106         gp = mol->mview->graphics + index;
10107         gp->visible = 1;
10108         MoleculeCallback_notifyModification(mol, 0);
10109         return self;
10110 }
10111
10112 /*
10113  *  call-seq:
10114  *     hide_graphic(graphic_index) -> self
10115  *
10116  *  Disable the visible flag of the graphic_index-th graphic object
10117  *   
10118  */
10119 static VALUE
10120 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10121 {
10122         MainViewGraphic *gp;
10123     Molecule *mol;
10124         int index;
10125     Data_Get_Struct(self, Molecule, mol);
10126         if (mol->mview == NULL)
10127                 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10128         index = NUM2INT(rb_Integer(gval));
10129         if (index < 0 || index >= mol->mview->ngraphics)
10130                 rb_raise(rb_eArgError, "the graphic index is out of range");
10131         gp = mol->mview->graphics + index;
10132         gp->visible = 0;
10133         MoleculeCallback_notifyModification(mol, 0);
10134         return self;
10135 }
10136
10137 /*
10138  *  call-seq:
10139  *     show_text(string)
10140  *
10141  *  Show the string in the info text box.
10142  */
10143 static VALUE
10144 s_Molecule_ShowText(VALUE self, VALUE arg)
10145 {
10146     Molecule *mol;
10147     Data_Get_Struct(self, Molecule, mol);
10148         if (mol->mview != NULL)
10149                 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10150         return Qnil;
10151 }
10152
10153 #pragma mark ------ MD Support ------
10154
10155 /*
10156  *  call-seq:
10157  *     md_arena -> MDArena
10158  *
10159  *  Returns the MDArena object associated to this molecule. If no MDArena is associated to
10160  *  this molecule, a new arena is created.
10161  */
10162 static VALUE
10163 s_Molecule_MDArena(VALUE self)
10164 {
10165     Molecule *mol;
10166         VALUE retval;
10167     Data_Get_Struct(self, Molecule, mol);
10168         if (mol->arena == NULL)
10169                 md_arena_new(mol);
10170         retval = ValueFromMDArena(mol->arena);
10171         return retval;
10172 }
10173
10174 /*
10175  *  call-seq:
10176  *     set_parameter_attr(type, index, key, value, src) -> value
10177  *
10178  *  This method is used only internally.
10179  */
10180 static VALUE
10181 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10182 {
10183         /*  This method is called from MolAction to change a MM parameter attribute.  */
10184     Molecule *mol;
10185         VALUE pval;
10186         ParameterRef *pref;
10187         UnionPar *up;
10188     Data_Get_Struct(self, Molecule, mol);
10189         pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10190         vval = s_ParameterRef_SetAttr(pval, kval, vval);
10191         
10192         /*  This is the special part of this method; it allows modification of the src field. */
10193         /*  (ParameterRef#set_attr sets 0 to the src field)  */
10194         Data_Get_Struct(pval, ParameterRef, pref);
10195         up = ParameterRefGetPar(pref);
10196         up->bond.src = FIX2INT(sval);
10197         
10198         return vval;
10199 }
10200
10201 /*
10202  *  call-seq:
10203  *     parameter -> Parameter
10204  *
10205  *  Get the local parameter of this molecule. If not defined, returns nil.
10206  */
10207 static VALUE
10208 s_Molecule_Parameter(VALUE self)
10209 {
10210     Molecule *mol;
10211     Data_Get_Struct(self, Molecule, mol);
10212 /*      if (mol->par == NULL)
10213                 return Qnil; */
10214         return s_NewParameterValueFromValue(self);
10215 }
10216
10217 /*
10218  *  call-seq:
10219  *     start_step       -> Integer
10220  *
10221  *  Returns the start step (defined by dcd format).
10222  */
10223 static VALUE
10224 s_Molecule_StartStep(VALUE self)
10225 {
10226     Molecule *mol;
10227     Data_Get_Struct(self, Molecule, mol);
10228         return INT2NUM(mol->startStep);
10229 }
10230
10231 /*
10232  *  call-seq:
10233  *     start_step = Integer
10234  *
10235  *  Set the start step (defined by dcd format).
10236  */
10237 static VALUE
10238 s_Molecule_SetStartStep(VALUE self, VALUE val)
10239 {
10240     Molecule *mol;
10241     Data_Get_Struct(self, Molecule, mol);
10242         mol->startStep = NUM2INT(rb_Integer(val));
10243         return val;
10244 }
10245
10246 /*
10247  *  call-seq:
10248  *     steps_per_frame       -> Integer
10249  *
10250  *  Returns the number of steps between frames (defined by dcd format).
10251  */
10252 static VALUE
10253 s_Molecule_StepsPerFrame(VALUE self)
10254 {
10255     Molecule *mol;
10256     Data_Get_Struct(self, Molecule, mol);
10257         return INT2NUM(mol->stepsPerFrame);
10258 }
10259
10260 /*
10261  *  call-seq:
10262  *     steps_per_frame = Integer
10263  *
10264  *  Set the number of steps between frames (defined by dcd format).
10265  */
10266 static VALUE
10267 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10268 {
10269     Molecule *mol;
10270     Data_Get_Struct(self, Molecule, mol);
10271         mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10272         return val;
10273 }
10274
10275 /*
10276  *  call-seq:
10277  *     ps_per_step       -> Float
10278  *
10279  *  Returns the time increment (in picoseconds) for one step (defined by dcd format).
10280  */
10281 static VALUE
10282 s_Molecule_PsPerStep(VALUE self)
10283 {
10284     Molecule *mol;
10285     Data_Get_Struct(self, Molecule, mol);
10286         return rb_float_new(mol->psPerStep);
10287 }
10288
10289 /*
10290  *  call-seq:
10291  *     ps_per_step = Float
10292  *
10293  *  Set the time increment (in picoseconds) for one step (defined by dcd format).
10294  */
10295 static VALUE
10296 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10297 {
10298     Molecule *mol;
10299     Data_Get_Struct(self, Molecule, mol);
10300         mol->psPerStep = NUM2DBL(rb_Float(val));
10301         return val;
10302 }
10303
10304 static VALUE
10305 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10306 {
10307         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.");
10308 }
10309
10310 #pragma mark ------ MO Handling ------
10311
10312 /*
10313  *  call-seq:
10314  *     selectedMO -> IntGroup
10315  *
10316  *  Returns a group of selected mo in the "MO Info" table. If the MO info table
10317  *  is not selected, returns nil. If the MO info table is selected but no MOs 
10318  *  are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10319  */
10320 static VALUE
10321 s_Molecule_SelectedMO(VALUE self)
10322 {
10323     Molecule *mol;
10324         IntGroup *ig;
10325         VALUE val;
10326     Data_Get_Struct(self, Molecule, mol);
10327         if (mol->mview == NULL)
10328                 return Qnil;
10329         ig = MainView_selectedMO(mol->mview);
10330         if (ig == NULL)
10331                 return Qnil;
10332         IntGroupOffset(ig, 1);
10333         val = ValueFromIntGroup(ig);
10334         IntGroupRelease(ig);
10335         return val;
10336 }
10337
10338 /*
10339  *  call-seq:
10340  *     default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10341  *
10342  *  Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10343  *  If the molecule does not contain a basis set information, then returns nil.
10344  */
10345 static VALUE
10346 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10347 {
10348     Molecule *mol;
10349         Vector o, dx, dy, dz;
10350         Int nx, ny, nz;
10351         VALUE nval;
10352         Int npoints = 80 * 80 * 80;
10353     Data_Get_Struct(self, Molecule, mol);
10354         if (mol->bset == NULL)
10355                 return Qnil;
10356         rb_scan_args(argc, argv, "01", &nval);
10357         if (nval != Qnil)
10358                 npoints = NUM2INT(rb_Integer(nval));
10359         if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10360                 return Qnil;
10361         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));
10362 }
10363
10364 static int
10365 s_Cubegen_callback(double progress, void *ref)
10366 {
10367         MyAppCallback_setProgressValue(progress);
10368         if (MyAppCallback_checkInterrupt())
10369                 return 1;
10370         else return 0;
10371 }
10372
10373 /*
10374  *  call-seq:
10375  *     cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10376  *     cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10377  *
10378  *  Calculate the molecular orbital with number mo and create a 'cube' file.
10379  *  In the first form, the cube size is estimated from the atomic coordinates. In the
10380  *  second form, the cube dimension is explicitly given.
10381  *  Returns fname when successful, nil otherwise.
10382  *  If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10383  *  If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10384  *  (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10385  */
10386 static VALUE
10387 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10388 {
10389         VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10390     Molecule *mol;
10391         Int mono, nx, ny, nz, npoints;
10392         Vector o, dx, dy, dz;
10393         int index, n;
10394         char buf[1024];
10395     Data_Get_Struct(self, Molecule, mol);
10396         if (mol->bset == NULL)
10397                 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10398         rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10399         
10400         /*  Set up parameters  */
10401         mono = NUM2INT(rb_Integer(mval));
10402         if (mono < 0 || mono > mol->bset->ncomps)
10403                 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d, or 0 as 'arbitrary vector')", mono, mol->bset->ncomps);
10404         if (RTEST(bval)) {
10405                 if (mol->bset->rflag != 0)
10406                         rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10407                 mono += mol->bset->ncomps;
10408         }
10409                 
10410         if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10411                 /*  Automatic grid formation  */
10412                 if (oval != Qnil)
10413                         npoints = NUM2INT(rb_Integer(oval));
10414                 else npoints = 0;
10415                 if (npoints == 0)
10416                         npoints = 1000000;
10417                 else if (npoints < 8)
10418                         rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10419                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10420                         rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10421                 ival = dxval;
10422                 bval = dyval;
10423         } else {
10424                 VectorFromValue(oval, &o);
10425                 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10426                         VectorFromValue(dxval, &dx);
10427                 else {
10428                         dx.x = NUM2DBL(rb_Float(dxval));
10429                         dx.y = dx.z = 0.0;
10430                 }
10431                 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10432                         VectorFromValue(dyval, &dy);
10433                 else {
10434                         dy.y = NUM2DBL(rb_Float(dyval));
10435                         dy.x = dy.z = 0.0;
10436                 }
10437                 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10438                         VectorFromValue(dzval, &dz);
10439                 else {
10440                         dz.z = NUM2DBL(rb_Float(dzval));
10441                         dz.x = dz.y = 0.0;
10442                 }
10443                 nx = NUM2INT(rb_Integer(nxval));
10444                 ny = NUM2INT(rb_Integer(nyval));
10445                 nz = NUM2INT(rb_Integer(nzval));
10446                 if (nx <= 0 || ny <= 0 || nz <= 0)
10447                         rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10448                 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10449                         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);
10450         }
10451         
10452         /*  Calc MO  */
10453         index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10454         if (index == -2)
10455                 rb_interrupt();
10456         else if (index < 0)
10457                 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10458         
10459         /*  Output to file  */
10460         MoleculeCallback_displayName(mol, buf, sizeof buf);
10461         n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10462         if (n != 0)
10463                 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10464         
10465         /*  Discard the cube  */
10466         MoleculeClearCubeAtIndex(mol, index);
10467         return fval;
10468 }
10469
10470 /*
10471  *  call-seq:
10472  *     clear_surface
10473  *
10474  *  Clear the MO surface if present.
10475  */
10476 static VALUE
10477 s_Molecule_ClearSurface(VALUE self)
10478 {
10479     Molecule *mol;
10480     Data_Get_Struct(self, Molecule, mol);
10481         if (mol->mcube != NULL)
10482                 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10483         return self;
10484 }
10485
10486 /*
10487  *  call-seq:
10488  *     hide_surface
10489  *
10490  *  Hide the MO surface if present.
10491  */
10492 static VALUE
10493 s_Molecule_HideSurface(VALUE self)
10494 {
10495     Molecule *mol;
10496     Data_Get_Struct(self, Molecule, mol);
10497         if (mol->mcube != NULL) {
10498                 mol->mcube->hidden = 1;
10499                 MoleculeCallback_notifyModification(mol, 0);
10500         }
10501         return self;
10502 }
10503
10504 /*
10505  *  call-seq:
10506  *     show_surface
10507  *
10508  *  Show the MO surface if present.
10509  */
10510 static VALUE
10511 s_Molecule_ShowSurface(VALUE self)
10512 {
10513     Molecule *mol;
10514     Data_Get_Struct(self, Molecule, mol);
10515         if (mol->mcube != NULL) {
10516                 mol->mcube->hidden = 0;
10517                 MoleculeCallback_notifyModification(mol, 0);
10518         }
10519         return self;
10520 }
10521
10522 /*
10523  *  call-seq:
10524  *     create_surface(mo, attr = nil)
10525  *
10526  *  Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10527  *  then it denotes the beta orbital.
10528  *  If mo is nil, then the attributes of the current surface are modified.
10529  *  Attributes:
10530  *    :npoints : the approximate number of grid points
10531  *    :expand  : the scale factor to expand/shrink the display box size for each atom,
10532  *    :thres   : the threshold for the isovalue surface
10533  *  If the molecule does not contain MO information, raises exception.
10534  */
10535 static VALUE
10536 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10537 {
10538     Molecule *mol;
10539         Vector o, dx, dy, dz;
10540         Int nmo, nx, ny, nz, i;
10541         Int need_recalc = 0;
10542         VALUE nval, hval, aval;
10543         Int npoints;
10544         Double expand;
10545         Double thres;
10546         Double d[4];
10547     Data_Get_Struct(self, Molecule, mol);
10548         rb_scan_args(argc, argv, "11", &nval, &hval);
10549         if (mol->bset == NULL)
10550                 rb_raise(rb_eMolbyError, "No MO information is given");
10551         if (nval == Qnil) {
10552                 nmo = -1;
10553         } else if (nval == ID2SYM(rb_intern("total_density"))) {
10554                 nmo = mol->bset->nmos + 1;
10555         } else {
10556                 nmo = NUM2INT(rb_Integer(nval));
10557                 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10558                         rb_raise(rb_eMolbyError, "MO index (%d) is out of range; should be 1..%d (or -1..-%d for beta orbitals); (0 is acceptable as arbitrary vector)", nmo, mol->bset->nmos, mol->bset->ncomps);
10559                 if (nmo < 0)
10560                         nmo = -nmo + mol->bset->ncomps;
10561         }
10562         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10563                 npoints = NUM2INT(rb_Integer(aval));
10564                 need_recalc = 1;
10565         } else if (mol->mcube != NULL) {
10566                 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10567         } else npoints = 80 * 80 * 80;
10568         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10569                 expand = NUM2DBL(rb_Float(aval));
10570         } else if (mol->mcube != NULL) {
10571                 expand = mol->mcube->expand;
10572         } else expand = 1.0;
10573         if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10574                 thres = NUM2DBL(rb_Float(aval));
10575         } else if (mol->mcube != NULL) {
10576                 thres = mol->mcube->thres;
10577         } else thres = 0.05;
10578         if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10579                 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10580                         rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10581                 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10582                         rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10583         }
10584         for (nx = 0; nx < 2; nx++) {
10585                 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10586                 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10587                         aval = rb_ary_to_ary(aval);
10588                         if (RARRAY_LEN(aval) < 3) {
10589                         raise:
10590                                 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10591                         }
10592                         for (i = 0; i < 4; i++)
10593                                 d[i] = mol->mcube->c[nx].rgba[i];
10594                         for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10595                                 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10596                                 if (d[i] < 0.0 && d[i] > 1.0)
10597                                         goto raise;
10598                         }
10599                         for (i = 0; i < 4; i++)
10600                                 mol->mcube->c[nx].rgba[i] = d[i];
10601                 }
10602         }
10603         if (mol->mcube->expand != expand)
10604                 need_recalc = 1;
10605         mol->mcube->thres = thres;
10606         mol->mcube->expand = expand;
10607         if (nmo < 0) {
10608                 if (mol->mcube->idn < 0)
10609                         return self;  /*  Only set attributes for now  */
10610                 if (need_recalc)
10611                         nmo = mol->mcube->idn;  /*  Force recalculation  */
10612         }
10613         if (MoleculeUpdateMCube(mol, nmo) != 0)
10614                 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10615         return self;
10616 }
10617
10618 /*
10619  *  call-seq:
10620  *     set_surface_attr(attr = nil)
10621  *
10622  *  Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10623  */
10624 static VALUE
10625 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10626 {
10627         VALUE args[2];
10628         args[0] = Qnil;
10629         args[1] = hval;
10630         return s_Molecule_CreateSurface(2, args, self);
10631 }
10632
10633 /*
10634  *  call-seq:
10635  *     nelpots
10636  *
10637  *  Get the number of electrostatic potential info.
10638  */
10639 static VALUE
10640 s_Molecule_NElpots(VALUE self)
10641 {
10642         Molecule *mol;
10643     Data_Get_Struct(self, Molecule, mol);
10644         return INT2NUM(mol->nelpots);
10645 }
10646
10647 /*
10648  *  call-seq:
10649  *     elpot(idx)
10650  *
10651  *  Get the electrostatic potential info at the given index. If present, then the
10652  *  return value is [Vector, Float] (position and potential). If not present, then
10653  *  returns nil.
10654  */
10655 static VALUE
10656 s_Molecule_Elpot(VALUE self, VALUE ival)
10657 {
10658         Molecule *mol;
10659         int idx;
10660     Data_Get_Struct(self, Molecule, mol);
10661         idx = NUM2INT(rb_Integer(ival));
10662         if (idx < 0 || idx >= mol->nelpots)
10663                 return Qnil;
10664         return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10665 }
10666
10667 /*
10668  *  call-seq:
10669  *     clear_basis_set
10670  *
10671  *  Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10672  *  cube and marching cube information are discarded. This operation is _not_ undoable!
10673  */
10674 static VALUE
10675 s_Molecule_ClearBasisSet(VALUE self)
10676 {
10677         Molecule *mol;
10678     Data_Get_Struct(self, Molecule, mol);
10679         if (mol != NULL) {
10680                 if (mol->bset != NULL) {
10681                         BasisSetRelease(mol->bset);
10682                         mol->bset = NULL;
10683                 }
10684                 if (mol->mcube != NULL) {
10685                         MoleculeDeallocateMCube(mol->mcube);
10686                         mol->mcube = NULL;
10687                 }
10688         }
10689         return self;
10690 }
10691
10692 /*
10693  *  call-seq:
10694  *     add_gaussian_orbital_shell(atom_index, sym, no_of_primitives[, additional_exponent])
10695  *
10696  *  To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10697  *  and the number of primitives.
10698  *  Additional exponent is for JANPA only; implements an additinal r^N component that
10699  *  appears in cartesian->spherical conversion.
10700  *  Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type; -2, D5-type;
10701  *                 3, F-type; -3, F7-type; 4, G-type; -4, G9-type.
10702  *  Or: "s", S-type; "p", P-type; "sp", SP-type; "d", D-type; "d5", D5-type;
10703  *      "f", F-type; "f7", F7-type; "g", G-type; "g9", G9-type
10704  */
10705 static VALUE
10706 s_Molecule_AddGaussianOrbitalShell(int argc, VALUE *argv, VALUE self)
10707 {
10708         Molecule *mol;
10709     int sym, nprims, a_idx, n, add_exp;
10710     VALUE aval, symval, npval, addval;
10711     Data_Get_Struct(self, Molecule, mol);
10712     rb_scan_args(argc, argv, "31", &aval, &symval, &npval, &addval);
10713     if (rb_obj_is_kind_of(symval, rb_cString)) {
10714         const char *p = StringValuePtr(symval);
10715         if (strcasecmp(p, "s") == 0)
10716             sym = 0;
10717         else if (strcasecmp(p, "p") == 0)
10718             sym = 1;
10719         else if (strcasecmp(p, "sp") == 0)
10720             sym = -1;
10721         else if (strcasecmp(p, "d") == 0)
10722             sym = 2;
10723         else if (strcasecmp(p, "d5") == 0)
10724             sym = -2;
10725         else if (strcasecmp(p, "f") == 0)
10726             sym = 3;
10727         else if (strcasecmp(p, "f7") == 0)
10728             sym = -3;
10729         else if (strcasecmp(p, "g") == 0)
10730             sym = 4;
10731         else if (strcasecmp(p, "g9") == 0)
10732             sym = -4;
10733         else
10734             rb_raise(rb_eArgError, "Unknown orbital type '%s'", p);
10735     } else {
10736         sym = NUM2INT(rb_Integer(symval));
10737     }
10738         a_idx = NUM2INT(rb_Integer(aval));
10739         nprims = NUM2INT(rb_Integer(npval));
10740     if (addval != Qnil)
10741         add_exp = NUM2INT(rb_Integer(addval));
10742     else add_exp = 0;
10743         n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims, add_exp);
10744         if (n == -1)
10745                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10746         else if (n == -2)
10747                 rb_raise(rb_eMolbyError, "Low memory");
10748         else if (n == -3)
10749                 rb_raise(rb_eMolbyError, "Unknown orbital type");
10750         else if (n != 0)
10751                 rb_raise(rb_eMolbyError, "Unknown error");
10752         return self;
10753 }
10754
10755 /*
10756  *  call-seq:
10757  *     add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10758  *
10759  *  To be used internally. Add a gaussian primitive coefficients.
10760  */
10761 static VALUE
10762 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10763 {
10764         Molecule *mol;
10765         Int n;
10766         Double exponent, contraction, contraction_sp;
10767     Data_Get_Struct(self, Molecule, mol);
10768         exponent = NUM2DBL(rb_Float(expval));
10769         contraction = NUM2DBL(rb_Float(cval));
10770         contraction_sp = NUM2DBL(rb_Float(cspval));
10771         n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10772         if (n == -1)
10773                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10774         else if (n == -2)
10775                 rb_raise(rb_eMolbyError, "Low memory");
10776         else if (n != 0)
10777                 rb_raise(rb_eMolbyError, "Unknown error");
10778         return self;
10779 }
10780
10781 /*
10782  *  call-seq:
10783  *     get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10784  *
10785  *  Get the Gaussian shell information for the given MO coefficient index.
10786  *  The symmetry code is the same as in add_gaussian_orbital_shell.
10787  *  The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10788  *  is the number of MO component belonging to this shell.
10789  */
10790 static VALUE
10791 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10792 {
10793         Molecule *mol;
10794         ShellInfo *sp;
10795         int s_idx, sym;
10796     Data_Get_Struct(self, Molecule, mol);
10797         if (mol->bset == NULL)
10798                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10799         s_idx = NUM2INT(rb_Integer(sval));
10800         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10801                 return Qnil;
10802         sp = mol->bset->shells + s_idx;
10803         sym = sp->sym;
10804         switch (sym) {
10805                 case kGTOType_S:  sym = 0;  break;
10806                 case kGTOType_SP: sym = -1; break;
10807                 case kGTOType_P:  sym = 1;  break;
10808                 case kGTOType_D:  sym = 2;  break;
10809                 case kGTOType_D5: sym = -2; break;
10810                 case kGTOType_F:  sym = 3;  break;
10811                 case kGTOType_F7: sym = -3; break;
10812                 case kGTOType_G:  sym = 4;  break;
10813                 case kGTOType_G9: sym = -4; break;
10814                 default:
10815                         rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10816         }
10817         return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10818 }
10819
10820 /*
10821  *  call-seq:
10822  *     get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10823  *
10824  *  Get the Gaussian primitive coefficients for the given MO component.
10825  */
10826 static VALUE
10827 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10828 {
10829         Molecule *mol;
10830         ShellInfo *sp;
10831         PrimInfo *pp;
10832         int s_idx, i;
10833         VALUE retval, aval;
10834     Data_Get_Struct(self, Molecule, mol);
10835         if (mol->bset == NULL)
10836                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10837         s_idx = NUM2INT(rb_Integer(sval));
10838         if (s_idx < 0 || s_idx >= mol->bset->nshells)
10839                 return Qnil;
10840         sp = mol->bset->shells + s_idx;
10841         pp = mol->bset->priminfos + sp->p_idx;
10842         retval = rb_ary_new2(sp->nprim);
10843         for (i = 0; i < sp->nprim; i++) {
10844                 if (sp->sym == kGTOType_SP) {
10845                         /*  With P contraction coefficient  */
10846                         aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10847                 } else {
10848                         /*  Without P contraction coefficient  */
10849                         aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10850                 }
10851                 rb_ary_store(retval, i, aval);
10852         }
10853         return retval;
10854 }
10855
10856 /*
10857  *  call-seq:
10858  *     get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10859  *
10860  *  Get the Gaussian shell information for the given MO coefficient index.
10861  */
10862 static VALUE
10863 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10864 {
10865         Molecule *mol;
10866         Int n, c, atom_idx, shell_idx;
10867         char label[32];
10868     Data_Get_Struct(self, Molecule, mol);
10869         if (mol->bset == NULL)
10870                 rb_raise(rb_eMolbyError, "No basis set information is defined");
10871         c = NUM2INT(rb_Integer(cval));
10872         if (c < 0 || c >= mol->bset->ncomps)
10873                 return Qnil;
10874         n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10875         if (n != 0)
10876                 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10877         return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10878 }
10879
10880 /*
10881  *  call-seq:
10882  *     clear_mo_coefficients
10883  *
10884  *  Clear the existing MO coefficients.
10885  */
10886 static VALUE
10887 s_Molecule_ClearMOCoefficients(VALUE self)
10888 {
10889         Molecule *mol;
10890         Data_Get_Struct(self, Molecule, mol);
10891         if (mol->bset != NULL) {
10892                 if (mol->bset->moenergies != NULL) {
10893                         free(mol->bset->moenergies);
10894                         mol->bset->moenergies = NULL;
10895                 }
10896                 if (mol->bset->mo != NULL) {
10897                         free(mol->bset->mo);
10898                         mol->bset->mo = NULL;
10899                 }
10900                 mol->bset->nmos = 0;
10901         }
10902         return self;
10903 }
10904
10905 /*
10906  *  call-seq:
10907  *     set_mo_coefficients(idx, energy, coefficients)
10908  *
10909  *  To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system, 
10910  *  beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10911  *  Energy is the MO energy, and coefficients is an array
10912  *  of MO coefficients.
10913  */
10914 static VALUE
10915 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10916 {
10917         Molecule *mol;
10918         Int idx, ncomps, i;
10919         Double energy;
10920         Double *coeffs;
10921     Data_Get_Struct(self, Molecule, mol);
10922         idx = NUM2INT(rb_Integer(ival));
10923         energy = NUM2DBL(rb_Float(eval));
10924         aval = rb_ary_to_ary(aval);
10925         ncomps = RARRAY_LEN(aval);
10926         coeffs = (Double *)calloc(sizeof(Double), ncomps);
10927         if (coeffs == NULL) {
10928                 i = -2;
10929                 goto end;
10930         }
10931         for (i = 0; i < ncomps; i++)
10932                 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10933         i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10934 end:
10935         if (i == -1)
10936                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10937         else if (i == -2)
10938                 rb_raise(rb_eMolbyError, "Low memory");
10939         else if (i == -3)
10940                 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10941         else if (i == -4)
10942                 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10943         else if (i == -5)
10944                 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10945         else if (i != 0)
10946                 rb_raise(rb_eMolbyError, "Unknown error");
10947         return self;
10948 }
10949
10950 /*
10951  *  call-seq:
10952  *     get_mo_coefficients(idx)
10953  *
10954  *  To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10955  */
10956 static VALUE
10957 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10958 {
10959         Molecule *mol;
10960         Int idx, ncomps, n;
10961         Double energy;
10962         Double *coeffs;
10963         VALUE retval;
10964     Data_Get_Struct(self, Molecule, mol);
10965         idx = NUM2INT(rb_Integer(ival));
10966         ncomps = 0;
10967         coeffs = NULL;
10968         n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10969         if (n == -1)
10970                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10971         else if (n == -2)
10972                 rb_raise(rb_eMolbyError, "No basis set information is present");
10973         else if (n == -3)
10974                 return Qnil;  /*  Silently returns nil  */
10975         retval = rb_ary_new2(ncomps);
10976         for (n = 0; n < ncomps; n++)
10977                 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10978         free(coeffs);
10979         return retval;
10980 }
10981
10982 /*
10983  *  call-seq:
10984  *     get_mo_energy(idx)
10985  *
10986  *  To be used internally. Get the MO energy for the given MO index (1-based).
10987  */
10988 static VALUE
10989 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10990 {
10991         Molecule *mol;
10992         Int idx, n;
10993         Double energy;
10994     Data_Get_Struct(self, Molecule, mol);
10995         idx = NUM2INT(rb_Integer(ival));
10996         n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10997         if (n == -1)
10998                 rb_raise(rb_eMolbyError, "Molecule is emptry");
10999         else if (n == -2)
11000                 rb_raise(rb_eMolbyError, "No basis set information is present");
11001         else if (n == -3)
11002                 return Qnil;
11003         return rb_float_new(energy);
11004 }
11005
11006 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
11007
11008 static inline void
11009 s_InitMOInfoKeys(void)
11010 {
11011         if (sTypeSym == 0) {
11012                 sTypeSym = ID2SYM(rb_intern("type"));
11013                 sAlphaSym = ID2SYM(rb_intern("alpha"));
11014                 sBetaSym = ID2SYM(rb_intern("beta"));
11015                 sNcompsSym = ID2SYM(rb_intern("ncomps"));
11016                 sNshellsSym = ID2SYM(rb_intern("nshells"));
11017         }
11018 }
11019
11020 /*
11021  *  call-seq:
11022  *     set_mo_info(hash)
11023  *
11024  *  Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
11025  *  :alpha=>integer, :beta=>integer
11026  */
11027 static VALUE
11028 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
11029 {
11030         Molecule *mol;
11031         VALUE aval;
11032         Int rflag, na, nb, n;
11033         char *s;
11034     Data_Get_Struct(self, Molecule, mol);
11035         if (mol->bset != NULL) {
11036                 rflag = mol->bset->rflag;
11037                 na = mol->bset->ne_alpha;
11038                 nb = mol->bset->ne_beta;
11039         } else {
11040                 rflag = 1;
11041                 na = 0;
11042                 nb = 0;
11043         }
11044         if (hval != Qnil) {
11045                 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
11046                         s = StringValuePtr(aval);
11047                         if (strcasecmp(s, "RHF") == 0)
11048                                 rflag = 1;
11049                         else if (strcasecmp(s, "UHF") == 0)
11050                                 rflag = 0;
11051                         else if (strcasecmp(s, "ROHF") == 0)
11052                                 rflag = 2;
11053                 }
11054                 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
11055                         n = NUM2INT(rb_Integer(aval));
11056                         if (n >= 0)
11057                                 na = n;
11058                 }
11059                 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
11060                         n = NUM2INT(rb_Integer(aval));
11061                         if (n >= 0)
11062                                 nb = n;
11063                 }
11064                 MoleculeSetMOInfo(mol, rflag, na, nb);
11065         }
11066         return self;
11067 }
11068
11069 /*
11070  *  call-seq:
11071  *     get_mo_info(key)
11072  *
11073  *  Get the MO info. The key is as described in set_mo_info.
11074  *  Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11075  */
11076 static VALUE
11077 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11078 {
11079         Molecule *mol;
11080     Data_Get_Struct(self, Molecule, mol);
11081         if (mol->bset == NULL)
11082                 return Qnil;
11083         if (kval == sTypeSym) {
11084                 switch (mol->bset->rflag) {
11085                         case 0: return Ruby_NewEncodedStringValue2("UHF");
11086                         case 1: return Ruby_NewEncodedStringValue2("RHF");
11087                         case 2: return Ruby_NewEncodedStringValue2("ROHF");
11088                         default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11089                 }
11090         } else if (kval == sAlphaSym) {
11091                 return INT2NUM(mol->bset->ne_alpha);
11092         } else if (kval == sBetaSym) {
11093                 return INT2NUM(mol->bset->ne_beta);
11094         } else if (kval == sNcompsSym) {
11095                 return INT2NUM(mol->bset->ncomps);
11096         } else if (kval == sNshellsSym) {
11097                 return INT2NUM(mol->bset->nshells);
11098         } else {
11099                 kval = rb_inspect(kval);
11100                 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11101                 return Qnil;  /*  Does not reach here  */
11102         }
11103 }
11104
11105 /*
11106  *  call-seq:
11107  *     mo_type
11108  *
11109  *  Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11110  */
11111 static VALUE
11112 s_Molecule_MOType(VALUE self)
11113 {
11114         return s_Molecule_GetMOInfo(self, sTypeSym);
11115 }
11116
11117 #pragma mark ------ Molecular Topology ------
11118
11119 /*
11120  *  call-seq:
11121  *     search_equivalent_atoms(ig = nil)
11122  *
11123  *  Search equivalent atoms (within the atom group if given). Returns an array of integers.
11124  */
11125 static VALUE
11126 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11127 {
11128         Molecule *mol;
11129         Int *result, i;
11130         VALUE val;
11131         IntGroup *ig;
11132     Data_Get_Struct(self, Molecule, mol);
11133         if (mol->natoms == 0)
11134                 return Qnil;
11135         rb_scan_args(argc, argv, "01", &val);
11136         if (val != Qnil)
11137                 ig = s_Molecule_AtomGroupFromValue(self, val);
11138         else ig = NULL;
11139         result = MoleculeSearchEquivalentAtoms(mol, ig);
11140         if (result == NULL)
11141                 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11142         if (ig != NULL)
11143                 IntGroupRelease(ig);
11144         val = rb_ary_new2(mol->natoms);
11145         for (i = 0; i < mol->natoms; i++)
11146                 rb_ary_push(val, INT2NUM(result[i]));
11147         free(result);
11148         return val;
11149 }
11150
11151 /*
11152  *  call-seq:
11153  *     create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11154  *
11155  *  Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11156  *  Name is the name of the new pi anchor, and group is the atoms that define
11157  *  the pi system. Type (a String) is an atom type for MM implementation.
11158  *  Weights represent the relative significance of the component atoms; if omitted, then
11159  *  1.0/n (n is the number of component atoms) is assumed for all atoms.
11160  *  The weight values will be normalized so that the sum of the weights is 1.0.
11161  *  The weight values must be positive.
11162  *  Index is the atom index where the created pi-anchor is inserted in the 
11163  *  atoms array; if omitted, the pi-anchor is inserted after the component atom
11164  *  having the largest index.
11165  *  Pi anchors are appear in the atom list along with other ordinary atoms. The list
11166  *  of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11167  */
11168 static VALUE
11169 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11170 {
11171         Molecule *mol;
11172         VALUE nval, gval;
11173         IntGroup *ig;
11174         Int i, n, idx, last_component;
11175         Atom a, *ap;
11176         PiAnchor an;
11177         AtomRef *aref;
11178         if (argc < 2 || argc >= 6)
11179                 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11180         nval = *argv++;
11181         gval = *argv++;
11182         argc -= 2;
11183     Data_Get_Struct(self, Molecule, mol);
11184     if (gval == Qnil)
11185         ig = NULL;
11186     else
11187         ig = s_Molecule_AtomGroupFromValue(self, gval);
11188     if (ig == NULL || IntGroupGetCount(ig) == 0)
11189     rb_raise(rb_eMolbyError, "atom group is not given correctly");
11190         memset(&a, 0, sizeof(a));
11191         memset(&an, 0, sizeof(an));
11192         strncpy(a.aname, StringValuePtr(nval), 4);
11193         if (a.aname[0] == '_')
11194                 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11195         a.type = AtomTypeEncodeToUInt("##");  /*  Default atom type for pi_anchor  */
11196         for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11197                 if (n >= mol->natoms) {
11198                         AtomConnectResize(&an.connect, 0);
11199                         rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11200                 }
11201                 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11202                 last_component = n;
11203         }
11204         if (an.connect.count == 0)
11205                 rb_raise(rb_eMolbyError, "no atoms are specified");
11206         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11207         for (i = 0; i < an.connect.count; i++) {
11208                 an.coeffs[i] = 1.0 / an.connect.count;
11209         }
11210         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11211                 /*  Atom type  */
11212                 if (argv[0] != Qnil)
11213                         a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11214                 argc--;
11215                 argv++;
11216         }
11217         if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11218                 if (argv[0] != Qnil) {
11219                         VALUE aval = rb_ary_to_ary(argv[0]);
11220                         Double d, sum;
11221                         if (RARRAY_LEN(aval) != an.connect.count)
11222                                 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11223                         for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11224                                 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11225                                 if (d <= 0.0)
11226                                         rb_raise(rb_eMolbyError, "the weight value must be positive");
11227                                 sum += d;
11228                                 an.coeffs[i] = d;
11229                         }
11230                         for (i = 0; i < an.connect.count; i++)
11231                                 an.coeffs[i] /= sum;
11232                 }
11233                 argc--;
11234                 argv++;
11235         }
11236         if (argc > 0 && argv[0] != Qnil) {
11237                 /*  Index  */
11238                 idx = NUM2INT(rb_Integer(argv[0]));
11239         } else idx = -1;
11240         if (idx < 0 || idx > mol->natoms) {
11241                 /*  Immediately after the last specified atom  */
11242                 idx = last_component + 1;
11243         }
11244         a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11245         memmove(a.anchor, &an, sizeof(PiAnchor));
11246         /*  Use residue information of the last specified atom  */
11247         ap = ATOM_AT_INDEX(mol->atoms, last_component);
11248         a.resSeq = ap->resSeq;
11249         strncpy(a.resName, ap->resName, 4);
11250         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11251                 return Qnil;
11252         MoleculeCalculatePiAnchorPosition(mol, idx);
11253     aref = AtomRefNew(mol, idx);
11254     return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11255 }
11256
11257 #pragma mark ------ Molecular Properties ------
11258
11259 /*
11260  *  call-seq:
11261  *     set_property(name, value[, index]) -> value
11262  *     set_property(name, values, group) -> values
11263  *
11264  *  Set molecular property. A property is a floating-point number with a specified name,
11265  *  and can be set for each frame separately. The name of the property is given as a String.
11266  *  The value can be a single floating point number, which is set to the current frame.
11267  *  
11268  */
11269 static VALUE
11270 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11271 {
11272         Molecule *mol;
11273         VALUE nval, vval, ival;
11274         char *name;
11275         IntGroup *ig;
11276         Int i, n, idx, fidx;
11277         Double *dp;
11278         rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11279     Data_Get_Struct(self, Molecule, mol);
11280         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11281                 idx = NUM2INT(rb_Integer(nval));
11282                 if (idx < 0 || idx >= mol->nmolprops)
11283                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11284         } else {
11285                 name = StringValuePtr(nval);
11286                 idx = MoleculeLookUpProperty(mol, name);
11287                 if (idx < 0) {
11288                         idx = MoleculeCreateProperty(mol, name);
11289                         if (idx < 0)
11290                                 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11291                 }
11292         }
11293         if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11294                 if (ival == Qnil)
11295                         fidx = mol->cframe;
11296                 else {
11297                         fidx = NUM2INT(rb_Integer(ival));
11298                         n = MoleculeGetNumberOfFrames(mol);
11299                         if (fidx < 0 || fidx >= n)
11300                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11301                 }
11302                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11303                 dp = (Double *)malloc(sizeof(Double));
11304                 *dp = NUM2DBL(rb_Float(vval));
11305                 n = 1;
11306         } else {
11307                 vval = rb_ary_to_ary(vval);
11308                 ig = IntGroupFromValue(ival);
11309                 n = IntGroupGetCount(ig);
11310                 if (n == 0)
11311                         rb_raise(rb_eMolbyError, "No frames are specified");
11312                 if (RARRAY_LEN(vval) < n)
11313                         rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11314                 dp = (Double *)calloc(sizeof(Double), n);
11315                 for (i = 0; i < n; i++)
11316                         dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11317         }
11318         
11319         MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11320         free(dp);
11321         IntGroupRelease(ig);
11322         return self;
11323 }
11324
11325 /*
11326  *  call-seq:
11327  *     get_property(name[, index]) -> value
11328  *     get_property(name, group) -> values
11329  *
11330  *  Get molecular property. In the first form, a property value for a single frame is returned.
11331  *  (If index is omitted, then the value for the current frame is given)
11332  *  In the second form, an array of property values for the given frames is returned.
11333  *  If name is not one of known properties or a valid index integer, exception is raised.
11334  */
11335 static VALUE
11336 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11337 {
11338         Molecule *mol;
11339         VALUE nval, ival;
11340         char *name;
11341         IntGroup *ig;
11342         Int i, n, idx, fidx;
11343         Double *dp;
11344         rb_scan_args(argc, argv, "11", &nval, &ival);
11345     Data_Get_Struct(self, Molecule, mol);
11346         if (mol->nmolprops == 0)
11347                 rb_raise(rb_eMolbyError, "The molecule has no properties");
11348         if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11349                 idx = NUM2INT(rb_Integer(nval));
11350                 if (idx < 0 || idx >= mol->nmolprops)
11351                         rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11352         } else {
11353                 name = StringValuePtr(nval);
11354                 idx = MoleculeLookUpProperty(mol, name);
11355                 if (idx < 0)
11356                         rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11357         }
11358         if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11359                 if (ival == Qnil)
11360                         fidx = mol->cframe;
11361                 else {
11362                         fidx = NUM2INT(rb_Integer(ival));
11363                         n = MoleculeGetNumberOfFrames(mol);
11364                         if (fidx < 0 || fidx >= n)
11365                                 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11366                 }
11367                 ig = IntGroupNewWithPoints(fidx, 1, -1);
11368                 ival = INT2FIX(fidx);
11369                 n = 1;
11370         } else {
11371                 ig = IntGroupFromValue(ival);
11372                 n = IntGroupGetCount(ig);
11373                 if (n == 0)
11374                         return rb_ary_new();
11375         }
11376         dp = (Double *)calloc(sizeof(Double), n);
11377         MoleculeGetProperty(mol, idx, ig, dp);  
11378         if (FIXNUM_P(ival))
11379                 ival = rb_float_new(dp[0]);
11380         else {
11381                 ival = rb_ary_new();
11382                 for (i = n - 1; i >= 0; i--) {
11383                         nval = rb_float_new(dp[i]);
11384                         rb_ary_store(ival, i, nval);
11385                 }
11386         }
11387         free(dp);
11388         IntGroupRelease(ig);
11389         return ival;
11390 }
11391
11392 /*
11393  *  call-seq:
11394  *     property_names -> Array
11395  *
11396  *  Get an array of property names.
11397  */
11398 static VALUE
11399 s_Molecule_PropertyNames(VALUE self)
11400 {
11401         Molecule *mol;
11402         VALUE rval, nval;
11403         int i;
11404     Data_Get_Struct(self, Molecule, mol);
11405         rval = rb_ary_new();
11406         for (i = mol->nmolprops - 1; i >= 0; i--) {
11407                 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11408                 rb_ary_store(rval, i, nval);
11409         }
11410         return rval;
11411 }
11412
11413 #pragma mark ------ Class methods ------
11414
11415 /*
11416  *  call-seq:
11417  *     current       -> Molecule
11418  *
11419  *  Get the currently "active" molecule.
11420  */
11421 static VALUE
11422 s_Molecule_Current(VALUE klass)
11423 {
11424         return ValueFromMolecule(MoleculeCallback_currentMolecule());
11425 }
11426
11427 /*
11428  *  call-seq:
11429  *     Molecule[]          -> Molecule
11430  *     Molecule[n]         -> Molecule
11431  *     Molecule[name]      -> Molecule
11432  *     Molecule[name, k]   -> Molecule
11433  *     Molecule[regex]     -> Molecule
11434  *     Molecule[regex, k]  -> Molecule
11435  *
11436  *  Molecule[] is equivalent to Molecule.current.
11437  *  Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11438  *  Molecule[name] gives the first document (in the order of creation time) that has
11439  *  the given name. If a second argument (k) is given, the k-th document that has the
11440  *  given name is returned.
11441  *  Molecule[regex] gives the first document (in the order of creation time) that
11442  *  has a name matching the regular expression. If a second argument (k) is given, 
11443  *  the k-th document that has a name matching the re is returned.
11444  */
11445 static VALUE
11446 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11447 {
11448         VALUE val, kval;
11449         int idx, k;
11450         Molecule *mol;
11451         char buf[1024];
11452         rb_scan_args(argc, argv, "02", &val, &kval);
11453         if (val == Qnil)
11454                 return s_Molecule_Current(klass);
11455         if (rb_obj_is_kind_of(val, rb_cInteger)) {
11456                 idx = NUM2INT(val);
11457                 mol = MoleculeCallback_moleculeAtIndex(idx);
11458         } else if (rb_obj_is_kind_of(val, rb_cString)) {
11459                 char *p = StringValuePtr(val);
11460                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11461                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11462                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11463                         if (strcmp(buf, p) == 0 && --k == 0)
11464                                 break;
11465                 }
11466         } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11467                 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11468                 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11469                         VALUE name;
11470                         MoleculeCallback_displayName(mol, buf, sizeof buf);
11471                         name = Ruby_NewEncodedStringValue2(buf);
11472                         if (rb_reg_match(val, name) != Qnil && --k == 0)
11473                                 break;
11474                 }       
11475         } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11476         
11477         if (mol == NULL)
11478                 return Qnil;
11479         else return ValueFromMolecule(mol);
11480 }
11481
11482 /*
11483  *  call-seq:
11484  *     list         -> array of Molecules
11485  *
11486  *  Get the list of molecules associated to the documents, in the order of creation
11487  *  time of the document. If no document is open, returns an empry array.
11488  */
11489 static VALUE
11490 s_Molecule_List(VALUE klass)
11491 {
11492         Molecule *mol;
11493         int i;
11494         VALUE ary;
11495         i = 0;
11496         ary = rb_ary_new();
11497         while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11498                 rb_ary_push(ary, ValueFromMolecule(mol));
11499                 i++;
11500         }
11501         return ary;
11502 }
11503
11504 /*
11505  *  call-seq:
11506  *     ordered_list         -> array of Molecules
11507  *
11508  *  Get the list of molecules associated to the documents, in the order of front-to-back
11509  *  ordering of the associated window. If no document is open, returns an empry array.
11510  */
11511 static VALUE
11512 s_Molecule_OrderedList(VALUE klass)
11513 {
11514         Molecule *mol;
11515         int i;
11516         VALUE ary;
11517         i = 0;
11518         ary = rb_ary_new();
11519         while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11520                 rb_ary_push(ary, ValueFromMolecule(mol));
11521                 i++;
11522         }
11523         return ary;
11524 }
11525
11526 #pragma mark ------ Call Subprocess ------
11527
11528 /*  The callback functions for call_subprocess_async  */
11529 static int
11530 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11531 {
11532         int ruby_status;
11533         VALUE procval, retval, args[2];
11534         args[0] = ValueFromMolecule(mol);
11535         args[1] = INT2NUM(status);
11536         procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11537         if (procval != Qnil) {
11538                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11539                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11540                         return 1;
11541         }
11542         return 0;
11543 }
11544
11545 static int
11546 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11547 {
11548         int ruby_status;
11549         VALUE procval, retval, args[2];
11550         args[0] = ValueFromMolecule(mol);
11551         args[1] = INT2NUM(tcount);
11552         procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11553         if (procval != Qnil) {
11554                 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11555                 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11556                         return 1;
11557         }
11558         return 0;
11559 }
11560
11561 /*
11562  *  call-seq:
11563  *     call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11564  *
11565  *  Call subprocess asynchronically.
11566  *  cmd is either a single string of an array of string. If it is a single string, then
11567  *  it will be given to wxExecute as a single argument. In this case, the string can be
11568  *  split into arguments by whitespace. If this behavior is not intended, then use an array
11569  *  containing a single string.
11570  *  If end_callback is given, it will be called (with two arguments self and termination status)
11571  *  when the subprocess terminated.
11572  *  If timer_callback is given, it will be called (also with two arguments, self and timer count).
11573  *  If timer_callback returns nil or false, then the subprocess will be interrupted.
11574  *  If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11575  *  filename begins with ">>", then the message will be appended to the file.
11576  *  If the filename is "/dev/null" or "NUL", then the message will be lost.
11577  *  If the argument is nil, then the message will be sent to the Ruby console.
11578  *  Returns the process ID as an integer.
11579  */
11580 static VALUE
11581 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11582 {
11583         VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11584         Molecule *mol;
11585         char *sout, *serr;
11586     const char **cmdargv;
11587         int n;
11588         FILE *fpout, *fperr;
11589         rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11590         Data_Get_Struct(self, Molecule, mol);
11591
11592         if (stdout_val == Qnil) {
11593                 fpout = (FILE *)1;
11594         } else {
11595                 sout = StringValuePtr(stdout_val);
11596                 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11597                         fpout = NULL;
11598                 else {
11599                         if (strncmp(sout, ">>", 2) == 0) {
11600                                 sout += 2;
11601                                 fpout = fopen(sout, "a");
11602                         } else {
11603                                 if (*sout == '>')
11604                                         sout++;
11605                                 fpout = fopen(sout, "w");
11606                         }
11607                         if (fpout == NULL)
11608                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11609                 }
11610         }
11611         if (stderr_val == Qnil) {
11612                 fperr = (FILE *)1;
11613         } else {
11614                 serr = StringValuePtr(stderr_val);
11615                 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11616                         fperr = NULL;
11617                 else {
11618                         if (strncmp(serr, ">>", 2) == 0) {
11619                                 serr += 2;
11620                                 fpout = fopen(serr, "a");
11621                         } else {
11622                                 if (*serr == '>')
11623                                         serr++;
11624                                 fperr = fopen(serr, "w");
11625                         }
11626                         if (fperr == NULL)
11627                                 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11628                 }
11629         }
11630         
11631         /*  Register procs as instance variables  */
11632         rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11633         rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11634     
11635     if (rb_obj_is_kind_of(cmd, rb_cString)) {
11636         cmdargv = calloc(sizeof(cmdargv[0]), 3);
11637         cmdargv[0] = StringValuePtr(cmd);
11638         cmdargv[1] = "";
11639         cmdargv[2] = NULL;
11640     } else {
11641         cmd = rb_ary_to_ary(cmd);
11642         cmdargv = calloc(sizeof(cmdargv[0]), RARRAY_LEN(cmd) + 1);
11643         for (n = 0; n < RARRAY_LEN(cmd); n++) {
11644             cmdargv[n] = StringValuePtr(RARRAY_PTR(cmd)[n]);
11645         }
11646         cmdargv[n] = NULL;
11647     }
11648         n = MoleculeCallback_callSubProcessAsync(mol, cmdargv, s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11649         if (fpout != NULL && fpout != (FILE *)1)
11650                 fclose(fpout);
11651         if (fperr != NULL && fperr != (FILE *)1)
11652                 fclose(fperr);
11653         return INT2NUM(n);
11654 }
11655
11656 #pragma mark ====== Define Molby Classes ======
11657
11658 void
11659 Init_Molby(void)
11660 {
11661         int i;
11662         
11663         /*  Define module Molby  */
11664         rb_mMolby = rb_define_module("Molby");
11665         
11666         /*  Define Vector3D, Transform, IntGroup  */
11667         Init_MolbyTypes();
11668         
11669         /*  Define MDArena  */
11670         Init_MolbyMDTypes();
11671
11672         /*  class Molecule  */
11673         rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11674
11675         rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11676     rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11677     rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11678         rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11679         rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11680
11681     rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11682     rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11683     rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11684     rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11685     rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11686     rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11687     rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11688     rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11689     rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11690     rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11691         rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11692     rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11693     rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11694     rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11695     rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11696     rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11697     rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11698     rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11699         rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11700         rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11701         rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11702         rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11703         
11704     rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11705         rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11706     rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11707     rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11708     rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11709
11710     rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11711     rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11712     rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11713     rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11714     rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11715     rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11716         rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11717         rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11718         rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11719         rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11720         rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11721         rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11722         rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11723         rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11724         rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11725         
11726         rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11727         rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11728         rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11729         rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11730         rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11731         
11732         rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11733     rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11734         rb_define_alias(rb_cMolecule, "+", "add");
11735     rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11736         rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11737         rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11738         rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11739         rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11740         rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11741         rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11742         rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11743         rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11744         rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11745         rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11746         rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11747         rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11748         rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11749         rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11750         rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11751         rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11752         rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11753         rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11754         rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11755         rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11756
11757         rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11758         rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11759         rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11760         rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11761         rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11762
11763         rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11764         rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11765         rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11766         rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11767         rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11768         rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11769         rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11770         rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11771         rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11772
11773         rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11774         rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11775         rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11776         rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11777         rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11778         rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11779         rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11780         rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11781         rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11782         rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11783         rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11784         rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11785         rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11786         rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11787         rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11788         rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11789         rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11790         rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11791         rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11792         rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11793         rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11794         rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11795
11796         rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11797         rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11798         rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11799         rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11800         rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11801         rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11802         rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11803
11804         rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11805         rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11806         rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11807         rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11808         rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11809         rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11810         rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11811         rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11812         rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11813         rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11814         rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11815         rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11816         rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11817
11818         rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11819         rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11820         rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11821         rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11822         rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11823         rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11824
11825         rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11826         rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11827         rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11828         rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);      
11829         rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11830         rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11831         rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11832         rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11833         rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11834         rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11835         rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11836         rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11837         rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11838         rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11839         rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0);  /*  obsolete  */
11840         rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1);  /*  obsolete  */
11841         rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms=");  /*  obsolete  */
11842         rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11843         rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11844         rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11845         rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11846         rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11847         rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11848         rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11849         rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11850         rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11851         rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11852         rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11853         rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11854         rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11855         rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11856         rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11857         rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11858         rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11859         rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11860         rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11861         rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11862         rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11863         rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11864         rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11865         rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11866         rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11867         rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11868         rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11869         rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11870         rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11871         rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11872         rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11873         rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11874         rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11875         rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11876         rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11877         rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11878         rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11879         
11880         rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11881         rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11882         rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11883         rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11884         rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11885         rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11886         rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11887         rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11888         rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11889         rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11890         rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11891         rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11892         rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11893
11894         rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11895         rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11896         rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11897         rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11898         rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11899         rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11900         rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11901         rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11902         rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11903         rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11904         rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11905         rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11906         rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11907         rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11908                 
11909         rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11910         rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11911         rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11912         rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11913         rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11914         rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11915         rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11916         rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11917         rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11918         rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11919         rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11920         rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, -1);
11921         rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11922         rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11923         rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11924         rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11925         rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11926         rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11927         rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11928         rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11929         rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11930         rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11931         rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11932
11933         rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11934         rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11935         
11936         rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11937         rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11938         rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11939                 
11940         rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11941         rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11942         rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11943         rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11944         
11945         rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11946         
11947         /*  class MolEnumerable  */
11948         rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11949     rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11950         rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11951         rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11952     rb_define_alias(rb_cMolEnumerable, "size", "length");
11953         rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11954         rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11955
11956         /*  class AtomRef  */
11957         rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11958         for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11959                 char buf[64];
11960                 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11961                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11962                 s_AtomAttrDefTable[i].id = rb_intern(buf);
11963                 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11964                 strcat(buf, "=");
11965                 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11966         }
11967         rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11968         rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11969         rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11970         rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11971         s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11972         rb_global_variable(&s_SetAtomAttrString);
11973         rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11974         rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11975
11976         /*  class Parameter  */
11977         rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11978         rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11979         rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11980         rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11981         rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11982         rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11983         rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11984         rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11985         rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11986         rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11987         rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11988         rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11989         rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11990         rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11991         rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11992         rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11993         rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11994         rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11995         rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11996         rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11997         rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11998         rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11999         rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
12000         rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12001         rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12002         rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12003         rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
12004         rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
12005         rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
12006         rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
12007         rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
12008         rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
12009         rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
12010         rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
12011         rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
12012         rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
12013         rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
12014         rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
12015         rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
12016         rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
12017         rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
12018         rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
12019         rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
12020         rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
12021         rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
12022         rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
12023         rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
12024         rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
12025         rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
12026         rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
12027         rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12028         rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12029         rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12030         rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
12031
12032         /*  class ParEnumerable  */
12033         rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
12034     rb_include_module(rb_cParEnumerable, rb_mEnumerable);
12035         rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
12036         rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
12037         rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
12038         rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
12039         rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
12040         rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
12041         rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
12042         rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
12043         rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
12044         
12045         /*  class ParameterRef  */
12046         rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
12047         for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
12048                 char buf[64];
12049                 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
12050                 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
12051                 s_ParameterAttrDefTable[i].id = rb_intern(buf);
12052                 if (s_ParameterAttrDefTable[i].symref != NULL)
12053                         *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
12054                 if (s_ParameterAttrDefTable[i].setter != NULL) {
12055                         strcat(buf, "=");
12056                         rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
12057                 }
12058         }
12059         rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
12060         rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
12061         rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
12062         rb_define_alias(rb_cParameterRef, "get_attr", "[]");
12063         rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
12064         rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
12065         rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
12066         rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
12067
12068         /*  class MolbyError  */
12069         rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
12070
12071         /*  module Kernel  */
12072         rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
12073         rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
12074         rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
12075         rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
12076         rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
12077         rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
12078         rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
12079         rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12080         rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12081         rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12082         rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12083         rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12084         rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12085         rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12086         rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12087         rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12088         rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12089         rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12090         rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12091         rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12092         rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12093         rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12094         rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12095         rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12096     rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12097     rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12098     rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12099     rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12100     rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12101     rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12102
12103         /*  class IO  */
12104         rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12105         
12106         s_ID_equal = rb_intern("==");
12107         g_RubyID_call = rb_intern("call");
12108         
12109         s_InitMOInfoKeys();
12110         
12111         /*  Symbols for graphics  */
12112         s_LineSym = ID2SYM(rb_intern("line"));
12113         s_PolySym = ID2SYM(rb_intern("poly"));
12114         s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12115         s_ConeSym = ID2SYM(rb_intern("cone"));
12116         s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12117 }
12118
12119 #pragma mark ====== Interface with RubyDialog class ======
12120
12121 RubyValue
12122 RubyDialogCallback_parentModule(void)
12123 {
12124         return (RubyValue)rb_mMolby;
12125 }
12126
12127 #pragma mark ====== External functions ======
12128
12129 static VALUE s_ruby_top_self = Qfalse;
12130 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12131 static VALUE s_ruby_export_local_variables = Qfalse;
12132
12133 static VALUE
12134 s_evalRubyScriptOnMoleculeSub(VALUE val)
12135 {
12136         void **ptr = (void **)val;
12137         Molecule *mol = (Molecule *)ptr[1];
12138         VALUE sval, fnval, lnval, retval;
12139         VALUE binding;
12140
12141         /*  Clear the error information (store in the history array if necessary)  */
12142         sval = rb_errinfo();
12143         if (sval != Qnil) {
12144                 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12145                 rb_set_errinfo(Qnil);
12146         }
12147
12148         if (s_ruby_top_self == Qfalse) {
12149                 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12150         }
12151         if (s_ruby_get_binding_for_molecule == Qfalse) {
12152                 const char *s1 =
12153                  "lambda { |_mol_, _bind_| \n"
12154                  "  _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12155                  "  _proc_.call(_mol_) } ";
12156                 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12157                 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12158         }
12159         if (s_ruby_export_local_variables == Qfalse) {
12160                 const char *s2 =
12161                 "lambda { |_bind_| \n"
12162                 "   # find local variables newly defined in _bind_ \n"
12163                 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12164                 " _a_.each { |_vsym_| \n"
12165                 "   _vname_ = _vsym_.to_s \n"
12166                 "   _vval_ = _bind_.eval(_vname_) \n"
12167                 "   #  Define local variable \n"
12168                 "   TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12169                 "   #  Then set value  \n"
12170                 "   TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12171                 " } \n"
12172                 "}";
12173                 s_ruby_export_local_variables = rb_eval_string(s2);
12174                 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12175         }
12176         if (ptr[2] == NULL) {
12177                 char *scr;
12178                 /*  String literal: we need to specify string encoding  */
12179 #if defined(__WXMSW__)
12180                 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12181 #else
12182                 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12183 #endif
12184                 sval = Ruby_NewEncodedStringValue2(scr);
12185                 free(scr);
12186                 fnval = Ruby_NewEncodedStringValue2("(eval)");
12187                 lnval = INT2FIX(0);
12188         } else {
12189                 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12190                 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12191                 lnval = INT2FIX(1);
12192         }
12193         binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12194         if (mol != NULL) {
12195                 VALUE mval = ValueFromMolecule(mol);
12196                 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12197         }
12198         retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12199         if (mol != NULL) {
12200                 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12201         }
12202         return retval;
12203 }
12204
12205 RubyValue
12206 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12207 {
12208         RubyValue retval;
12209         void *args[3];
12210         VALUE save_interrupt_flag;
12211 /*      char *save_ruby_sourcefile;
12212         int save_ruby_sourceline; */
12213         if (gMolbyIsCheckingInterrupt) {
12214                 MolActionAlertRubyIsRunning();
12215                 *status = -1;
12216                 return (RubyValue)Qnil;
12217         }
12218         gMolbyRunLevel++;
12219         args[0] = (void *)script;
12220         args[1] = (void *)mol;
12221         args[2] = (void *)fname;
12222         save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12223 /*      save_ruby_sourcefile = ruby_sourcefile;
12224         save_ruby_sourceline = ruby_sourceline; */
12225         retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12226         if (*status != 0) {
12227                 /*  Is this 'exit' exception?  */
12228                 VALUE last_exception = rb_gv_get("$!");
12229                 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12230                         /*  Capture exit and return the status value  */
12231                         retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12232                         *status = 0;
12233                         rb_set_errinfo(Qnil);
12234                 }
12235         }
12236         s_SetInterruptFlag(Qnil, save_interrupt_flag);
12237 /*      ruby_sourcefile = save_ruby_sourcefile;
12238         ruby_sourceline = save_ruby_sourceline; */
12239         gMolbyRunLevel--;
12240         return retval;
12241 }
12242
12243 /*  For debug  */
12244 char *
12245 Ruby_inspectValue(RubyValue value)
12246 {
12247     int status;
12248     static char buf[256];
12249     VALUE val = (VALUE)value;
12250     gMolbyRunLevel++;
12251     val = rb_protect(rb_inspect, val, &status);
12252     gMolbyRunLevel--;
12253     if (status == 0) {
12254         char *str = StringValuePtr(val);
12255         strncpy(buf, str, sizeof(buf) - 1);
12256         buf[sizeof(buf) - 1] = 0;
12257     } else {
12258         snprintf(buf, sizeof(buf), "Error status = %d", status);
12259     }
12260     return buf;
12261 }
12262
12263 int
12264 Ruby_showValue(RubyValue value, char **outValueString)
12265 {
12266         VALUE val = (VALUE)value;
12267         if (gMolbyIsCheckingInterrupt) {
12268                 MolActionAlertRubyIsRunning();
12269                 return 0;
12270         }
12271         if (val != Qnil) {
12272                 int status;
12273                 char *str;
12274                 gMolbyRunLevel++;
12275                 val = rb_protect(rb_inspect, val, &status);
12276                 gMolbyRunLevel--;
12277                 if (status != 0)
12278                         return status;
12279                 str = StringValuePtr(val);
12280                 if (outValueString != NULL)
12281                         *outValueString = strdup(str);
12282                 MyAppCallback_showScriptMessage("%s", str);
12283         } else {
12284                 if (outValueString != NULL)
12285                         *outValueString = NULL;
12286         }
12287         return 0;
12288 }
12289
12290 void
12291 Ruby_showError(int status)
12292 {
12293         static const int tag_raise = 6;
12294     char *main_message = "Molby script error";
12295         char *msg = NULL, *msg2;
12296         VALUE val, backtrace;
12297         int interrupted = 0;
12298     int exit_status = -1;
12299         if (status == tag_raise) {
12300                 VALUE errinfo = rb_errinfo();
12301                 VALUE eclass = CLASS_OF(errinfo);
12302                 if (eclass == rb_eInterrupt) {
12303             main_message = "Molby script interrupted";
12304             msg = "Interrupt";
12305                         interrupted = 1;
12306         } else if (eclass == rb_eSystemExit) {
12307             main_message = "Molby script exit";
12308             interrupted = 2;
12309             val = rb_eval_string_protect("$!.status", &status);
12310             if (status == 0) {
12311                 exit_status = NUM2INT(rb_Integer(val));
12312                 asprintf(&msg, "Molby script exit with status %d", exit_status);
12313             } else {
12314                 asprintf(&msg, "Molby script exit with unknown status");
12315             }
12316         }
12317         }
12318         gMolbyRunLevel++;
12319     if (exit_status != 0) {
12320         backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12321         if (msg == NULL) {
12322             val = rb_eval_string_protect("$!.to_s", &status);
12323             if (status == 0)
12324                 msg = RSTRING_PTR(val);
12325             else
12326                 msg = "(message not available)";
12327         }
12328         asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12329     } else {
12330         msg2 = strdup(msg);
12331     }
12332         MyAppCallback_messageBox(msg2, main_message, 0, 3);
12333         free(msg2);
12334     if (interrupted == 2) {
12335         free(msg);
12336         if (!gUseGUI && exit_status == 0)
12337             exit(0);  // Capture exit(0) here and force exit
12338     }
12339         gMolbyRunLevel--;
12340 }
12341
12342 /*  Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode.  */
12343 int
12344 Molby_loadScript(const char *script, int from_file)
12345 {
12346     int status;
12347     gMolbyRunLevel++;
12348     if (from_file)
12349         rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12350     else
12351         rb_eval_string_protect(script, &status);
12352     gMolbyRunLevel--;
12353     return status;
12354 }
12355
12356 void
12357 Molby_getDescription(char **versionString, char **auxString)
12358 {
12359         extern const char *gVersionString, *gCopyrightString;
12360         extern int gRevisionNumber;
12361         extern char *gLastBuildString;
12362     char *s1, *s2;
12363         char *revisionString;
12364         if (gRevisionNumber > 0) {
12365                 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12366         } else revisionString = "";
12367
12368     asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12369 #if defined(__WXMSW__)
12370     #if TARGET_ARCH == 64
12371              "Molby (64 bit)",
12372     #else
12373              "Molby (32 bit)",
12374     #endif
12375 #else
12376              "Molby",
12377 #endif
12378              gVersionString, revisionString, gCopyrightString, gLastBuildString);
12379     if (gUseGUI) {
12380         asprintf(&s2,
12381                  "\nIncluding:\n"
12382                  "%s"
12383                  "ruby %s, http://www.ruby-lang.org/\n"
12384                  "%s\n"
12385                  "FFTW 3.3.2, http://www.fftw.org/\n"
12386                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12387                  "  and Massachusetts Institute of Technology\n"
12388                  "JANPA 2.01, https://janpa.sourceforge.net/\n"
12389                  "  Copyright (C) 2014, Tymofii Nikolaienko",
12390                  MyAppCallback_getGUIDescriptionString(),
12391                  gRubyVersion, gRubyCopyright);
12392     } else {
12393         asprintf(&s2,
12394                  "Including "
12395                  "ruby %s, http://www.ruby-lang.org/\n"
12396                  "%s\n"
12397                  "FFTW 3.3.2, http://www.fftw.org/\n"
12398                  "  Copyright (C) 2003, 2007-11 Matteo Frigo"
12399                  "  and Massachusetts Institute of Technology\n"
12400                  "JANPA 2.01, https://janpa.sourceforge.net/\n"
12401                  "  Copyright (C) 2014, Tymofii Nikolaienko",
12402                  gRubyVersion, gRubyCopyright);
12403
12404     }
12405         if (revisionString[0] != 0)
12406                 free(revisionString);
12407         if (versionString != NULL)
12408         *versionString = s1;
12409     if (auxString != NULL)
12410         *auxString = s2;
12411 }
12412
12413 void
12414 Molby_startup(const char *script, const char *dir)
12415 {
12416         VALUE val;
12417         int status;
12418         char *libpath;
12419         char *respath, *p, *wbuf;
12420
12421         /*  Get version/copyright string from Ruby interpreter  */
12422         {
12423                 gRubyVersion = strdup(ruby_version);
12424                 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12425                                  "  ",  /*  Indent for displaying in About dialog  */
12426                                  RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12427         }
12428         
12429         /*  Read build and revision information for Molby  */
12430 /*      {
12431                 char buf[200];
12432                 extern int gRevisionNumber;
12433                 extern char *gLastBuildString;
12434                 FILE *fp = fopen("../buildInfo.txt", "r");
12435                 gLastBuildString = "";
12436                 if (fp != NULL) {
12437                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12438                                 char *p1 = strchr(buf, '\"');
12439                                 char *p2 = strrchr(buf, '\"');
12440                                 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12441                                         memmove(buf, p1 + 1, p2 - p1 - 1);
12442                                         buf[p2 - p1 - 1] = 0;
12443                                         asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12444                                 }
12445                         }
12446                         fclose(fp);
12447                 }
12448                 fp = fopen("../revisionInfo.txt", "r");
12449                 gRevisionNumber = 0;
12450                 if (fp != NULL) {
12451                         if (fgets(buf, sizeof(buf), fp) != NULL) {
12452                                 gRevisionNumber = strtol(buf, NULL, 0);
12453                         }
12454                         fclose(fp);
12455                 }
12456     } */
12457
12458     if (!gUseGUI) {
12459         char *wbuf2;
12460         Molby_getDescription(&wbuf, &wbuf2);
12461         MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12462         free(wbuf);
12463         free(wbuf2);
12464     }
12465         
12466         /*  Read atom display parameters  */
12467         if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12468         MyAppCallback_setConsoleColor(1);
12469         MyAppCallback_showScriptMessage("%s", wbuf);
12470         MyAppCallback_setConsoleColor(0);
12471                 free(wbuf);
12472         }
12473         
12474         /*  Read default parameters  */
12475         ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12476         if (wbuf != NULL) {
12477         MyAppCallback_setConsoleColor(1);
12478         MyAppCallback_showScriptMessage("%s", wbuf);
12479         MyAppCallback_setConsoleColor(0);
12480                 free(wbuf);
12481         }
12482                 
12483         /*  Initialize Ruby interpreter  */
12484 #if __WXMSW__
12485         if (gUseGUI) {
12486                 /*  On Windows, fileno(stdin|stdout|stderr) returns -2 and
12487                     it causes rb_bug() (= fatal error) during ruby_init().
12488                     As a workaround, these standard streams are reopend as
12489                     NUL stream.  */
12490                 freopen("NUL", "r", stdin);
12491                 freopen("NUL", "w", stdout);
12492                 freopen("NUL", "w", stderr);
12493         }
12494 #endif
12495         ruby_init();
12496
12497         {
12498         /*  Initialize CP932/Windows-31J encodings  */
12499                 extern void Init_shift_jis(void), Init_windows_31j(void),  Init_trans_japanese_sjis(void);
12500         extern int rb_enc_alias(const char *, const char *);
12501         Init_shift_jis();
12502         Init_windows_31j();
12503         Init_trans_japanese_sjis();
12504         rb_enc_alias("CP932", "Windows-31J");
12505     }
12506     
12507 #if defined(__WXMSW__)
12508     {
12509         /*  Set default external encoding  */
12510         /*  The following snippet is taken from encoding.c  */
12511         extern void rb_enc_set_default_external(VALUE encoding);
12512         char cp[sizeof(int) * 8 / 3 + 22];
12513         int status;
12514         VALUE enc;
12515         snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12516         enc = rb_eval_string_protect(cp, &status);
12517         if (status == 0 && !NIL_P(enc)) {
12518             rb_enc_set_default_external(enc);
12519         }
12520         }
12521 #endif
12522
12523         /*  Initialize loadpath; the specified directory, "lib" subdirectory, and "."  */
12524         ruby_incpush(".");
12525         asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12526         ruby_incpush(libpath);
12527         free(libpath);
12528         ruby_incpush(dir);
12529
12530         ruby_script("Molby");
12531         
12532         /*  Find the resource path (the parent directory of the given directory)  */
12533         respath = strdup(dir);
12534         p = strrchr(respath, '/');
12535         if (p == NULL && PATH_SEPARATOR != '/')
12536                 p = strrchr(respath, PATH_SEPARATOR);
12537         if (p != NULL)
12538                 *p = 0;
12539         val = Ruby_NewFileStringValue(respath);
12540         rb_define_global_const("MolbyResourcePath", val);
12541         free(respath);
12542
12543         /*  Define Molby classes  */
12544         Init_Molby();
12545     if (gUseGUI)
12546         RubyDialogInitClass();
12547
12548         rb_define_const(rb_mMolby, "ResourcePath", val);
12549         val = Ruby_NewFileStringValue(dir);
12550         rb_define_const(rb_mMolby, "ScriptPath", val);
12551         asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12552         val = Ruby_NewFileStringValue(p);
12553         rb_define_const(rb_mMolby, "MbsfPath", val);    
12554         free(p);
12555         
12556         p = MyAppCallback_getHomeDir();
12557         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12558         rb_define_const(rb_mMolby, "HomeDirectory", val);
12559         free(p);
12560         p = MyAppCallback_getDocumentHomeDir();
12561         val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12562         rb_define_const(rb_mMolby, "DocumentDirectory", val);
12563         free(p);
12564         
12565     if (gUseGUI)
12566         rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12567     else
12568         rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12569
12570     {
12571         /*  Create objects for stdout and stderr  */
12572         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12573         rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12574         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12575         rb_gv_set("$stdout", val);
12576         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12577         rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12578         rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12579         rb_gv_set("$stderr", val);
12580
12581         /*  Create objects for stdin  */
12582         val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12583         rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12584         rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12585         rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12586         rb_gv_set("$stdin", val);
12587     }
12588         
12589         /*  Global variable to hold error information  */
12590         rb_define_variable("$backtrace", &gMolbyBacktrace);
12591         rb_define_variable("$error_history", &gMolbyErrorHistory);
12592         gMolbyErrorHistory = rb_ary_new();
12593         
12594         /*  Global variables for script menus  */
12595         rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12596         rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12597         gScriptMenuCommands = rb_ary_new();
12598         gScriptMenuEnablers = rb_ary_new();
12599         
12600     if (gUseGUI) {
12601         /*  Register interrupt check code  */
12602         rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12603         /*  Start interval timer (for periodic polling of interrupt); firing every 50 msec  */
12604         s_SetIntervalTimer(0, 50);
12605     }
12606         
12607         /*  Read the startup script  */
12608         if (script != NULL && script[0] != 0) {
12609                 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12610                 gMolbyRunLevel++;
12611                 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12612                 gMolbyRunLevel--;
12613                 if (status != 0)
12614                         Ruby_showError(status);
12615                 else
12616                         MyAppCallback_showScriptMessage("Done.\n");
12617         }
12618 }
12619
12620 void
12621 Molby_buildARGV(int argc, const char **argv)
12622 {
12623         int i;
12624     rb_ary_clear(rb_argv);
12625     for (i = 0; i < argc; i++) {
12626                 VALUE arg = rb_tainted_str_new2(argv[i]);
12627                 OBJ_FREEZE(arg);
12628                 rb_ary_push(rb_argv, arg);
12629     }
12630 }