Clazz.declarePackage ("JS");
Clazz.load (["JU.JmolMolecule", "JU.BS", "$.Lst"], "JS.SmilesSearch", ["java.lang.Float", "java.util.Hashtable", "JU.AU", "$.SB", "JS.InvalidSmilesException", "$.SmilesAromatic", "$.SmilesAtom", "$.SmilesBond", "$.SmilesMeasure", "$.SmilesParser", "$.VTemp", "JU.BSUtil", "$.Edge", "$.Logger"], function () {
c$ = Clazz.decorateAsClass (function () {
this.isSmarts = false;
this.top = null;
this.pattern = null;
this.patternAtoms = null;
this.targetAtoms = null;
this.targetAtomCount = 0;
this.bsSelected = null;
this.v = null;
this.aromaticOpen = false;
this.aromaticStrict = false;
this.aromaticPlanar = false;
this.aromaticDouble = false;
this.noAromatic = false;
this.ignoreAtomClass = false;
this.ignoreStereochemistry = false;
this.invertStereochemistry = false;
this.exitFirstMatch = false;
this.groupByModel = false;
this.setAtropicity = false;
this.patternAromatic = false;
this.haveTopo = false;
this.isTopology = false;
this.patternBioSequence = false;
this.subSearches = null;
this.haveSelected = false;
this.haveBondStereochemistry = false;
this.stereo = null;
this.needRingData = false;
this.needAromatic = true;
this.needRingMemberships = false;
this.nDouble = 0;
this.ringDataMax = -2147483648;
this.ringSets = null;
this.ringCount = 0;
this.measures = null;
this.flags = 0;
this.bsAromatic = null;
this.bsAromatic5 = null;
this.bsAromatic6 = null;
this.atropKeys = null;
this.lastChainAtom = null;
this.asVector = false;
this.getMaps = false;
this.isNormalized = false;
this.haveComponents = false;
this.isSilent = false;
this.isRingCheck = false;
this.selectedAtomCount = 0;
this.ringData = null;
this.ringCounts = null;
this.ringConnections = null;
this.bsFound = null;
this.htNested = null;
this.nNested = 0;
this.nestedBond = null;
this.vReturn = null;
this.bsReturn = null;
this.bsCheck = null;
Clazz.instantialize (this, arguments);
}, JS, "SmilesSearch", JU.JmolMolecule);
Clazz.prepareFields (c$, function () {
this.patternAtoms =  new Array (16);
this.measures =  new JU.Lst ();
this.bsAromatic =  new JU.BS ();
this.bsAromatic5 =  new JU.BS ();
this.bsAromatic6 =  new JU.BS ();
this.bsFound =  new JU.BS ();
this.bsReturn =  new JU.BS ();
});
Clazz.makeConstructor (c$, 
function () {
Clazz.superConstructor (this, JS.SmilesSearch, []);
this.top = this;
this.v =  new JS.VTemp ();
});
Clazz.defineMethod (c$, "setTop", 
function (parent) {
while (parent.top !== parent) parent = parent.top;

this.top = parent;
}, "JS.SmilesSearch");
c$.addFlags = Clazz.defineMethod (c$, "addFlags", 
function (flags, strFlags) {
if (strFlags.indexOf ("OPEN") >= 0) flags |= 5;
if (strFlags.indexOf ("BIO") >= 0) flags |= 1048576;
if (strFlags.indexOf ("HYDROGEN") >= 0) flags |= 4096;
if (strFlags.indexOf ("FIRSTMATCHONLY") >= 0) flags |= 8;
if (strFlags.indexOf ("STRICT") >= 0) flags |= 256;
if (strFlags.indexOf ("PLANAR") >= 0) flags |= 1024;
if (strFlags.indexOf ("NOAROMATIC") >= 0 || strFlags.indexOf ("NONAROMATIC") >= 0) flags |= 16;
if (strFlags.indexOf ("AROMATICDOUBLE") >= 0) flags |= 512;
if (strFlags.indexOf ("AROMATICDEFINED") >= 0) flags |= 128;
if (strFlags.indexOf ("MMFF94") >= 0) flags |= 768;
if (strFlags.indexOf ("TOPOLOGY") >= 0) flags |= 8192;
if (strFlags.indexOf ("NOATOMCLASS") >= 0) flags |= 2048;
if (strFlags.indexOf ("NOSTEREO") >= 0) {
flags |= 32;
} else if (strFlags.indexOf ("INVERTSTEREO") >= 0) {
if ((flags & 64) != 0) flags &= -65;
 else flags |= 64;
}if (strFlags.indexOf ("ATOMCOMMENT") >= 0) flags |= 131072;
if (strFlags.indexOf ("GROUPBYMODEL") >= 0) flags |= 67108864;
if ((flags & 1048576) == 1048576) {
if (strFlags.indexOf ("NOCOMMENT") >= 0) flags |= 34603008;
if (strFlags.indexOf ("UNMATCHED") >= 0) flags |= 3145728;
if (strFlags.indexOf ("COVALENT") >= 0) flags |= 5242880;
if (strFlags.indexOf ("HBOND") >= 0) flags |= 9437184;
}return flags;
}, "~N,~S");
Clazz.defineMethod (c$, "setFlags", 
function (flags) {
this.flags = flags;
this.exitFirstMatch = new Boolean (this.exitFirstMatch | ((flags & 8) == 8)).valueOf ();
this.aromaticOpen = ((flags & 5) == 5);
this.aromaticDouble = ((flags & 512) == 512);
this.aromaticStrict = ((flags & 256) == 256);
this.aromaticPlanar = ((flags & 1024) == 1024);
this.groupByModel = ((flags & 67108864) == 67108864);
this.noAromatic = ((flags & 16) == 16);
this.ignoreAtomClass = ((flags & 2048) == 2048);
this.ignoreStereochemistry = ((flags & 32) == 32);
this.invertStereochemistry = !this.ignoreStereochemistry && ((flags & 64) == 64);
}, "~N");
Clazz.defineMethod (c$, "set", 
function () {
if (this.patternAtoms.length > this.ac) this.patternAtoms = JU.AU.arrayCopyObject (this.patternAtoms, this.ac);
this.nodes = this.patternAtoms;
this.isTopology = true;
this.patternAromatic = false;
this.patternBioSequence = true;
for (var i = this.ac; --i >= 0; ) {
var atom = this.patternAtoms[i];
if (this.isTopology && atom.isDefined ()) this.isTopology = false;
if (!atom.isBioResidue) this.patternBioSequence = false;
if (atom.isAromatic) this.patternAromatic = true;
atom.setBondArray ();
if (!this.isSmarts && atom.bioType == '\0' && !atom.setHydrogenCount ()) throw  new JS.InvalidSmilesException ("unbracketed atoms must be one of: B, C, N, O, P, S, F, Cl, Br, I, *,");
}
if (this.haveComponents) {
for (var i = this.ac; --i >= 0; ) {
var a = this.patternAtoms[i];
var bonds = a.bonds;
var ia = a.component;
for (var j = a.bondCount; --j >= 0; ) {
var b = bonds[j];
var ib;
if (b.isConnection && b.atom2 === a && (ib = b.atom1.component) != ia) {
for (var k = this.ac; --k >= 0; ) if (this.patternAtoms[k].component == ia) this.patternAtoms[k].component = ib;

}}
}
}});
Clazz.defineMethod (c$, "setSelected", 
function (bs) {
if (bs == null) {
bs = JU.BS.newN (this.targetAtomCount);
bs.setBits (0, this.targetAtomCount);
}this.bsSelected = bs;
}, "JU.BS");
Clazz.defineMethod (c$, "addAtom", 
function () {
return this.appendAtom ( new JS.SmilesAtom ());
});
Clazz.defineMethod (c$, "appendAtom", 
function (sAtom) {
if (this.ac >= this.patternAtoms.length) this.patternAtoms = JU.AU.doubleLength (this.patternAtoms);
return this.patternAtoms[this.ac] = sAtom.setIndex (this.ac++);
}, "JS.SmilesAtom");
Clazz.defineMethod (c$, "addNested", 
function (pattern) {
if (this.htNested == null) this.htNested =  new java.util.Hashtable ();
this.setNested (++this.nNested, pattern);
return this.nNested;
}, "~S");
Clazz.defineMethod (c$, "clear", 
function () {
this.bsReturn.clearAll ();
this.nNested = 0;
this.htNested = null;
this.nestedBond = null;
this.clearBsFound (-1);
});
Clazz.defineMethod (c$, "clearBsFound", 
 function (iAtom) {
if (iAtom < 0) {
if (this.bsCheck == null) {
this.bsFound.clearAll ();
}} else this.bsFound.clear (iAtom);
}, "~N");
Clazz.defineMethod (c$, "setNested", 
function (iNested, o) {
this.top.htNested.put ("_" + iNested, o);
}, "~N,~O");
Clazz.defineMethod (c$, "getNested", 
function (iNested) {
return this.top.htNested.get ("_" + iNested);
}, "~N");
Clazz.defineMethod (c$, "getMissingHydrogenCount", 
function () {
var n = 0;
var nH;
for (var i = 0; i < this.ac; i++) if ((nH = this.patternAtoms[i].explicitHydrogenCount) >= 0) n += nH;

return n;
});
Clazz.defineMethod (c$, "setRingData", 
function (bsA, vRings, doProcessAromatic) {
if (this.isTopology || this.patternBioSequence) this.needAromatic = false;
if (this.needAromatic) this.needRingData = true;
var noAromatic = ((this.flags & 16) == 16);
this.needAromatic = new Boolean (this.needAromatic & ( new Boolean ((bsA == null) & !noAromatic).valueOf ())).valueOf ();
if (!this.needAromatic) {
this.bsAromatic.clearAll ();
if (bsA != null) this.bsAromatic.or (bsA);
if (!this.needRingMemberships && !this.needRingData) return;
}this.getRingData (vRings, this.needRingData, doProcessAromatic);
}, "JU.BS,~A,~B");
Clazz.defineMethod (c$, "getRingData", 
function (vRings, needRingData, doTestAromatic) {
var isStrict = this.aromaticStrict || !this.aromaticOpen && !this.aromaticPlanar;
var isOpenNotStrict = this.aromaticOpen && !this.aromaticStrict;
var strictness = (!isStrict ? 0 : (this.flags & 768) == 768 ? 2 : 1);
var checkExplicit = (strictness == 0);
var isDefined = ((this.flags & 128) == 128);
var doFinalize = (this.needAromatic && doTestAromatic && (isStrict || isOpenNotStrict));
var aromaticMax = 7;
var lstAromatic = (vRings == null ?  new JU.Lst () : (vRings[3] =  new JU.Lst ()));
var lstSP2 = (doFinalize ?  new JU.Lst () : null);
var eCounts = (doFinalize ?  Clazz.newIntArray (this.targetAtomCount, 0) : null);
if (isDefined && this.needAromatic) {
JS.SmilesAromatic.checkAromaticDefined (this.targetAtoms, this.bsSelected, this.bsAromatic);
strictness = 0;
}var nAtoms = this.targetAtomCount;
var justCheckBonding = (nAtoms == 0 || (Clazz.instanceOf (this.targetAtoms[0], JS.SmilesAtom)));
if (this.ringDataMax < 0) this.ringDataMax = 8;
if (strictness > 0 && this.ringDataMax < 6) this.ringDataMax = 6;
if (needRingData) {
this.ringCounts =  Clazz.newIntArray (nAtoms, 0);
this.ringConnections =  Clazz.newIntArray (this.targetAtomCount, 0);
this.ringData =  new Array (this.ringDataMax + 1);
}this.ringSets =  new JU.SB ();
var s = "****";
var max = this.ringDataMax;
while (s.length < max) s += s;

for (var i = 3; i <= max; i++) {
if (i > nAtoms) continue;
var smarts = "*1" + s.substring (0, i - 2) + "*1";
var search = JS.SmilesParser.newSearch (smarts, true, true);
var vR = this.subsearch (search, 2);
if (vRings != null && i <= 5) {
var v =  new JU.Lst ();
for (var j = vR.size (); --j >= 0; ) v.addLast (vR.get (j));

vRings[i - 3] = v;
}if (vR.size () == 0) continue;
if (this.needAromatic && !isDefined && i >= 4 && i <= aromaticMax) JS.SmilesAromatic.setAromatic (i, this.targetAtoms, this.bsSelected, vR, this.bsAromatic, strictness, isOpenNotStrict, justCheckBonding, checkExplicit, this.v, lstAromatic, lstSP2, eCounts, doTestAromatic);
if (needRingData) {
this.ringData[i] =  new JU.BS ();
for (var k = vR.size (); --k >= 0; ) {
var r = vR.get (k);
this.ringData[i].or (r);
for (var j = r.nextSetBit (0); j >= 0; j = r.nextSetBit (j + 1)) this.ringCounts[j]++;

}
}}
if (this.needAromatic) {
if (doFinalize) JS.SmilesAromatic.finalizeAromatic (this.targetAtoms, this.bsAromatic, lstAromatic, lstSP2, eCounts, isOpenNotStrict, isStrict);
this.bsAromatic5.clearAll ();
this.bsAromatic6.clearAll ();
for (var i = lstAromatic.size (); --i >= 0; ) {
var bs = lstAromatic.get (i);
bs.and (this.bsAromatic);
switch (bs.cardinality ()) {
case 5:
this.bsAromatic5.or (bs);
break;
case 6:
this.bsAromatic6.or (bs);
break;
}
}
}if (needRingData) {
for (var i = this.bsSelected.nextSetBit (0); i >= 0; i = this.bsSelected.nextSetBit (i + 1)) {
var atom = this.targetAtoms[i];
var bonds = atom.getEdges ();
if (bonds != null) for (var k = bonds.length; --k >= 0; ) if (this.ringCounts[atom.getBondedAtomIndex (k)] > 0) this.ringConnections[i]++;

}
}}, "~A,~B,~B");
Clazz.defineMethod (c$, "subsearch", 
function (search, submode) {
search.ringSets = this.ringSets;
search.targetAtoms = this.targetAtoms;
search.targetAtomCount = this.targetAtomCount;
search.bsSelected = this.bsSelected;
search.htNested = this.htNested;
search.haveTopo = this.haveTopo;
search.bsCheck = this.bsCheck;
search.isSmarts = true;
search.bsAromatic = this.bsAromatic;
search.bsAromatic5 = this.bsAromatic5;
search.bsAromatic6 = this.bsAromatic6;
search.ringData = this.ringData;
search.ringCounts = this.ringCounts;
search.ringConnections = this.ringConnections;
switch (submode) {
case 1:
search.exitFirstMatch = false;
break;
case 2:
search.isRingCheck = true;
search.isSilent = true;
search.asVector = true;
break;
case 3:
search.ignoreAtomClass = this.ignoreAtomClass;
search.aromaticDouble = this.aromaticDouble;
search.haveSelected = this.haveSelected;
search.exitFirstMatch = this.exitFirstMatch;
search.getMaps = this.getMaps;
search.asVector = this.asVector;
search.vReturn = this.vReturn;
search.bsReturn = this.bsReturn;
search.haveBondStereochemistry = this.haveBondStereochemistry;
break;
}
return search.search2 (submode == 1);
}, "JS.SmilesSearch,~N");
Clazz.defineMethod (c$, "search", 
function () {
return this.search2 (false);
});
Clazz.defineMethod (c$, "search2", 
 function (firstAtomOnly) {
this.setFlags (this.flags);
if (!this.isRingCheck && JU.Logger.debugging && !this.isSilent) JU.Logger.debug ("SmilesSearch processing " + this.pattern);
if (this.vReturn == null && (this.asVector || this.getMaps)) this.vReturn =  new JU.Lst ();
if (this.bsSelected == null) {
this.bsSelected = JU.BS.newN (this.targetAtomCount);
this.bsSelected.setBits (0, this.targetAtomCount);
}this.selectedAtomCount = this.bsSelected.cardinality ();
if (this.subSearches != null) {
for (var i = 0; i < this.subSearches.length; i++) {
if (this.subSearches[i] == null) continue;
this.subsearch (this.subSearches[i], 3);
if (this.exitFirstMatch) {
if (this.vReturn == null ? this.bsReturn.nextSetBit (0) >= 0 : this.vReturn.size () > 0) break;
}}
} else if (this.ac > 0) {
if (this.nestedBond == null) {
this.clearBsFound (-1);
} else {
this.bsReturn.clearAll ();
}this.nextPatternAtom (-1, -1, firstAtomOnly, -1);
}return (this.asVector || this.getMaps ? this.vReturn : this.bsReturn);
}, "~B");
Clazz.defineMethod (c$, "nextPatternAtom", 
 function (atomNum, iAtom, firstAtomOnly, c) {
var jmolAtom;
var jmolBonds;
if (++atomNum < this.ac) {
var newPatternAtom = this.patternAtoms[atomNum];
var newPatternBond = (iAtom >= 0 ? newPatternAtom.getBondTo (null) : atomNum == 0 ? this.nestedBond : null);
if (newPatternBond == null) {
var bs = JU.BSUtil.copy (this.bsFound);
var bs0 = JU.BSUtil.copy (this.bsFound);
if (newPatternAtom.notBondedIndex >= 0) {
var pa = this.patternAtoms[newPatternAtom.notBondedIndex];
var a = pa.getMatchingAtom ();
if (pa.isBioAtom) {
var ii = a.getOffsetResidueAtom ("\0", 1);
if (ii >= 0) bs.set (ii);
ii = a.getOffsetResidueAtom ("\0", -1);
if (ii >= 0) bs.set (ii);
} else {
jmolBonds = a.getEdges ();
for (var k = 0; k < jmolBonds.length; k++) bs.set (jmolBonds[k].getOtherAtomNode (a).getIndex ());

}}var skipGroup = ((newPatternAtom.isBioAtomWild));
var j1 = this.bsSelected.nextSetBit (0);
j1 = (skipGroup && j1 >= 0 ? this.targetAtoms[j1].getOffsetResidueAtom ("\0", j1) : j1);
var oldJmolComponent;
var oldPatternComponent = (atomNum > 0 ? this.patternAtoms[atomNum - 1] : newPatternAtom).component;
var thisPatternComponent = newPatternAtom.component;
var checkComponents = this.haveComponents && thisPatternComponent != -2147483648;
for (var j = j1; j >= 0; j = this.bsSelected.nextSetBit (j + 1)) {
if (!bs.get (j) && !this.bsFound.get (j)) {
jmolAtom = this.targetAtoms[j];
if (checkComponents && !this.isRingCheck) {
c = (this.groupByModel ? jmolAtom.getModelIndex () : jmolAtom.getMoleculeNumber (false));
oldJmolComponent = (atomNum > 0 ? this.patternAtoms[atomNum - 1].matchingComponent : c);
if ((oldPatternComponent == thisPatternComponent) != (oldJmolComponent == c)) continue;
}if (!this.nextTargetAtom (newPatternAtom, jmolAtom, atomNum, j, firstAtomOnly, c)) return false;
}if (skipGroup) {
j1 = this.targetAtoms[j].getOffsetResidueAtom (newPatternAtom.bioAtomName, 1);
if (j1 >= 0) j = j1 - 1;
}}
this.bsFound = bs0;
return true;
}jmolAtom = newPatternBond.atom1.getMatchingAtom ();
switch (newPatternBond.order) {
case 96:
var nextGroupAtom = jmolAtom.getOffsetResidueAtom (newPatternAtom.bioAtomName, 1);
if (nextGroupAtom >= 0) {
var bs = JU.BSUtil.copy (this.bsFound);
jmolAtom.getGroupBits (this.bsFound);
if (this.doCheckAtom (nextGroupAtom) && !this.nextTargetAtom (newPatternAtom, this.targetAtoms[nextGroupAtom], atomNum, nextGroupAtom, firstAtomOnly, c)) return false;
this.bsFound = bs;
}return true;
case 112:
var vLinks =  new JU.Lst ();
jmolAtom.getCrossLinkVector (vLinks, true, true);
var bs = JU.BSUtil.copy (this.bsFound);
jmolAtom.getGroupBits (this.bsFound);
for (var j = 2; j < vLinks.size (); j += 3) {
var ia = vLinks.get (j).intValue ();
if (this.doCheckAtom (ia) && !this.nextTargetAtom (newPatternAtom, this.targetAtoms[ia], atomNum, ia, firstAtomOnly, c)) return false;
}
this.bsFound = bs;
return true;
}
jmolBonds = jmolAtom.getEdges ();
if (jmolBonds != null) for (var j = 0; j < jmolBonds.length; j++) {
var ia = jmolAtom.getBondedAtomIndex (j);
if (this.doCheckAtom (ia) && !this.nextTargetAtom (newPatternAtom, this.targetAtoms[ia], atomNum, ia, firstAtomOnly, c)) return false;
}
this.clearBsFound (iAtom);
return true;
}if (!this.ignoreStereochemistry && !this.isRingCheck && !this.checkStereochemistry ()) return true;
var bs =  new JU.BS ();
var nMatch = 0;
for (var j = 0; j < this.ac; j++) {
var i = this.patternAtoms[j].getMatchingAtomIndex ();
if (!firstAtomOnly && this.top.haveSelected && !this.patternAtoms[j].selected) continue;
nMatch++;
bs.set (i);
if (this.patternAtoms[j].isBioAtomWild) this.targetAtoms[i].getGroupBits (bs);
if (firstAtomOnly) break;
if (!this.isSmarts) if (!this.setAtropicity && this.patternAtoms[j].explicitHydrogenCount > 0) {
var atom = this.targetAtoms[i];
for (var k = 0, n = atom.getEdges ().length; k < n; k++) {
var ia = atom.getBondedAtomIndex (k);
if (this.targetAtoms[ia].getElementNumber () == 1) bs.set (ia);
}
}}
if (!this.isSmarts && bs.cardinality () != this.selectedAtomCount) return true;
if (this.bsCheck != null) {
if (firstAtomOnly) {
this.bsCheck.clearAll ();
for (var j = 0; j < this.ac; j++) {
this.bsCheck.set (this.patternAtoms[j].getMatchingAtomIndex ());
}
if (this.bsCheck.cardinality () != this.ac) return true;
} else {
if (bs.cardinality () != this.ac) return true;
}}this.bsReturn.or (bs);
if (this.getMaps) {
var map =  Clazz.newIntArray (nMatch, 0);
for (var j = 0, nn = 0; j < this.ac; j++) {
if (!firstAtomOnly && this.top.haveSelected && !this.patternAtoms[j].selected) continue;
map[nn++] = this.patternAtoms[j].getMatchingAtomIndex ();
}
this.vReturn.addLast (map);
return !this.exitFirstMatch;
}if (this.asVector) {
var isOK = true;
for (var j = this.vReturn.size (); --j >= 0 && isOK; ) isOK = !((this.vReturn.get (j)).equals (bs));

if (!isOK) return true;
this.vReturn.addLast (bs);
}if (this.isRingCheck) {
this.ringSets.append (" ");
for (var k = atomNum * 3 + 2; --k > atomNum; ) this.ringSets.append ("-").appendI (this.patternAtoms[(k <= atomNum * 2 ? atomNum * 2 - k + 1 : k - 1) % atomNum].getMatchingAtomIndex ());

this.ringSets.append ("- ");
return true;
}if (this.exitFirstMatch) return false;
return (bs.cardinality () != this.selectedAtomCount);
}, "~N,~N,~B,~N");
Clazz.defineMethod (c$, "doCheckAtom", 
 function (j) {
return this.bsSelected.get (j) && !this.bsFound.get (j);
}, "~N");
Clazz.defineMethod (c$, "nextTargetAtom", 
 function (patternAtom, jmolAtom, atomNum, iAtom, firstAtomOnly, c) {
var jmolBonds;
if (!this.isRingCheck && !this.isTopology) {
if (patternAtom.subAtoms == null) {
if (!this.checkPrimitiveAtom (patternAtom, iAtom)) return true;
} else if (patternAtom.isAND) {
for (var i = 0; i < patternAtom.nSubAtoms; i++) if (!this.checkPrimitiveAtom (patternAtom.subAtoms[i], iAtom)) return true;

} else {
for (var i = 0; i < patternAtom.nSubAtoms; i++) if (!this.nextTargetAtom (patternAtom.subAtoms[i], jmolAtom, atomNum, iAtom, firstAtomOnly, c)) return false;

return true;
}}jmolBonds = jmolAtom.getEdges ();
for (var i = patternAtom.getBondCount (); --i >= 0; ) {
var patternBond = patternAtom.getBond (i);
if (patternBond.getAtomIndex2 () != patternAtom.index) continue;
var atom1 = patternBond.atom1;
var matchingAtom = atom1.getMatchingAtomIndex ();
switch (patternBond.order) {
case 96:
case 112:
if (!this.checkMatchBond (patternAtom, atom1, patternBond, iAtom, matchingAtom, null)) return true;
break;
default:
var k = 0;
var jmolBond = null;
for (; k < jmolBonds.length; k++) if ((jmolBond = jmolBonds[k]).isCovalent () && (jmolBond.getAtomIndex1 () == matchingAtom || jmolBond.getAtomIndex2 () == matchingAtom)) break;

if (k == jmolBonds.length) return true;
if (!this.checkMatchBond (patternAtom, atom1, patternBond, iAtom, matchingAtom, jmolBond)) return true;
}
}
patternAtom = this.patternAtoms[patternAtom.index];
patternAtom.setMatchingAtom (this.targetAtoms[iAtom], iAtom);
patternAtom.matchingComponent = c;
if (JU.Logger.debugging && !this.isRingCheck) {
for (var i = 0; i <= atomNum; i++) JU.Logger.debug ("pattern atoms " + this.patternAtoms[i] + " " + this.patternAtoms[i].matchingComponent);

JU.Logger.debug ("--ss--");
}this.bsFound.set (iAtom);
if (!this.nextPatternAtom (atomNum, iAtom, firstAtomOnly, c)) return false;
if (iAtom >= 0) this.clearBsFound (iAtom);
return true;
}, "JS.SmilesAtom,JU.Node,~N,~N,~B,~N");
Clazz.defineMethod (c$, "checkPrimitiveAtom", 
 function (patternAtom, iTarget) {
if (patternAtom.nSubAtoms > 0) {
for (var i = 0; i < patternAtom.nSubAtoms; i++) if (this.checkPrimitiveAtom (patternAtom.subAtoms[i], iTarget)) return true;

return false;
}var targetAtom = this.targetAtoms[iTarget];
var foundAtom = patternAtom.not;
while (true) {
if (patternAtom.iNested > 0) {
var o = this.getNested (patternAtom.iNested);
if (Clazz.instanceOf (o, JS.SmilesSearch)) {
var search = o;
if (patternAtom.isBioAtom) search.nestedBond = patternAtom.getBondTo (null);
o = this.subsearch (search, 1);
if (o == null) o =  new JU.BS ();
if (!patternAtom.isBioAtom) this.setNested (patternAtom.iNested, o);
}foundAtom = (patternAtom.not != ((o).get (iTarget)));
break;
}var na = targetAtom.getElementNumber ();
var n = patternAtom.elementNumber;
if (na >= 0 && n >= 0 && n != na) break;
if (patternAtom.isBioResidue) {
var a = targetAtom;
if (patternAtom.bioAtomName != null && (patternAtom.isLeadAtom () ? !a.isLeadAtom () : !patternAtom.bioAtomName.equals (a.getAtomName ().toUpperCase ()))) break;
if (patternAtom.residueName != null && !patternAtom.residueName.equals (a.getGroup3 (false).toUpperCase ())) break;
if (patternAtom.residueNumber != -2147483648 && patternAtom.residueNumber != a.getResno ()) break;
if (patternAtom.insCode != '\0' && patternAtom.insCode != a.getInsertionCode ()) break;
if (patternAtom.residueChar != null || patternAtom.elementNumber == -2) {
var atype = a.getBioSmilesType ();
var ptype = patternAtom.getBioSmilesType ();
var ok = true;
var isNucleic = false;
switch (ptype) {
case '\0':
case '*':
ok = true;
break;
case 'n':
ok = (atype == 'r' || atype == 'c');
isNucleic = true;
break;
case 'r':
case 'c':
isNucleic = true;
default:
ok = (atype == ptype);
break;
}
if (!ok) break;
var s = a.getGroup1 ('\0').toUpperCase ();
var resChar = (patternAtom.residueChar == null ? '*' : patternAtom.residueChar.charAt (0));
var isOK = (resChar == s.charAt (0));
switch (resChar) {
case '*':
isOK = true;
break;
case 'N':
isOK = isNucleic ? (atype == 'r' || atype == 'c') : isOK;
break;
case 'R':
isOK = isNucleic ? a.isPurine () : isOK;
break;
case 'Y':
isOK = isNucleic ? a.isPyrimidine () : isOK;
break;
}
if (!isOK) break;
}if (patternAtom.isBioAtom) {
if (patternAtom.notCrossLinked && a.getCrossLinkVector (null, true, true)) break;
}} else {
if (patternAtom.atomNumber != -2147483648 && patternAtom.atomNumber != targetAtom.getAtomNumber ()) break;
if (patternAtom.jmolIndex >= 0 && targetAtom.getIndex () != patternAtom.jmolIndex) break;
if (patternAtom.atomType != null && !patternAtom.atomType.equals (targetAtom.getAtomType ())) break;
if ((n = patternAtom.getAtomicMass ()) != -2147483648 && (n >= 0 && n != (na = targetAtom.getIsotopeNumber ()) || n < 0 && na != 0 && -n != na)) break;
if (!this.noAromatic && !patternAtom.aromaticAmbiguous && patternAtom.isAromatic != this.bsAromatic.get (iTarget)) break;
if ((n = patternAtom.getCharge ()) != -2147483648 && n != targetAtom.getFormalCharge ()) break;
n = patternAtom.getCovalentHydrogenCount () + patternAtom.explicitHydrogenCount;
if (n >= 0 && n != targetAtom.getTotalHydrogenCount ()) break;
if ((n = patternAtom.implicitHydrogenCount) != -2147483648) {
na = targetAtom.getImplicitHydrogenCount ();
if (n == -1 ? na == 0 : n != na) break;
}if (patternAtom.degree > 0 && patternAtom.degree != targetAtom.getCovalentBondCount () - targetAtom.getImplicitHydrogenCount ()) break;
if (patternAtom.nonhydrogenDegree > 0 && patternAtom.nonhydrogenDegree != targetAtom.getCovalentBondCount () - targetAtom.getCovalentHydrogenCount ()) break;
if (this.isSmarts && patternAtom.valence > 0 && patternAtom.valence != targetAtom.getTotalValence ()) break;
if (patternAtom.connectivity > 0 && patternAtom.connectivity != targetAtom.getCovalentBondCountPlusMissingH ()) break;
if (patternAtom.atomNumber != -2147483648 && patternAtom.atomNumber != targetAtom.getAtomNumber ()) break;
if (patternAtom.jmolIndex >= 0 && targetAtom.getIndex () != patternAtom.jmolIndex) break;
if (patternAtom.atomType != null && !patternAtom.atomType.equals (targetAtom.getAtomType ())) break;
if (!this.ignoreAtomClass || this.isSmarts) {
if (!Float.isNaN (patternAtom.atomClass) && patternAtom.atomClass != targetAtom.getFloatProperty ("property_atomclass")) break;
}if (this.ringData != null) {
if (patternAtom.ringSize >= -1) {
if (patternAtom.ringSize <= 0) {
if ((this.ringCounts[iTarget] == 0) != (patternAtom.ringSize == 0)) break;
} else {
var rd = this.ringData[patternAtom.ringSize == 500 ? 5 : patternAtom.ringSize == 600 ? 6 : patternAtom.ringSize];
if (rd == null || !rd.get (iTarget)) break;
if (!this.noAromatic) if (patternAtom.ringSize == 500) {
if (!this.bsAromatic5.get (iTarget)) break;
} else if (patternAtom.ringSize == 600) {
if (!this.bsAromatic6.get (iTarget)) break;
}}}if (patternAtom.ringMembership >= -1) {
if (patternAtom.ringMembership == -1 ? this.ringCounts[iTarget] == 0 : this.ringCounts[iTarget] != patternAtom.ringMembership) break;
}if (patternAtom.ringConnectivity >= 0) {
n = this.ringConnections[iTarget];
if (patternAtom.ringConnectivity == -1 && n == 0 || patternAtom.ringConnectivity != -1 && n != patternAtom.ringConnectivity) break;
}}}foundAtom = !foundAtom;
break;
}
return foundAtom;
}, "JS.SmilesAtom,~N");
Clazz.defineMethod (c$, "checkMatchBond", 
 function (patternAtom, atom1, patternBond, iAtom, matchingAtom, bond) {
if (patternBond.bondsOr != null) {
for (var ii = 0; ii < patternBond.nBondsOr; ii++) if (this.checkMatchBond (patternAtom, atom1, patternBond.bondsOr[ii], iAtom, matchingAtom, bond)) return true;

return false;
}if (!this.isRingCheck && !this.isTopology) if (patternBond.nPrimitives == 0) {
if (!this.checkPrimitiveBond (patternBond, iAtom, matchingAtom, bond)) return false;
} else {
for (var i = 0; i < patternBond.nPrimitives; i++) {
var prim = patternBond.setPrimitive (i);
if (!this.checkPrimitiveBond (prim, iAtom, matchingAtom, bond)) return false;
}
}patternBond.matchingBond = bond;
return true;
}, "JS.SmilesAtom,JS.SmilesAtom,JS.SmilesBond,~N,~N,JU.Edge");
Clazz.defineMethod (c$, "checkPrimitiveBond", 
 function (patternBond, iAtom1, iAtom2, bond) {
var bondFound = false;
switch (patternBond.order) {
case 96:
return (patternBond.isNot != (this.targetAtoms[iAtom2].getOffsetResidueAtom ("\0", 1) == this.targetAtoms[iAtom1].getOffsetResidueAtom ("\0", 0)));
case 112:
return (patternBond.isNot != this.targetAtoms[iAtom1].isCrossLinked (this.targetAtoms[iAtom2]));
}
var isAromatic1 = (!this.noAromatic && this.bsAromatic.get (iAtom1));
var isAromatic2 = (!this.noAromatic && this.bsAromatic.get (iAtom2));
var order = bond.getCovalentOrder ();
var patternOrder = patternBond.order;
if (isAromatic1 && isAromatic2) {
switch (patternOrder) {
case 17:
case 65:
bondFound = JS.SmilesSearch.isRingBond (this.ringSets, iAtom1, iAtom2);
break;
case 1:
bondFound = !this.isSmarts || !JS.SmilesSearch.isRingBond (this.ringSets, iAtom1, iAtom2);
break;
case 2:
bondFound = this.isNormalized || this.aromaticDouble && (order == 2 || order == 514);
break;
case 65537:
case 65538:
bondFound = !patternBond.isNot;
break;
case 81:
case -1:
bondFound = true;
break;
}
} else {
switch (patternOrder) {
case 17:
if (!this.noAromatic) break;
case 81:
case -1:
bondFound = true;
break;
case 1:
case 1025:
case 1041:
switch (order) {
case 1:
case 1025:
case 1041:
bondFound = true;
break;
}
break;
case 65537:
case 65538:
switch (order) {
case 1:
case 65537:
case 65538:
bondFound = !patternBond.isNot;
break;
}
break;
case 2:
case 3:
case 4:
bondFound = (order == patternOrder);
break;
case 65:
bondFound = JS.SmilesSearch.isRingBond (this.ringSets, iAtom1, iAtom2);
break;
}
}return bondFound != patternBond.isNot;
}, "JS.SmilesBond,~N,~N,JU.Edge");
c$.isRingBond = Clazz.defineMethod (c$, "isRingBond", 
function (ringSets, i, j) {
return (ringSets != null && ringSets.indexOf ("-" + i + "-" + j + "-") >= 0);
}, "JU.SB,~N,~N");
Clazz.defineMethod (c$, "checkStereochemistry", 
 function () {
for (var i = 0; i < this.measures.size (); i++) if (!this.measures.get (i).check ()) return false;

if (this.stereo != null && !this.stereo.checkStereoChemistry (this, this.v)) return false;
if (!this.haveBondStereochemistry) return true;
var lstAtrop = null;
var b = null;
for (var k = 0; k < this.ac; k++) {
var sAtom1 = this.patternAtoms[k];
var sAtom2 = null;
var sAtomDirected1 = null;
var sAtomDirected2 = null;
var dir1 = 0;
var dir2 = 0;
var bondType = 0;
var nBonds = sAtom1.getBondCount ();
var isAtropisomer = false;
var indexOrder = true;
for (var j = 0; j < nBonds; j++) {
b = sAtom1.getBond (j);
var isAtom2 = (b.atom2 === sAtom1);
indexOrder = (b.atom1.index < b.atom2.index);
var type = b.order;
switch (type) {
case 65537:
case 65538:
if (!indexOrder) continue;
case 2:
if (isAtom2) continue;
sAtom2 = b.atom2;
bondType = type;
isAtropisomer = (type != 2);
if (isAtropisomer) dir1 = (b.isNot ? -1 : 1);
break;
case 1025:
case 1041:
sAtomDirected1 = (isAtom2 ? b.atom1 : b.atom2);
dir1 = (isAtom2 != (type == 1025) ? 1 : -1);
break;
}
}
if (isAtropisomer) {
if (this.setAtropicity) {
if (lstAtrop == null) lstAtrop =  new JU.Lst ();
lstAtrop.addLast (b);
continue;
}var b1 = sAtom1.getBond (b.atropType[0]);
if (b1 == null) return false;
sAtomDirected1 = b1.getOtherAtom (sAtom1);
b1 = sAtom2.getBond (b.atropType[1]);
if (b1 == null) return false;
sAtomDirected2 = b1.getOtherAtom (sAtom2);
if (JU.Logger.debugging) JU.Logger.info ("atropisomer check for atoms " + sAtomDirected1 + sAtom1 + " " + sAtom2 + sAtomDirected2);
} else {
if (sAtom2 == null || dir1 == 0) continue;
var a10 = sAtom1;
var nCumulene = 0;
while (sAtom2.getBondCount () == 2 && sAtom2.getValence () == 4) {
nCumulene++;
var e2 = sAtom2.getEdges ();
var e = e2[e2[0].getOtherAtomNode (sAtom2) === a10 ? 1 : 0];
a10 = sAtom2;
sAtom2 = e.getOtherAtomNode (sAtom2);
}
if (nCumulene % 2 == 1) continue;
nBonds = sAtom2.getBondCount ();
for (var j = 0; j < nBonds && dir2 == 0; j++) {
b = sAtom2.getBond (j);
var type = b.order;
switch (type) {
case 1025:
case 1041:
var isAtom2 = (b.atom2 === sAtom2);
sAtomDirected2 = (isAtom2 ? b.atom1 : b.atom2);
dir2 = (isAtom2 != (type == 1025) ? 1 : -1);
break;
}
}
if (dir2 == 0) continue;
}var dbAtom1 = sAtom1.getMatchingAtom ();
var dbAtom2 = sAtom2.getMatchingAtom ();
var dbAtom1a = sAtomDirected1.getMatchingAtom ();
var dbAtom2a = sAtomDirected2.getMatchingAtom ();
if (dbAtom1a == null || dbAtom2a == null) return false;
if (this.haveTopo) this.setTopoCoordinates (dbAtom1, dbAtom2, dbAtom1a, dbAtom2a, bondType);
var d = JS.SmilesMeasure.setTorsionData (dbAtom1a, dbAtom1, dbAtom2, dbAtom2a, this.v, isAtropisomer);
if (isAtropisomer) {
d *= dir1 * (bondType == 65537 ? 1 : -1) * (indexOrder ? 1 : -1);
if (JU.Logger.debugging) JU.Logger.info ("atrop dihedral " + d + " " + sAtom1 + " " + sAtom2 + " " + b);
if (d < 1.0) return false;
} else {
if (this.v.vTemp1.dot (this.v.vTemp2) * dir1 * dir2 < 0) return false;
}}
if (this.setAtropicity) {
this.atropKeys = "";
for (var i = 0; i < lstAtrop.size (); i++) this.atropKeys += "," + this.getAtropIndex ((b = lstAtrop.get (i)), true) + this.getAtropIndex (b, false);

}return true;
});
Clazz.defineMethod (c$, "getAtropIndex", 
 function (b, isFirst) {
var s1 = (isFirst ? b.atom1 : b.atom2);
var a1 = s1.getMatchingAtom ();
var a11 = JU.Edge.getAtropismNode (b.matchingBond.order, a1, isFirst);
var b1 = s1.bonds;
for (var i = s1.getBondCount (); --i >= 0; ) if ((b1[i].getOtherAtomNode (s1)).getMatchingAtom () === a11) return i + 1;

return 0;
}, "JS.SmilesBond,~B");
Clazz.defineMethod (c$, "setTopoCoordinates", 
 function (dbAtom1, dbAtom2, dbAtom1a, dbAtom2a, bondType) {
dbAtom1.set (-1, 0, 0);
dbAtom2.set (1, 0, 0);
if (bondType != 2) {
var bond = dbAtom1.getBondTo (dbAtom2);
var dir = (bond.order == 65537 ? 1 : -1);
dbAtom1a.set (-1, 1, 0);
dbAtom2a.set (1, 1, dir / 2.0);
return;
}var nBonds = 0;
var dir1 = 0;
var bonds = dbAtom1.getEdges ();
for (var k = bonds.length; --k >= 0; ) {
var bond = bonds[k];
if (bond.order == 2) continue;
var atom = bond.getOtherAtomNode (dbAtom1);
atom.set (-1, (nBonds++ == 0) ? -1 : 1, 0);
var mode = (bond.getAtomIndex2 () == dbAtom1.getIndex () ? nBonds : -nBonds);
switch (bond.order) {
case 1025:
dir1 = mode;
break;
case 1041:
dir1 = -mode;
}
}
var dir2 = 0;
nBonds = 0;
var atoms =  new Array (2);
bonds = dbAtom2.getEdges ();
for (var k = bonds.length; --k >= 0; ) {
var bond = bonds[k];
if (bond.order == 2) continue;
var atom = bond.getOtherAtomNode (dbAtom2);
atoms[nBonds] = atom;
atom.set (1, (nBonds++ == 0) ? 1 : -1, 0);
var mode = (bond.getAtomIndex2 () == dbAtom2.getIndex () ? nBonds : -nBonds);
switch (bond.order) {
case 1025:
dir2 = mode;
break;
case 1041:
dir2 = -mode;
}
}
if ((dir1 * dir2 > 0) == (Math.abs (dir1) % 2 == Math.abs (dir2) % 2)) {
var y = (atoms[0]).y;
(atoms[0]).y = (atoms[1]).y;
(atoms[1]).y = y;
}}, "JS.SmilesAtom,JS.SmilesAtom,JS.SmilesAtom,JS.SmilesAtom,~N");
Clazz.defineMethod (c$, "createTopoMap", 
function (bsAro) {
var isForMF = (bsAro == null);
var nAtomsMissing = this.getMissingHydrogenCount ();
var totalAtoms = this.ac + nAtomsMissing;
var atoms =  new Array (totalAtoms);
this.targetAtoms = atoms;
for (var i = 0, ptAtom = 0; i < this.ac; i++, ptAtom++) {
var sAtom = this.patternAtoms[i];
var n = sAtom.explicitHydrogenCount;
if (n < 0) n = 0;
var atom = atoms[ptAtom] =  new JS.SmilesAtom ().setTopoAtom (sAtom.component, ptAtom, sAtom.symbol, sAtom.getCharge ());
atom.implicitHydrogenCount = n;
if (isForMF) continue;
atom.mapIndex = i;
atom.stereo = sAtom.stereo;
atom.setAtomicMass (sAtom.getAtomicMass ());
atom.bioAtomName = sAtom.bioAtomName;
atom.residueName = sAtom.residueName;
atom.residueChar = sAtom.residueChar;
atom.residueNumber = sAtom.residueNumber;
atom.atomNumber = sAtom.residueNumber;
atom.insCode = sAtom.insCode;
atom.atomClass = sAtom.atomClass;
atom.explicitHydrogenCount = 0;
atom.isBioAtom = sAtom.isBioAtom;
atom.bioType = sAtom.bioType;
atom.$isLeadAtom = sAtom.$isLeadAtom;
if (!isForMF && sAtom.isAromatic) bsAro.set (ptAtom);
sAtom.setMatchingAtom (null, ptAtom);
var bonds =  new Array (sAtom.getBondCount () + n);
atom.setBonds (bonds);
while (--n >= 0) {
var atomH = atoms[++ptAtom] =  new JS.SmilesAtom ().setTopoAtom (atom.component, ptAtom, "H", 0);
atomH.mapIndex = -i - 1;
atomH.setBonds ( new Array (1));
var b =  new JS.SmilesBond (atom, atomH, 1, false);
if (JU.Logger.debugging) JU.Logger.info ("" + b);
}
}
if (isForMF) return;
for (var i = 0; i < this.ac; i++) {
var sAtom = this.patternAtoms[i];
var i1 = sAtom.getMatchingAtomIndex ();
var atom1 = atoms[i1];
var n = sAtom.getBondCount ();
for (var j = 0; j < n; j++) {
var sBond = sAtom.getBond (j);
var firstAtom = (sBond.atom1 === sAtom);
if (firstAtom) {
var order = 1;
switch (sBond.order) {
case 1:
case 2:
case 3:
case 4:
case 1025:
case 1041:
case 65537:
case 65538:
case 112:
case 96:
order = sBond.order;
break;
case 17:
order = 514;
break;
}
var atom2 = atoms[sBond.atom2.getMatchingAtomIndex ()];
var b =  new JS.SmilesBond (atom1, atom2, order, false);
atom2.bondCount--;
if (JU.Logger.debugging) JU.Logger.info ("" + b);
} else {
var atom2 = atoms[sBond.atom1.getMatchingAtomIndex ()];
var b = atom2.getBondTo (atom1);
atom1.addBond (b);
}}
}
for (var i = 0; i < totalAtoms; i++) {
var a = atoms[i];
var bonds = a.bonds;
if (bonds.length < 2 || bonds[0].isFromPreviousTo (a)) continue;
for (var k = bonds.length; --k >= 1; ) if (bonds[k].isFromPreviousTo (a)) {
var b = bonds[k];
bonds[k] = bonds[0];
bonds[0] = b;
break;
}
}
if (!this.ignoreStereochemistry) for (var i = this.ac; --i >= 0; ) {
var sAtom = this.patternAtoms[i];
if (sAtom.stereo != null) sAtom.stereo.fixStereo (sAtom);
}
}, "JU.BS");
c$.normalizeAromaticity = Clazz.defineMethod (c$, "normalizeAromaticity", 
function (atoms, bsAromatic, flags) {
var ss =  new JS.SmilesSearch ();
ss.setFlags (flags);
ss.targetAtoms = atoms;
ss.targetAtomCount = atoms.length;
ss.bsSelected = JU.BSUtil.newBitSet2 (0, atoms.length);
var vRings = JU.AU.createArrayOfArrayList (4);
ss.setRingData (null, vRings, true);
bsAromatic.or (ss.bsAromatic);
if (!bsAromatic.isEmpty ()) {
var lst = vRings[3];
for (var i = lst.size (); --i >= 0; ) {
var bs = lst.get (i);
for (var j = bs.nextSetBit (0); j >= 0; j = bs.nextSetBit (j + 1)) {
var a = atoms[j];
if (a.isAromatic || a.elementNumber == -2 || a.elementNumber == 0) continue;
a.setSymbol (a.symbol.toLowerCase ());
}
}
}}, "~A,JU.BS,~N");
Clazz.defineMethod (c$, "getSelections", 
function () {
var ht = this.top.htNested;
if (ht == null || this.targetAtoms.length == 0) return;
var htNew =  new java.util.Hashtable ();
for (var entry, $entry = ht.entrySet ().iterator (); $entry.hasNext () && ((entry = $entry.next ()) || true);) {
var key = entry.getValue ().toString ();
if (key.startsWith ("select")) {
var bs = (htNew.containsKey (key) ? htNew.get (key) : this.targetAtoms[0].findAtomsLike (key.substring (6)));
if (bs == null) bs =  new JU.BS ();
htNew.put (key, bs);
entry.setValue (bs);
}}
});
c$.getNormalThroughPoints = Clazz.defineMethod (c$, "getNormalThroughPoints", 
function (pointA, pointB, pointC, vNorm, vAB, vAC) {
vAB.sub2 (pointB, pointA);
vAC.sub2 (pointC, pointA);
vNorm.cross (vAB, vAC);
vNorm.normalize ();
vAB.setT (pointA);
return -vAB.dot (vNorm);
}, "JU.Node,JU.Node,JU.Node,JU.V3,JU.V3,JU.V3");
Clazz.defineMethod (c$, "findImplicitHydrogen", 
function (atom) {
var edges = atom.getEdges ();
for (var i = edges.length; --i >= 0; ) {
var k = atom.getBondedAtomIndex (i);
if (this.targetAtoms[k].getElementNumber () == 1 && !this.bsFound.get (k)) return this.targetAtoms[k];
}
return null;
}, "JU.Node");
Clazz.defineMethod (c$, "toString", 
function () {
var sb =  new JU.SB ().append (this.pattern);
sb.append ("\nmolecular formula: " + this.getMolecularFormula (true, null, false));
return sb.toString ();
});
Clazz.defineStatics (c$,
"SUBMODE_NESTED", 1,
"SUBMODE_RINGCHECK", 2,
"SUBMODE_OR", 3);
});
