1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2009-06-12 07:58:28 -0500 (Fri, 12 Jun 2009) $
4  * $Revision: 11009 $
5  *
6  * Copyright (C) 2003-2006  Miguel, Jmol Development, www.jmol.org
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package org.jmol.script;
25 
26 import java.util.Hashtable;
27 import java.util.Map;
28 
29 import javajs.util.A4;
30 import javajs.util.AU;
31 import javajs.util.CU;
32 import javajs.util.DF;
33 import javajs.util.Lst;
34 import javajs.util.M3;
35 import javajs.util.M4;
36 import javajs.util.P3;
37 import javajs.util.P4;
38 import javajs.util.PT;
39 import javajs.util.Quat;
40 import javajs.util.T3;
41 import javajs.util.V3;
42 
43 import javajs.util.BS;
44 import org.jmol.modelset.BondSet;
45 import org.jmol.util.BSUtil;
46 import org.jmol.util.BoxInfo;
47 import org.jmol.util.Escape;
48 import org.jmol.util.Logger;
49 import org.jmol.viewer.Viewer;
50 
51 /**
52  * Reverse Polish Notation Engine for IF, SET, and @{...}
53  *
54  * Just a (not so simple?) RPN processor that can handle boolean, int, float,
55  * String, Point3f, BitSet, Array, Hashtable, Matrix3f, Matrix4f
56  *
57  * -- Bob Hanson 2/16/2007
58  *
59  * @author hansonr
60  *
61  */
62 public class ScriptMathProcessor {
63 
64   public boolean wasX;
65   public boolean asBitSet;
66   public int oPt = -1; // used in asynchronous load to mark which file is being loaded
67 
68   private boolean chk;
69   private boolean wasSyntaxCheck;
70   private boolean debugHigh;
71   private ScriptExpr eval;
72   private Viewer vwr;
73 
74   private T[] oStack = new T[8];
75   private SV[] xStack = new SV[8];
76   private char[] ifStack = new char[8];
77   private int ifPt = -1;
78   private int xPt = -1;
79   private int parenCount;
80   private int squareCount;
81   private int braceCount;
82   private boolean isArrayItem;
83   private boolean asVector;
84 
85   private boolean haveSpaceBeforeSquare;
86   private int equalCount;
87 
88   private int ptid = 0;
89   private int ptx = Integer.MAX_VALUE;
90   private int pto = Integer.MAX_VALUE;
91   private boolean isSpecialAssignment;
92   private boolean doSelections = true;
93   private boolean assignLeft;
94   private boolean allowUnderflow;
95   private boolean isAssignment;
96 
97   /**
98    *
99    * @param eval
100    * @param isSpecialAssignment
101    *       x[n] = ...
102    * @param isArrayItem
103    * @param asVector
104    *       return a Lst(SV) from getResult()
105    * @param asBitSet
106    *       return a (SV)bitset
107    * @param allowUnderflow
108    *       expression can terminate prior to end of statement
109    *
110    * @param key
111    */
ScriptMathProcessor(ScriptExpr eval, boolean isSpecialAssignment, boolean isArrayItem, boolean asVector, boolean asBitSet, boolean allowUnderflow, String key)112   ScriptMathProcessor(ScriptExpr eval, boolean isSpecialAssignment, boolean isArrayItem,
113       boolean asVector, boolean asBitSet, boolean allowUnderflow, String key) {
114     this.eval = eval;
115     this.isSpecialAssignment = assignLeft = isSpecialAssignment;
116     this.isAssignment = (isSpecialAssignment || key != null);
117     this.vwr = eval.vwr;
118     this.debugHigh = eval.debugHigh;
119     this.chk = wasSyntaxCheck = eval.chk;
120     this.isArrayItem = isArrayItem;
121     this.asVector = asVector || isArrayItem;
122     this.asBitSet = asBitSet;
123     this.allowUnderflow = allowUnderflow; // atom expressions only
124     wasX = isArrayItem;
125     if (debugHigh)
126       Logger.debug("initialize RPN");
127   }
128 
endAssignment()129   public boolean endAssignment() {
130     assignLeft = false;
131     return (doSelections = false);
132   }
133 
134   @SuppressWarnings("unchecked")
getResult()135   SV getResult() throws ScriptException {
136     boolean isOK = true;
137     while (isOK && oPt >= 0 && oStack[oPt] != null)
138       isOK = operate();
139     if (isOK) {
140       if (asVector) {
141         // check for y = x x  or  y = x + ;
142         if (isAssignment && (xPt > 0 && oPt < 0 || oPt >= 0 && (oStack[oPt] != null)))
143           eval.invArg();
144         Lst<SV> result = new Lst<SV>();
145         for (int i = 0; i <= xPt; i++)
146           result.addLast(isSpecialAssignment ? xStack[i] : SV.selectItemVar(xStack[i]));
147         if (lastAssignedString != null) {
148           result.removeItemAt(0);
149           result.add(0, lastAssignedString);
150           lastAssignedString.intValue = xStack[0].intValue;
151         }
152         return SV.newV(T.vector, result);
153       }
154       if (xPt == 0) {
155         SV x = xStack[0];
156         if (chk) {
157           if (asBitSet)
158             return SV.newV(T.bitset, new BS());
159           return x;
160         }
161         if (x.tok == T.bitset
162             || x.tok == T.varray || x.tok == T.barray
163             || x.tok == T.string
164             || x.tok == T.matrix3f || x.tok == T.matrix4f)
165           x = SV.selectItemVar(x);
166         if (asBitSet && x.tok == T.varray)
167           x = SV.newV(T.bitset,
168               SV.unEscapeBitSetArray((Lst<SV>) x.value, false));
169         return x;
170       }
171     }
172     if (!allowUnderflow && (xPt >= 0 || oPt >= 0))
173       eval.invArg();
174     return null;
175   }
176 
putX(SV x)177   private void putX(SV x) {
178     if (skipping)
179       return;
180     if (wasX) {
181       try {
182         addOp(T.tokenComma);
183       } catch (ScriptException e) {
184        // System.out.println("Error adding comma");
185       }
186     }
187     if (++xPt == xStack.length)
188       xStack = (SV[]) AU.doubleLength(xStack);
189     if (xPt < 0)
190       System.out.println("testing scriptemaafe");
191     xStack[xPt] = x;
192     ptx = ++ptid;
193     if (debugHigh) {
194       Logger.debug("\nputx= " + x + " ptx=" + ptid);
195     }
196   }
197 
putOp(T op)198   private void putOp(T op) {
199     if (++oPt >= oStack.length)
200       oStack = (T[]) AU.doubleLength(oStack);
201     oStack[oPt] = op;
202     pto = ++ptid;
203     if (debugHigh) {
204       Logger.debug("\nputop=" + op + " pto=" + ptid);
205     }
206   }
207 
putIf(char c)208   private void putIf(char c) {
209     if (++ifPt >= ifStack.length)
210       ifStack = (char[]) AU.doubleLength(ifStack);
211     ifStack[ifPt] = c;
212   }
213 
addXCopy(SV x)214   public boolean addXCopy(SV x) {
215     // copying int and decimal in case of ++ or --
216     switch (x.tok){
217     case T.integer:
218       x = SV.newI(x.intValue);
219       break;
220     case T.decimal:
221       x = SV.newV(T.decimal, x.value);
222       break;
223     }
224     return addX(x);
225   }
226 
addX(SV x)227   public boolean addX(SV x) {
228     // the standard entry point
229     putX(x);
230     return wasX = true;
231   }
232 
addXObj(Object x)233   public boolean addXObj(Object x) {
234     // the generic, slower, entry point
235     SV v = SV.getVariable(x);
236     if (v == null)
237       return false;
238     putX(v);
239     return wasX = true;
240   }
241 
addXStr(String x)242   public boolean addXStr(String x) {
243     putX(SV.newS(x));
244     return wasX = true;
245   }
246 
addXBool(boolean x)247   public boolean addXBool(boolean x) {
248     putX(SV.getBoolean(x));
249     return wasX = true;
250   }
251 
addXInt(int x)252   public boolean addXInt(int x) {
253     // no check for unary minus
254     putX(SV.newI(x));
255     return wasX = true;
256   }
257 
addXList(Lst<?> x)258   public boolean addXList(Lst<?> x) {
259     putX(SV.getVariableList(x));
260     return wasX = true;
261   }
262 
addXMap(Map<String, ?> x)263   public boolean addXMap(Map<String, ?> x) {
264     putX(SV.getVariableMap(x));
265     return wasX = true;
266   }
267 
addXM3(M3 x)268   public boolean addXM3(M3 x) {
269     putX(SV.newV(T.matrix3f, x));
270     return wasX = true;
271   }
272 
addXM4(M4 x)273   public boolean addXM4(M4 x) {
274     putX(SV.newV(T.matrix4f, x));
275     return wasX = true;
276   }
277 
addXFloat(float x)278   public boolean addXFloat(float x) {
279     if (Float.isNaN(x))
280       return addXStr("NaN");
281     putX(SV.newF(x));
282     return wasX = true;
283   }
284 
addXBs(BS bs)285   public boolean addXBs(BS bs) {
286     // the standard entry point for bit sets
287     putX(SV.newV(T.bitset, bs));
288     return wasX = true;
289   }
290 
addXPt(P3 pt)291   public boolean addXPt(P3 pt) {
292     putX(SV.newV(T.point3f, pt));
293     return wasX = true;
294   }
295 
addXPt4(P4 pt)296   public boolean addXPt4(P4 pt) {
297     putX(SV.newV(T.point4f, pt));
298     return wasX = true;
299   }
300 
addXNum(T x)301   public boolean addXNum(T x) throws ScriptException {
302     // corrects for x-3 being x - 3
303     // only when coming from expression() or parameterExpression()
304     // and only when number is not flagged as forced negative
305     // as when x -3
306     SV v;
307     if (x instanceof SV) {
308       v = (SV) x;
309     } else {
310       switch (x.tok) {
311       case T.decimal:
312         if (wasX) {
313           float f = ((Float) x.value).floatValue();
314           if (f < 0 || f == 0 && 1 / f == Float.NEGATIVE_INFINITY) {
315             addOp(T.tokenMinus);
316             v = SV.newF(-f);
317             break;
318           }
319         }
320         v = SV.newV(T.decimal, x.value);
321         break;
322       default:
323         int iv = x.intValue;
324         if (wasX && iv < 0) {
325           addOp(T.tokenMinus);
326           iv = -iv;
327         }
328         v = SV.newI(iv);
329         break;
330       }
331     }
332     putX(v);
333     return wasX = true;
334   }
335 
addXAV(SV[] x)336   public boolean addXAV(SV[] x) {
337     putX(SV.getVariableAV(x));
338     return wasX = true;
339   }
340 
addXAD(double[] x)341   public boolean addXAD(double[] x) {
342     putX(SV.getVariableAD(x));
343     return wasX = true;
344   }
345 
addXAS(String[] x)346   public boolean addXAS(String[] x) {
347     putX(SV.getVariableAS(x));
348     return wasX = true;
349   }
350 
addXAI(int[] x)351   public boolean addXAI(int[] x) {
352     putX(SV.getVariableAI(x));
353     return wasX = true;
354   }
355 
addXAII(int[][] x)356   public boolean addXAII(int[][] x) {
357     putX(SV.getVariableAII(x));
358     return wasX = true;
359   }
360 
addXAF(float[] x)361   public boolean addXAF(float[] x) {
362     putX(SV.getVariableAF(x));
363     return wasX = true;
364   }
365 
addXAFF(float[][] x)366   public boolean addXAFF(float[][] x) {
367     putX(SV.getVariableAFF(x));
368     return wasX = true;
369   }
370 
isOpFunc(T op)371   private static boolean isOpFunc(T op) {
372     return (op != null && (T.tokAttr(op.tok, T.mathfunc) && op != T.tokenArraySquare
373         || op.tok == T.propselector && T.tokAttr(op.intValue, T.mathfunc)));
374   }
375 
376   private boolean skipping;
377   private SV lastAssignedString;
378 
379   /**
380    * addOp The primary driver of the Reverse Polish Notation evaluation engine.
381    *
382    * This method loads operators onto the oStack[] and processes them based on a
383    * precedence system. Operands are added by addX() onto the xStack[].
384    *
385    * We check here for syntax issues that were not caught in the compiler. I
386    * suppose that should be done at compilation stage, but this is how it is for
387    * now.
388    *
389    * The processing of functional arguments and (___?___:___) constructs is
390    * carried out by pushing markers onto the stacks that later can be used to
391    * fill argument lists or turn "skipping" on or off. Note that in the case of
392    * skipped sections of ( ? : ) no attempt is made to do syntax checking.
393    * [That's not entirely true -- when syntaxChecking is true, that is, when the
394    * user is typing at the Jmol application console, then this code is being
395    * traversed with dummy variables. That could be improved, for sure.
396    *
397    * Actually, there's plenty of room for improvement here. I did this based on
398    * what I learned in High School in 1974 -- 35 years ago! -- when I managed to
399    * build a mini FORTRAN compiler from scratch in machine code. That was fun.
400    * (This was fun, too.)
401    *
402    * -- Bob Hanson, hansonr@stolaf.edu 6/9/2009
403    *
404    *
405    * @param op
406    * @return false if an error condition arises
407    * @throws ScriptException
408    */
addOp(T op)409   public boolean addOp(T op) throws ScriptException {
410     return addOpAllowMath(op, true, T.nada);
411   }
412 
addOpAllowMath(T op, boolean allowMathFunc, int tokNext)413   boolean addOpAllowMath(T op, boolean allowMathFunc, int tokNext) throws ScriptException {
414 
415     if (debugHigh) {
416       dumpStacks("adding " + op + " wasx=" + wasX);
417     }
418 
419     // are we skipping due to a ( ? : ) construct?
420     int tok0 = (oPt >= 0 && oStack[oPt] != null ? oStack[oPt].tok : T.nada);
421     skipping = (ifPt >= 0 && (ifStack[ifPt] == 'F' || ifStack[ifPt] == 'X'));
422     if (skipping)
423       return checkSkip(op, tok0);
424 
425     // Do we have the appropriate context for this operator?
426 
427     int tok;
428     boolean isDotSelector = (op.tok == T.propselector);
429 
430     if (isDotSelector && !wasX)
431       return false;
432 
433     boolean isMathFunc = (allowMathFunc && isOpFunc(op));
434 
435     // the word "plane" can also appear alone, not as a function
436     if (oPt >= 1 && op.tok != T.leftparen && tok0 == T.plane)
437       tok0 = oStack[--oPt].tok;
438 
439     // math functions as arguments appear without a prefixing operator
440 
441     // check context, and for some cases, handle them here
442     T newOp = null;
443     boolean isLeftOp = false;
444     switch (op.tok) {
445     case T.spacebeforesquare:
446       haveSpaceBeforeSquare = true;
447       return true;
448     case T.comma:
449       if (!wasX)
450         return false;
451       break;
452     case T.minusMinus:
453     case T.plusPlus:
454       // check for [a ++b]
455       if (wasX && op.intValue == -1 && addOp(T.tokenComma))
456           return addOp(op);
457       break;
458     case T.rightsquare:
459       break;
460     case T.rightparen: // () without argument allowed only for math funcs
461       if (!wasX && oPt >= 1 && tok0 == T.leftparen
462           && !isOpFunc(oStack[oPt - 1]))
463         return false;
464       break;
465     case T.minus:
466       if (!wasX)
467         op = SV.newV(T.unaryMinus, "-");
468       break;
469     case T.min:
470     case T.max:
471     case T.average:
472     case T.sum:
473     case T.sum2:
474     case T.stddev:
475     case T.minmaxmask: // ALL
476       tok = (oPt < 0 ? T.nada : tok0);
477       if (!wasX || !(tok == T.propselector || tok == T.bonds || tok == T.atoms))
478         return false;
479       oStack[oPt].intValue |= op.tok;
480       return true;
481     case T.leftsquare: // {....}[n][m]
482       isLeftOp = true;
483       if (!wasX || haveSpaceBeforeSquare) {
484         squareCount++;
485         op = newOp = T.tokenArraySquare;
486         haveSpaceBeforeSquare = false;
487       }
488       break;
489     case T.opNot:
490     case T.leftparen:
491       isLeftOp = true;
492       //$FALL-THROUGH$
493     default:
494       if (isMathFunc) {
495         boolean isArgument = (oPt >= 1 && tok0 == T.leftparen);
496         if (isDotSelector) {
497           if (tokNext == T.leftparen) {
498           // check for {hash}.x(), which is not allowed
499           // if this is desired, one needs to use {hash}..x()
500             if (xStack[xPt].tok == T.hash)
501               return false;
502           }
503         } else if (wasX && !isArgument) {
504           return false;
505         }
506         newOp = op;
507         isLeftOp = true;
508         break;
509       }
510       if (wasX == isLeftOp && tok0 != T.propselector) {
511         // for now, because we have .label and .label()
512         if (!wasX || !allowMathFunc)
513           return false;
514         if (addOp(T.tokenComma))
515           return addOp(op);
516       }
517       break;
518     }
519 
520     // what is left are standard operators
521 
522     // Q: Do we need to operate?
523     // A: Well, we must have an operator if...
524     while (oPt >= 0
525         // ...that operator is not :,
526         //    because that's part of an array definition
527         && tok0 != T.colon
528         // ... and we don't have ++ or -- coming our way
529         //     with no X on hand or definitely left-operator
530         && (op.tok != T.minusMinus && op.tok != T.plusPlus || wasX)
531         // ...and we do not have x( or x[ or func(....
532         //   because the function must come first
533         //   unless we have x.y.z( or x.y.z[
534         //   in which case we DO need to do that selector first
535         && (!isLeftOp || tok0 == T.propselector
536             && (op.tok == T.propselector || op.tok == T.leftsquare))
537         // ...and previous operator has equal or higher precedence
538         && T.getPrecedence(tok0) >= T.getPrecedence(op.tok)
539         // ...and this is not x - - y, because unary minus operates from
540         //   right to left.
541         && (tok0 != T.unaryMinus || op.tok != T.unaryMinus)) {
542 
543       // ) and ] must wait until matching ( or [ is found
544       if (op.tok == T.rightparen && tok0 == T.leftparen) {
545         // (x[2]) finalizes the selection
546         if (xPt >= 0)
547           xStack[xPt] = SV.selectItemVar(xStack[xPt]);
548         wasX = true;
549         break;
550       }
551       if (op.tok == T.rightsquare && tok0 == T.array) {
552         // we are done; just leave the array on the stack
553         break;
554       }
555       if (op.tok == T.rightsquare && tok0 == T.leftsquare) {
556         // this must be a selector
557         if (isArrayItem && squareCount == 1 && equalCount == 0) {
558           // x[3] = .... ; add a special flag for this,
559           // waiting until the very end to apply it.
560           wasX = false;
561           addX(SV.newT(T.tokenArrayOpen));
562           break;
563         }
564         if (!doSelection())
565           return false;
566         wasX = true;
567         break;
568       }
569       // if not, it's time to operate
570 
571       if (!operate())
572         return false;
573       tok0 = (oPt >= 0 && oStack[oPt] != null ? oStack[oPt].tok : 0);
574     }
575 
576     // now add a marker on the xStack if necessary
577 
578     if (newOp != null) {
579       wasX = false;
580       addX(SV.newV(T.opEQ, newOp));
581     }
582 
583     // fix up counts and operand flag
584     // right ) and ] are not added to the stack
585 
586     switch (op.tok) {
587     case T.leftparen:
588       //System.out.println("----------(----------");
589       parenCount++;
590       wasX = false;
591       break;
592     case T.opIf:
593       //System.out.println("---------IF---------");
594       boolean isFirst = getX().asBoolean();
595       if (tok0 == T.colon)
596         ifPt--;
597       else
598         putOp(T.tokenColon);
599       putIf(isFirst ? 'T' : 'F');
600       skipping = !isFirst;
601       wasX = false;
602       // dumpStacks("(.." + isFirst + "...?<--here ... :...skip...) ");
603       return true;
604     case T.colon:
605       //System.out.println("----------:----------");
606       if (tok0 != T.colon)
607         return false;
608       if (ifPt < 0)
609         return false;
610       ifStack[ifPt] = 'X';
611       wasX = false;
612       skipping = true;
613       // dumpStacks("(..True...? ... :<--here ...skip...) ");
614       return true;
615     case T.rightparen:
616       //System.out.println("----------)----------");
617       wasX = true;
618       if (parenCount-- <= 0)
619         return false;
620       if (tok0 == T.colon) {
621         // remove markers
622         ifPt--;
623         oPt--;
624         // dumpStacks("(..False...? ...skip... : ...)<--here ");
625       }
626       oPt--;
627       if (oPt < 0)
628         return true;
629       if (isOpFunc(oStack[oPt])) {
630          wasX = false;
631          if(!evaluateFunction(T.nada))
632            return false;
633       }
634       skipping = (ifPt >= 0 && ifStack[ifPt] == 'X');
635       return true;
636     case T.comma:
637       wasX = false;
638       return true;
639     case T.leftsquare:
640       squareCount++;
641       wasX = false;
642       break;
643     case T.rightsquare:
644       wasX = true;
645       if (squareCount-- <= 0 || oPt < 0 || !doSelections)
646         return !doSelections;
647       if (oStack[oPt].tok == T.array)
648         return evaluateFunction(T.leftsquare);
649       oPt--;
650       return true;
651     case T.propselector:
652       wasX = (!allowMathFunc || !T.tokAttr(op.intValue, T.mathfunc));
653       break;
654     case T.leftbrace:
655       braceCount++;
656       wasX = false;
657       break;
658     case T.rightbrace:
659       if (braceCount-- <= 0)
660         return false;
661       wasX = false;
662       break;
663     case T.opAnd:
664     case T.opOr:
665       if (!wasSyntaxCheck && xPt < 0)
666         return false;
667       if (!wasSyntaxCheck && xStack[xPt].tok != T.bitset
668           && xStack[xPt].tok != T.varray) {
669         // check to see if we need to evaluate the second operand or not
670         // if not, then set this to syntax check in order to skip :)
671         // Jmol 12.0.4, Jmol 12.1.2
672         boolean tf = getX().asBoolean();
673         addX(SV.getBoolean(tf));
674         if (tf == (op.tok == T.opOr)) { // TRUE or.. FALSE and...
675           chk = true;
676           op = (op.tok == T.opOr ? T.tokenOrTRUE : T.tokenAndFALSE);
677         }
678       }
679       wasX = false;
680       break;
681     case T.plusPlus:
682     case T.minusMinus:
683       break;
684     case T.opEQ:
685       if (squareCount == 0) {
686         doSelections = true;
687         assignLeft = false;
688         equalCount++;
689       }
690       wasX = false;
691       break;
692     default:
693       wasX = false;
694     }
695 
696     // add the operator if possible
697 
698     putOp(op);
699 
700     // immediate operation check:
701     switch (op.tok) {
702     case T.propselector:
703       return (((op.intValue & ~T.minmaxmask) == T.function
704           && op.intValue != T.function)? evaluateFunction(T.nada) : true);
705     case T.plusPlus:
706     case T.minusMinus:
707       return (wasX ? operate() : true);
708     }
709 
710     return true;
711   }
712 
checkSkip(T op, int tok0)713   private boolean checkSkip(T op, int tok0) {
714     switch (op.tok) {
715     case T.leftparen:
716       putOp(op);
717       break;
718     case T.colon:
719       // dumpStacks("skipping -- :");
720       if (tok0 != T.colon || ifStack[ifPt] == 'X')
721         break; // ignore if not a clean opstack or T already processed
722       // no object here because we were skipping
723       // set to flag end of this parens
724       ifStack[ifPt] = 'T';
725       wasX = false;
726       // dumpStacks("(..False...? .skip.. :<--here.... )");
727       skipping = false;
728       break;
729     case T.rightparen:
730       if (tok0 == T.leftparen) {
731         oPt--; // clear opstack
732         break;
733       }
734       // dumpStacks("skipping -- )");
735       if (tok0 != T.colon) {
736         putOp(op);
737         break;
738       }
739       wasX = true;
740       // and remove markers
741       ifPt--;
742       oPt -= 2;
743       skipping = false;
744       // dumpStacks("(..True...? ... : ...skip...)<--here ");
745       break;
746     }
747     return true;
748   }
749 
doSelection()750   private boolean doSelection() {
751     if (xPt < 0 || xPt == 0 && !isArrayItem) {
752       return false;
753     }
754     SV var1 = xStack[xPt--];
755     SV var = xStack[xPt];
756     if ((var.tok == T.varray || var.tok == T.barray) && var.intValue != Integer.MAX_VALUE)
757 
758       if (var1.tok == T.string || assignLeft && squareCount == 1) {
759         // immediate drill-down
760         // allow for x[1]["test"][1]["here"]
761         // common in getproperty business
762         // also x[1][2][3] = ....
763         // prior to 12.2/3.18, x[1]["id"] was misread as x[1][0]
764         xStack[xPt] = var = (SV) SV.selectItemTok(var, Integer.MIN_VALUE);
765       }
766     if (assignLeft && var.tok != T.string)
767       lastAssignedString = null;
768     switch (var.tok) {
769     case T.hash:
770     case T.context:
771       if (doSelections) {
772         SV v = var.mapValue(SV.sValue(var1));
773         xStack[xPt] = (v == null ? SV.newS("") : v);
774       } else {
775         xPt++;
776         putOp(null); // final operations terminator
777       }
778       return true;
779     default:
780       var = SV.newS(SV.sValue(var));
781       //$FALL-THROUGH$
782     case T.bitset:
783     case T.barray:
784     case T.varray:
785     case T.string:
786     case T.matrix3f:
787     case T.matrix4f:
788       if (doSelections || var.tok == T.varray
789           && var.intValue == Integer.MAX_VALUE) {
790         xStack[xPt] = (SV) SV.selectItemTok(var, var1.asInt());
791         if (assignLeft && var.tok == T.string && squareCount == 1)
792           lastAssignedString = var;
793       } else {
794         xPt++;
795       }
796       if (!doSelections)
797         putOp(null); // final operations terminator
798 
799       break;
800     }
801     return true;
802   }
803 
dumpStacks(String message)804   void dumpStacks(String message) {
805     Logger.debug("\n\n------------------\nRPN stacks: " + message + "\n");
806     for (int i = 0; i <= xPt; i++)
807       Logger.debug("x[" + i + "]: " + xStack[i]);
808     Logger.debug("\n");
809     for (int i = 0; i <= oPt; i++)
810       Logger.debug("o[" + i + "]: " + oStack[i] + " prec="
811           + (oStack[i] == null ? "--" : "" + T.getPrecedence(oStack[i].tok)));
812     Logger.debug(" ifStack = " + (new String(ifStack)).substring(0, ifPt + 1));
813   }
814 
getX()815   public SV getX() throws ScriptException {
816     if (xPt < 0)
817       eval.error(ScriptError.ERROR_endOfStatementUnexpected);
818     SV v = SV.selectItemVar(xStack[xPt]);
819     xStack[xPt--] = null;
820     wasX = false;
821     return v;
822   }
823 
getXTok()824   public int getXTok() {
825     return  (xPt < 0 ? T.nada : xStack[xPt].tok);
826   }
827 
evaluateFunction(int tok)828   private boolean evaluateFunction(int tok) throws ScriptException {
829     T op = oStack[oPt--];
830     // for .xxx or .xxx() functions
831     // we store the token in the intValue field of the propselector token
832     if (tok == T.nada)
833       tok = (op.tok == T.propselector ? op.intValue & ~T.minmaxmask : op.tok);
834 
835     int nParamMax = T.getMaxMathParams(tok); // note - this is NINE for
836     // dot-operators
837     int nParam = 0;
838     int pt = xPt;
839     while (pt >= 0 && xStack[pt--].value != op)
840       nParam++;
841     if (nParamMax > 0 && nParam > nParamMax)
842       return false;
843     SV[] args = new SV[nParam];
844     for (int i = nParam; --i >= 0;)
845       args[i] = getX();
846     xPt--;
847     // no script checking of functions because
848     // we cannot know what variables are real
849     // if this is a property selector, as in x.func(), then we
850     // just exit; otherwise we add a new TRUE to xStack
851 
852     if (!chk)
853       return eval.getMathExt().evaluate(this, op, args, tok);
854     if (op.tok == T.propselector)
855       xPt--; // pop x in "x.func(...)"
856     if (xPt < 0)
857       xPt = 0;
858     switch (tok) {
859     case T.connected:
860     case T.polyhedra:
861     case T.search:
862     case T.smiles:
863     case T.within:
864     case T.contact:
865       return addXBs(new BS());
866     }
867     return addXBool(true);
868   }
869 
870 
operate()871   private boolean operate() throws ScriptException {
872     T op = oStack[oPt--];
873     P3 pt;
874     M3 m;
875     M4 m4;
876     String s;
877     SV x1;
878     if (debugHigh) {
879       dumpStacks("operate: " + op);
880     }
881 
882     // check for a[3][2]
883     if (op.tok == T.opEQ
884         && (isArrayItem && squareCount == 0 && equalCount == 1 && oPt < 0 || oPt >= 0
885             && oStack[oPt] == null))
886       return true;
887 
888     SV x2;
889     switch (op.tok) {
890     case T.minusMinus:
891     case T.plusPlus:
892       if (xPt >= 0 && xStack[xPt].canIncrement()) {
893         x2 = xStack[xPt--];
894         wasX = false;
895         break;
896       }
897       //$FALL-THROUGH$
898     default:
899       x2 = getX();
900       break;
901     }
902     if (x2 == T.tokenArrayOpen)
903       return false;
904 
905     // unary:
906 
907     switch (op.tok) {
908     case T.minusMinus:
909     case T.plusPlus:
910       // we are looking out for an array selection here
911       x1 = x2;
912       if (!chk) {
913         //System.out.println("ptx="+ ptx + " " + pto);
914         if (ptx < pto) {
915           // x++ must make a copy first
916           x1 = SV.newS("").setv(x2);
917         }
918         if (!x2.increment(op.tok == T.plusPlus ? 1 : -1))
919           return false;
920         if (ptx > pto) {
921           // ++x must make a copy after
922           x1 = SV.newS("").setv(x2);
923         }
924       }
925       wasX = false;
926       putX(x1); // reverse getX()
927       wasX = true;
928       return true;
929     case T.unaryMinus:
930       switch (x2.tok) {
931       case T.integer:
932         return addXInt(-x2.asInt());
933       case T.point3f:
934         pt = P3.newP((P3) x2.value);
935         pt.scale(-1f);
936         return addXPt(pt);
937       case T.point4f:
938         P4 pt4 = P4.newPt((P4) x2.value);
939         pt4.scale4(-1f);
940         return addXPt4(pt4);
941       case T.matrix3f:
942         m = M3.newM3((M3) x2.value);
943         m.invert();
944         return addXM3(m);
945       case T.matrix4f:
946         m4 = M4.newM4((M4) x2.value);
947         m4.invert();
948         return addXM4(m4);
949       case T.bitset:
950         return addXBs(BSUtil.copyInvert((BS) x2.value,
951             (x2.value instanceof BondSet ? vwr.ms.bondCount : vwr.ms.ac)));
952       }
953       return addXFloat(-x2.asFloat());
954     case T.opNot:
955       if (chk)
956         return addXBool(true);
957       switch (x2.tok) {
958       case T.point4f: // quaternion
959         return addXPt4((Quat.newP4((P4) x2.value)).inv().toPoint4f());
960       case T.matrix3f:
961         m = M3.newM3((M3) x2.value);
962         m.invert();
963         return addXM3(m);
964       case T.matrix4f:
965         return addXM4(M4.newM4((M4) x2.value).invert());
966       case T.bitset:
967         return addXBs(BSUtil.copyInvert((BS) x2.value,
968             (x2.value instanceof BondSet ? vwr.ms.bondCount : vwr.ms.ac)));
969       default:
970         return addXBool(!x2.asBoolean());
971       }
972     case T.propselector:
973       int iv = (op.intValue == T.opIf ? T.opIf  : op.intValue & ~T.minmaxmask);
974       if (chk)
975         return addXObj(SV.newS(""));
976       if (vwr.allowArrayDotNotation)
977         switch (x2.tok) {
978         case T.hash:
979         case T.context:
980           switch (iv) {
981           // reserved words XXXX for x.XXXX
982           case T.array:
983           case T.keys:
984           case T.size:
985           case T.type:
986             break;
987           //$FALL-THROUGH$
988           default:
989             SV ret = x2.mapValue((String) op.value);
990             return addXObj(ret == null ? SV.newS("") : ret);
991           }
992           break;
993         }
994       switch (iv) {
995       case T.array:
996         return addX(x2.toArray());
997       case T.opIf: // '?'
998       case T.identifier:
999         // special flag to get all properties.
1000         return (x2.tok == T.bitset && (chk ? addXStr("") : getAllProperties(x2,
1001             (String) op.value)));
1002       case T.type:
1003         return addXStr(typeOf(x2));
1004       case T.keys:
1005         String[] keys = x2.getKeys((op.intValue & T.minmaxmask) == T.minmaxmask);
1006         return (keys == null ? addXStr("") : addXAS(keys));
1007       case T.length:
1008       case T.count:
1009       case T.size:
1010         if (iv == T.length && x2.value instanceof BondSet)
1011           break;
1012         return addXInt(SV.sizeOf(x2));
1013       case T.lines:
1014         switch (x2.tok) {
1015         case T.matrix3f:
1016         case T.matrix4f:
1017           s = SV.sValue(x2);
1018           s = PT.rep(s.substring(1, s.length() - 1), "],[", "]\n[");
1019           break;
1020         case T.string:
1021           s = (String) x2.value;
1022           break;
1023         default:
1024           s = SV.sValue(x2);
1025         }
1026         s = PT.rep(s, "\n\r", "\n").replace('\r', '\n');
1027         return addXAS(PT.split(s, "\n"));
1028       case T.color:
1029         switch (x2.tok) {
1030         case T.string:
1031         case T.varray:
1032           return addXPt(CU.colorPtFromString(SV.sValue(x2)));
1033         case T.integer:
1034         case T.decimal:
1035           return addXPt(vwr.getColorPointForPropertyValue(SV.fValue(x2)));
1036         case T.point3f:
1037           return addXStr(Escape.escapeColor(CU.colorPtToFFRGB((P3) x2.value)));
1038         default:
1039           // handle bitset later
1040         }
1041         break;
1042       case T.boundbox:
1043         return (chk ? addXStr("x") : getBoundBox(x2));
1044       }
1045       if (chk)
1046         return addXStr(SV.sValue(x2));
1047       if (x2.tok == T.string) {
1048         Object v = SV.unescapePointOrBitsetAsVariable(SV.sValue(x2));
1049         if (!(v instanceof SV))
1050           return false;
1051         x2 = (SV) v;
1052       }
1053       if (op.tok == x2.tok)
1054         x2 = getX();
1055       return getPointOrBitsetOperation(op, x2);
1056     }
1057 
1058     // binary:
1059     x1 = getX();
1060     if (chk) {
1061       if (op == T.tokenAndFALSE || op == T.tokenOrTRUE)
1062         chk = false;
1063       return addX(SV.newT(x1));
1064     }
1065 
1066     return binaryOp(op, x1, x2);
1067   }
1068 
1069   private static final String qMods = " w:0 x:1 y:2 z:3 normal:4 eulerzxz:5 eulerzyz:6 vector:-1 theta:-2 axisx:-3 axisy:-4 axisz:-5 axisangle:-6 matrix:-9";
1070 
1071   @SuppressWarnings("unchecked")
binaryOp(T op, SV x1, SV x2)1072   public boolean binaryOp(T op, SV x1, SV x2) throws ScriptException {
1073     P3 pt;
1074     P4 pt4;
1075     M3 m;
1076     String s;
1077     float f;
1078 
1079     switch (op.tok) {
1080     case T.opAND:
1081     case T.opAnd:
1082       switch (x1.tok) {
1083       case T.bitset:
1084         BS bs = (BS) x1.value;
1085         switch (x2.tok) {
1086         case T.integer:
1087           int x = x2.asInt();
1088           return (addXBool(x < 0 ? false : bs.get(x)));
1089         case T.bitset:
1090           bs = BSUtil.copy(bs);
1091           bs.and((BS) x2.value);
1092           return addXBs(bs);
1093         }
1094         break;
1095       }
1096       return addXBool(x1.asBoolean() && x2.asBoolean());
1097     case T.opOr:
1098       switch (x1.tok) {
1099       case T.bitset:
1100         BS bs = BSUtil.copy((BS) x1.value);
1101         switch (x2.tok) {
1102         case T.bitset:
1103           bs.or((BS) x2.value);
1104           return addXBs(bs);
1105         case T.integer:
1106           int x = x2.asInt();
1107           if (x < 0)
1108             break;
1109           bs.set(x);
1110           return addXBs(bs);
1111         case T.varray:
1112           Lst<SV> sv = (Lst<SV>) x2.value;
1113           for (int i = sv.size(); --i >= 0;) {
1114             int b = sv.get(i).asInt();
1115             if (b >= 0)
1116               bs.set(b);
1117           }
1118           return addXBs(bs);
1119         }
1120         break;
1121       case T.varray:
1122         return addX(SV.concatList(x1, x2, false));
1123       }
1124       return addXBool(x1.asBoolean() || x2.asBoolean());
1125     case T.opXor:
1126       if (x1.tok == T.bitset && x2.tok == T.bitset) {
1127         BS bs = BSUtil.copy((BS) x1.value);
1128         bs.xor((BS) x2.value);
1129         return addXBs(bs);
1130       }
1131       boolean a = x1.asBoolean();
1132       boolean b = x2.asBoolean();
1133       return addXBool(a && !b || b && !a);
1134     case T.opToggle:
1135       if (x1.tok != T.bitset || x2.tok != T.bitset)
1136         return false;
1137       return addXBs(
1138           BSUtil.toggleInPlace(BSUtil.copy((BS) x1.value), (BS) x2.value));
1139     case T.opLE:
1140       return addXBool(x1.tok == T.integer && x2.tok == T.integer
1141           ? x1.intValue <= x2.intValue
1142           : x1.asFloat() <= x2.asFloat());
1143     case T.opGE:
1144       return addXBool(x1.tok == T.integer && x2.tok == T.integer
1145           ? x1.intValue >= x2.intValue
1146           : x1.asFloat() >= x2.asFloat());
1147     case T.opGT:
1148       return addXBool(
1149           x1.tok == T.integer && x2.tok == T.integer ? x1.intValue > x2.intValue
1150               : x1.asFloat() > x2.asFloat());
1151     case T.opLT:
1152       return addXBool(
1153           x1.tok == T.integer && x2.tok == T.integer ? x1.intValue < x2.intValue
1154               : x1.asFloat() < x2.asFloat());
1155     case T.opEQ:
1156       return addXBool(SV.areEqual(x1, x2));
1157     case T.opNE:
1158       return addXBool(!SV.areEqual(x1, x2));
1159     case T.opLIKE:
1160       return addXBool(SV.isLike(x1, x2));
1161     case T.plus:
1162       switch (x1.tok) {
1163       case T.hash:
1164         Map<String, SV> ht = new Hashtable<String, SV>(
1165             (Map<String, SV>) x1.value);
1166         Map<String, SV> map = x2.getMap();
1167         if (map != null)
1168           ht.putAll(map); // new Jmol 14.18
1169         return addX(SV.getVariableMap(ht));
1170       case T.integer:
1171         if (!isDecimal(x2))
1172           return addXInt(x1.intValue + x2.asInt());
1173         break;
1174       case T.string:
1175         return addX(SV.newS(SV.sValue(x1) + SV.sValue(x2)));
1176       case T.point3f:
1177         pt = P3.newP((P3) x1.value);
1178         switch (x2.tok) {
1179         case T.point3f:
1180           pt.add((P3) x2.value);
1181           return addXPt(pt);
1182         case T.point4f:
1183           // extract {xyz}
1184           pt4 = (P4) x2.value;
1185           pt.add(P3.new3(pt4.x, pt4.y, pt4.z));
1186           return addXPt(pt);
1187         default:
1188           f = x2.asFloat();
1189           return addXPt(P3.new3(pt.x + f, pt.y + f, pt.z + f));
1190         }
1191       case T.matrix3f:
1192         switch (x2.tok) {
1193         case T.matrix3f:
1194           m = M3.newM3((M3) x1.value);
1195           m.add((M3) x2.value);
1196           return addXM3(m);
1197         case T.point3f:
1198           return addXM4(getMatrix4f((M3) x1.value, (P3) x2.value));
1199         }
1200         break;
1201       case T.matrix4f:
1202         switch (x2.tok) {
1203         case T.point3f:
1204           M4 m4b = M4.newM4((M4) x1.value);
1205           m4b.add((T3) x2.value);
1206           return addXM4(m4b);
1207         }
1208         break;
1209       case T.point4f:
1210         Quat q1 = Quat.newP4((P4) x1.value);
1211         switch (x2.tok) {
1212         default:
1213           return addXPt4(q1.add(x2.asFloat()).toPoint4f());
1214         case T.point4f:
1215           return addXPt4(q1.mulQ(Quat.newP4((P4) x2.value)).toPoint4f());
1216         }
1217       case T.varray:
1218         return addX(SV.concatList(x1, x2, true));
1219       }
1220       return addXFloat(x1.asFloat() + x2.asFloat());
1221     case T.minus:
1222       switch (x1.tok) {
1223       case T.integer:
1224         if (!isDecimal(x2))
1225           return addXInt(x1.intValue - x2.asInt());
1226         break;
1227       case T.string:
1228         if (!isDecimal(x2) && !isDecimal(x1))
1229           return addXInt(x1.asInt() - x2.asInt());
1230         break;
1231       case T.hash:
1232         Map<String, SV> ht = new Hashtable<String, SV>(
1233             (Map<String, SV>) x1.value);
1234         ht.remove(SV.sValue(x2));
1235         return addX(SV.getVariableMap(ht));
1236       case T.matrix3f:
1237         if (x2.tok != T.matrix3f)
1238           break;
1239         m = M3.newM3((M3) x1.value);
1240         m.sub((M3) x2.value);
1241         return addXM3(m);
1242       case T.matrix4f:
1243         if (x2.tok != T.matrix4f)
1244           break;
1245         M4 m4 = M4.newM4((M4) x1.value);
1246         m4.sub((M4) x2.value);
1247         return addXM4(m4);
1248       case T.point3f:
1249         pt = P3.newP((P3) x1.value);
1250         switch (x2.tok) {
1251         case T.point3f:
1252           pt.sub((P3) x2.value);
1253           return addXPt(pt);
1254         case T.point4f:
1255           // extract {xyz}
1256           pt4 = (P4) x2.value;
1257           pt.sub(P3.new3(pt4.x, pt4.y, pt4.z));
1258           return addXPt(pt);
1259         }
1260         f = x2.asFloat();
1261         return addXPt(P3.new3(pt.x - f, pt.y - f, pt.z - f));
1262       case T.point4f:
1263         Quat q1 = Quat.newP4((P4) x1.value);
1264         if (x2.tok == T.point4f) {
1265           Quat q2 = Quat.newP4((P4) x2.value);
1266           return addXPt4(q2.mulQ(q1.inv()).toPoint4f());
1267         }
1268         return addXPt4(q1.add(-x2.asFloat()).toPoint4f());
1269       }
1270       return addXFloat(x1.asFloat() - x2.asFloat());
1271     case T.mul3:
1272       if (x1.tok == T.point3f && x2.tok == T.point3f) {
1273         pt = (P3) x1.value;
1274         P3 pt2 = (P3) x2.value;
1275         return addXPt(P3.new3(pt.x * pt2.x, pt.y * pt2.y, pt.z * pt2.z));
1276       }
1277       //$FALL-THROUGH$
1278     case T.times:
1279       switch (x1.tok) {
1280       case T.integer:
1281         return (isDecimal(x2) ? addXFloat(x1.intValue * x2.asFloat())
1282             : addXInt(x1.intValue * x2.asInt()));
1283       case T.string:
1284         return (isDecimal(x2) || isDecimal(x1)
1285             ? addXFloat(x1.asFloat() * x2.asFloat())
1286             : addXInt(x1.asInt() * x2.asInt()));
1287       }
1288       pt = (x1.tok == T.matrix3f || x1.tok == T.matrix4f ? ptValue(x2, null)
1289           : x2.tok == T.matrix3f ? ptValue(x1, null) : null);
1290       pt4 = (x1.tok == T.matrix4f ? planeValue(x2)
1291           : x2.tok == T.matrix4f ? planeValue(x1) : null);
1292       // checking here to make sure arrays remain arrays and
1293       // points remain points with matrix operations.
1294       // we check x2, because x1 could be many things.
1295       switch (x2.tok) {
1296       case T.matrix3f:
1297         if (pt != null) {
1298           // pt * m
1299           M3 m3b = M3.newM3((M3) x2.value);
1300           m3b.transpose();
1301           P3 pt1 = P3.newP(pt);
1302           m3b.rotate(pt1);
1303           return (x1.tok == T.varray
1304               ? addX(SV.getVariableAF(new float[] { pt1.x, pt1.y, pt1.z }))
1305               : addXPt(pt1));
1306         }
1307         if (pt4 != null)
1308           // q * m --> q
1309           return addXPt4(
1310               (Quat.newP4(pt4).mulQ(Quat.newM((M3) x2.value))).toPoint4f());
1311         break;
1312       case T.matrix4f:
1313         // pt4 * m4
1314         // [a b c d] * m4
1315         if (pt4 != null) {
1316           M4 m4b = M4.newM4((M4) x2.value);
1317           m4b.transpose();
1318           P4 pt41 = P4.newPt(pt4);
1319           m4b.transform(pt41);
1320           return (x1.tok == T.varray
1321               ? addX(SV.getVariableAF(
1322                   new float[] { pt41.x, pt41.y, pt41.z, pt41.w }))
1323               : addXPt4(pt41));
1324         }
1325         break;
1326       }
1327       switch (x1.tok) {
1328       case T.matrix3f:
1329         M3 m3 = (M3) x1.value;
1330         if (pt != null) {
1331           P3 pt1 = P3.newP(pt);
1332           m3.rotate(pt1);
1333           return (x2.tok == T.varray
1334               ? addX(SV.getVariableAF(new float[] { pt1.x, pt1.y, pt1.z }))
1335               : addXPt(pt1));
1336         }
1337         switch (x2.tok) {
1338         case T.matrix3f:
1339           m = M3.newM3((M3) x2.value);
1340           m.mul2(m3, m);
1341           return addXM3(m);
1342         case T.point4f:
1343           // m * q
1344           return addXM3(
1345               Quat.newM(m3).mulQ(Quat.newP4((P4) x2.value)).getMatrix());
1346         case T.varray:
1347           Lst<SV> l = x2.getList();
1348           Lst<P3> lnew = new Lst<P3>();
1349           for (int i = l.size(); --i >= 0;) {
1350             P3 pt1 = P3.newP(SV.ptValue(l.get(i)));
1351             m3.rotate(pt1);
1352             lnew.add(pt1);
1353           }
1354           return addXList(lnew);
1355         }
1356         f = x2.asFloat();
1357         A4 aa = new A4();
1358         aa.setM(m3);
1359         aa.angle *= f;
1360         return addXM3(new M3().setAA(aa));
1361       case T.matrix4f:
1362         M4 m4 = (M4) x1.value;
1363         if (pt != null) {
1364           P3 pt1 = P3.newP(pt);
1365           m4.rotTrans(pt1);
1366           return (x2.tok == T.varray
1367               ? addX(SV.getVariableAF(new float[] { pt1.x, pt1.y, pt1.z }))
1368               : addXPt(pt1));
1369         }
1370         if (pt4 != null) {
1371           m4.transform(pt4);
1372           return (x2.tok == T.varray
1373               ? addX(
1374                   SV.getVariableAF(new float[] { pt4.x, pt4.y, pt4.z, pt4.w }))
1375               : addXPt4(pt4));
1376         }
1377         switch (x2.tok) {
1378         case T.matrix4f:
1379           M4 m4b = M4.newM4((M4) x2.value);
1380           m4b.mul2(m4, m4b);
1381           return addXM4(m4b);
1382         case T.varray:
1383           Lst<SV> l = x2.getList();
1384           Lst<P3> lnew = new Lst<P3>();
1385           for (int i = l.size(); --i >= 0;) {
1386             P3 pt1 = P3.newP(SV.ptValue(l.get(i)));
1387             m4.rotTrans(pt1);
1388             lnew.add(pt1);
1389           }
1390           return addXList(lnew);
1391         }
1392         return addXStr("NaN");
1393       case T.point3f:
1394         pt = P3.newP((P3) x1.value);
1395         switch (x2.tok) {
1396         case T.point3f:
1397           P3 pt2 = ((P3) x2.value);
1398           return addXFloat(pt.x * pt2.x + pt.y * pt2.y + pt.z * pt2.z);
1399         }
1400         f = x2.asFloat();
1401         return addXPt(P3.new3(pt.x * f, pt.y * f, pt.z * f));
1402       case T.point4f:
1403         if (x2.tok == T.point4f)
1404           // quaternion multiplication
1405           // note that Point4f is {x,y,z,w} so we use that for
1406           // quaternion notation as well here.
1407           return addXPt4(Quat.newP4((P4) x1.value)
1408               .mulQ(Quat.newP4((P4) x2.value)).toPoint4f());
1409         return addXPt4(Quat.newP4((P4) x1.value).mul(x2.asFloat()).toPoint4f());
1410       }
1411       return addXFloat(x1.asFloat() * x2.asFloat());
1412     case T.divide:
1413       float f2;
1414       switch (x1.tok) {
1415       case T.integer:
1416         if (x2.tok == T.integer && x2.intValue != 0)
1417           return addXInt(x1.intValue / x2.intValue);
1418         int n = (isDecimal(x2) ? 0 : x2.asInt());
1419         if (n != 0)
1420           return addXInt(x1.intValue / n);
1421         break;
1422       case T.string:
1423         int i2;
1424         if (!isDecimal(x1) && !isDecimal(x2) && (i2 = x2.asInt()) != 0)
1425           return addXInt(x1.asInt() / i2);
1426         break;
1427       case T.point3f:
1428         pt = P3.newP((P3) x1.value);
1429         return addXPt(
1430             (f2 = x2.asFloat()) == 0 ? P3.new3(Float.NaN, Float.NaN, Float.NaN)
1431                 : P3.new3(pt.x / f2, pt.y / f2, pt.z / f2));
1432       case T.point4f:
1433         return addXPt4(x2.tok == T.point4f
1434             ? Quat.newP4((P4) x1.value).div(Quat.newP4((P4) x2.value))
1435                 .toPoint4f()
1436             : (f2 = x2.asFloat()) == 0
1437                 ? P4.new4(Float.NaN, Float.NaN, Float.NaN, Float.NaN)
1438                 : Quat.newP4((P4) x1.value).mul(1 / f2).toPoint4f());
1439       }
1440       return addXFloat(x1.asFloat() / x2.asFloat());
1441     case T.leftdivide:
1442       f = x2.asFloat();
1443       if (x1.tok == T.point4f) {
1444         return (f == 0
1445             ? addXPt4(P4.new4(Float.NaN, Float.NaN, Float.NaN, Float.NaN))
1446             : x2.tok == T.point4f
1447                 ? addXPt4(Quat.newP4((P4) x1.value)
1448                     .divLeft(Quat.newP4((P4) x2.value)).toPoint4f())
1449                 : addXPt4(Quat.newP4((P4) x1.value).mul(1 / f).toPoint4f()));
1450       }
1451       return addXInt(
1452           f == 0 ? 0 : (int) Math.floor(x1.asFloat() / x2.asFloat()));
1453     case T.timestimes:
1454       f = (float) Math.pow(x1.asFloat(), x2.asFloat());
1455       return (x1.tok == T.integer && x2.tok == T.integer ? addXInt((int) f)
1456           : addXFloat(f));
1457     case T.percent:
1458       // more than just modulus
1459 
1460       // float % n round to n digits; n = 0 does "nice" rounding
1461       // String % -n trim to width n; left justify
1462       // String % n trim to width n; right justify
1463       // Point3f % n ah... sets to multiple of unit cell!
1464       // bitset % n
1465       // Point3f * Point3f does dot product
1466       // Point3f / Point3f divides by magnitude
1467       // float * Point3f gets magnitude
1468       // Point4f % n returns q0, q1, q2, q3, or theta
1469       // Point4f % Point4f
1470       s = null;
1471       int n = x2.asInt();
1472       switch (x1.tok) {
1473       case T.on:
1474       case T.off:
1475       case T.integer:
1476       default:
1477         break;
1478       case T.decimal:
1479         f = x1.asFloat();
1480         // neg is scientific notation
1481         if (n == 0)
1482           return addXInt(Math.round(f));
1483         s = DF.formatDecimal(f, n);
1484         return addXStr(s);
1485       case T.string:
1486         s = (String) x1.value;
1487         return addXStr(n == 0 ? PT.trim(s, "\n\t ")
1488             : n == 9999 ? s.toUpperCase()
1489                 : n == -9999 ? s.toLowerCase()
1490                     : n > 0 ? PT.formatS(s, n, n, false, false)
1491                         : PT.formatS(s, n, n - 1, true, false));
1492       case T.varray:
1493         String[] list = SV.strListValue(x1);
1494         for (int i = 0; i < list.length; i++) {
1495           if (n == 0)
1496             list[i] = list[i].trim();
1497           else if (n > 0)
1498             list[i] = PT.formatS(list[i], n, n, true, false);
1499           else
1500             list[i] = PT.formatS(s, -n, n, false, false);
1501         }
1502         return addXAS(list);
1503       case T.point3f:
1504         pt = P3.newP((P3) x1.value);
1505         vwr.toUnitCell(pt, P3.new3(n, n, n));
1506         return addXPt(pt);
1507       case T.point4f:
1508         pt4 = (P4) x1.value;
1509         if (x2.tok == T.point3f)
1510           return addXPt(
1511               (P3) (Quat.newP4(pt4)).transform2((P3) x2.value, new P3()));
1512         if (x2.tok == T.point4f) {
1513           P4 v4 = P4.newPt((P4) x2.value);
1514           (Quat.newP4(pt4)).getThetaDirected(v4);
1515           return addXPt4(v4);
1516         }
1517         if (n == 0 && x2.tok == T.string) {
1518           s = " " + x2.value.toString().trim().toLowerCase() + ":";
1519           int i = qMods.indexOf(s);
1520           n = (i >= 0 ? PT.parseInt(qMods.substring(i + s.length())) : -99);
1521         }
1522         switch (n) {
1523         // q%0 w
1524         // q%1 x
1525         // q%2 y
1526         // q%3 z
1527         // q%4 normal
1528         // q%5 EulerZXZ (degrees)
1529         // q%6 EulerZYZ (degrees)
1530         // q%-1 vector(1)
1531         // q%-2 theta
1532         // q%-3 Matrix column 0
1533         // q%-4 Matrix column 1
1534         // q%-5 Matrix column 2
1535         // q%-6 AxisAngle format
1536         // q%-9 Matrix format
1537         case 0:
1538           return addXFloat(pt4.w);
1539         case 1:
1540           return addXFloat(pt4.x);
1541         case 2:
1542           return addXFloat(pt4.y);
1543         case 3:
1544           return addXFloat(pt4.z);
1545         }
1546         Quat q = Quat.newP4(pt4);
1547         switch (n) {
1548         case 4:
1549           return addXPt(P3.newP(q.getNormal()));
1550         case 5:
1551           return addXAF(q.getEulerZXZ());
1552         case 6:
1553           return addXAF(q.getEulerZYZ());
1554         case -1:
1555           return addXPt(P3.newP(q.getVector(-1)));
1556         case -2:
1557           return addXFloat(q.getTheta());
1558         case -3:
1559           return addXPt(P3.newP(q.getVector(0)));
1560         case -4:
1561           return addXPt(P3.newP(q.getVector(1)));
1562         case -5:
1563           return addXPt(P3.newP(q.getVector(2)));
1564         case -6:
1565           A4 ax = q.toAxisAngle4f();
1566           return addXPt4(
1567               P4.new4(ax.x, ax.y, ax.z, (float) (ax.angle * 180 / Math.PI)));
1568         case -9:
1569           return addXM3(q.getMatrix());
1570         default:
1571           return addXStr("NaN");
1572         }
1573       case T.matrix4f:
1574         M4 m4 = (M4) x1.value;
1575         switch (n) {
1576         case 1:
1577           M3 m3 = new M3();
1578           m4.getRotationScale(m3);
1579           return addXM3(m3);
1580         case 2:
1581           V3 v3 = new V3();
1582           m4.getTranslation(v3);
1583           return addXPt(P3.newP(v3));
1584         default:
1585           return false;
1586         }
1587       case T.bitset:
1588         return addXBs(SV.bsSelectRange(x1, n));
1589       }
1590       return addXInt(n == 0 ? x1.asInt() : x1.asInt() % n);
1591     }
1592     return true;
1593   }
1594 
isDecimal(SV x)1595   private boolean isDecimal(SV x) {
1596     String s;
1597     return (x.tok == T.decimal || x.tok == T.string
1598         && ((s = SV.sValue(x).trim()).indexOf(".") >= 0 || s.indexOf("+") > 0 || s
1599             .lastIndexOf("-") > 0));
1600   }
1601 
ptValue(SV x, BS bsRestrict)1602   public P3 ptValue(SV x, BS bsRestrict) throws ScriptException {
1603     Object pt;
1604     switch (x.tok) {
1605     case T.point3f:
1606       return (P3) x.value;
1607     case T.bitset:
1608       BS bs = (BS) x.value;
1609       if (bs.isEmpty())
1610         break;
1611       if (bsRestrict != null) {
1612         bs = BSUtil.copy(bs);
1613         bs.and(bsRestrict);
1614       }
1615       return (P3) eval.getBitsetProperty(bs, null, T.xyz, null,
1616           null, x.value, null, false, Integer.MAX_VALUE, false);
1617     case T.string:
1618       pt = Escape.uP(SV.sValue(x));
1619       if (pt instanceof P3)
1620         return (P3) pt;
1621       break;
1622     case T.varray:
1623       pt = Escape.uP("{" + SV.sValue(x).replace(']',' ').replace('[',' ') + "}");
1624       if (pt instanceof P3)
1625         return (P3) pt;
1626       break;
1627     }
1628     return null;
1629   }
1630 
planeValue(T x)1631   public static P4 planeValue(T x) {
1632     Object pt;
1633     switch (x.tok) {
1634     case T.point4f:
1635       return (P4) x.value;
1636     case T.varray:
1637       break;
1638     case T.string:
1639       String s = (String) x.value;
1640       boolean isMinus = s.startsWith("-");
1641       float f = (isMinus ? -1 : 1);
1642       if (isMinus)
1643         s = s.substring(1);
1644       P4 p4 = null;
1645       switch (s.length() < 2 ? -1
1646           : "xy yz xz x= y= z=".indexOf(s.substring(0, 2))) {
1647       case 0:
1648         return P4.new4(1, 1, 0, f);
1649       case 3:
1650         return P4.new4(0, 1, 1, f);
1651       case 6:
1652         return P4.new4(1, 0, 1, f);
1653       case 9:
1654         p4 = P4.new4(1, 0, 0, -f * PT.parseFloat(s.substring(2)));
1655         break;
1656       case 12:
1657         p4 = P4.new4(0, 1, 0, -f * PT.parseFloat(s.substring(2)));
1658         break;
1659       case 15:
1660         p4 = P4.new4(0, 0, 1, -f * PT.parseFloat(s.substring(2)));
1661         break;
1662       }
1663       if (p4 != null && !Float.isNaN(p4.w))
1664         return p4;
1665       break;
1666     default:
1667       return null;
1668     }
1669     pt = Escape.uP(SV.sValue(x));
1670     return (pt instanceof P4 ? (P4) pt : null);
1671   }
1672 
typeOf(SV x)1673   static private String typeOf(SV x) {
1674     int tok = (x == null ? T.nada : x.tok);
1675     switch (tok) {
1676     case T.on:
1677     case T.off:
1678       return "boolean";
1679     case T.bitset:
1680       return (x.value instanceof BondSet ? "bondset" : "bitset");
1681     case T.integer:
1682     case T.decimal:
1683     case T.point3f:
1684     case T.point4f:
1685     case T.string:
1686     case T.varray:
1687     case T.hash:
1688     case T.barray:
1689     case T.matrix3f:
1690     case T.matrix4f:
1691     case T.context:
1692       return T.astrType[tok];
1693     }
1694     return "?";
1695   }
1696 
getAllProperties(SV x2, String abbr)1697   private boolean getAllProperties(SV x2, String abbr)
1698       throws ScriptException {
1699     BS bs = (BS) x2.value;
1700     Lst<T> tokens;
1701     int n = bs.cardinality();
1702     if (n == 0 || !abbr.endsWith("?")
1703         || (tokens = T.getAtomPropertiesLike(abbr.substring(0, abbr
1704             .length() - 1))) == null)
1705       return addXStr("");
1706     Map<String, Object> ht = new Hashtable<String, Object>();
1707     int index = (n == 1 ? bs.nextSetBit(0) : Integer.MAX_VALUE);
1708     for (int i = tokens.size(); --i >= 0;) {
1709       T t = tokens.get(i);
1710       int tok = t.tok;
1711       switch (tok) {
1712       case T.configuration:
1713       case T.cell:
1714         continue;
1715       default:
1716         if (index == Integer.MAX_VALUE)
1717           tok |= T.minmaxmask;
1718         ht.put((String) t.value, SV.getVariable(
1719             eval.getBitsetProperty(bs, null, tok, null, null, null, null, false, index, true)));
1720       }
1721     }
1722     return addXMap(ht);
1723   }
1724 
getMatrix4f(M3 matRotate, T3 vTranslate)1725   public static M4 getMatrix4f(M3 matRotate, T3 vTranslate) {
1726     return M4.newMV(matRotate, vTranslate == null ? new V3() : V3.newV(vTranslate));
1727   }
1728 
getBoundBox(SV x2)1729   private boolean getBoundBox(SV x2) {
1730     if (x2.tok != T.bitset)
1731       return false;
1732     BoxInfo b = vwr.ms.getBoxInfo((BS) x2.value, 1);
1733     P3[] pts = b.getBoundBoxPoints(true);
1734     Lst<P3> list = new  Lst<P3>();
1735     for (int i = 0; i < 4; i++)
1736       list.addLast(pts[i]);
1737     return addXList(list);
1738   }
1739 
getPointOrBitsetOperation(T op, SV x2)1740   private boolean getPointOrBitsetOperation(T op, SV x2)
1741       throws ScriptException {
1742     switch (x2.tok) {
1743     case T.varray:
1744       switch (op.intValue) {
1745       case T.min:
1746       case T.max:
1747       case T.average:
1748       case T.stddev:
1749       case T.sum:
1750       case T.sum2:
1751       case T.pivot:
1752         return addXObj(eval.getMathExt().getMinMax(x2.getList(), op.intValue));
1753       case T.pop:
1754         return addX(x2.pushPop(null, null));
1755       case T.sort:
1756       case T.reverse:
1757         return addX(x2
1758             .sortOrReverse(op.intValue == T.reverse ? Integer.MIN_VALUE : 1));
1759       }
1760       SV[] list2 = new SV[x2.getList().size()];
1761       for (int i = 0; i < list2.length; i++) {
1762         Object v = SV.unescapePointOrBitsetAsVariable(x2.getList()
1763             .get(i));
1764         if (!(v instanceof SV)
1765             || !getPointOrBitsetOperation(op, (SV) v))
1766           return false;
1767         list2[i] = xStack[xPt--];
1768       }
1769       return addXAV(list2);
1770     case T.point3f:
1771       switch (op.intValue) {
1772       case T.atomx:
1773       case T.x:
1774         return addXFloat(((P3) x2.value).x);
1775       case T.atomy:
1776       case T.y:
1777         return addXFloat(((P3) x2.value).y);
1778       case T.atomz:
1779       case T.z:
1780         return addXFloat(((P3) x2.value).z);
1781       case T.xyz:
1782         P3 pt = P3.newP((P3) x2.value);
1783         // assumes a fractional coordinate
1784         vwr.toCartesian(pt, false);
1785         return addXPt(pt);
1786       case T.fracx:
1787       case T.fracy:
1788       case T.fracz:
1789       case T.fracxyz:
1790         P3 ptf = P3.newP((P3) x2.value);
1791         vwr.toFractional(ptf, false);
1792         return (op.intValue == T.fracxyz ? addXPt(ptf)
1793             : addXFloat(op.intValue == T.fracx ? ptf.x
1794                 : op.intValue == T.fracy ? ptf.y : ptf.z));
1795       case T.fux:
1796       case T.fuy:
1797       case T.fuz:
1798       case T.fuxyz:
1799         P3 ptfu = P3.newP((P3) x2.value);
1800         vwr.toFractional(ptfu, true);
1801         return (op.intValue == T.fuxyz ? addXPt(ptfu)
1802             : addXFloat(op.intValue == T.fux ? ptfu.x
1803                 : op.intValue == T.fuy ? ptfu.y : ptfu.z));
1804       case T.unitx:
1805       case T.unity:
1806       case T.unitz:
1807       case T.unitxyz:
1808         P3 ptu = P3.newP((P3) x2.value);
1809         vwr.toUnitCell(ptu, null);
1810         vwr.toFractional(ptu, false);
1811         return (op.intValue == T.unitxyz ? addXPt(ptu)
1812             : addXFloat(op.intValue == T.unitx ? ptu.x
1813                 : op.intValue == T.unity ? ptu.y : ptu.z));
1814       }
1815       break;
1816     case T.point4f:
1817       switch (op.intValue) {
1818       case T.atomx:
1819       case T.x:
1820         return addXFloat(((P4) x2.value).x);
1821       case T.atomy:
1822       case T.y:
1823         return addXFloat(((P4) x2.value).y);
1824       case T.atomz:
1825       case T.z:
1826         return addXFloat(((P4) x2.value).z);
1827       case T.w:
1828         return addXFloat(((P4) x2.value).w);
1829       }
1830       break;
1831     case T.bitset:
1832       boolean isAtoms = (op.intValue != T.bonds);
1833       if (!isAtoms && x2.value instanceof BondSet)
1834         return addX(x2);
1835       BS bs = (BS) x2.value;
1836       if (isAtoms && bs.cardinality() == 1 && (op.intValue & T.minmaxmask) == 0)
1837         op.intValue |= T.min;
1838       Object val = eval.getBitsetProperty(bs, null, op.intValue, null,
1839           null, null, op.value, false, x2.index, true);
1840       return (isAtoms ? addXObj(val) : addX(SV.newV(T.bitset, BondSet.newBS(
1841           (BS) val, vwr.ms.getAtomIndices(bs)))));
1842     }
1843     return false;
1844   }
1845 
1846 
1847 }
1848