public RangeMassDecomposer getDecomposerFor(IIsotope[] alphabet) { for (RangeMassDecomposer decomposer : decomposerCache) { if (decomposer.isCompatible(alphabet)) { return decomposer; } } if (decomposerCache.size() >= maximalNumberOfCachedDecomposers) decomposerCache.remove(0); final RangeMassDecomposer decomposer = new RangeMassDecomposer(alphabet); decomposerCache.add(decomposer); return decomposer; }
/** * Initializes the decomposer. Computes the extended residue table. This have to be done only one time for * a given alphabet, independently from the masses you want to decompose. This method is called automatically * if you compute the decompositions, so call it only if you want to control the time of the initialisation. */ private void init() { if (ERTs != null) return; synchronized (this) { if (ERTs != null) return; discretizeMasses(); divideByGCD(); computeLCMs(); calcERT(); computeErrors(); } }
/** * Check if a mass is decomposable. This is done in constant time (especially: it is very very very fast!). * But it doesn't check if there is a valid decomposition. Therefore, even if the method returns true, * all decompositions may be invalid for the given validator or given bounds. * #decompose(mass) uses this function before starting the decomposition, therefore this method should only * be used if you don't want to start the decomposition algorithm. * * @return true if the mass is decomposable, ignoring bounds or any additional filtering rule */ boolean maybeDecomposable(double from, double to) { init(); final int[][][] ERTs = this.ERTs; final int[] minmax = new int[2]; //normal version seems to be faster, because it returns after first hit integerBound(from, to, minmax); final int a = weights.get(0).getIntegerMass(); for (int i = minmax[0]; i <= minmax[1]; ++i) { final int r = i % a; if (i >= ERTs[0][r][weights.size() - 1]) return true; } return false; }
init(); if (to < 0d || from < 0d) throw new IllegalArgumentException("Expect positive mass for decomposition: [" + from + ", " + to + "]"); integerBound(cfrom, cto, minmax); final int deviation = minmax[1] - minmax[0]; calcERT(deviation);
private void divideByGCD() { if (weights.size() > 0) { int d = gcd(weights.get(0).getIntegerMass(), weights.get(1).getIntegerMass()); for (int i = 2; i < weights.size(); ++i) { d = gcd(d, weights.get(i).getIntegerMass()); if (d == 1) return; } precision *= d; for (ChemicalElement weight : weights) { weight.setIntegerMass(weight.getIntegerMass() / d); } } }
if ((1 << (currentLength - 1)) <= deviation) calcERT(deviation);
/** * @param allowedIsotopes array of the elements of the alphabet */ RangeMassDecomposer(IIsotope[] allowedIsotopes) { this.ERTs = null; this.precision = findOptimalPrecision(); final int n = allowedIsotopes.length; this.weights = new ArrayList<>(n); this.elements = new IIsotope[allowedIsotopes.length]; for (IIsotope allowedIsotope : allowedIsotopes) { weights.add(new ChemicalElement(allowedIsotope, allowedIsotope.getExactMass())); } Collections.sort(weights); for (int i = 0; i < n; ++i) { elements[i] = weights.get(i).getOwner(); } }
/** * Initiate the MolecularFormulaGenerator. * * @param minMass * Lower boundary of the target mass range * @param maxMass * Upper boundary of the target mass range * @param mfRange * A range of elemental compositions defining the search space * @throws IllegalArgumentException * In case some of the isotopes in mfRange has undefined exact * mass or in case illegal parameters are provided (e.g., * negative mass values or empty MolecularFormulaRange) * @see MolecularFormulaRange */ RoundRobinFormulaGenerator(final IChemObjectBuilder builder, final double minMass, final double maxMass, final MolecularFormulaRange mfRange) { this.builder = builder; final List<IIsotope> isotopes = new ArrayList<>(mfRange.getIsotopeCount()); for (IIsotope iso : mfRange.isotopes()) { if (mfRange.getIsotopeCountMin(iso) >= 0 && mfRange.getIsotopeCountMax(iso) > 0) isotopes.add(iso); } this.decomposer = DecomposerFactory.getInstance().getDecomposerFor(isotopes.toArray(new IIsotope[isotopes.size()])).decomposeIterator(minMass, maxMass, mfRange); this.done = false; this.mfRange = mfRange; }
d = gcd(firstLongVal, weights.get(j).getIntegerMass()); for (int p = 0; p < d; p++) { // Need to start d Round Robin loops if (p == 0) {
private void computeLCMs() { final ChemicalElement first = weights.get(0); first.setL(1); first.setLcm(first.getIntegerMass()); for (int i = 1; i < weights.size(); i++) { final ChemicalElement weight = weights.get(i); int temp = first.getIntegerMass() / gcd(first.getIntegerMass(), weight.getIntegerMass()); weight.setL(temp); weight.setLcm(temp * weight.getIntegerMass()); } }